/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.provider; import android.content.ContentResolver; import android.content.UriPermission; import android.net.Uri; import android.os.Environment; import android.os.FileUtils; import android.os.SystemClock; import android.support.v4.provider.DocumentFile; import android.test.AndroidTestCase; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.List; /** * Tests for {@link DocumentFile} */ public class DocumentFileTest extends AndroidTestCase { private Uri treeUri; private File root; private File rootFoo; private File rootMeow; private File rootMeowCat; private File rootMeowDog; private File rootMeowBar; private static final String FOO = "foo.randomext"; private static final String MEOW = "meow"; private static final String CAT = "cat.jpg"; private static final String DOG = "DOG.PDF"; private static final String BAR = "bar.png"; @Override protected void setUp() throws Exception { super.setUp(); final ContentResolver resolver = getContext().getContentResolver(); final List<UriPermission> perms = resolver.getPersistedUriPermissions(); if (perms.isEmpty()) { throw new RuntimeException( "Failed to find outstanding grant; did you run the activity first?"); } else { treeUri = perms.get(0).getUri(); } root = Environment.getExternalStorageDirectory(); rootFoo = new File(root, FOO); rootMeow = new File(root, MEOW); rootMeowCat = new File(rootMeow, CAT); rootMeowDog = new File(rootMeow, DOG); rootMeowBar = new File(rootMeow, BAR); resetRoot(); } private void resetRoot() throws Exception { final File tmp = new File(root, "bark.pdf"); FileUtils.deleteContents(tmp); tmp.delete(); FileUtils.deleteContents(rootMeow); rootMeow.mkdir(); rootMeowBar.mkdir(); writeInt(rootFoo, 12); writeInt(rootMeowCat, 24); writeInt(rootMeowDog, 48); } private interface DocumentTest { public void exec(DocumentFile doc) throws Exception; } public void testSimple() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); assertTrue("isDirectory", doc.isDirectory()); assertFalse("isFile", doc.isFile()); assertTrue("canRead", doc.canRead()); assertTrue("canWrite", doc.canWrite()); assertTrue("exists", doc.exists()); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testTraverse() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); // Root needs to at least contain our test file and dir final DocumentFile foo = doc.findFile(FOO); final DocumentFile meow = doc.findFile(MEOW); assertTrue("isFile", foo.isFile()); assertTrue("isDirectory", meow.isDirectory()); // Traverse inside, and expect to find exact number of items DocumentFile[] docs = meow.listFiles(); assertEquals("length", 3, docs.length); final DocumentFile cat = meow.findFile(CAT); final DocumentFile dog = meow.findFile(DOG); final DocumentFile bar = meow.findFile(BAR); assertTrue("isFile", cat.isFile()); assertTrue("isFile", dog.isFile()); assertTrue("isDirectory", bar.isDirectory()); // Empty directory is empty assertEquals("length", 0, bar.listFiles().length); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testReadAndWrite() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); final DocumentFile foo = doc.findFile(FOO); assertEquals("file", 12, readInt(rootFoo)); assertEquals("uri", 12, readInt(foo.getUri())); // Underlying storage may not have sub-second resolution, so // wait a few seconds. SystemClock.sleep(2000); // Ensure provider write makes its way to disk final long beforeTime = foo.lastModified(); writeInt(foo.getUri(), 13); final long afterTime = foo.lastModified(); assertEquals("file", 13, readInt(rootFoo)); assertEquals("uri", 13, readInt(foo.getUri())); // Make sure we kicked time forward assertTrue("lastModified", afterTime > beforeTime); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testMimes() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); final DocumentFile foo = doc.findFile(FOO); final DocumentFile meow = doc.findFile(MEOW); final DocumentFile cat = meow.findFile(CAT); final DocumentFile dog = meow.findFile(DOG); final DocumentFile bar = meow.findFile(BAR); assertEquals(null, doc.getType()); assertEquals("application/octet-stream", foo.getType()); assertEquals(null, meow.getType()); assertEquals("image/jpeg", cat.getType()); assertEquals("application/pdf", dog.getType()); assertEquals(null, bar.getType()); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testCreate() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); final DocumentFile meow = doc.findFile(MEOW); assertEquals("length", 3, meow.listFiles().length); // Create file with MIME final DocumentFile newFile = meow.createFile("text/plain", "My New File"); assertEquals("My New File.txt", newFile.getName()); assertEquals("text/plain", newFile.getType()); assertTrue("isFile", newFile.isFile()); assertFalse("isDirectory", newFile.isDirectory()); assertEquals("length", 0, newFile.length()); writeInt(newFile.getUri(), 0); assertEquals("length", 4, newFile.length()); // Create raw file final DocumentFile newRaw = meow.createFile("application/octet-stream", "myrawfile"); assertEquals("myrawfile", newRaw.getName()); assertEquals("application/octet-stream", newRaw.getType()); assertTrue("isFile", newRaw.isFile()); assertFalse("isDirectory", newRaw.isDirectory()); // Create directory final DocumentFile newDir = meow.createDirectory("My New Directory.png"); assertEquals("My New Directory.png", newDir.getName()); assertFalse("isFile", newDir.isFile()); assertTrue("isDirectory", newDir.isDirectory()); assertEquals("length", 0, newDir.listFiles().length); // And overall dir grew assertEquals("length", 6, meow.listFiles().length); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testDelete() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); final DocumentFile meow = doc.findFile(MEOW); final DocumentFile cat = meow.findFile(CAT); final DocumentFile dog = meow.findFile(DOG); // Delete single file assertTrue(cat.delete()); assertNull("cat", meow.findFile(CAT)); // Other file still exists assertTrue("exists", dog.exists()); // Delete entire tree assertTrue(meow.delete()); assertNull("meow", doc.findFile(MEOW)); // Nuking tree deleted other file assertFalse("exists", dog.exists()); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } public void testRename() throws Exception { final DocumentTest test = new DocumentTest() { @Override public void exec(DocumentFile doc) throws Exception { resetRoot(); DocumentFile meow = doc.findFile(MEOW); DocumentFile cat = meow.findFile(CAT); DocumentFile dog = meow.findFile(DOG); assertTrue(dog.exists()); // Rename a file assertEquals("cat.jpg", cat.getName()); assertEquals("image/jpeg", cat.getType()); assertTrue(cat.renameTo("music.aAc")); assertEquals("music.aAc", cat.getName()); assertEquals("audio/aac", cat.getType()); // Rename a directory assertEquals("meow", meow.getName()); assertEquals(null, meow.getType()); assertTrue(meow.isDirectory()); assertEquals(3, meow.listFiles().length); assertTrue(meow.renameTo("bark.pdf")); assertEquals("bark.pdf", meow.getName()); assertEquals(null, meow.getType()); assertTrue(meow.isDirectory()); assertEquals(3, meow.listFiles().length); // Current implementation of ExternalStorageProvider invalidates // all children documents when directory is renamed. assertFalse(dog.exists()); // But we can find it again dog = meow.findFile(DOG); assertTrue(dog.exists()); } }; test.exec(DocumentFile.fromFile(root)); test.exec(DocumentFile.fromTreeUri(getContext(), treeUri)); } private void writeInt(Uri uri, int value) throws IOException { final DataOutputStream os = new DataOutputStream( getContext().getContentResolver().openOutputStream(uri)); try { os.writeInt(value); } finally { os.close(); } } private static void writeInt(File file, int value) throws IOException { final DataOutputStream os = new DataOutputStream(new FileOutputStream(file)); try { os.writeInt(value); } finally { os.close(); } } private int readInt(Uri uri) throws IOException { final DataInputStream is = new DataInputStream( getContext().getContentResolver().openInputStream(uri)); try { return is.readInt(); } finally { is.close(); } } private static int readInt(File file) throws IOException { final DataInputStream is = new DataInputStream(new FileInputStream(file)); try { return is.readInt(); } finally { is.close(); } } }