Home | History | Annotate | Download | only in shadows
      1 package org.robolectric.shadows;
      2 
      3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
      4 
      5 import android.os.StatFs;
      6 import java.io.File;
      7 import java.util.Map;
      8 import java.util.TreeMap;
      9 import org.robolectric.annotation.Implementation;
     10 import org.robolectric.annotation.Implements;
     11 import org.robolectric.annotation.Resetter;
     12 
     13 /**
     14  * Robolectic doesn't provide actual filesystem stats; rather, it provides the ability to specify
     15  * stats values in advance.
     16  *
     17  * @see #registerStats(File, int, int, int)
     18  */
     19 @Implements(StatFs.class)
     20 public class ShadowStatFs {
     21   public static final int BLOCK_SIZE = 4096;
     22   private static final Stats DEFAULT_STATS = new Stats(0, 0, 0);
     23   private static TreeMap<String, Stats> stats = new TreeMap<>();
     24   private Stats stat;
     25 
     26   @Implementation
     27   protected void __constructor__(String path) {
     28     restat(path);
     29   }
     30 
     31   @Implementation
     32   protected int getBlockSize() {
     33     return BLOCK_SIZE;
     34   }
     35 
     36   @Implementation
     37   protected int getBlockCount() {
     38     return stat.blockCount;
     39   }
     40 
     41   @Implementation
     42   protected int getFreeBlocks() {
     43     return stat.freeBlocks;
     44   }
     45 
     46   @Implementation(minSdk = JELLY_BEAN_MR2)
     47   protected long getFreeBlocksLong() {
     48     return stat.freeBlocks;
     49   }
     50 
     51   @Implementation(minSdk = JELLY_BEAN_MR2)
     52   protected long getFreeBytes() {
     53     return getBlockSizeLong() * getFreeBlocksLong();
     54   }
     55 
     56   @Implementation(minSdk = JELLY_BEAN_MR2)
     57   protected long getAvailableBytes() {
     58     return getBlockSizeLong() * getAvailableBlocksLong();
     59   }
     60 
     61   @Implementation(minSdk = JELLY_BEAN_MR2)
     62   protected long getTotalBytes() {
     63     return getBlockSizeLong() * getBlockCountLong();
     64   }
     65 
     66   @Implementation
     67   protected int getAvailableBlocks() {
     68     return stat.availableBlocks;
     69   }
     70 
     71   @Implementation
     72   protected void restat(String path) {
     73     Map.Entry<String, Stats> mapEntry = stats.floorEntry(path);
     74     for (;;) {
     75       // We will hit all matching paths, longest one first. We may hit non-matching paths before we
     76       // find the right one.
     77       if (mapEntry == null) {
     78         stat = DEFAULT_STATS;
     79         return;
     80       }
     81       String key = mapEntry.getKey();
     82       if (path.startsWith(key)) {
     83         stat = mapEntry.getValue();
     84         return;
     85       }
     86       mapEntry = stats.lowerEntry(key);
     87     }
     88   }
     89 
     90   /** Robolectric always uses a block size of `4096`. */
     91   @Implementation(minSdk = JELLY_BEAN_MR2)
     92   protected long getBlockSizeLong() {
     93     return BLOCK_SIZE;
     94   }
     95 
     96   @Implementation(minSdk = JELLY_BEAN_MR2)
     97   protected long getBlockCountLong() {
     98     return stat.blockCount;
     99   }
    100 
    101   @Implementation(minSdk = JELLY_BEAN_MR2)
    102   protected long getAvailableBlocksLong() {
    103     return stat.availableBlocks;
    104   }
    105 
    106   /**
    107    * Register stats for a path, which will be used when a matching {@link StatFs} instance is
    108    * created.
    109    *
    110    * @param path path to the file
    111    * @param blockCount number of blocks
    112    * @param freeBlocks number of free blocks
    113    * @param availableBlocks number of available blocks
    114    */
    115   public static void registerStats(File path, int blockCount, int freeBlocks, int availableBlocks) {
    116     registerStats(path.getAbsolutePath(), blockCount, freeBlocks, availableBlocks);
    117   }
    118 
    119   /**
    120    * Register stats for a path, which will be used when a matching {@link StatFs} instance is
    121    * created.  A {@link StatFs} instance matches if it extends path. If several registered paths
    122    * match, we pick the longest one.
    123    *
    124    * @param path path to the file
    125    * @param blockCount number of blocks
    126    * @param freeBlocks number of free blocks
    127    * @param availableBlocks number of available blocks
    128    */
    129   public static void registerStats(String path, int blockCount, int freeBlocks,
    130       int availableBlocks) {
    131     stats.put(path, new Stats(blockCount, freeBlocks, availableBlocks));
    132   }
    133 
    134   @Resetter
    135   public static void reset() {
    136     stats.clear();
    137   }
    138 
    139   private static class Stats {
    140     Stats(int blockCount, int freeBlocks, int availableBlocks) {
    141       this.blockCount = blockCount;
    142       this.freeBlocks = freeBlocks;
    143       this.availableBlocks = availableBlocks;
    144     }
    145     int blockCount, freeBlocks, availableBlocks;
    146   }
    147 }
    148