• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.documentsui
17 
18 import android.app.Activity
19 import android.app.UiAutomation
20 import android.content.ContentResolver
21 import android.content.Context
22 import android.content.Intent
23 import android.os.Bundle
24 import android.os.RemoteException
25 import android.provider.DocumentsContract
26 import android.view.KeyEvent
27 import android.view.MotionEvent
28 import androidx.test.core.app.ActivityScenario
29 import androidx.test.platform.app.InstrumentationRegistry
30 import androidx.test.uiautomator.Configurator
31 import androidx.test.uiautomator.UiDevice
32 import com.android.documentsui.base.Features
33 import com.android.documentsui.base.Features.RuntimeFeatures
34 import com.android.documentsui.base.RootInfo
35 import com.android.documentsui.base.UserId
36 import com.android.documentsui.bots.Bots
37 import com.android.documentsui.files.FilesActivity
38 import java.io.IOException
39 import java.util.Objects
40 
41 /**
42  * Provides basic test environment for UI tests:
43  * - Launches activity
44  * - Creates and gives access to test root directories and test files
45  * - Cleans up the test environment
46  */
47 abstract class ActivityTestJunit4<T : Activity?> {
48     @JvmField
49     var bots: Bots? = null
50 
51     @JvmField
52     var device: UiDevice? = null
53 
54     @JvmField
55     var context: Context? = null
56     var userId: UserId? = null
57     var automation: UiAutomation? = null
58 
59     @JvmField
60     var features: Features? = null
61 
62     /**
63      * Returns the root that will be opened within the activity.
64      * By default tests are started with one of the test roots.
65      * Override the method if you want to open different root on start.
66      * @return Root that will be opened. Return null if you want to open activity's default root.
67      */
68     protected open var initialRoot: RootInfo? = null
69 
70     @JvmField
71     var rootDir0: RootInfo? = null
72 
73     @JvmField
74     var rootDir1: RootInfo? = null
75     protected var mResolver: ContentResolver? = null
76 
77     @JvmField
78     protected var mDocsHelper: DocumentsProviderHelper? = null
79     protected var mActivityScenario: ActivityScenario<T?>? = null
80     private var initialScreenOffTimeoutValue: String? = null
81     private var initialSleepTimeoutValue: String? = null
82 
83     protected val testingProviderAuthority: String
84         /**
85          * Returns the authority of the testing provider begin used.
86          * By default it's StubProvider's authority.
87          * @return Authority of the provider.
88          */
89         get() = StubProvider.DEFAULT_AUTHORITY
90 
91     /**
92      * Resolves testing roots.
93      */
94     @Throws(RemoteException::class)
setupTestingRootsnull95     protected fun setupTestingRoots() {
96         rootDir0 = mDocsHelper!!.getRoot(StubProvider.ROOT_0_ID)
97         rootDir1 = mDocsHelper!!.getRoot(StubProvider.ROOT_1_ID)
98         this.initialRoot = rootDir0
99     }
100 
101     @Throws(Exception::class)
setUpnull102     open fun setUp() {
103         device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
104         // NOTE: Must be the "target" context, else security checks in content provider will fail.
105         context = InstrumentationRegistry.getInstrumentation().getTargetContext()
106         userId = UserId.DEFAULT_USER
107         automation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
108         features = RuntimeFeatures(context!!.getResources(), null)
109 
110         bots = Bots(device, automation, context, TIMEOUT)
111 
112         Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_MOUSE)
113 
114         mResolver = context!!.getContentResolver()
115         mDocsHelper = DocumentsProviderHelper(
116             userId, this.testingProviderAuthority, context,
117             this.testingProviderAuthority
118         )
119 
120         device!!.setOrientationNatural()
121         device!!.pressKeyCode(KeyEvent.KEYCODE_WAKEUP)
122 
123         disableScreenOffAndSleepTimeouts()
124 
125         setupTestingRoots()
126 
127         launchActivity()
128         resetStorage()
129 
130         // Since at the launch of activity, ROOT_0 and ROOT_1 have no files, drawer will
131         // automatically open for phone devices. Espresso register click() as (x, y) MotionEvents,
132         // so if a drawer is on top of a file we want to select, it will actually click the drawer.
133         // Thus to start a clean state, we always try to close first.
134         bots!!.roots!!.closeDrawer()
135 
136         // Configure the provider back to default.
137         mDocsHelper!!.configure(null, Bundle.EMPTY)
138     }
139 
140     @Throws(Exception::class)
tearDownnull141     open fun tearDown() {
142         device!!.unfreezeRotation()
143         mDocsHelper!!.cleanUp()
144         restoreScreenOffAndSleepTimeouts()
145         mActivityScenario!!.close()
146     }
147 
launchActivitynull148     protected fun launchActivity() {
149         val intent = Intent(context, FilesActivity::class.java)
150         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
151         if (this.initialRoot != null) {
152             intent.setAction(Intent.ACTION_VIEW)
153             intent.setDataAndType(
154                 this.initialRoot!!.uri,
155                 DocumentsContract.Root.MIME_TYPE_ITEM
156             )
157         }
158         mActivityScenario = ActivityScenario.launch(intent)
159     }
160 
161     @Throws(RemoteException::class)
resetStoragenull162     protected fun resetStorage() {
163         mDocsHelper!!.clear(null, null)
164         device!!.waitForIdle()
165     }
166 
167     @Throws(RemoteException::class)
initTestFilesnull168     protected open fun initTestFiles() {
169         mDocsHelper!!.createFolder(this.initialRoot, dirName1)
170         mDocsHelper!!.createDocument(this.initialRoot, "text/plain", fileName1)
171         mDocsHelper!!.createDocument(this.initialRoot, "image/png", fileName2)
172         mDocsHelper!!.createDocumentWithFlags(
173             initialRoot!!.documentId,
174             "text/plain",
175             fileNameNoRename,
176             DocumentsContract.Document.FLAG_SUPPORTS_WRITE
177         )
178 
179         mDocsHelper!!.createDocument(rootDir1, "text/plain", fileName3)
180         mDocsHelper!!.createDocument(rootDir1, "text/plain", fileName4)
181     }
182 
183     @Throws(IOException::class)
disableScreenOffAndSleepTimeoutsnull184     private fun disableScreenOffAndSleepTimeouts() {
185         initialScreenOffTimeoutValue = device!!.executeShellCommand(
186             "settings get system screen_off_timeout"
187         )
188         initialSleepTimeoutValue = device!!.executeShellCommand(
189             "settings get secure sleep_timeout"
190         )
191         device!!.executeShellCommand("settings put system screen_off_timeout -1")
192         device!!.executeShellCommand("settings put secure sleep_timeout -1")
193     }
194 
195     @Throws(IOException::class)
restoreScreenOffAndSleepTimeoutsnull196     private fun restoreScreenOffAndSleepTimeouts() {
197         Objects.requireNonNull<String?>(initialScreenOffTimeoutValue)
198         Objects.requireNonNull<String?>(initialSleepTimeoutValue)
199         try {
200             device!!.executeShellCommand(
201                 "settings put system screen_off_timeout $initialScreenOffTimeoutValue"
202             )
203             device!!.executeShellCommand(
204                 "settings put secure sleep_timeout $initialSleepTimeoutValue"
205             )
206         } finally {
207             initialScreenOffTimeoutValue = null
208             initialSleepTimeoutValue = null
209         }
210     }
211 
212     companion object {
213         // Testing files. For custom ones, override initTestFiles().
214         const val dirName1 = "Dir1"
215         const val childDir1 = "ChildDir1"
216         const val fileName1 = "file1.log"
217         const val fileName2 = "file12.png"
218         const val fileName3 = "anotherFile0.log"
219         const val fileName4 = "poodles.text"
220         const val fileNameNoRename = "NO_RENAMEfile.txt"
221         const val TIMEOUT = 5000
222     }
223 }
224