1 /* 2 * Copyright (C) 2015 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 com.android.documentsui.archives.ReadableArchive; 20 import com.android.documentsui.tests.R; 21 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.os.ParcelFileDescriptor; 26 import android.provider.DocumentsContract.Document; 27 import android.support.test.InstrumentationRegistry; 28 import android.system.ErrnoException; 29 import android.system.Os; 30 import android.system.OsConstants; 31 import android.test.AndroidTestCase; 32 import android.test.suitebuilder.annotation.MediumTest; 33 34 import java.io.File; 35 import java.io.FileOutputStream; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.util.Scanner; 39 import java.util.concurrent.ExecutorService; 40 import java.util.concurrent.Executors; 41 import java.util.concurrent.TimeUnit; 42 43 @MediumTest 44 public class ReadableArchiveTest extends AndroidTestCase { 45 private static final Uri ARCHIVE_URI = Uri.parse("content://i/love/strawberries"); 46 private static final String NOTIFICATION_URI = 47 "content://com.android.documentsui.archives/notification-uri"; 48 private ExecutorService mExecutor = null; 49 private Archive mArchive = null; 50 private TestUtils mTestUtils = null; 51 52 @Override 53 public void setUp() throws Exception { 54 super.setUp(); 55 mExecutor = Executors.newSingleThreadExecutor(); 56 mTestUtils = new TestUtils(InstrumentationRegistry.getTargetContext(), 57 InstrumentationRegistry.getContext(), mExecutor); 58 } 59 60 @Override 61 public void tearDown() throws Exception { 62 mExecutor.shutdown(); 63 assertTrue(mExecutor.awaitTermination(3 /* timeout */, TimeUnit.SECONDS)); 64 if (mArchive != null) { 65 mArchive.close(); 66 } 67 super.tearDown(); 68 } 69 70 public static ArchiveId createArchiveId(String path) { 71 return new ArchiveId(ARCHIVE_URI, ParcelFileDescriptor.MODE_READ_ONLY, path); 72 } 73 74 public void loadArchive(ParcelFileDescriptor descriptor) throws IOException { 75 mArchive = ReadableArchive.createForParcelFileDescriptor( 76 InstrumentationRegistry.getTargetContext(), 77 descriptor, 78 ARCHIVE_URI, 79 ParcelFileDescriptor.MODE_READ_ONLY, 80 Uri.parse(NOTIFICATION_URI)); 81 } 82 83 public void testQueryChildDocument() throws IOException { 84 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 85 final Cursor cursor = mArchive.queryChildDocuments( 86 createArchiveId("/").toDocumentId(), null, null); 87 88 assertTrue(cursor.moveToFirst()); 89 assertEquals( 90 createArchiveId("/file1.txt").toDocumentId(), 91 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 92 assertEquals("file1.txt", 93 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 94 assertEquals("text/plain", 95 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 96 assertEquals(13, 97 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 98 99 assertTrue(cursor.moveToNext()); 100 assertEquals(createArchiveId("/dir1/").toDocumentId(), 101 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 102 assertEquals("dir1", 103 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 104 assertEquals(Document.MIME_TYPE_DIR, 105 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 106 assertEquals(0, 107 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 108 109 assertTrue(cursor.moveToNext()); 110 assertEquals( 111 createArchiveId("/dir2/").toDocumentId(), 112 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 113 assertEquals("dir2", 114 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 115 assertEquals(Document.MIME_TYPE_DIR, 116 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 117 assertEquals(0, 118 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 119 120 assertFalse(cursor.moveToNext()); 121 122 // Check if querying children works too. 123 final Cursor childCursor = mArchive.queryChildDocuments( 124 createArchiveId("/dir1/").toDocumentId(), null, null); 125 126 assertTrue(childCursor.moveToFirst()); 127 assertEquals( 128 createArchiveId("/dir1/cherries.txt").toDocumentId(), 129 childCursor.getString(childCursor.getColumnIndexOrThrow( 130 Document.COLUMN_DOCUMENT_ID))); 131 assertEquals("cherries.txt", 132 childCursor.getString(childCursor.getColumnIndexOrThrow( 133 Document.COLUMN_DISPLAY_NAME))); 134 assertEquals("text/plain", 135 childCursor.getString(childCursor.getColumnIndexOrThrow( 136 Document.COLUMN_MIME_TYPE))); 137 assertEquals(17, 138 childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 139 } 140 141 public void testQueryChildDocument_NoDirs() throws IOException { 142 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.no_dirs)); 143 final Cursor cursor = mArchive.queryChildDocuments( 144 createArchiveId("/").toDocumentId(), null, null); 145 146 assertTrue(cursor.moveToFirst()); 147 assertEquals( 148 createArchiveId("/dir1/").toDocumentId(), 149 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 150 assertEquals("dir1", 151 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 152 assertEquals(Document.MIME_TYPE_DIR, 153 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 154 assertEquals(0, 155 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 156 assertFalse(cursor.moveToNext()); 157 158 final Cursor childCursor = mArchive.queryChildDocuments( 159 createArchiveId("/dir1/").toDocumentId(), null, null); 160 161 assertTrue(childCursor.moveToFirst()); 162 assertEquals( 163 createArchiveId("/dir1/dir2/").toDocumentId(), 164 childCursor.getString(childCursor.getColumnIndexOrThrow( 165 Document.COLUMN_DOCUMENT_ID))); 166 assertEquals("dir2", 167 childCursor.getString(childCursor.getColumnIndexOrThrow( 168 Document.COLUMN_DISPLAY_NAME))); 169 assertEquals(Document.MIME_TYPE_DIR, 170 childCursor.getString(childCursor.getColumnIndexOrThrow( 171 Document.COLUMN_MIME_TYPE))); 172 assertEquals(0, 173 childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 174 assertFalse(childCursor.moveToNext()); 175 176 final Cursor childCursor2 = mArchive.queryChildDocuments( 177 createArchiveId("/dir1/dir2/").toDocumentId(), 178 null, null); 179 180 assertTrue(childCursor2.moveToFirst()); 181 assertEquals( 182 createArchiveId("/dir1/dir2/cherries.txt").toDocumentId(), 183 childCursor2.getString(childCursor.getColumnIndexOrThrow( 184 Document.COLUMN_DOCUMENT_ID))); 185 assertFalse(childCursor2.moveToNext()); 186 } 187 188 public void testQueryChildDocument_EmptyDirs() throws IOException { 189 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.empty_dirs)); 190 final Cursor cursor = mArchive.queryChildDocuments( 191 createArchiveId("/").toDocumentId(), null, null); 192 193 assertTrue(cursor.moveToFirst()); 194 assertEquals( 195 createArchiveId("/dir1/").toDocumentId(), 196 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 197 assertEquals("dir1", 198 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 199 assertEquals(Document.MIME_TYPE_DIR, 200 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 201 assertEquals(0, 202 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 203 assertFalse(cursor.moveToNext()); 204 205 final Cursor childCursor = mArchive.queryChildDocuments( 206 createArchiveId("/dir1/").toDocumentId(), null, null); 207 208 assertTrue(childCursor.moveToFirst()); 209 assertEquals( 210 createArchiveId("/dir1/dir2/").toDocumentId(), 211 childCursor.getString(childCursor.getColumnIndexOrThrow( 212 Document.COLUMN_DOCUMENT_ID))); 213 assertEquals("dir2", 214 childCursor.getString(childCursor.getColumnIndexOrThrow( 215 Document.COLUMN_DISPLAY_NAME))); 216 assertEquals(Document.MIME_TYPE_DIR, 217 childCursor.getString(childCursor.getColumnIndexOrThrow( 218 Document.COLUMN_MIME_TYPE))); 219 assertEquals(0, 220 childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 221 222 assertTrue(childCursor.moveToNext()); 223 assertEquals( 224 createArchiveId("/dir1/dir3/").toDocumentId(), 225 childCursor.getString(childCursor.getColumnIndexOrThrow( 226 Document.COLUMN_DOCUMENT_ID))); 227 assertEquals("dir3", 228 childCursor.getString(childCursor.getColumnIndexOrThrow( 229 Document.COLUMN_DISPLAY_NAME))); 230 assertEquals(Document.MIME_TYPE_DIR, 231 childCursor.getString(childCursor.getColumnIndexOrThrow( 232 Document.COLUMN_MIME_TYPE))); 233 assertEquals(0, 234 childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 235 assertFalse(cursor.moveToNext()); 236 237 final Cursor childCursor2 = mArchive.queryChildDocuments( 238 createArchiveId("/dir1/dir2/").toDocumentId(), 239 null, null); 240 assertFalse(childCursor2.moveToFirst()); 241 242 final Cursor childCursor3 = mArchive.queryChildDocuments( 243 createArchiveId("/dir1/dir3/").toDocumentId(), 244 null, null); 245 assertFalse(childCursor3.moveToFirst()); 246 } 247 248 public void testGetDocumentType() throws IOException { 249 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 250 assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType( 251 createArchiveId("/dir1/").toDocumentId())); 252 assertEquals("text/plain", mArchive.getDocumentType( 253 createArchiveId("/file1.txt").toDocumentId())); 254 } 255 256 public void testIsChildDocument() throws IOException { 257 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 258 final String documentId = createArchiveId("/").toDocumentId(); 259 assertTrue(mArchive.isChildDocument(documentId, 260 createArchiveId("/dir1/").toDocumentId())); 261 assertFalse(mArchive.isChildDocument(documentId, 262 createArchiveId("/this-does-not-exist").toDocumentId())); 263 assertTrue(mArchive.isChildDocument( 264 createArchiveId("/dir1/").toDocumentId(), 265 createArchiveId("/dir1/cherries.txt").toDocumentId())); 266 assertTrue(mArchive.isChildDocument(documentId, 267 createArchiveId("/dir1/cherries.txt").toDocumentId())); 268 } 269 270 public void testQueryDocument() throws IOException { 271 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 272 final Cursor cursor = mArchive.queryDocument( 273 createArchiveId("/dir2/strawberries.txt").toDocumentId(), 274 null); 275 276 assertTrue(cursor.moveToFirst()); 277 assertEquals( 278 createArchiveId("/dir2/strawberries.txt").toDocumentId(), 279 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID))); 280 assertEquals("strawberries.txt", 281 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME))); 282 assertEquals("text/plain", 283 cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE))); 284 assertEquals(21, 285 cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE))); 286 } 287 288 public void testOpenDocument() throws IOException, ErrnoException { 289 loadArchive(mTestUtils.getSeekableDescriptor(R.raw.archive)); 290 commonTestOpenDocument(); 291 } 292 293 public void testOpenDocument_NonSeekable() throws IOException, ErrnoException { 294 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 295 commonTestOpenDocument(); 296 } 297 298 // Common part of testOpenDocument and testOpenDocument_NonSeekable. 299 void commonTestOpenDocument() throws IOException, ErrnoException { 300 final ParcelFileDescriptor descriptor = mArchive.openDocument( 301 createArchiveId("/dir2/strawberries.txt").toDocumentId(), 302 "r", null /* signal */); 303 assertTrue(Archive.canSeek(descriptor)); 304 try (final ParcelFileDescriptor.AutoCloseInputStream inputStream = 305 new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) { 306 Os.lseek(descriptor.getFileDescriptor(), "I love ".length(), OsConstants.SEEK_SET); 307 assertEquals("strawberries!", new Scanner(inputStream).nextLine()); 308 Os.lseek(descriptor.getFileDescriptor(), 0, OsConstants.SEEK_SET); 309 assertEquals("I love strawberries!", new Scanner(inputStream).nextLine()); 310 } 311 } 312 313 public void testCanSeek() throws IOException { 314 assertTrue(Archive.canSeek(mTestUtils.getSeekableDescriptor(R.raw.archive))); 315 assertFalse(Archive.canSeek(mTestUtils.getNonSeekableDescriptor(R.raw.archive))); 316 } 317 318 public void testBrokenArchive() throws IOException { 319 loadArchive(mTestUtils.getNonSeekableDescriptor(R.raw.archive)); 320 final Cursor cursor = mArchive.queryChildDocuments( 321 createArchiveId("/").toDocumentId(), null, null); 322 } 323 } 324