Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c3c6954 authored by Steve McKay's avatar Steve McKay
Browse files

Use StubProvider for functional tests.

Add a "UiBot" class for driving the UI from tests.
Add a "DocumentsProviderHelper" class for convenient test doc setup.
Update FilesActivityUiTest to use "TargetContext" which is
    necessary in order to perform ContentProvider
    operations.
Fix a bug where CopyTest relied on implicit order of roots.
Don't include guava in tests...since it breaks functional tests (incompatible class def).
Add test coverage for:
- basic roots list.
- basic files list.
- Live updates to files list.
- basic delete operations.

Bug: 24988170
Change-Id: I2ec01a5e1a474314cb33efb6e92df0f61dfcc1da
NOTE: This is currently broken at the point I try to init files in the stub roots.
parent f789ef7e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava ub-uiautomator
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target ub-uiautomator

LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
+101 −107
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.net.Uri;
import android.os.Parcelable;
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.test.MoreAsserts;
import android.test.ServiceTestCase;
import android.test.mock.MockContentResolver;
@@ -36,6 +37,7 @@ import android.util.Log;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.DocumentStack;
import com.android.documentsui.model.RootInfo;

import com.google.common.collect.Lists;

import libcore.io.IoUtils;
@@ -52,70 +54,19 @@ import java.util.concurrent.TimeoutException;

public class CopyTest extends ServiceTestCase<CopyService> {

    /**
     * A test resolver that enables this test suite to listen for notifications that mark when copy
     * operations are done.
     */
    class TestContentResolver extends MockContentResolver {
        private CountDownLatch mReadySignal;
        private CountDownLatch mNotificationSignal;

        public TestContentResolver() {
            mReadySignal = new CountDownLatch(1);
        }

        /**
         * Wait for the given number of files to be copied to destination. Times out after 1 sec.
         */
        public void waitForChanges(int count) throws Exception {
            // Wait for no more than 1 second by default.
            waitForChanges(count, 1000);
        }

        /**
         * Wait for files to be copied to destination.
         *
         * @param count Number of files to wait for.
         * @param timeOut Timeout in ms. TimeoutException will be thrown if this function times out.
         */
        public void waitForChanges(int count, int timeOut) throws Exception {
            mNotificationSignal = new CountDownLatch(count);
            // Signal that the test is now waiting for files.
            mReadySignal.countDown();
            if (!mNotificationSignal.await(timeOut, TimeUnit.MILLISECONDS)) {
                throw new TimeoutException("Timed out waiting for file operations to complete.");
            }
        }

        @Override
        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
            // Wait until the test is ready to receive file notifications.
            try {
                mReadySignal.await();
            } catch (InterruptedException e) {
                Log.d(TAG, "Interrupted while waiting for file copy readiness");
                Thread.currentThread().interrupt();
            }
            if (DocumentsContract.isDocumentUri(mContext, uri)) {
                Log.d(TAG, "Notification: " + uri);
                // Watch for document URI change notifications - this signifies the end of a copy.
                mNotificationSignal.countDown();
            }
        }
    };

    public CopyTest() {
        super(CopyService.class);
    }

    private static String AUTHORITY = "com.android.documentsui.stubprovider";
    private static String DST = "sd1";
    private static String SRC = "sd0";
    private static String SRC_ROOT = StubProvider.ROOT_0_ID;
    private static String DST_ROOT = StubProvider.ROOT_1_ID;
    private static String TAG = "CopyTest";
    private List<RootInfo> mRoots;

    private Context mContext;
    private TestContentResolver mResolver;
    private ContentProviderClient mClient;
    private DocumentsProviderHelper mDocHelper;
    private StubProvider mStorage;
    private Context mSystemContext;

@@ -129,18 +80,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        // Reset the stub provider's storage.
        mStorage.clearCacheAndBuildRoots();

        mRoots = Lists.newArrayList();
        Uri queryUri = DocumentsContract.buildRootsUri(AUTHORITY);
        Cursor cursor = null;
        try {
            cursor = mClient.query(queryUri, null, null, null, null);
            while (cursor.moveToNext()) {
                mRoots.add(RootInfo.fromRootsCursor(AUTHORITY, cursor));
            }
        } finally {
            IoUtils.closeQuietly(cursor);
        }

        mDocHelper = new DocumentsProviderHelper(AUTHORITY, mClient);
    }

    @Override
