Home | History | Annotate | Download | only in archives
      1 /*
      2  * Copyright (C) 2016 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 com.android.documentsui.archives;
     18 
     19 import static junit.framework.Assert.assertEquals;
     20 import static junit.framework.Assert.assertFalse;
     21 import static junit.framework.Assert.assertNotNull;
     22 import static junit.framework.Assert.assertNull;
     23 import static junit.framework.Assert.assertTrue;
     24 import static junit.framework.Assert.fail;
     25 
     26 import android.content.ContentProviderClient;
     27 import android.content.ContentResolver;
     28 import android.content.Context;
     29 import android.database.ContentObserver;
     30 import android.database.Cursor;
     31 import android.media.ExifInterface;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.os.ParcelFileDescriptor;
     35 import android.os.RemoteException;
     36 import android.provider.DocumentsContract;
     37 import android.support.test.InstrumentationRegistry;
     38 import android.support.test.filters.MediumTest;
     39 import android.support.test.runner.AndroidJUnit4;
     40 import android.text.TextUtils;
     41 
     42 import org.junit.After;
     43 import org.junit.Before;
     44 import org.junit.Test;
     45 import org.junit.runner.RunWith;
     46 
     47 import java.util.concurrent.CountDownLatch;
     48 import java.util.concurrent.ExecutorService;
     49 import java.util.concurrent.Executors;
     50 import java.util.concurrent.TimeUnit;
     51 
     52 @RunWith(AndroidJUnit4.class)
     53 @MediumTest
     54 public class ArchivesProviderTest {
     55 
     56     private Context mContext;
     57     private ExecutorService mExecutor = null;
     58 
     59     @Before
     60     public void setUp() throws Exception {
     61         mContext = InstrumentationRegistry.getContext();
     62         mExecutor = Executors.newSingleThreadExecutor();
     63     }
     64 
     65     @After
     66     public void tearDown() throws Exception {
     67         mExecutor.shutdown();
     68         assertTrue(mExecutor.awaitTermination(3 /* timeout */, TimeUnit.SECONDS));
     69     }
     70 
     71     @Test
     72     public void testQueryRoots() throws InterruptedException, RemoteException {
     73         final ContentResolver resolver = mContext.getContentResolver();
     74         final Uri rootsUri = DocumentsContract.buildRootsUri(ArchivesProvider.AUTHORITY);
     75         try (final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
     76                 rootsUri)) {
     77             final Cursor cursor = client.query(rootsUri, null, null, null, null, null);
     78             assertNotNull("Cursor must not be null.", cursor);
     79             assertEquals(0, cursor.getCount());
     80         }
     81     }
     82 
     83     @Test
     84     public void testOpen_Success() throws InterruptedException {
     85         final Uri sourceUri = DocumentsContract.buildDocumentUri(
     86                 ResourcesProvider.AUTHORITY, "archive.zip");
     87         final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri,
     88                 ParcelFileDescriptor.MODE_READ_ONLY);
     89 
     90         final Uri childrenUri = DocumentsContract.buildChildDocumentsUri(
     91                 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri));
     92 
     93         final ContentResolver resolver = mContext.getContentResolver();
     94         final CountDownLatch latch = new CountDownLatch(1);
     95 
     96         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
     97                 archiveUri);
     98         ArchivesProvider.acquireArchive(client, archiveUri);
     99 
    100         {
    101             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    102             assertNotNull("Cursor must not be null. File not found?", cursor);
    103 
    104             assertEquals(0, cursor.getCount());
    105             final Bundle extras = cursor.getExtras();
    106             assertEquals(true, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false));
    107             assertNull(extras.getString(DocumentsContract.EXTRA_ERROR));
    108 
    109             final Uri notificationUri = cursor.getNotificationUri();
    110             assertNotNull(notificationUri);
    111 
    112             resolver.registerContentObserver(notificationUri, false, new ContentObserver(null) {
    113                 @Override
    114                 public void onChange(boolean selfChange, Uri uri) {
    115                     latch.countDown();
    116                 }
    117             });
    118         }
    119 
    120         latch.await(3, TimeUnit.SECONDS);
    121         {
    122             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    123             assertNotNull("Cursor must not be null. File not found?", cursor);
    124 
    125             assertEquals(3, cursor.getCount());
    126             final Bundle extras = cursor.getExtras();
    127             assertEquals(false, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false));
    128             assertNull(extras.getString(DocumentsContract.EXTRA_ERROR));
    129         }
    130 
    131         ArchivesProvider.releaseArchive(client, archiveUri);
    132         client.release();
    133     }
    134 
    135     @Test
    136     public void testOpen_Failure() throws InterruptedException {
    137         final Uri sourceUri = DocumentsContract.buildDocumentUri(
    138                 ResourcesProvider.AUTHORITY, "broken.zip");
    139         final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri,
    140                 ParcelFileDescriptor.MODE_READ_ONLY);
    141 
    142         final Uri childrenUri = DocumentsContract.buildChildDocumentsUri(
    143                 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri));
    144 
    145         final ContentResolver resolver = mContext.getContentResolver();
    146         final CountDownLatch latch = new CountDownLatch(1);
    147 
    148         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
    149                 archiveUri);
    150         ArchivesProvider.acquireArchive(client, archiveUri);
    151 
    152         {
    153             // TODO: Close this and any other cursor in this file.
    154             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    155             assertNotNull("Cursor must not be null. File not found?", cursor);
    156 
    157             assertEquals(0, cursor.getCount());
    158             final Bundle extras = cursor.getExtras();
    159             assertEquals(true, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false));
    160             assertNull(extras.getString(DocumentsContract.EXTRA_ERROR));
    161 
    162             final Uri notificationUri = cursor.getNotificationUri();
    163             assertNotNull(notificationUri);
    164 
    165             resolver.registerContentObserver(notificationUri, false, new ContentObserver(null) {
    166                 @Override
    167                 public void onChange(boolean selfChange, Uri uri) {
    168                     latch.countDown();
    169                 }
    170             });
    171         }
    172 
    173         latch.await(3, TimeUnit.SECONDS);
    174         {
    175             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    176             assertNotNull("Cursor must not be null. File not found?", cursor);
    177 
    178             assertEquals(0, cursor.getCount());
    179             final Bundle extras = cursor.getExtras();
    180             assertEquals(false, extras.getBoolean(DocumentsContract.EXTRA_LOADING, false));
    181             assertFalse(TextUtils.isEmpty(extras.getString(DocumentsContract.EXTRA_ERROR)));
    182         }
    183 
    184         ArchivesProvider.releaseArchive(client, archiveUri);
    185         client.release();
    186     }
    187 
    188     @Test
    189     public void testOpen_ClosesOnRelease() throws InterruptedException {
    190         final Uri sourceUri = DocumentsContract.buildDocumentUri(
    191                 ResourcesProvider.AUTHORITY, "archive.zip");
    192         final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri,
    193                 ParcelFileDescriptor.MODE_READ_ONLY);
    194 
    195         final Uri childrenUri = DocumentsContract.buildChildDocumentsUri(
    196                 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri));
    197 
    198         final ContentResolver resolver = mContext.getContentResolver();
    199 
    200         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
    201                 archiveUri);
    202 
    203         // Acquire twice to ensure that the refcount works correctly.
    204         ArchivesProvider.acquireArchive(client, archiveUri);
    205         ArchivesProvider.acquireArchive(client, archiveUri);
    206 
    207         {
    208             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    209             assertNotNull("Cursor must not be null. File not found?", cursor);
    210         }
    211 
    212         ArchivesProvider.releaseArchive(client, archiveUri);
    213 
    214         {
    215             final Cursor cursor = resolver.query(childrenUri, null, null, null, null, null);
    216             assertNotNull("Cursor must not be null. File not found?", cursor);
    217         }
    218 
    219         ArchivesProvider.releaseArchive(client, archiveUri);
    220 
    221         try {
    222             resolver.query(childrenUri, null, null, null, null, null);
    223             fail("The archive was expected to be invalid on the last release call.");
    224         } catch (IllegalStateException e) {
    225             // Expected.
    226         }
    227 
    228         client.release();
    229     }
    230 
    231     @Test
    232     public void testNoNotificationAfterAllReleased() throws InterruptedException, RemoteException {
    233         final Uri sourceUri = DocumentsContract.buildDocumentUri(
    234                 ResourcesProvider.AUTHORITY, "archive.zip");
    235         final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri,
    236                 ParcelFileDescriptor.MODE_READ_ONLY);
    237 
    238         final Uri childrenUri = DocumentsContract.buildChildDocumentsUri(
    239                 ArchivesProvider.AUTHORITY, DocumentsContract.getDocumentId(archiveUri));
    240 
    241         final ContentResolver resolver = mContext.getContentResolver();
    242 
    243         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
    244                 archiveUri);
    245 
    246         ArchivesProvider.acquireArchive(client, archiveUri);
    247         final Cursor cursor = client.query(childrenUri, null, null, null, null, null);
    248         final Bundle extra = cursor.getExtras();
    249         assertTrue(extra.getBoolean(DocumentsContract.EXTRA_LOADING, false));
    250         final Uri notificationUri = cursor.getNotificationUri();
    251 
    252         ArchivesProvider.releaseArchive(client, archiveUri);
    253         final CountDownLatch latch = new CountDownLatch(1);
    254         resolver.registerContentObserver(notificationUri, false, new ContentObserver(null) {
    255             @Override
    256             public void onChange(boolean selfChange, Uri uri) {
    257                 latch.countDown();
    258             }
    259         });
    260 
    261         // Assert that there is no notification if no one has acquired this archive and this wait
    262         // times out.
    263         assertFalse(latch.await(1, TimeUnit.SECONDS));
    264 
    265         client.release();
    266     }
    267 
    268     @Test
    269     public void testGetDocumentMetadata() throws InterruptedException, RemoteException {
    270         final Uri sourceUri = DocumentsContract.buildDocumentUri(
    271                 ResourcesProvider.AUTHORITY, "images.zip");
    272         final Uri archiveUri = ArchivesProvider.buildUriForArchive(sourceUri,
    273                 ParcelFileDescriptor.MODE_READ_ONLY);
    274 
    275         final ContentResolver resolver = mContext.getContentResolver();
    276         final ContentProviderClient client =
    277                 resolver.acquireUnstableContentProviderClient(archiveUri);
    278 
    279         ArchivesProvider.acquireArchive(client, archiveUri);
    280 
    281         Uri archivedImageUri = Uri.parse(
    282                 "content://com.android.documentsui.archives/document/content%3A%2F%2F"
    283                 + "com.android.documentsui.archives.resourcesprovider%2F"
    284                 + "document%2Fimages.zip%23268435456%23%2Ffreddy.jpg");
    285 
    286         Bundle metadata = DocumentsContract.getDocumentMetadata(client, archivedImageUri);
    287         assertNotNull(metadata);
    288         Bundle exif = metadata.getBundle(DocumentsContract.METADATA_EXIF);
    289         assertNotNull(exif);
    290 
    291         assertEquals(3036, exif.getInt(ExifInterface.TAG_IMAGE_WIDTH));
    292         assertEquals(4048, exif.getInt(ExifInterface.TAG_IMAGE_LENGTH));
    293         assertEquals("Pixel", exif.getString(ExifInterface.TAG_MODEL));
    294 
    295         ArchivesProvider.releaseArchive(client, archiveUri);
    296         client.release();
    297     }
    298 }
    299