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

Commit bc253407 authored by Ben Reich's avatar Ben Reich
Browse files

Create builder pattern to initialise custom test files

Most tests simply rely on the default folder and file creation, however
some of the test use custom logic. This introduces the ability to
create custom directory structures using the TestFilesRule.

Along with this it removes the mDocsHelper logic from the
ActivityTestJunit4 class. The resetting of storage removes the files
on disk but doesn't clear them from the database, this leads to odd
test behaviours where you can see the file but not perform actions on
them.

Bug: 407646510
Test: atest com.android.documentsui.FilesActivityUiTest
Flag: EXEMPT test change
Change-Id: Ic111d66dab423dec7edabb60baf45bdbb06b9fd9
parent f9eb5e78
Loading
Loading
Loading
Loading
+78 −9
Original line number Diff line number Diff line
@@ -15,27 +15,96 @@
 */
package com.android.documentsui.rules

import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import androidx.test.platform.app.InstrumentationRegistry
import com.android.documentsui.DocumentsProviderHelper
import com.android.documentsui.StubProvider
import com.android.documentsui.base.RootInfo
import com.android.documentsui.base.UserId
import org.junit.rules.ExternalResource

/** Rule that creates test files in a test. */
class TestFilesRule() : ExternalResource() {
    private val docsHelper = DocumentsProviderHelper(
/**
 * Rule that creates test files in a test.
 * When `skipCreation` is false, this essentially falls back to providing a `docsHelper`.
 */
class TestFilesRule(private val skipCreation: Boolean = false) : ExternalResource() {
    lateinit var docsHelper: DocumentsProviderHelper

    // A map of the URIs that are created, used to keep track of names of items that are created
    // as children of other items.
    private val createdUris = mutableMapOf<String, Uri>()

    // The creation operations are deferred as the rules are instantiated prior to the environment
    // being ready.
    private val deferredOperations = mutableListOf<() -> Unit>()

    override fun before() {
        docsHelper =
            DocumentsProviderHelper(
                UserId.DEFAULT_USER,
                StubProvider.DEFAULT_AUTHORITY,
                InstrumentationRegistry.getInstrumentation().context,
        StubProvider.DEFAULT_AUTHORITY
                StubProvider.DEFAULT_AUTHORITY,
            )

    override fun before() {
        docsHelper.clear(null, null)
        docsHelper.configure(null, Bundle.EMPTY)

        if (skipCreation) {
            require(
                deferredOperations.isEmpty()
            ) { "Have deferred operations yet requested to skip creation." }
            return
        }

        if (deferredOperations.isEmpty()) {
            createDefault()
        } else {
            deferredOperations.forEach { it() }
        }
    }

    /** Create a folder in `root`. */
    fun createFolderInRoot(root: String, folderName: String): TestFilesRule {
        deferredOperations.add {
            val rootInfo = docsHelper.getRoot(root)
            val uri = docsHelper.createFolder(rootInfo, folderName)
            require(!createdUris.containsKey(folderName)) { "$folderName has already been created" }
            createdUris[folderName] = uri
        }
        return this
    }

    /** Creates a folder in `root` with `parentName`. The `parentName` must be already created. */
    fun createFolderWithParent(parentName: String, folderName: String): TestFilesRule {
        deferredOperations.add {
            val parentUri = createdUris[parentName]
            requireNotNull(parentUri) { "Parent folder $parentName not initialized" }
            val uri = docsHelper.createFolder(parentUri, folderName)
            createdUris[folderName] = uri
        }
        return this
    }

    /** Creates a file in `root` with the specified `fileName` and `mimeType`. */
    fun createFileInRoot(root: String, fileName: String, mimeType: String): TestFilesRule {
        deferredOperations.add {
            val rootInfo = docsHelper.getRoot(root)
            val uri = docsHelper.createDocument(rootInfo, mimeType, fileName)
            createdUris[fileName] = uri
        }
        return this
    }

    /** Returns`RootInfo` for the for the specified `root`. */
    fun getRoot(root: String): RootInfo {
        return docsHelper.getRoot(root)
    }

    /** Creates a default set of files for testing. */
    private fun createDefault() {
        val root0 = docsHelper.getRoot(StubProvider.ROOT_0_ID)
        val root1 = docsHelper.getRoot(StubProvider.ROOT_1_ID)

@@ -46,7 +115,7 @@ class TestFilesRule() : ExternalResource() {
            root0.documentId,
            "text/plain",
            FILE_NAME_NO_RENAME,
            DocumentsContract.Document.FLAG_SUPPORTS_WRITE
            DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
        )

        docsHelper.createDocument(root1, "text/plain", FILE_NAME_3)
+1 −8
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import android.app.UiAutomation
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.RemoteException
import android.provider.DocumentsContract
import android.view.KeyEvent
@@ -78,6 +77,7 @@ abstract class ActivityTestJunit4<T : Activity?> {

    @JvmField
    protected var mDocsHelper: DocumentsProviderHelper? = null

    @JvmField
    protected var mActivityScenario: ActivityScenario<T?>? = null
    private var initialScreenOffTimeoutValue: String? = null
@@ -126,24 +126,18 @@ abstract class ActivityTestJunit4<T : Activity?> {
        disableScreenOffAndSleepTimeouts()

        setupTestingRoots()

        launchActivity()
        resetStorage()

        // Since at the launch of activity, ROOT_0 and ROOT_1 have no files, drawer will
        // automatically open for phone devices. Espresso register click() as (x, y) MotionEvents,
        // so if a drawer is on top of a file we want to select, it will actually click the drawer.
        // Thus to start a clean state, we always try to close first.
        bots!!.roots!!.closeDrawer()

        // Configure the provider back to default.
        mDocsHelper!!.configure(null, Bundle.EMPTY)
    }

    @After
    fun tearDown() {
        device!!.unfreezeRotation()
        mDocsHelper!!.cleanUp()
        restoreScreenOffAndSleepTimeouts()
        mActivityScenario!!.close()
    }
@@ -163,7 +157,6 @@ abstract class ActivityTestJunit4<T : Activity?> {

    @Throws(RemoteException::class)
    protected fun resetStorage() {
        mDocsHelper!!.clear(null, null)
        device!!.waitForIdle()
    }

+5 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import androidx.test.filters.LargeTest;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.files.FilesActivity;
import com.android.documentsui.filters.HugeLongTest;
import com.android.documentsui.rules.TestFilesRule;

import org.junit.Rule;
import org.junit.Test;
@@ -44,6 +45,9 @@ public class FilesActivityDefaultsUiTest extends ActivityTestJunit4<FilesActivit
    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Rule
    public final TestFilesRule mTestFilesRule = new TestFilesRule(/* skipCreation */ true);

    @Override
    protected RootInfo getInitialRoot() {
        return null;  // test the default, unaffected state of the app.
@@ -54,7 +58,7 @@ public class FilesActivityDefaultsUiTest extends ActivityTestJunit4<FilesActivit
    public void testNavigate_FromEmptyDirectory() throws Exception {
        device.waitForIdle();

        bots.roots.openRoot(rootDir0.title);
        bots.roots.openRoot(mTestFilesRule.getRoot(ROOT_0_ID).title);

        String msg = String.valueOf(context.getString(R.string.empty));
        bots.directory.assertPlaceholderMessageText(msg);
+16 −21
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.documentsui;

import static com.android.documentsui.StubProvider.ROOT_0_ID;
import static com.android.documentsui.StubProvider.ROOT_1_ID;
import static com.android.documentsui.base.Providers.AUTHORITY_STORAGE;
import static com.android.documentsui.base.Providers.ROOT_ID_DEVICE;
import static com.android.documentsui.flags.Flags.FLAG_HIDE_ROOTS_ON_DESKTOP_RO;
@@ -30,7 +32,6 @@ import static junit.framework.Assert.assertTrue;
import android.app.Instrumentation;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.RemoteException;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -45,8 +46,8 @@ import com.android.documentsui.base.UserId;
import com.android.documentsui.files.FilesActivity;
import com.android.documentsui.filters.HugeLongTest;
import com.android.documentsui.inspector.InspectorActivity;
import com.android.documentsui.rules.TestFilesRule;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,22 +59,16 @@ public class FilesActivityUiTest extends ActivityTestJunit4<FilesActivity> {
    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Before
    public void setUpTest() throws RemoteException {
        initTestFiles();
    }

    private void initTestFiles() throws RemoteException {
        Uri uri = mDocsHelper.createFolder(rootDir0, dirName1);
        mDocsHelper.createFolder(uri, childDir1);

        mDocsHelper.createDocument(rootDir0, "text/plain", "file0.log");
        mDocsHelper.createDocument(rootDir0, "image/png", "file1.png");
        mDocsHelper.createDocument(rootDir0, "text/csv", "file2.csv");

        mDocsHelper.createDocument(rootDir1, "text/plain", "anotherFile0.log");
        mDocsHelper.createDocument(rootDir1, "text/plain", "poodles.text");
    }
    @Rule
    public final TestFilesRule mTestFilesRule =
            new TestFilesRule()
                    .createFolderInRoot(ROOT_0_ID, TestFilesRule.DIR_NAME_1)
                    .createFolderWithParent(TestFilesRule.DIR_NAME_1, "ChildDir1")
                    .createFileInRoot(ROOT_0_ID, "file0.log", "text/plain")
                    .createFileInRoot(ROOT_0_ID, "file1.png", "image/png")
                    .createFileInRoot(ROOT_0_ID, "file2.csv", "text/csv")
                    .createFileInRoot(ROOT_0_ID, "anotherFile0.log", "text/plain")
                    .createFileInRoot(ROOT_0_ID, "poodles.text", "text/plain");

    // Recents is a strange meta root that gathers entries from other providers.
    // It is special cased in a variety of ways, which is why we just want
@@ -163,7 +158,7 @@ public class FilesActivityUiTest extends ActivityTestJunit4<FilesActivity> {
    }

    private void filesListed_LiveUpdates() throws Exception {
        mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich");
        mTestFilesRule.createFileInRoot(ROOT_0_ID, "yummers/sandwich", "Ham & Cheese.sandwich");

        bots.directory.waitForDocument("Ham & Cheese.sandwich");
        bots.directory.assertDocumentsPresent(
@@ -198,12 +193,12 @@ public class FilesActivityUiTest extends ActivityTestJunit4<FilesActivity> {
    @Test
    public void testNavigate_inFixedLayout_whileHasSelection() throws Exception {
        if (bots.main.inFixedLayout()) {
            bots.roots.openRoot(rootDir0.title);
            bots.roots.openRoot(mTestFilesRule.getRoot(ROOT_0_ID).title);
            device.waitForIdle();
            bots.directory.selectDocument("file0.log", 1);

            // ensure no exception is thrown while navigating to a different root
            bots.roots.openRoot(rootDir1.title);
            bots.roots.openRoot(mTestFilesRule.getRoot(ROOT_1_ID).title);
        }
    }

+12 −6
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;

import com.android.documentsui.files.FilesActivity;
import com.android.documentsui.rules.TestFilesRule;
import com.android.documentsui.sorting.SortDimension;
import com.android.documentsui.sorting.SortModel;

@@ -65,6 +66,12 @@ public class SortDocumentUiTest extends ActivityTestJunit4<FilesActivity> {
    private static final String[] FILES_IN_TYPE_ASC = {FILE_2, FILE_3, FILE_1};
    private static final String[] FILES_IN_TYPE_DESC = reverse(FILES_IN_TYPE_ASC);

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Rule
    public final TestFilesRule mTestFilesRule = new TestFilesRule(/* skipCreation */ true);

    private static String[] reverse(String[] array) {
        String[] ret = new String[array.length];

@@ -75,9 +82,6 @@ public class SortDocumentUiTest extends ActivityTestJunit4<FilesActivity> {
        return ret;
    }

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Before
    public void setUpTest() {
        bots.roots.closeDrawer();
@@ -95,14 +99,16 @@ public class SortDocumentUiTest extends ActivityTestJunit4<FilesActivity> {
     */
    private void initFiles(long sleep) throws Exception {
        for (int i = 0; i < FILES.length; ++i) {
            Uri uri = mDocsHelper.createDocument(getInitialRoot(), MIMES[i], FILES[i]);
            mDocsHelper.writeDocument(uri, FILES[i].getBytes());
            Uri uri =
                    mTestFilesRule.docsHelper.createDocument(
                            StubProvider.ROOT_0_ID, MIMES[i], FILES[i]);
            mTestFilesRule.docsHelper.writeDocument(uri, FILES[i].getBytes());

            Thread.sleep(sleep);
        }

        for (String dir : DIRS) {
            mDocsHelper.createFolder(getInitialRoot(), dir);
            mTestFilesRule.docsHelper.createFolder(StubProvider.ROOT_0_ID, dir);

            Thread.sleep(sleep);
        }
Loading