@@ -154,7 +94,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {
     */
    public void testCopyFile() throws Exception {
        String srcPath = "/test0.txt";
        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                "The five boxing wizards jump quickly".getBytes());

        assertDstFileCountEquals(0);
@@ -172,7 +112,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {
    public void testMoveFile() throws Exception {
        String srcPath = "/test0.txt";
        String testContent = "The five boxing wizards jump quickly";
        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain", testContent.getBytes());
        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes());

        assertDstFileCountEquals(0);

@@ -185,9 +125,9 @@ public class CopyTest extends ServiceTestCase<CopyService> {

        // Verify that one file was moved; check file contents.
        assertDstFileCountEquals(1);
        assertDoesNotExist(SRC, srcPath);
        assertDoesNotExist(SRC_ROOT, srcPath);

        byte[] dstContent = readFile(DST, srcPath);
        byte[] dstContent = readFile(DST_ROOT, srcPath);
        MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent);
    }

@@ -206,9 +146,9 @@ public class CopyTest extends ServiceTestCase<CopyService> {
                "/test2.txt"
        };
        List<Uri> testFiles = Lists.newArrayList(
                mStorage.createFile(SRC, srcPaths[0], "text/plain", testContent[0].getBytes()),
                mStorage.createFile(SRC, srcPaths[1], "text/plain", testContent[1].getBytes()),
                mStorage.createFile(SRC, srcPaths[2], "text/plain", testContent[2].getBytes()));
                mStorage.createFile(SRC_ROOT, srcPaths[0], "text/plain", testContent[0].getBytes()),
                mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()),
                mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes()));

        assertDstFileCountEquals(0);

@@ -226,7 +166,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {

    public void testCopyEmptyDir() throws Exception {
        String srcPath = "/emptyDir";
        Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
        Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
                null);

        assertDstFileCountEquals(0);
@@ -239,13 +179,13 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        assertDstFileCountEquals(1);

        // Verify that the dst exists and is a directory.
        File dst = mStorage.getFile(DST, srcPath);
        File dst = mStorage.getFile(DST_ROOT, srcPath);
        assertTrue(dst.isDirectory());
    }

    public void testMoveEmptyDir() throws Exception {
        String srcPath = "/emptyDir";
        Uri testDir = mStorage.createFile(SRC, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
        Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR,
                null);

        assertDstFileCountEquals(0);
@@ -260,11 +200,11 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        assertDstFileCountEquals(1);

        // Verify that the dst exists and is a directory.
        File dst = mStorage.getFile(DST, srcPath);
        File dst = mStorage.getFile(DST_ROOT, srcPath);
        assertTrue(dst.isDirectory());

        // Verify that the src was cleaned up.
        assertDoesNotExist(SRC, srcPath);
        assertDoesNotExist(SRC_ROOT, srcPath);
    }

    public void testMovePopulatedDir() throws Exception {
@@ -280,11 +220,11 @@ public class CopyTest extends ServiceTestCase<CopyService> {
                srcDir + "/test2.txt"
        };
        // Create test dir; put some files in it.
        Uri testDir = mStorage.createFile(SRC, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
        Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
                null);
        mStorage.createFile(SRC, srcFiles[0], "text/plain", testContent[0].getBytes());
        mStorage.createFile(SRC, srcFiles[1], "text/plain", testContent[1].getBytes());
        mStorage.createFile(SRC, srcFiles[2], "text/plain", testContent[2].getBytes());
        mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
        mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
        mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());

        Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir));
        moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE);
@@ -295,24 +235,24 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        mResolver.waitForChanges(11);

        // Check the content of the moved files.
        File dst = mStorage.getFile(DST, srcDir);
        File dst = mStorage.getFile(DST_ROOT, srcDir);
        assertTrue(dst.isDirectory());
        for (int i = 0; i < testContent.length; ++i) {
            byte[] dstContent = readFile(DST, srcFiles[i]);
            byte[] dstContent = readFile(DST_ROOT, srcFiles[i]);
            MoreAsserts.assertEquals("Copied file contents differ", testContent[i].getBytes(),
                    dstContent);
        }

        // Check that the src files were removed.
        assertDoesNotExist(SRC, srcDir);
        assertDoesNotExist(SRC_ROOT, srcDir);
        for (String srcFile : srcFiles) {
            assertDoesNotExist(SRC, srcFile);
            assertDoesNotExist(SRC_ROOT, srcFile);
        }
    }

    public void testCopyFileWithReadErrors() throws Exception {
        String srcPath = "/test0.txt";
        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                "The five boxing wizards jump quickly".getBytes());

        assertDstFileCountEquals(0);
