Merge "Copy tests for archives to DocumentsUI." into nyc-andromeda-dev
diff --git a/src/com/android/documentsui/archives/Archive.java b/src/com/android/documentsui/archives/Archive.java
index 35ecb54..e7a9f79 100644
--- a/src/com/android/documentsui/archives/Archive.java
+++ b/src/com/android/documentsui/archives/Archive.java
@@ -171,6 +171,7 @@
             @Nullable Uri notificationUri)
             throws IOException {
         File snapshotFile = null;
+        // TODO: Do not create a snapshot if a seekable file descriptor is passed.
         try {
             // Create a copy of the archive, as ZipFile doesn't operate on streams.
             // Moreover, ZipInputStream would be inefficient for large files on
@@ -267,6 +268,14 @@
             return false;
         }
 
+        // TODO: Include the fake '/' entry in mEntries, and remove this check.
+        // Fake entries are for directories which do not have entries in the ZIP
+        // archive, but are reachable. Eg. /a, /a/b and /a/b/c for a single-entry
+        // archive containing /a/b/c/file.txt.
+        if (parsedParentId.mPath.equals("/")) {
+            return true;
+        }
+
         final ZipEntry parentEntry = mEntries.get(parsedParentId.mPath);
         if (parentEntry == null || !parentEntry.isDirectory()) {
             return false;
diff --git a/tests/Android.mk b/tests/Android.mk
index 8c2896a..9d3b64e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -1,6 +1,4 @@
-
 LOCAL_PATH := $(call my-dir)
-
 include $(CLEAR_VARS)
 
 # unittests
@@ -8,6 +6,10 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, common) \
     $(call all-java-files-under, unit) \
     $(call all-java-files-under, functional)
+
+# For testing ZIP files.
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator espresso-core guava
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
@@ -16,4 +18,3 @@
 LOCAL_CERTIFICATE := platform
 
 include $(BUILD_PACKAGE)
-
diff --git a/tests/res/raw/archive.zip b/tests/res/raw/archive.zip
new file mode 100644
index 0000000..c3b8d22
--- /dev/null
+++ b/tests/res/raw/archive.zip
Binary files differ
diff --git a/tests/res/raw/empty_dirs.zip b/tests/res/raw/empty_dirs.zip
new file mode 100644
index 0000000..1dd2251
--- /dev/null
+++ b/tests/res/raw/empty_dirs.zip
Binary files differ
diff --git a/tests/res/raw/no_dirs.zip b/tests/res/raw/no_dirs.zip
new file mode 100644
index 0000000..e178ae1
--- /dev/null
+++ b/tests/res/raw/no_dirs.zip
Binary files differ
diff --git a/tests/unit/com/android/documentsui/archives/ArchiveTest.java b/tests/unit/com/android/documentsui/archives/ArchiveTest.java
new file mode 100644
index 0000000..ba8205c
--- /dev/null
+++ b/tests/unit/com/android/documentsui/archives/ArchiveTest.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2015 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 com.android.documentsui.archives;
+
+import com.android.documentsui.archives.Archive;
+import com.android.documentsui.tests.R;
+
+import android.database.Cursor;
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.support.test.InstrumentationRegistry;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Scanner;
+
+@MediumTest
+public class ArchiveTest extends AndroidTestCase {
+    private static final Uri ARCHIVE_URI = Uri.parse("content://i/love/strawberries");
+    private static final String NOTIFICATION_URI = "content://notification-uri";
+    private Archive mArchive = null;
+    private ArchiveId mArchiveId;
+
+    public static ArchiveId createArchiveId(String path) {
+        return new ArchiveId(ARCHIVE_URI, path);
+    }
+
+    public void loadArchive(int resource) {
+        // Extract the file from resources.
+        File file = null;
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final Context testContext = InstrumentationRegistry.getContext();
+        try {
+            file = File.createTempFile("com.android.documentsui.archives.tests{",
+                    "}.zip", context.getCacheDir());
+            try (
+                final FileOutputStream outputStream =
+                        new ParcelFileDescriptor.AutoCloseOutputStream(
+                                ParcelFileDescriptor.open(
+                                        file, ParcelFileDescriptor.MODE_WRITE_ONLY));
+                final InputStream inputStream =
+                        testContext.getResources().openRawResource(resource);
+            ) {
+                final byte[] buffer = new byte[32 * 1024];
+                int bytes;
+                while ((bytes = inputStream.read(buffer)) != -1) {
+                    outputStream.write(buffer, 0, bytes);
+                }
+                outputStream.flush();
+
+            }
+            mArchive = Archive.createForParcelFileDescriptor(
+                  context,
+                  ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY),
+                  ARCHIVE_URI,
+                  Uri.parse(NOTIFICATION_URI));
+        } catch (IOException e) {
+            fail(String.valueOf(e));
+        } finally {
+            // On UNIX the file will be still available for processes which opened it, even
+            // after deleting it. Remove it ASAP, as it won't be used by anyone else.
+            if (file != null) {
+                file.delete();
+            }
+        }
+    }
+
+    @Override
+    public void tearDown() {
+        if (mArchive != null) {
+            mArchive.close();
+        }
+    }
+
+    public void testQueryChildDocument() throws IOException {
+        loadArchive(R.raw.archive);
+        final Cursor cursor = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "/").toDocumentId(), null, null);
+
+        assertTrue(cursor.moveToFirst());
+        assertEquals(new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir1",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+
+        assertTrue(cursor.moveToNext());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir2/").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir2",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+
+        assertTrue(cursor.moveToNext());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "file1.txt").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("file1.txt",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals("text/plain",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(13,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+
+        assertFalse(cursor.moveToNext());
+
+        // Check if querying children works too.
+        final Cursor childCursor = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(), null, null);
+
+        assertTrue(childCursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/cherries.txt").toDocumentId(),
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("cherries.txt",
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DISPLAY_NAME)));
+        assertEquals("text/plain",
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_MIME_TYPE)));
+        assertEquals(17,
+                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+    }
+
+    public void testQueryChildDocument_NoDirs() throws IOException {
+        loadArchive(R.raw.no_dirs);
+        final Cursor cursor = mArchive.queryChildDocuments(
+            new ArchiveId(ARCHIVE_URI, "/").toDocumentId(), null, null);
+
+        assertTrue(cursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir1",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+        assertFalse(cursor.moveToNext());
+
+        final Cursor childCursor = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(), null, null);
+
+        assertTrue(childCursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir2/").toDocumentId(),
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir2",
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+        assertFalse(childCursor.moveToNext());
+
+        final Cursor childCursor2 = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir2/").toDocumentId(),
+                null, null);
+
+        assertTrue(childCursor2.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir2/cherries.txt").toDocumentId(),
+                childCursor2.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DOCUMENT_ID)));
+        assertFalse(childCursor2.moveToNext());
+    }
+
+    public void testQueryChildDocument_EmptyDirs() throws IOException {
+        loadArchive(R.raw.empty_dirs);
+        final Cursor cursor = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "/").toDocumentId(), null, null);
+
+        assertTrue(cursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir1",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+        assertFalse(cursor.moveToNext());
+
+        final Cursor childCursor = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(), null, null);
+
+        assertTrue(childCursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir2/").toDocumentId(),
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir2",
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+
+        assertTrue(childCursor.moveToNext());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir3/").toDocumentId(),
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("dir3",
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_DISPLAY_NAME)));
+        assertEquals(Document.MIME_TYPE_DIR,
+                childCursor.getString(childCursor.getColumnIndexOrThrow(
+                        Document.COLUMN_MIME_TYPE)));
+        assertEquals(0,
+                childCursor.getInt(childCursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+        assertFalse(cursor.moveToNext());
+
+        final Cursor childCursor2 = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir2/").toDocumentId(),
+                null, null);
+        assertFalse(childCursor2.moveToFirst());
+
+        final Cursor childCursor3 = mArchive.queryChildDocuments(
+                new ArchiveId(ARCHIVE_URI, "dir1/dir3/").toDocumentId(),
+                null, null);
+        assertFalse(childCursor3.moveToFirst());
+    }
+
+    public void testGetDocumentType() throws IOException {
+        loadArchive(R.raw.archive);
+        assertEquals(Document.MIME_TYPE_DIR, mArchive.getDocumentType(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId()));
+        assertEquals("text/plain", mArchive.getDocumentType(
+                new ArchiveId(ARCHIVE_URI, "file1.txt").toDocumentId()));
+    }
+
+    public void testIsChildDocument() throws IOException {
+        loadArchive(R.raw.archive);
+        final String documentId = new ArchiveId(ARCHIVE_URI, "/").toDocumentId();
+        assertTrue(mArchive.isChildDocument(documentId,
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId()));
+        assertFalse(mArchive.isChildDocument(documentId,
+                new ArchiveId(ARCHIVE_URI, "this-does-not-exist").toDocumentId()));
+        assertTrue(mArchive.isChildDocument(
+                new ArchiveId(ARCHIVE_URI, "dir1/").toDocumentId(),
+                new ArchiveId(ARCHIVE_URI, "dir1/cherries.txt").toDocumentId()));
+        assertTrue(mArchive.isChildDocument(documentId,
+                new ArchiveId(ARCHIVE_URI, "dir1/cherries.txt").toDocumentId()));
+    }
+
+    public void testQueryDocument() throws IOException {
+        loadArchive(R.raw.archive);
+        final Cursor cursor = mArchive.queryDocument(
+                new ArchiveId(ARCHIVE_URI, "dir2/strawberries.txt").toDocumentId(),
+                null);
+
+        assertTrue(cursor.moveToFirst());
+        assertEquals(
+                new ArchiveId(ARCHIVE_URI, "dir2/strawberries.txt").toDocumentId(),
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DOCUMENT_ID)));
+        assertEquals("strawberries.txt",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_DISPLAY_NAME)));
+        assertEquals("text/plain",
+                cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE)));
+        assertEquals(21,
+                cursor.getInt(cursor.getColumnIndexOrThrow(Document.COLUMN_SIZE)));
+    }
+
+    public void testOpenDocument() throws IOException {
+        loadArchive(R.raw.archive);
+        final ParcelFileDescriptor descriptor = mArchive.openDocument(
+                new ArchiveId(ARCHIVE_URI, "dir2/strawberries.txt").toDocumentId(),
+                "r", null /* signal */);
+        try (final ParcelFileDescriptor.AutoCloseInputStream inputStream =
+                new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
+            assertEquals("I love strawberries!", new Scanner(inputStream).nextLine());
+        }
+    }
+}