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