diff options
Diffstat (limited to 'tests')
12 files changed, 356 insertions, 85 deletions
diff --git a/tests/common/com/android/documentsui/bots/UiBot.java b/tests/common/com/android/documentsui/bots/UiBot.java index f30cb93b8..4ec75bdc6 100644 --- a/tests/common/com/android/documentsui/bots/UiBot.java +++ b/tests/common/com/android/documentsui/bots/UiBot.java @@ -25,6 +25,8 @@ import static androidx.test.espresso.matcher.ViewMatchers.withClassName; import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withText; +import static com.android.documentsui.flags.Flags.useMaterial3; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -68,37 +70,48 @@ import java.util.List; */ public class UiBot extends Bots.BaseBot { - public static String targetPackageName; - @SuppressWarnings("unchecked") private static final Matcher<View> TOOLBAR = allOf( isAssignableFrom(Toolbar.class), withId(R.id.toolbar)); - @SuppressWarnings("unchecked") private static final Matcher<View> ACTIONBAR = allOf( withClassName(endsWith("ActionBarContextView"))); - @SuppressWarnings("unchecked") private static final Matcher<View> TEXT_ENTRY = allOf( withClassName(endsWith("EditText"))); - @SuppressWarnings("unchecked") private static final Matcher<View> TOOLBAR_OVERFLOW = allOf( withClassName(endsWith("OverflowMenuButton")), ViewMatchers.isDescendantOfA(TOOLBAR)); - @SuppressWarnings("unchecked") private static final Matcher<View> ACTIONBAR_OVERFLOW = allOf( withClassName(endsWith("OverflowMenuButton")), ViewMatchers.isDescendantOfA(ACTIONBAR)); + public static String targetPackageName; + public UiBot(UiDevice device, Context context, int timeout) { super(device, context, timeout); targetPackageName = InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageName(); } + private static Matcher<Object> withToolbarTitle(final Matcher<CharSequence> textMatcher) { + return new BoundedMatcher<Object, Toolbar>(Toolbar.class) { + @Override + public boolean matchesSafely(Toolbar toolbar) { + return textMatcher.matches(toolbar.getTitle()); + } + + @Override + public void describeTo(Description description) { + description.appendText("with toolbar title: "); + textMatcher.describeTo(description); + } + }; + } + public void assertWindowTitle(String expected) { onView(TOOLBAR) .check(matches(withToolbarTitle(is(expected)))); @@ -198,7 +211,11 @@ public class UiBot extends Bots.BaseBot { } public void clickActionbarOverflowItem(String label) { - onView(ACTIONBAR_OVERFLOW).perform(click()); + if (useMaterial3()) { + onView(TOOLBAR_OVERFLOW).perform(click()); + } else { + onView(ACTIONBAR_OVERFLOW).perform(click()); + } // Click the item by label, since Espresso doesn't support lookup by id on overflow. onView(withText(label)).perform(click()); } @@ -214,9 +231,10 @@ public class UiBot extends Bots.BaseBot { } public boolean waitForActionModeBarToAppear() { + String actionModeId = useMaterial3() ? "toolbar" : "action_mode_bar"; UiObject2 bar = - mDevice.wait(Until.findObject( - By.res(mTargetPackage + ":id/action_mode_bar")), mTimeout); + mDevice.wait( + Until.findObject(By.res(mTargetPackage + ":id/" + actionModeId)), mTimeout); return (bar != null); } @@ -307,20 +325,4 @@ public class UiBot extends Bots.BaseBot { // TODO: use the system string ? android.R.string.action_menu_overflow_description return mDevice.findObject(selector); } - - private static Matcher<Object> withToolbarTitle( - final Matcher<CharSequence> textMatcher) { - return new BoundedMatcher<Object, Toolbar>(Toolbar.class) { - @Override - public boolean matchesSafely(Toolbar toolbar) { - return textMatcher.matches(toolbar.getTitle()); - } - - @Override - public void describeTo(Description description) { - description.appendText("with toolbar title: "); - textMatcher.describeTo(description); - } - }; - } } diff --git a/tests/common/com/android/documentsui/testing/TestDocumentsProvider.java b/tests/common/com/android/documentsui/testing/TestDocumentsProvider.java index 5c5ed856f..c7e884fde 100644 --- a/tests/common/com/android/documentsui/testing/TestDocumentsProvider.java +++ b/tests/common/com/android/documentsui/testing/TestDocumentsProvider.java @@ -17,6 +17,7 @@ package com.android.documentsui.testing; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.ProviderInfo; import android.database.Cursor; @@ -25,6 +26,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; +import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsProvider; @@ -91,13 +93,59 @@ public class TestDocumentsProvider extends DocumentsProvider { return mNextRecentDocuments; } + private String getStringColumn(Cursor cursor, String name) { + return cursor.getString(cursor.getColumnIndexOrThrow(name)); + } + + private long getLongColumn(Cursor cursor, String name) { + return cursor.getLong(cursor.getColumnIndexOrThrow(name)); + } + + @Override + public Cursor querySearchDocuments(@NonNull String rootId, @Nullable String[] projection, + @NonNull Bundle queryArgs) { + TestCursor cursor = new TestCursor(DOCUMENTS_PROJECTION); + if (mNextChildDocuments == null) { + return cursor; + } + for (boolean hasNext = mNextChildDocuments.moveToFirst(); hasNext; + hasNext = mNextChildDocuments.moveToNext()) { + String displayName = getStringColumn(mNextChildDocuments, Document.COLUMN_DISPLAY_NAME); + String mimeType = getStringColumn(mNextChildDocuments, Document.COLUMN_MIME_TYPE); + long lastModified = getLongColumn(mNextChildDocuments, Document.COLUMN_LAST_MODIFIED); + long size = getLongColumn(mNextChildDocuments, Document.COLUMN_SIZE); + + if (DocumentsContract.matchSearchQueryArguments(queryArgs, displayName, mimeType, + lastModified, size)) { + cursor.newRow() + .add(Document.COLUMN_DOCUMENT_ID, + getStringColumn(mNextChildDocuments, Document.COLUMN_DOCUMENT_ID)) + .add(Document.COLUMN_MIME_TYPE, + getStringColumn(mNextChildDocuments, Document.COLUMN_MIME_TYPE)) + .add(Document.COLUMN_DISPLAY_NAME, + getStringColumn(mNextChildDocuments, Document.COLUMN_DISPLAY_NAME)) + .add(Document.COLUMN_LAST_MODIFIED, + getLongColumn(mNextChildDocuments, Document.COLUMN_LAST_MODIFIED)) + .add(Document.COLUMN_FLAGS, + getLongColumn(mNextChildDocuments, Document.COLUMN_FLAGS)) + .add(Document.COLUMN_SUMMARY, + getStringColumn(mNextChildDocuments, Document.COLUMN_SUMMARY)) + .add(Document.COLUMN_SIZE, + getLongColumn(mNextChildDocuments, Document.COLUMN_SIZE)) + .add(Document.COLUMN_ICON, + getLongColumn(mNextChildDocuments, Document.COLUMN_ICON)); + } + } + return cursor; + } + @Override public Cursor querySearchDocuments(String rootId, String query, String[] projection) { - if (mNextChildDocuments != null) { - return filterCursorByString(mNextChildDocuments, query); + if (mNextChildDocuments == null) { + return null; } - return mNextChildDocuments; + return filterCursorByString(mNextChildDocuments, query); } @Override diff --git a/tests/functional/com/android/documentsui/ActivityTestJunit4.kt b/tests/functional/com/android/documentsui/ActivityTestJunit4.kt index daab0cdb4..8f1d4f860 100644 --- a/tests/functional/com/android/documentsui/ActivityTestJunit4.kt +++ b/tests/functional/com/android/documentsui/ActivityTestJunit4.kt @@ -47,11 +47,16 @@ import java.util.Objects abstract class ActivityTestJunit4<T : Activity?> { @JvmField var bots: Bots? = null + + @JvmField var device: UiDevice? = null + + @JvmField var context: Context? = null var userId: UserId? = null var automation: UiAutomation? = null + @JvmField var features: Features? = null /** @@ -60,7 +65,12 @@ abstract class ActivityTestJunit4<T : Activity?> { * Override the method if you want to open different root on start. * @return Root that will be opened. Return null if you want to open activity's default root. */ - protected var initialRoot: RootInfo? = null + protected open var initialRoot: RootInfo? = null + + @JvmField + var rootDir0: RootInfo? = null + + @JvmField var rootDir1: RootInfo? = null protected var mResolver: ContentResolver? = null @@ -83,8 +93,9 @@ abstract class ActivityTestJunit4<T : Activity?> { */ @Throws(RemoteException::class) protected fun setupTestingRoots() { - this.initialRoot = mDocsHelper!!.getRoot(StubProvider.ROOT_0_ID) + rootDir0 = mDocsHelper!!.getRoot(StubProvider.ROOT_0_ID) rootDir1 = mDocsHelper!!.getRoot(StubProvider.ROOT_1_ID) + this.initialRoot = rootDir0 } @Throws(Exception::class) @@ -154,7 +165,7 @@ abstract class ActivityTestJunit4<T : Activity?> { } @Throws(RemoteException::class) - protected fun initTestFiles() { + protected open fun initTestFiles() { mDocsHelper!!.createFolder(this.initialRoot, dirName1) mDocsHelper!!.createDocument(this.initialRoot, "text/plain", fileName1) mDocsHelper!!.createDocument(this.initialRoot, "image/png", fileName2) @@ -201,6 +212,7 @@ abstract class ActivityTestJunit4<T : Activity?> { companion object { // Testing files. For custom ones, override initTestFiles(). const val dirName1 = "Dir1" + const val childDir1 = "ChildDir1" const val fileName1 = "file1.log" const val fileName2 = "file12.png" const val fileName3 = "anotherFile0.log" diff --git a/tests/functional/com/android/documentsui/FileCopyUiTest.java b/tests/functional/com/android/documentsui/FileCopyUiTest.java index 65a5d257f..42681ee82 100644 --- a/tests/functional/com/android/documentsui/FileCopyUiTest.java +++ b/tests/functional/com/android/documentsui/FileCopyUiTest.java @@ -489,7 +489,7 @@ public class FileCopyUiTest extends ActivityTest<FilesActivity> { } @HugeLongTest - public void ignored_testRecursiveCopyDocuments_InternalStorageToDownloadsProvider() + public void testRecursiveCopyDocuments_InternalStorageToDownloadsProvider() throws Exception { // Create Download folder if it doesn't exist. DocumentInfo info = mStorageDocsHelper.findFile(mPrimaryRoot.documentId, "Download"); diff --git a/tests/functional/com/android/documentsui/FilesActivityDefaultsUiTest.java b/tests/functional/com/android/documentsui/FilesActivityDefaultsUiTest.java index a33cca37a..2cea176ba 100644 --- a/tests/functional/com/android/documentsui/FilesActivityDefaultsUiTest.java +++ b/tests/functional/com/android/documentsui/FilesActivityDefaultsUiTest.java @@ -18,24 +18,46 @@ 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.flags.Flags.FLAG_HIDE_ROOTS_ON_DESKTOP; -import android.os.RemoteException; +import android.content.pm.PackageManager; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import com.android.documentsui.base.RootInfo; import com.android.documentsui.files.FilesActivity; import com.android.documentsui.filters.HugeLongTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + @LargeTest -public class FilesActivityDefaultsUiTest extends ActivityTest<FilesActivity> { +@RunWith(AndroidJUnit4.class) +public class FilesActivityDefaultsUiTest extends ActivityTestJunit4<FilesActivity> { + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Before + public void setUp() throws Exception { + super.setUp(); + } - public FilesActivityDefaultsUiTest() { - super(FilesActivity.class); + @After + public void tearDown() throws Exception { + super.tearDown(); } @Override - protected void initTestFiles() throws RemoteException { + protected void initTestFiles() { // Overriding to init with no items in test roots } @@ -44,6 +66,7 @@ public class FilesActivityDefaultsUiTest extends ActivityTest<FilesActivity> { return null; // test the default, unaffected state of the app. } + @Test @HugeLongTest public void testNavigate_FromEmptyDirectory() throws Exception { device.waitForIdle(); @@ -57,8 +80,10 @@ public class FilesActivityDefaultsUiTest extends ActivityTest<FilesActivity> { device.pressBack(); } + @Test @HugeLongTest - public void testDefaultRoots() throws Exception { + @RequiresFlagsDisabled(FLAG_HIDE_ROOTS_ON_DESKTOP) + public void testDefaultRoots_hideRootsOnDesktopFlagDisabled() throws Exception { device.waitForIdle(); // Should also have Drive, but that requires pre-configuration of devices @@ -71,4 +96,29 @@ public class FilesActivityDefaultsUiTest extends ActivityTest<FilesActivity> { ROOT_0_ID, ROOT_1_ID); } + + @Test + @HugeLongTest + @RequiresFlagsEnabled(FLAG_HIDE_ROOTS_ON_DESKTOP) + public void testDefaultRoots_hideRootsOnDesktopFlagEnabled() throws Exception { + device.waitForIdle(); + + String[] expectedRoots; + if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) { + expectedRoots = new String[]{"Downloads", + ROOT_0_ID, + ROOT_1_ID}; + } else { + expectedRoots = new String[]{ + "Images", + "Videos", + "Audio", + "Downloads", + ROOT_0_ID, + ROOT_1_ID}; + } + // Should also have Drive, but that requires pre-configuration of devices + // We omit for now. + bots.roots.assertRootsPresent(expectedRoots); + } } diff --git a/tests/functional/com/android/documentsui/FilesActivityUiTest.java b/tests/functional/com/android/documentsui/FilesActivityUiTest.java index 697dee6df..1a160c130 100644 --- a/tests/functional/com/android/documentsui/FilesActivityUiTest.java +++ b/tests/functional/com/android/documentsui/FilesActivityUiTest.java @@ -16,29 +16,46 @@ package com.android.documentsui; +import static com.android.documentsui.flags.Flags.FLAG_HIDE_ROOTS_ON_DESKTOP; + import android.app.Instrumentation; import android.net.Uri; import android.os.RemoteException; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import com.android.documentsui.files.FilesActivity; import com.android.documentsui.filters.HugeLongTest; import com.android.documentsui.inspector.InspectorActivity; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + @LargeTest -public class FilesActivityUiTest extends ActivityTest<FilesActivity> { +@RunWith(AndroidJUnit4.class) +public class FilesActivityUiTest extends ActivityTestJunit4<FilesActivity> { - public FilesActivityUiTest() { - super(FilesActivity.class); - } + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - @Override + @Before public void setUp() throws Exception { super.setUp(); initTestFiles(); } + @After + public void tearDown() throws Exception { + super.tearDown(); + } + @Override public void initTestFiles() throws RemoteException { Uri uri = mDocsHelper.createFolder(rootDir0, dirName1); @@ -55,6 +72,7 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { // 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 // to be able to click on it. + @Test public void testClickRecent() throws Exception { bots.roots.openRoot("Recent"); @@ -67,15 +85,19 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { } } + @Test + @RequiresFlagsDisabled(FLAG_HIDE_ROOTS_ON_DESKTOP) public void testRootClick_SetsWindowTitle() throws Exception { bots.roots.openRoot("Images"); bots.main.assertWindowTitle("Images"); } + @Test public void testFilesListed() throws Exception { bots.directory.assertDocumentsPresent("file0.log", "file1.png", "file2.csv"); } + @Test public void testFilesList_LiveUpdate() throws Exception { mDocsHelper.createDocument(rootDir0, "yummers/sandwich", "Ham & Cheese.sandwich"); @@ -84,6 +106,7 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { "file0.log", "file1.png", "file2.csv", "Ham & Cheese.sandwich"); } + @Test public void testNavigate_byBreadcrumb() throws Exception { bots.directory.openDocument(dirName1); bots.directory.waitForDocument(childDir1); // wait for known content @@ -96,6 +119,7 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { bots.directory.waitForDocument(dirName1); } + @Test public void testNavigate_inFixedLayout_whileHasSelection() throws Exception { if (bots.main.inFixedLayout()) { bots.roots.openRoot(rootDir0.title); @@ -107,6 +131,7 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { } } + @Test public void testNavigationToInspector() throws Exception { if(!features.isInspectorEnabled()) { return; @@ -118,7 +143,9 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { monitor.waitForActivityWithTimeout(TIMEOUT); } + @Test @HugeLongTest + @RequiresFlagsDisabled(FLAG_HIDE_ROOTS_ON_DESKTOP) public void testRootChange_UpdatesSortHeader() throws Exception { // switch to separate display modes for two separate roots. Each diff --git a/tests/functional/com/android/documentsui/TrampolineActivityTest.kt b/tests/functional/com/android/documentsui/TrampolineActivityTest.kt index c2201789b..76d703701 100644 --- a/tests/functional/com/android/documentsui/TrampolineActivityTest.kt +++ b/tests/functional/com/android/documentsui/TrampolineActivityTest.kt @@ -15,7 +15,10 @@ */ package com.android.documentsui +import android.app.Instrumentation import android.content.Intent +import android.content.Intent.ACTION_GET_CONTENT +import android.content.IntentFilter import android.os.Build.VERSION_CODES import android.platform.test.annotations.RequiresFlagsEnabled import android.platform.test.flag.junit.CheckFlagsRule @@ -29,7 +32,9 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.android.documentsui.flags.Flags.FLAG_REDIRECT_GET_CONTENT import com.android.documentsui.picker.TrampolineActivity +import java.util.Optional import java.util.regex.Pattern +import org.junit.After import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.BeforeClass @@ -49,15 +54,27 @@ import org.junit.runners.Suite.SuiteClasses class TrampolineActivityTest() { companion object { const val UI_TIMEOUT = 5000L - val PHOTOPICKER_PACKAGE_REGEX: Pattern = Pattern.compile(".*photopicker.*") + val PHOTOPICKER_PACKAGE_REGEX: Pattern = Pattern.compile(".*(photopicker|media\\.module).*") val DOCUMENTSUI_PACKAGE_REGEX: Pattern = Pattern.compile(".*documentsui.*") - private var device: UiDevice? = null + private lateinit var device: UiDevice + + private lateinit var monitor: Instrumentation.ActivityMonitor @BeforeClass @JvmStatic fun setUp() { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + // Monitor to wait for the activity that starts with the `ACTION_GET_CONTENT` intent. + val intentFilter = IntentFilter().apply { addAction(ACTION_GET_CONTENT) } + monitor = + Instrumentation.ActivityMonitor( + intentFilter, + null, // Expected result from startActivityForResult. + true, // Whether to block until activity started or not. + ) + InstrumentationRegistry.getInstrumentation().addMonitor(monitor) } } @@ -72,11 +89,11 @@ class TrampolineActivityTest() { data class GetContentIntentData( val mimeType: String, val expectedApp: AppType, - val extraMimeTypes: Array<String>? = null, + val extraMimeTypes: Optional<Array<String>> = Optional.empty(), ) { override fun toString(): String { - if (extraMimeTypes != null) { - return "${mimeType}_${extraMimeTypes.joinToString("_")}" + if (extraMimeTypes.isPresent) { + return "${mimeType}_${extraMimeTypes.get().joinToString("_")}" } return mimeType } @@ -101,32 +118,32 @@ class TrampolineActivityTest() { ), GetContentIntentData( mimeType = "image/*", - extraMimeTypes = arrayOf("video/*"), + extraMimeTypes = Optional.of(arrayOf("video/*")), expectedApp = AppType.PHOTOPICKER, ), GetContentIntentData( mimeType = "video/*", - extraMimeTypes = arrayOf("image/*"), + extraMimeTypes = Optional.of(arrayOf("image/*")), expectedApp = AppType.PHOTOPICKER, ), GetContentIntentData( mimeType = "video/*", - extraMimeTypes = arrayOf("text/*"), + extraMimeTypes = Optional.of(arrayOf("text/*")), expectedApp = AppType.DOCUMENTSUI, ), GetContentIntentData( mimeType = "video/*", - extraMimeTypes = arrayOf("image/*", "text/*"), + extraMimeTypes = Optional.of(arrayOf("image/*", "text/*")), expectedApp = AppType.DOCUMENTSUI, ), GetContentIntentData( mimeType = "*/*", - extraMimeTypes = arrayOf("image/*", "video/*"), - expectedApp = AppType.DOCUMENTSUI, + extraMimeTypes = Optional.of(arrayOf("image/*", "video/*")), + expectedApp = AppType.PHOTOPICKER, ), GetContentIntentData( mimeType = "image/*", - extraMimeTypes = arrayOf(), + extraMimeTypes = Optional.of(arrayOf()), expectedApp = AppType.DOCUMENTSUI, ) ) @@ -141,15 +158,23 @@ class TrampolineActivityTest() { @Before fun setUp() { val context = InstrumentationRegistry.getInstrumentation().targetContext - val intent = Intent(Intent.ACTION_GET_CONTENT) + val intent = Intent(ACTION_GET_CONTENT) intent.setClass(context, TrampolineActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.setType(testData.mimeType) - testData.extraMimeTypes?.let { intent.putExtra(Intent.EXTRA_MIME_TYPES, it) } + if (testData.extraMimeTypes.isPresent) { + testData.extraMimeTypes.get() + .forEach { intent.putExtra(Intent.EXTRA_MIME_TYPES, it) } + } context.startActivity(intent) } + @After + fun tearDown() { + monitor.waitForActivityWithTimeout(UI_TIMEOUT)?.finish() + } + @Test fun testCorrectAppIsLaunched() { val bySelector = when (testData.expectedApp) { @@ -157,7 +182,23 @@ class TrampolineActivityTest() { else -> By.pkg(DOCUMENTSUI_PACKAGE_REGEX) } - assertNotNull(device?.wait(Until.findObject(bySelector), UI_TIMEOUT)) + val builder = StringBuilder() + builder.append("Intent with mimetype ${testData.mimeType}") + if (testData.extraMimeTypes.isPresent) { + builder.append( + " and EXTRA_MIME_TYPES of ${ + testData.extraMimeTypes.get().joinToString(", ") + }" + ) + } + builder.append( + " didn't cause ${testData.expectedApp.name} to appear after ${UI_TIMEOUT}ms" + ) + + assertNotNull( + builder.toString(), + device.wait(Until.findObject(bySelector), UI_TIMEOUT) + ) } } @@ -170,21 +211,22 @@ class TrampolineActivityTest() { @Test fun testReferredGetContentFromPhotopickerShouldNotRedirectBack() { val context = InstrumentationRegistry.getInstrumentation().targetContext - val intent = Intent(Intent.ACTION_GET_CONTENT) + val intent = Intent(ACTION_GET_CONTENT) intent.setClass(context, TrampolineActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.setType("image/*") + intent.setType("*/*") + intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*")) context.startActivity(intent) - val moreButton = device?.wait(Until.findObject(By.desc("More")), UI_TIMEOUT) + val moreButton = device.wait(Until.findObject(By.descContains("More")), UI_TIMEOUT) moreButton?.click() - val browseButton = device?.wait(Until.findObject(By.textContains("Browse")), UI_TIMEOUT) + val browseButton = device.wait(Until.findObject(By.textContains("Browse")), UI_TIMEOUT) browseButton?.click() assertNotNull( "DocumentsUI has not launched", - device?.wait(Until.findObject(By.pkg(DOCUMENTSUI_PACKAGE_REGEX)), UI_TIMEOUT) + device.wait(Until.findObject(By.pkg(DOCUMENTSUI_PACKAGE_REGEX)), UI_TIMEOUT) ) } @@ -192,7 +234,7 @@ class TrampolineActivityTest() { @SdkSuppress(minSdkVersion = VERSION_CODES.S, maxSdkVersion = VERSION_CODES.S_V2) fun testAndroidSWithTakeoverGetContentDisabledShouldNotReferToDocumentsUI() { val context = InstrumentationRegistry.getInstrumentation().targetContext - val intent = Intent(Intent.ACTION_GET_CONTENT) + val intent = Intent(ACTION_GET_CONTENT) intent.setClass(context, TrampolineActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.setType("image/*") @@ -201,15 +243,15 @@ class TrampolineActivityTest() { // Disable Photopicker from taking over `ACTION_GET_CONTENT`. In this situation, it // should ALWAYS defer to DocumentsUI regardless if the mimetype satisfies the // conditions. - device?.executeShellCommand( + device.executeShellCommand( "device_config put mediaprovider take_over_get_content false" ) context.startActivity(intent) assertNotNull( - device?.wait(Until.findObject(By.pkg(DOCUMENTSUI_PACKAGE_REGEX)), UI_TIMEOUT) + device.wait(Until.findObject(By.pkg(DOCUMENTSUI_PACKAGE_REGEX)), UI_TIMEOUT) ) } finally { - device?.executeShellCommand( + device.executeShellCommand( "device_config delete mediaprovider take_over_get_content" ) } diff --git a/tests/unit/com/android/documentsui/UserManagerStateTest.java b/tests/unit/com/android/documentsui/UserManagerStateTest.java index 574d65552..6f869dcd5 100644 --- a/tests/unit/com/android/documentsui/UserManagerStateTest.java +++ b/tests/unit/com/android/documentsui/UserManagerStateTest.java @@ -67,6 +67,7 @@ public class UserManagerStateTest { private static final String PERSONAL = "Personal"; private static final String WORK = "Work"; private static final String PRIVATE = "Private"; + private static final String PACKAGE_NAME = "com.android.documentsui"; /** * Assume that the current user is SYSTEM_USER. @@ -158,6 +159,20 @@ public class UserManagerStateTest { .thenReturn(mDevicePolicyManager); when(mMockContext.getResources()).thenReturn( InstrumentationRegistry.getInstrumentation().getTargetContext().getResources()); + + when(mMockContext.getPackageName()).thenReturn(PACKAGE_NAME); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mSystemUser)).thenReturn( + mMockContext); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mManagedUser)).thenReturn( + mMockContext); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mPrivateUser)).thenReturn( + mMockContext); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mOtherUser)).thenReturn( + mMockContext); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mNormalUser)).thenReturn( + mMockContext); + when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, 0, mPrimaryUser)).thenReturn( + mMockContext); } @Test diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java index 2554ea52b..01dfa1c76 100644 --- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java +++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java @@ -16,6 +16,7 @@ package com.android.documentsui.files; +import static com.android.documentsui.flags.Flags.useMaterial3; import static com.android.documentsui.testing.IntentAsserts.assertHasAction; import static com.android.documentsui.testing.IntentAsserts.assertHasData; import static com.android.documentsui.testing.IntentAsserts.assertHasExtra; @@ -31,6 +32,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import android.app.Activity; import android.app.DownloadManager; @@ -89,6 +92,8 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; @@ -108,6 +113,7 @@ public class ActionHandlerTest { private TestFeatures mFeatures; private TestConfigStore mTestConfigStore; private boolean refreshAnswer = false; + @Mock private Runnable mMockCloseSelectionBar; @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -126,6 +132,7 @@ public class ActionHandlerTest { @Before public void setUp() { + MockitoAnnotations.initMocks(this); mFeatures = new TestFeatures(); mEnv = TestEnv.create(mFeatures); mActivity = TestActivity.create(mEnv); @@ -152,6 +159,14 @@ public class ActionHandlerTest { mEnv.selectDocument(TestEnv.FILE_GIF); } + private void assertSelectionContainerClosed() { + if (useMaterial3()) { + verify(mMockCloseSelectionBar, times(1)).run(); + } else { + assertTrue(mActionModeAddons.finishActionModeCalled); + } + } + @Test public void testOpenSelectedInNewWindow() { mHandler.openSelectedInNewWindow(); @@ -195,7 +210,7 @@ public class ActionHandlerTest { @Test public void testSpringOpenDirectory() { mHandler.springOpenDirectory(TestEnv.FOLDER_0); - assertTrue(mActionModeAddons.finishActionModeCalled); + assertSelectionContainerClosed(); assertEquals(TestEnv.FOLDER_0, mEnv.state.stack.peek()); } @@ -250,7 +265,7 @@ public class ActionHandlerTest { mHandler.deleteSelectedDocuments(docs, mEnv.state.stack.peek()); mActivity.startService.assertCalled(); - assertTrue(mActionModeAddons.finishActionModeCalled); + assertSelectionContainerClosed(); } @Test @@ -810,6 +825,23 @@ public class ActionHandlerTest { assertFalse(intent.getExtras().containsKey(Intent.EXTRA_TITLE)); } + @Test + public void testViewInOwner() { + mEnv.populateStack(); + + mEnv.selectionMgr.clearSelection(); + mEnv.selectDocument(TestEnv.FILE_PNG); + + mHandler.viewInOwner(); + mActivity.assertActivityStarted(DocumentsContract.ACTION_DOCUMENT_SETTINGS); + } + + @Test + public void testOpenSettings() { + mHandler.openSettings(TestProvidersAccess.HAMMY); + mActivity.assertActivityStarted(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS); + } + private void assertRootPicked(Uri expectedUri) throws Exception { mEnv.beforeAsserts(); @@ -828,10 +860,10 @@ public class ActionHandlerTest { mEnv.searchViewManager, mEnv::lookupExecutor, mActionModeAddons, + mMockCloseSelectionBar, mClipper, - null, // clip storage, not utilized unless we venture into *jumbo* clip territory. + null, // clip storage, not utilized unless we venture into *jumbo* clip territory. mDragAndDropManager, - mEnv.injector - ); + mEnv.injector); } } diff --git a/tests/unit/com/android/documentsui/loaders/BaseLoaderTest.kt b/tests/unit/com/android/documentsui/loaders/BaseLoaderTest.kt index 62434b71f..55f83bfea 100644 --- a/tests/unit/com/android/documentsui/loaders/BaseLoaderTest.kt +++ b/tests/unit/com/android/documentsui/loaders/BaseLoaderTest.kt @@ -15,6 +15,7 @@ */ package com.android.documentsui.loaders +import android.os.Bundle import android.os.Parcel import android.provider.DocumentsContract import com.android.documentsui.DirectoryResult @@ -47,8 +48,10 @@ data class LoaderTestParams( val query: String, // The delta from now that indicates maximum age of matched files. val lastModifiedDelta: Duration?, + // The extra arguments typically supplied by search view manager. + val otherArgs: Bundle, // The number of files that are expected, for the above parameters, to be found by a loader. - val expectedCount: Int + val expectedCount: Int, ) /** diff --git a/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt b/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt index cb0735b17..44c410eff 100644 --- a/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt +++ b/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt @@ -15,13 +15,19 @@ */ package com.android.documentsui.loaders +import android.os.Bundle +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.filters.SmallTest import com.android.documentsui.ContentLock import com.android.documentsui.base.DocumentInfo +import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_RW import com.android.documentsui.testing.TestFileTypeLookup import com.android.documentsui.testing.TestProvidersAccess import java.time.Duration import junit.framework.Assert.assertEquals +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -36,15 +42,24 @@ class FolderLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes @JvmStatic @Parameters(name = "with parameters {0}") fun data() = listOf( - LoaderTestParams("", null, TOTAL_FILE_COUNT), + LoaderTestParams("", null, Bundle(), TOTAL_FILE_COUNT), // The first file is at NOW, the second at NOW - 1h, etc. - LoaderTestParams("", Duration.ofMinutes(1L), 1), - LoaderTestParams("", Duration.ofMinutes(60L + 1), 2), - LoaderTestParams("", Duration.ofMinutes(TOTAL_FILE_COUNT * 60L + 1), TOTAL_FILE_COUNT), + LoaderTestParams("", Duration.ofMinutes(1L), Bundle(), 1), + LoaderTestParams("", Duration.ofMinutes(60L + 1), Bundle(), 2), + LoaderTestParams( + "", + Duration.ofMinutes(TOTAL_FILE_COUNT * 60L + 1), + Bundle(), + TOTAL_FILE_COUNT + ), ) } + @get:Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Test + @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW) fun testLoadInBackground() { val mockProvider = mEnv.mockProviders[TestProvidersAccess.DOWNLOADS.authority] val docs = createDocuments(TOTAL_FILE_COUNT) @@ -56,7 +71,8 @@ class FolderLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes testParams.lastModifiedDelta, null, true, - arrayOf<String>("*/*") + arrayOf<String>("*/*"), + testParams.otherArgs, ) val contentLock = ContentLock() // TODO(majewski): Is there a better way to create Downloads root folder DocumentInfo? diff --git a/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt b/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt index 94012b7ff..e480337ab 100644 --- a/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt +++ b/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt @@ -15,10 +15,16 @@ */ package com.android.documentsui.loaders +import android.os.Bundle +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.provider.DocumentsContract import androidx.test.filters.SmallTest import com.android.documentsui.ContentLock import com.android.documentsui.LockingContentObserver import com.android.documentsui.base.DocumentInfo +import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_RW import com.android.documentsui.testing.TestFileTypeLookup import com.android.documentsui.testing.TestProvidersAccess import java.time.Duration @@ -27,6 +33,8 @@ import java.util.concurrent.Executors import junit.framework.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Before +import org.junit.Ignore +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -34,6 +42,12 @@ import org.junit.runners.Parameterized.Parameters private const val TOTAL_FILE_COUNT = 8 +fun createQueryArgs(vararg mimeTypes: String): Bundle { + val args = Bundle() + args.putStringArray(DocumentsContract.QUERY_ARG_MIME_TYPES, arrayOf<String>(*mimeTypes)) + return args +} + @RunWith(Parameterized::class) @SmallTest class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTest() { @@ -45,15 +59,20 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes @JvmStatic @Parameters(name = "with parameters {0}") fun data() = listOf( - LoaderTestParams("sample", null, TOTAL_FILE_COUNT), - LoaderTestParams("txt", null, 2), - LoaderTestParams("foozig", null, 0), + LoaderTestParams("sample", null, Bundle(), TOTAL_FILE_COUNT), + LoaderTestParams("txt", null, Bundle(), 2), + LoaderTestParams("foozig", null, Bundle(), 0), // The first file is at NOW, the second at NOW - 1h; expect 2. - LoaderTestParams("sample", Duration.ofMinutes(60 + 1), 2), - // TODO(b:378590632): Add test for recents. + LoaderTestParams("sample", Duration.ofMinutes(60 + 1), Bundle(), 2), + LoaderTestParams("sample", null, createQueryArgs("image/*"), 2), + LoaderTestParams("sample", null, createQueryArgs("image/*", "video/*"), 6), + LoaderTestParams("sample", null, createQueryArgs("application/pdf"), 0), ) } + @get:Rule + val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + @Before override fun setUp() { super.setUp() @@ -61,6 +80,7 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes } @Test + @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW) fun testLoadInBackground() { val mockProvider = mEnv.mockProviders[TestProvidersAccess.DOWNLOADS.authority] val docs = createDocuments(TOTAL_FILE_COUNT) @@ -72,7 +92,8 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes testParams.lastModifiedDelta, null, true, - arrayOf("*/*") + arrayOf("*/*"), + testParams.otherArgs, ) val rootIds = listOf(TestProvidersAccess.DOWNLOADS) @@ -98,10 +119,13 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes } @Test + @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW) + @Ignore("b/397095797") fun testBlankQueryAndRecency() { val userIds = listOf(TestProvidersAccess.DOWNLOADS.userId) val rootIds = listOf(TestProvidersAccess.DOWNLOADS) - val noLastModifiedQueryOptions = QueryOptions(10, null, null, true, arrayOf("*/*")) + val noLastModifiedQueryOptions = + QueryOptions(10, null, null, true, arrayOf("*/*"), Bundle()) // Blank query and no last modified duration is invalid. assertThrows(IllegalArgumentException::class.java) { |