@@ -330,7 +270,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {

    public void testMoveFileWithReadErrors() throws Exception {
        String srcPath = "/test0.txt";
        Uri testFile = mStorage.createFile(SRC, srcPath, "text/plain",
        Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain",
                "The five boxing wizards jump quickly".getBytes());

        assertDstFileCountEquals(0);
@@ -352,7 +292,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        } finally {
            // Verify that the failed copy was cleaned up, and the src file wasn't removed.
            assertDstFileCountEquals(0);
            assertExists(SRC, srcPath);
            assertExists(SRC_ROOT, srcPath);
        }
        // The asserts above didn't fail, but the CopyService did something unexpected.
        fail("Extra file operations were detected");
@@ -371,12 +311,12 @@ public class CopyTest extends ServiceTestCase<CopyService> {
                srcDir + "/test2.txt"
        };
        // Create test dir; put some files in it.
        Uri testDir = mStorage.createFile(SRC, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
        Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR,
                null);
        mStorage.createFile(SRC, srcFiles[0], "text/plain", testContent[0].getBytes());
        mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes());
        Uri errFile = mStorage
                .createFile(SRC, srcFiles[1], "text/plain", testContent[1].getBytes());
        mStorage.createFile(SRC, srcFiles[2], "text/plain", testContent[2].getBytes());
                .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes());
        mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes());

        mStorage.simulateReadErrorsForFile(errFile);

@@ -391,22 +331,22 @@ public class CopyTest extends ServiceTestCase<CopyService> {

        // Check that both the src and dst dirs exist. The src dir shouldn't have been removed,
        // because it should contain the one errFile.
        assertTrue(mStorage.getFile(SRC, srcDir).isDirectory());
        assertTrue(mStorage.getFile(DST, srcDir).isDirectory());
        assertTrue(mStorage.getFile(SRC_ROOT, srcDir).isDirectory());
        assertTrue(mStorage.getFile(DST_ROOT, srcDir).isDirectory());

        // Check the content of the moved files.
        MoreAsserts.assertEquals("Copied file contents differ", testContent[0].getBytes(),
                readFile(DST, srcFiles[0]));
                readFile(DST_ROOT, srcFiles[0]));
        MoreAsserts.assertEquals("Copied file contents differ", testContent[2].getBytes(),
                readFile(DST, srcFiles[2]));
                readFile(DST_ROOT, srcFiles[2]));

        // Check that the src files were removed.
        assertDoesNotExist(SRC, srcFiles[0]);
        assertDoesNotExist(SRC, srcFiles[2]);
        assertDoesNotExist(SRC_ROOT, srcFiles[0]);
        assertDoesNotExist(SRC_ROOT, srcFiles[2]);

        // Check that the error file was not copied over.
        assertDoesNotExist(DST, srcFiles[1]);
        assertExists(SRC, srcFiles[1]);
        assertDoesNotExist(DST_ROOT, srcFiles[1]);
        assertExists(SRC_ROOT, srcFiles[1]);
    }

    /**
@@ -414,13 +354,14 @@ public class CopyTest extends ServiceTestCase<CopyService> {
     *
     * @throws FileNotFoundException
     */
    private Intent createCopyIntent(List<Uri> srcs) throws FileNotFoundException {
    private Intent createCopyIntent(List<Uri> srcs) throws Exception {
        final ArrayList<DocumentInfo> srcDocs = Lists.newArrayList();
        for (Uri src : srcs) {
            srcDocs.add(DocumentInfo.fromUri(mResolver, src));
        }

        final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, mRoots.get(1).documentId);
        RootInfo root = mDocHelper.getRoot(DST_ROOT);
        final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId);
        DocumentStack stack = new DocumentStack();
        stack.push(DocumentInfo.fromUri(mResolver, dst));
        final Intent copyIntent = new Intent(mContext, CopyService.class);
