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