Home | History | Annotate | Download | only in provider
      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