@@ -435,8 +376,9 @@ public class CopyTest extends ServiceTestCase<CopyService> {
     * Returns a count of the files in the given directory.
     */
    private void assertDstFileCountEquals(int expected) throws RemoteException {
        RootInfo dest = mDocHelper.getRoot(DST_ROOT);
        final Uri queryUri = DocumentsContract.buildChildDocumentsUri(AUTHORITY,
                mRoots.get(1).documentId);
                dest.documentId);
        Cursor c = null;
        int count = 0;
        try {
@@ -474,8 +416,8 @@ public class CopyTest extends ServiceTestCase<CopyService> {
    }

    private void assertCopied(String path) throws Exception {
        MoreAsserts.assertEquals("Copied file contents differ", readFile(SRC, path),
                readFile(DST, path));
        MoreAsserts.assertEquals("Copied file contents differ", readFile(SRC_ROOT, path),
                readFile(DST_ROOT, path));
    }

    /**
@@ -509,4 +451,56 @@ public class CopyTest extends ServiceTestCase<CopyService> {
        mStorage.attachInfo(mContext, info);
        mResolver.addProvider(AUTHORITY, mStorage);
    }

    /**
     * A test resolver that enables this test suite to listen for notifications that mark when copy
     * operations are done.
     */
    class TestContentResolver extends MockContentResolver {
        private CountDownLatch mReadySignal;
        private CountDownLatch mNotificationSignal;

        public TestContentResolver() {
            mReadySignal = new CountDownLatch(1);
        }

        /**
         * Wait for the given number of files to be copied to destination. Times out after 1 sec.
         */
        public void waitForChanges(int count) throws Exception {
            // Wait for no more than 1 second by default.
            waitForChanges(count, 1000);
        }

        /**
         * Wait for files to be copied to destination.
         *
         * @param count Number of files to wait for.
         * @param timeOut Timeout in ms. TimeoutException will be thrown if this function times out.
         */
        public void waitForChanges(int count, int timeOut) throws Exception {
            mNotificationSignal = new CountDownLatch(count);
            // Signal that the test is now waiting for files.
            mReadySignal.countDown();
            if (!mNotificationSignal.await(timeOut, TimeUnit.MILLISECONDS)) {
                throw new TimeoutException("Timed out waiting for file operations to complete.");
            }
        }

        @Override
        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
            // Wait until the test is ready to receive file notifications.
            try {
                mReadySignal.await();
            } catch (InterruptedException e) {
                Log.d(TAG, "Interrupted while waiting for file copy readiness");
                Thread.currentThread().interrupt();
            }
            if (DocumentsContract.isDocumentUri(mContext, uri)) {
                Log.d(TAG, "Notification: " + uri);
                // Watch for document URI change notifications - this signifies the end of a copy.
                mNotificationSignal.countDown();
            }
        }
    };
}
+88 −0
Original line number Diff line number Diff line
/*
 * 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;

import static com.android.documentsui.model.DocumentInfo.getCursorString;

import android.content.ContentProviderClient;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;

import com.android.documentsui.model.RootInfo;

import libcore.io.IoUtils;

/**
 * Provides support for creation of documents in a test settings.
 */
public class DocumentsProviderHelper {

    private final ContentProviderClient mClient;
    private final String mAuthority;

    public DocumentsProviderHelper(String authority, ContentProviderClient client) {
        mClient = client;
        mAuthority = authority;
    }

    public RootInfo getRoot(String id) throws RemoteException {
        final Uri rootsUri = DocumentsContract.buildRootsUri(mAuthority);

        Cursor cursor = null;
        try {
            cursor = mClient.query(rootsUri, null, null, null, null);
            while (cursor.moveToNext()) {
                if (id.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) {
                    return RootInfo.fromRootsCursor(mAuthority, cursor);
                }
            }
            throw new IllegalArgumentException("Can't find matching root for id=" + id);
        } catch (Exception e) {
            throw new RuntimeException("Can't load root for id=" + id , e);
        } finally {
            IoUtils.closeQuietly(cursor);
        }
    }

    public Uri createDocument(Uri parentUri, String mimeType, String name) {
        if (name.contains("/")) {
            throw new IllegalArgumentException("Name and mimetype probably interposed.");
        }
        try {
            return DocumentsContract.createDocument(mClient, parentUri, mimeType, name);
        } catch (RemoteException e) {
            throw new RuntimeException("Couldn't create document: " + name + " with mimetype " + mimeType, e);
        }
    }

    public Uri createFolder(Uri parentUri, String name) {
        return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
    }

    public Uri createDocument(RootInfo root, String mimeType, String name) {
        Uri rootUri = DocumentsContract.buildDocumentUri(mAuthority, root.documentId);
        return createDocument(rootUri, mimeType, name);
    }

    public Uri createFolder(RootInfo root, String name) {
        return createDocument(root, Document.MIME_TYPE_DIR, name);
    }
}
+115 −40

File changed.

Preview size limit exceeded, changes collapsed.

+63 −37

File changed.

Preview size limit exceeded, changes collapsed.

Loading