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

Commit 86040a82 authored by Steve McKay's avatar Steve McKay Committed by Android (Google) Code Review
Browse files

Merge "Add functional tests for FilesActivity."

parents 65cd70b7 01792570
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := TestDocumentsProvider
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
#LOCAL_SDK_VERSION := current

LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false

include $(BUILD_PACKAGE)
+30 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.documentsui.testing">
    <application>
        <provider android:name="TestDocumentsProvider"
                android:authorities="com.android.documentsui.testing"
                android:exported="true"
                android:grantUriPermissions="true"
                android:permission="android.permission.MANAGE_DOCUMENTS">
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>
        </provider>
    </application>
</manifest>
+299 −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.testing;

import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TestDocumentsProvider extends DocumentsProvider {
    private static final String TAG = "TestDocumentsProvider";

    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
            Root.COLUMN_ROOT_ID,
            Root.COLUMN_FLAGS,
            Root.COLUMN_ICON,
            Root.COLUMN_TITLE,
            Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,
    };

    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
            Document.COLUMN_DOCUMENT_ID,
            Document.COLUMN_MIME_TYPE,
            Document.COLUMN_DISPLAY_NAME,
            Document.COLUMN_LAST_MODIFIED,
            Document.COLUMN_FLAGS,
            Document.COLUMN_SIZE,
    };

    private static String[] resolveRootProjection(String[] projection) {
        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
    }

    private static String[] resolveDocumentProjection(String[] projection) {
        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
    }

    @Override
    public boolean onCreate() {
        resetRoots();
        return true;
    }

    @Override
    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));

        RowBuilder row = result.newRow();
        row.add(Root.COLUMN_ROOT_ID, "local");
        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY);
        row.add(Root.COLUMN_TITLE, "TEST-Local");
        row.add(Root.COLUMN_SUMMARY, "TEST-LocalSummary");
        row.add(Root.COLUMN_DOCUMENT_ID, "doc:local");

        row = result.newRow();
        row.add(Root.COLUMN_ROOT_ID, "create");
        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD);
        row.add(Root.COLUMN_TITLE, "TEST-Create");
        row.add(Root.COLUMN_DOCUMENT_ID, "doc:create");

        return result;
    }

    private Map<String, Doc> mDocs = new HashMap<>();

    private Doc mLocalRoot;
    private Doc mCreateRoot;

    private Doc buildDoc(String docId, String displayName, String mimeType) {
        final Doc doc = new Doc();
        doc.docId = docId;
        doc.displayName = displayName;
        doc.mimeType = mimeType;
        mDocs.put(doc.docId, doc);
        return doc;
    }

    public void resetRoots() {
        Log.d(TAG, "resetRoots()");

        mDocs.clear();

        mLocalRoot = buildDoc("doc:local", null, Document.MIME_TYPE_DIR);

        mCreateRoot = buildDoc("doc:create", null, Document.MIME_TYPE_DIR);
        mCreateRoot.flags = Document.FLAG_DIR_SUPPORTS_CREATE;

        {
            Doc file1 = buildDoc("doc:file1", "FILE1", "mime1/file1");
            file1.contents = "fileone".getBytes();
            file1.flags = Document.FLAG_SUPPORTS_WRITE;
            mLocalRoot.children.add(file1);
            mCreateRoot.children.add(file1);
        }

        {
            Doc file2 = buildDoc("doc:file2", "FILE2", "mime2/file2");
            file2.contents = "filetwo".getBytes();
            file2.flags = Document.FLAG_SUPPORTS_WRITE;
            mLocalRoot.children.add(file2);
            mCreateRoot.children.add(file2);
        }

        Doc dir1 = buildDoc("doc:dir1", "DIR1", Document.MIME_TYPE_DIR);
        mLocalRoot.children.add(dir1);

        {
            Doc file3 = buildDoc("doc:file3", "FILE3", "mime3/file3");
            file3.contents = "filethree".getBytes();
            file3.flags = Document.FLAG_SUPPORTS_WRITE;
            dir1.children.add(file3);
        }

        Doc dir2 = buildDoc("doc:dir2", "DIR2", Document.MIME_TYPE_DIR);
        mCreateRoot.children.add(dir2);

        {
            Doc file4 = buildDoc("doc:file4", "FILE4", "mime4/file4");
            file4.contents = "filefour".getBytes();
            file4.flags = Document.FLAG_SUPPORTS_WRITE;
            dir2.children.add(file4);
        }
    }

    private static class Doc {
        public String docId;
        public int flags;
        public String displayName;
        public long size;
        public String mimeType;
        public long lastModified;
        public byte[] contents;
        public List<Doc> children = new ArrayList<>();

        public void include(MatrixCursor result) {
            final RowBuilder row = result.newRow();
            row.add(Document.COLUMN_DOCUMENT_ID, docId);
            row.add(Document.COLUMN_DISPLAY_NAME, displayName);
            row.add(Document.COLUMN_SIZE, size);
            row.add(Document.COLUMN_MIME_TYPE, mimeType);
            row.add(Document.COLUMN_FLAGS, flags);
            row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
        }
    }

    @Override
    public boolean isChildDocument(String parentDocumentId, String documentId) {
        for (Doc doc : mDocs.get(parentDocumentId).children) {
            if (doc.docId.equals(documentId)) {
                return true;
            }
            if (Document.MIME_TYPE_DIR.equals(doc.mimeType)) {
                return isChildDocument(doc.docId, documentId);
            }
        }
        return false;
    }

    @Override
    public String createDocument(String parentDocumentId, String mimeType, String displayName)
            throws FileNotFoundException {
        final String docId = "doc:" + System.currentTimeMillis();
        final Doc doc = buildDoc(docId, displayName, mimeType);
        doc.flags = Document.FLAG_SUPPORTS_WRITE | Document.FLAG_SUPPORTS_RENAME;
        mDocs.get(parentDocumentId).children.add(doc);
        return docId;
    }

    @Override
    public String renameDocument(String documentId, String displayName)
            throws FileNotFoundException {
        mDocs.get(documentId).displayName = displayName;
        return null;
    }

    @Override
    public void deleteDocument(String documentId) throws FileNotFoundException {
        mDocs.remove(documentId);
        for (Doc doc : mDocs.values()) {
            doc.children.remove(documentId);
        }
    }

    @Override
    public Cursor queryDocument(String documentId, String[] projection)
            throws FileNotFoundException {
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
        mDocs.get(documentId).include(result);
        return result;
    }

    @Override
    public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
            String sortOrder) throws FileNotFoundException {
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
        for (Doc doc : mDocs.get(parentDocumentId).children) {
            doc.include(result);
        }
        return result;
    }

    @Override
    public ParcelFileDescriptor openDocument(String documentId, String mode,
            CancellationSignal signal) throws FileNotFoundException {
        final Doc doc = mDocs.get(documentId);
        if (doc == null) {
            throw new FileNotFoundException();
        }
        final ParcelFileDescriptor[] pipe;
        try {
            pipe = ParcelFileDescriptor.createPipe();
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        if (mode.contains("w")) {
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    synchronized (doc) {
                        try {
                            final InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
                                    pipe[0]);
                            doc.contents = readFullyNoClose(is);
                            is.close();
                            doc.notifyAll();
                        } catch (IOException e) {
                            Log.w(TAG, "Failed to stream", e);
                        }
                    }
                    return null;
                }
            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
            return pipe[1];
        } else {
            new AsyncTask<Void, Void, Void>() {
                @Override
                protected Void doInBackground(Void... params) {
                    synchronized (doc) {
                        try {
                            final OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
                                    pipe[1]);
                            while (doc.contents == null) {
                                doc.wait();
                            }
                            os.write(doc.contents);
                            os.close();
                        } catch (IOException e) {
                            Log.w(TAG, "Failed to stream", e);
                        } catch (InterruptedException e) {
                            Log.w(TAG, "Interuppted", e);
                        }
                    }
                    return null;
                }
            }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
            return pipe[0];
        }
    }

    private static byte[] readFullyNoClose(InputStream in) throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int count;
        while ((count = in.read(buffer)) != -1) {
            bytes.write(buffer, 0, count);
        }
        return bytes.toByteArray();
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -3,11 +3,12 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := tests
#LOCAL_SDK_VERSION := current

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
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 mockito-target guava ub-uiautomator

LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
@@ -15,3 +16,5 @@ LOCAL_INSTRUMENTATION_FOR := DocumentsUI
LOCAL_CERTIFICATE := platform

include $(BUILD_PACKAGE)

include $(LOCAL_PATH)/../testing/TestDocumentsProvider/Android.mk
+98 −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 android.content.Context;
import android.content.Intent;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
import android.support.test.uiautomator.Configurator;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.test.InstrumentationTestCase;
import android.view.MotionEvent;

import java.util.concurrent.TimeoutException;

public class FilesActivityUiTest extends InstrumentationTestCase {

    private static final String TAG = "FilesActivityUiTest";
    private static final String TARGET_PKG = "com.android.documentsui";
    private static final String LAUNCHER_PKG = "com.android.launcher";
    private static final int ONE_SECOND = 1000;
    private static final int FIVE_SECONDS = 5 * ONE_SECOND;

    private ActionBar mBar;
    private UiDevice mDevice;
    private Context mContext;

    public void setUp() throws TimeoutException {
        // Initialize UiDevice instance.
        mDevice = UiDevice.getInstance(getInstrumentation());

        Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE);

        // Start from the home screen.
        mDevice.pressHome();
        mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), FIVE_SECONDS);

        // Launch app.
        mContext = getInstrumentation().getContext();
        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(TARGET_PKG);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        mContext.startActivity(intent);

        // Wait for the app to appear.
        mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), FIVE_SECONDS);
        mDevice.waitForIdle();

        mBar = new ActionBar();
    }

    public void testSwitchMode() throws Exception {
        UiObject2 mode = mBar.gridMode(100);
        if (mode != null) {
            mode.click();
            assertNotNull(mBar.listMode(ONE_SECOND));
        } else {
            mBar.listMode(100).click();
            assertNotNull(mBar.gridMode(ONE_SECOND));
        }
    }

    private class ActionBar {

        public UiObject2 gridMode(int timeout) {
            // Note that we're using By.desc rather than By.res, because of b/25285770
            BySelector selector = By.desc("Grid view");
            if (timeout > 0) {
                mDevice.wait(Until.findObject(selector), timeout);
            }
            return mDevice.findObject(selector);
        }

        public UiObject2 listMode(int timeout) {
            // Note that we're using By.desc rather than By.res, because of b/25285770
            BySelector selector = By.desc("List view");
            if (timeout > 0) {
                mDevice.wait(Until.findObject(selector), timeout);
            }
            return mDevice.findObject(selector);
        }
    }
}