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