diff options
-rw-r--r-- | tests/Android.bp | 3 | ||||
-rw-r--r-- | tests/functional/com/android/documentsui/ActivityTestJunit4.kt | 211 | ||||
-rw-r--r-- | tests/functional/com/android/documentsui/SortDocumentUiTest.java | 39 |
3 files changed, 245 insertions, 8 deletions
diff --git a/tests/Android.bp b/tests/Android.bp index 2b2daa45d..f67d344dd 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -25,9 +25,11 @@ java_defaults { ], static_libs: [ + "androidx.test.core", "androidx.test.espresso.core", "androidx.test.ext.truth", "androidx.test.rules", + "androidx.test.ext.junit", "androidx.test.uiautomator_uiautomator", "docsui-flags-aconfig-java-lib", "flag-junit", @@ -93,6 +95,7 @@ android_library { srcs: [ "common/**/*.java", "functional/**/*.java", + "functional/**/*.kt", "unit/**/*.java", ], diff --git a/tests/functional/com/android/documentsui/ActivityTestJunit4.kt b/tests/functional/com/android/documentsui/ActivityTestJunit4.kt new file mode 100644 index 000000000..daab0cdb4 --- /dev/null +++ b/tests/functional/com/android/documentsui/ActivityTestJunit4.kt @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2024 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.app.Activity +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 +import android.view.MotionEvent +import androidx.test.core.app.ActivityScenario +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.Configurator +import androidx.test.uiautomator.UiDevice +import com.android.documentsui.base.Features +import com.android.documentsui.base.Features.RuntimeFeatures +import com.android.documentsui.base.RootInfo +import com.android.documentsui.base.UserId +import com.android.documentsui.bots.Bots +import com.android.documentsui.files.FilesActivity +import java.io.IOException +import java.util.Objects + +/** + * Provides basic test environment for UI tests: + * - Launches activity + * - Creates and gives access to test root directories and test files + * - Cleans up the test environment + */ +abstract class ActivityTestJunit4<T : Activity?> { + @JvmField + var bots: Bots? = null + var device: UiDevice? = null + var context: Context? = null + var userId: UserId? = null + var automation: UiAutomation? = null + + var features: Features? = null + + /** + * Returns the root that will be opened within the activity. + * By default tests are started with one of the test roots. + * 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 + var rootDir1: RootInfo? = null + protected var mResolver: ContentResolver? = null + + @JvmField + protected var mDocsHelper: DocumentsProviderHelper? = null + protected var mActivityScenario: ActivityScenario<T?>? = null + private var initialScreenOffTimeoutValue: String? = null + private var initialSleepTimeoutValue: String? = null + + protected val testingProviderAuthority: String + /** + * Returns the authority of the testing provider begin used. + * By default it's StubProvider's authority. + * @return Authority of the provider. + */ + get() = StubProvider.DEFAULT_AUTHORITY + + /** + * Resolves testing roots. + */ + @Throws(RemoteException::class) + protected fun setupTestingRoots() { + this.initialRoot = mDocsHelper!!.getRoot(StubProvider.ROOT_0_ID) + rootDir1 = mDocsHelper!!.getRoot(StubProvider.ROOT_1_ID) + } + + @Throws(Exception::class) + open fun setUp() { + device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + // NOTE: Must be the "target" context, else security checks in content provider will fail. + context = InstrumentationRegistry.getInstrumentation().getTargetContext() + userId = UserId.DEFAULT_USER + automation = InstrumentationRegistry.getInstrumentation().getUiAutomation() + features = RuntimeFeatures(context!!.getResources(), null) + + bots = Bots(device, automation, context, TIMEOUT) + + Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE) + + mResolver = context!!.getContentResolver() + mDocsHelper = DocumentsProviderHelper( + userId, this.testingProviderAuthority, context, + this.testingProviderAuthority + ) + + device!!.setOrientationNatural() + device!!.pressKeyCode(KeyEvent.KEYCODE_WAKEUP) + + 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) + } + + @Throws(Exception::class) + open fun tearDown() { + device!!.unfreezeRotation() + mDocsHelper!!.cleanUp() + restoreScreenOffAndSleepTimeouts() + mActivityScenario!!.close() + } + + protected fun launchActivity() { + val intent = Intent(context, FilesActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + if (this.initialRoot != null) { + intent.setAction(Intent.ACTION_VIEW) + intent.setDataAndType( + this.initialRoot!!.uri, + DocumentsContract.Root.MIME_TYPE_ITEM + ) + } + mActivityScenario = ActivityScenario.launch(intent) + } + + @Throws(RemoteException::class) + protected fun resetStorage() { + mDocsHelper!!.clear(null, null) + device!!.waitForIdle() + } + + @Throws(RemoteException::class) + protected fun initTestFiles() { + mDocsHelper!!.createFolder(this.initialRoot, dirName1) + mDocsHelper!!.createDocument(this.initialRoot, "text/plain", fileName1) + mDocsHelper!!.createDocument(this.initialRoot, "image/png", fileName2) + mDocsHelper!!.createDocumentWithFlags( + initialRoot!!.documentId, + "text/plain", + fileNameNoRename, + DocumentsContract.Document.FLAG_SUPPORTS_WRITE + ) + + mDocsHelper!!.createDocument(rootDir1, "text/plain", fileName3) + mDocsHelper!!.createDocument(rootDir1, "text/plain", fileName4) + } + + @Throws(IOException::class) + private fun disableScreenOffAndSleepTimeouts() { + initialScreenOffTimeoutValue = device!!.executeShellCommand( + "settings get system screen_off_timeout" + ) + initialSleepTimeoutValue = device!!.executeShellCommand( + "settings get secure sleep_timeout" + ) + device!!.executeShellCommand("settings put system screen_off_timeout -1") + device!!.executeShellCommand("settings put secure sleep_timeout -1") + } + + @Throws(IOException::class) + private fun restoreScreenOffAndSleepTimeouts() { + Objects.requireNonNull<String?>(initialScreenOffTimeoutValue) + Objects.requireNonNull<String?>(initialSleepTimeoutValue) + try { + device!!.executeShellCommand( + "settings put system screen_off_timeout $initialScreenOffTimeoutValue" + ) + device!!.executeShellCommand( + "settings put secure sleep_timeout $initialSleepTimeoutValue" + ) + } finally { + initialScreenOffTimeoutValue = null + initialSleepTimeoutValue = null + } + } + + companion object { + // Testing files. For custom ones, override initTestFiles(). + const val dirName1 = "Dir1" + const val fileName1 = "file1.log" + const val fileName2 = "file12.png" + const val fileName3 = "anotherFile0.log" + const val fileName4 = "poodles.text" + const val fileNameNoRename = "NO_RENAMEfile.txt" + const val TIMEOUT = 5000 + } +} diff --git a/tests/functional/com/android/documentsui/SortDocumentUiTest.java b/tests/functional/com/android/documentsui/SortDocumentUiTest.java index a6907d680..53bc372e0 100644 --- a/tests/functional/com/android/documentsui/SortDocumentUiTest.java +++ b/tests/functional/com/android/documentsui/SortDocumentUiTest.java @@ -19,13 +19,20 @@ package com.android.documentsui; import android.net.Uri; import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; import com.android.documentsui.files.FilesActivity; import com.android.documentsui.sorting.SortDimension; import com.android.documentsui.sorting.SortModel; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + @LargeTest -public class SortDocumentUiTest extends ActivityTest<FilesActivity> { +@RunWith(AndroidJUnit4.class) +public class SortDocumentUiTest extends ActivityTestJunit4<FilesActivity> { private static final String DIR_1 = "folder_1"; private static final String DIR_2 = "dir_2"; @@ -52,10 +59,6 @@ public class SortDocumentUiTest extends ActivityTest<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); - public SortDocumentUiTest() { - super(FilesActivity.class); - } - private static String[] reverse(String[] array) { String[] ret = new String[array.length]; @@ -66,12 +69,17 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { return ret; } - @Override + @Before public void setUp() throws Exception { super.setUp(); bots.roots.closeDrawer(); } + @After + public void tearDown() throws Exception { + super.tearDown(); + } + private void initFiles() throws Exception { initFiles(0); } @@ -84,24 +92,26 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { */ private void initFiles(long sleep) throws Exception { for (int i = 0; i < FILES.length; ++i) { - Uri uri = mDocsHelper.createDocument(rootDir0, MIMES[i], FILES[i]); + Uri uri = mDocsHelper.createDocument(getInitialRoot(), MIMES[i], FILES[i]); mDocsHelper.writeDocument(uri, FILES[i].getBytes()); Thread.sleep(sleep); } for (String dir : DIRS) { - mDocsHelper.createFolder(rootDir0, dir); + mDocsHelper.createFolder(getInitialRoot(), dir); Thread.sleep(sleep); } } + @Test public void testDefaultSortByNameAscending() throws Exception { initFiles(); bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_NAME_ASC); } + @Test public void testSortByName_Descending_listMode() throws Exception { initFiles(); @@ -112,6 +122,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_DESC, FILES_IN_NAME_DESC); } + @Test public void testSortBySize_Ascending_listMode() throws Exception { initFiles(); @@ -121,6 +132,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_ASC); } + @Test public void testSortBySize_Descending_listMode() throws Exception { initFiles(); @@ -130,6 +142,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_DESC); } + @Test public void testSortByModified_Ascending_listMode() throws Exception { initFiles(1000); @@ -139,6 +152,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS, FILES); } + @Test public void testSortByModified_Descending_listMode() throws Exception { initFiles(1000); @@ -148,6 +162,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_MODIFIED_DESC, FILES_IN_MODIFIED_DESC); } + @Test public void testSortByType_Ascending_listMode() throws Exception { initFiles(); @@ -158,6 +173,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_TYPE_ASC); } + @Test public void testSortByType_Descending_listMode() throws Exception { initFiles(); @@ -168,6 +184,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_TYPE_DESC); } + @Test public void testSortByName_Descending_gridMode() throws Exception { initFiles(); @@ -178,6 +195,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_DESC, FILES_IN_NAME_DESC); } + @Test public void testSortBySize_Ascending_gridMode() throws Exception { initFiles(); @@ -187,6 +205,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_ASC); } + @Test public void testSortBySize_Descending_gridMode() throws Exception { initFiles(); @@ -196,6 +215,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_SIZE_DESC); } + @Test public void testSortByModified_Ascending_gridMode() throws Exception { initFiles(1000); @@ -205,6 +225,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS, FILES); } + @Test public void testSortByModified_Descending_gridMode() throws Exception { initFiles(1000); @@ -214,6 +235,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_MODIFIED_DESC, FILES_IN_MODIFIED_DESC); } + @Test public void testSortByType_Ascending_gridMode() throws Exception { initFiles(); @@ -224,6 +246,7 @@ public class SortDocumentUiTest extends ActivityTest<FilesActivity> { bots.directory.assertOrder(DIRS_IN_NAME_ASC, FILES_IN_TYPE_ASC); } + @Test public void testSortByType_Descending_gridMode() throws Exception { initFiles(); |