1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.v4.provider; 18 19 import android.content.ContentResolver; 20 import android.content.UriPermission; 21 import android.net.Uri; 22 import android.os.Environment; 23 import android.os.FileUtils; 24 import android.os.SystemClock; 25 import android.support.v4.provider.DocumentFile; 26 import android.test.AndroidTestCase; 27 28 import java.io.DataInputStream; 29 import java.io.DataOutputStream; 30 import java.io.File; 31 import java.io.FileInputStream; 32 import java.io.FileOutputStream; 33 import java.io.IOException; 34 import java.util.List; 35 36 /** 37 * Tests for {@link DocumentFile} 38 */ 39 public class DocumentFileTest extends AndroidTestCase { 40 41 private Uri treeUri; 42 43 private File root; 44 private File rootFoo; 45 private File rootMeow; 46 private File rootMeowCat; 47 private File rootMeowDog; 48 private File rootMeowBar; 49 50 private static final String FOO = "foo.randomext"; 51 private static final String MEOW = "meow"; 52 private static final String CAT = "cat.jpg"; 53 private static final String DOG = "DOG.PDF"; 54 private static final String BAR = "bar.png"; 55 56 @Override 57 protected void setUp() throws Exception { 58 super.setUp(); 59 60 final ContentResolver resolver = getContext().getContentResolver(); 61 final List<UriPermission> perms = resolver.getPersistedUriPermissions(); 62 63 if (perms.isEmpty()) { 64 throw new RuntimeException( 65 "Failed to find outstanding grant; did you run the activity first?"); 66 } else { 67 treeUri = perms.get(0).getUri(); 68 } 69 70 root = Environment.getExternalStorageDirectory(); 71 rootFoo = new File(root, FOO); 72 rootMeow = new File(root, MEOW); 73 rootMeowCat = new File(rootMeow, CAT); 74 rootMeowDog = new File(rootMeow, DOG); 75 rootMeowBar = new File(rootMeow, BAR); 76 77 resetRoot(); 78 } 79 80 private void resetRoot() throws Exception { 81 final File tmp = new File(root, "bark.pdf"); 82 FileUtils.deleteContents(tmp); 83 tmp.delete(); 84 85 FileUtils.deleteContents(rootMeow); 86 rootMeow.mkdir(); 87 rootMeowBar.mkdir(); 88 89 writeInt(rootFoo, 12); 90 writeInt(rootMeowCat, 24); 91 writeInt(rootMeowDog, 48); 92 } 93 94 private interface DocumentTest { 95 public void exec(DocumentFile doc) throws Exception; 96 } 97 98 public void testSimple() throws Exception { 99 final DocumentTest test = new DocumentTest() { 100 @Override 101 public void exec(DocumentFile doc) throws Exception { 102 resetRoot(); 103 assertTrue("isDirectory", doc.isDirectory()); 104 assertFalse("isFile", doc.isFile()); 105 assertTrue("canRead", doc.canRead()); 106 assertTrue("canWrite", doc.canWrite()); 107 assertTrue("exists", doc.exists()); 108 } 109 }; 110 111 test.exec(DocumentFile.fromFile(root)); 112 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 113 } 114 115 public void testTraverse() throws Exception { 116 final DocumentTest test = new DocumentTest() { 117 @Override 118 public void exec(DocumentFile doc) throws Exception { 119 resetRoot(); 120 121 // Root needs to at least contain our test file and dir 122 final DocumentFile foo = doc.findFile(FOO); 123 final DocumentFile meow = doc.findFile(MEOW); 124 assertTrue("isFile", foo.isFile()); 125 assertTrue("isDirectory", meow.isDirectory()); 126 127 // Traverse inside, and expect to find exact number of items 128 DocumentFile[] docs = meow.listFiles(); 129 assertEquals("length", 3, docs.length); 130 131 final DocumentFile cat = meow.findFile(CAT); 132 final DocumentFile dog = meow.findFile(DOG); 133 final DocumentFile bar = meow.findFile(BAR); 134 assertTrue("isFile", cat.isFile()); 135 assertTrue("isFile", dog.isFile()); 136 assertTrue("isDirectory", bar.isDirectory()); 137 138 // Empty directory is empty 139 assertEquals("length", 0, bar.listFiles().length); 140 } 141 }; 142 143 test.exec(DocumentFile.fromFile(root)); 144 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 145 } 146 147 public void testReadAndWrite() throws Exception { 148 final DocumentTest test = new DocumentTest() { 149 @Override 150 public void exec(DocumentFile doc) throws Exception { 151 resetRoot(); 152 153 final DocumentFile foo = doc.findFile(FOO); 154 assertEquals("file", 12, readInt(rootFoo)); 155 assertEquals("uri", 12, readInt(foo.getUri())); 156 157 // Underlying storage may not have sub-second resolution, so 158 // wait a few seconds. 159 SystemClock.sleep(2000); 160 161 // Ensure provider write makes its way to disk 162 final long beforeTime = foo.lastModified(); 163 writeInt(foo.getUri(), 13); 164 final long afterTime = foo.lastModified(); 165 166 assertEquals("file", 13, readInt(rootFoo)); 167 assertEquals("uri", 13, readInt(foo.getUri())); 168 169 // Make sure we kicked time forward 170 assertTrue("lastModified", afterTime > beforeTime); 171 } 172 }; 173 174 test.exec(DocumentFile.fromFile(root)); 175 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 176 } 177 178 public void testMimes() throws Exception { 179 final DocumentTest test = new DocumentTest() { 180 @Override 181 public void exec(DocumentFile doc) throws Exception { 182 resetRoot(); 183 184 final DocumentFile foo = doc.findFile(FOO); 185 final DocumentFile meow = doc.findFile(MEOW); 186 final DocumentFile cat = meow.findFile(CAT); 187 final DocumentFile dog = meow.findFile(DOG); 188 final DocumentFile bar = meow.findFile(BAR); 189 190 assertEquals(null, doc.getType()); 191 assertEquals("application/octet-stream", foo.getType()); 192 assertEquals(null, meow.getType()); 193 assertEquals("image/jpeg", cat.getType()); 194 assertEquals("application/pdf", dog.getType()); 195 assertEquals(null, bar.getType()); 196 } 197 }; 198 199 test.exec(DocumentFile.fromFile(root)); 200 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 201 } 202 203 public void testCreate() throws Exception { 204 final DocumentTest test = new DocumentTest() { 205 @Override 206 public void exec(DocumentFile doc) throws Exception { 207 resetRoot(); 208 209 final DocumentFile meow = doc.findFile(MEOW); 210 assertEquals("length", 3, meow.listFiles().length); 211 212 // Create file with MIME 213 final DocumentFile newFile = meow.createFile("text/plain", "My New File"); 214 assertEquals("My New File.txt", newFile.getName()); 215 assertEquals("text/plain", newFile.getType()); 216 assertTrue("isFile", newFile.isFile()); 217 assertFalse("isDirectory", newFile.isDirectory()); 218 219 assertEquals("length", 0, newFile.length()); 220 writeInt(newFile.getUri(), 0); 221 assertEquals("length", 4, newFile.length()); 222 223 // Create raw file 224 final DocumentFile newRaw = meow.createFile("application/octet-stream", 225 "myrawfile"); 226 assertEquals("myrawfile", newRaw.getName()); 227 assertEquals("application/octet-stream", newRaw.getType()); 228 assertTrue("isFile", newRaw.isFile()); 229 assertFalse("isDirectory", newRaw.isDirectory()); 230 231 // Create directory 232 final DocumentFile newDir = meow.createDirectory("My New Directory.png"); 233 assertEquals("My New Directory.png", newDir.getName()); 234 assertFalse("isFile", newDir.isFile()); 235 assertTrue("isDirectory", newDir.isDirectory()); 236 assertEquals("length", 0, newDir.listFiles().length); 237 238 // And overall dir grew 239 assertEquals("length", 6, meow.listFiles().length); 240 } 241 }; 242 243 test.exec(DocumentFile.fromFile(root)); 244 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 245 } 246 247 public void testDelete() throws Exception { 248 final DocumentTest test = new DocumentTest() { 249 @Override 250 public void exec(DocumentFile doc) throws Exception { 251 resetRoot(); 252 253 final DocumentFile meow = doc.findFile(MEOW); 254 final DocumentFile cat = meow.findFile(CAT); 255 final DocumentFile dog = meow.findFile(DOG); 256 257 // Delete single file 258 assertTrue(cat.delete()); 259 assertNull("cat", meow.findFile(CAT)); 260 261 // Other file still exists 262 assertTrue("exists", dog.exists()); 263 264 // Delete entire tree 265 assertTrue(meow.delete()); 266 assertNull("meow", doc.findFile(MEOW)); 267 268 // Nuking tree deleted other file 269 assertFalse("exists", dog.exists()); 270 } 271 }; 272 273 test.exec(DocumentFile.fromFile(root)); 274 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 275 } 276 277 public void testRename() throws Exception { 278 final DocumentTest test = new DocumentTest() { 279 @Override 280 public void exec(DocumentFile doc) throws Exception { 281 resetRoot(); 282 283 DocumentFile meow = doc.findFile(MEOW); 284 DocumentFile cat = meow.findFile(CAT); 285 DocumentFile dog = meow.findFile(DOG); 286 assertTrue(dog.exists()); 287 288 // Rename a file 289 assertEquals("cat.jpg", cat.getName()); 290 assertEquals("image/jpeg", cat.getType()); 291 292 assertTrue(cat.renameTo("music.aAc")); 293 assertEquals("music.aAc", cat.getName()); 294 assertEquals("audio/aac", cat.getType()); 295 296 // Rename a directory 297 assertEquals("meow", meow.getName()); 298 assertEquals(null, meow.getType()); 299 assertTrue(meow.isDirectory()); 300 assertEquals(3, meow.listFiles().length); 301 302 assertTrue(meow.renameTo("bark.pdf")); 303 assertEquals("bark.pdf", meow.getName()); 304 assertEquals(null, meow.getType()); 305 assertTrue(meow.isDirectory()); 306 assertEquals(3, meow.listFiles().length); 307 308 // Current implementation of ExternalStorageProvider invalidates 309 // all children documents when directory is renamed. 310 assertFalse(dog.exists()); 311 312 // But we can find it again 313 dog = meow.findFile(DOG); 314 assertTrue(dog.exists()); 315 } 316 }; 317 318 test.exec(DocumentFile.fromFile(root)); 319 test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); 320 } 321 322 private void writeInt(Uri uri, int value) throws IOException { 323 final DataOutputStream os = new DataOutputStream( 324 getContext().getContentResolver().openOutputStream(uri)); 325 try { 326 os.writeInt(value); 327 } finally { 328 os.close(); 329 } 330 } 331 332 private static void writeInt(File file, int value) throws IOException { 333 final DataOutputStream os = new DataOutputStream(new FileOutputStream(file)); 334 try { 335 os.writeInt(value); 336 } finally { 337 os.close(); 338 } 339 } 340 341 private int readInt(Uri uri) throws IOException { 342 final DataInputStream is = new DataInputStream( 343 getContext().getContentResolver().openInputStream(uri)); 344 try { 345 return is.readInt(); 346 } finally { 347 is.close(); 348 } 349 } 350 351 private static int readInt(File file) throws IOException { 352 final DataInputStream is = new DataInputStream(new FileInputStream(file)); 353 try { 354 return is.readInt(); 355 } finally { 356 is.close(); 357 } 358 } 359 } 360