• 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.bedstead.testapps
17 
18 import com.android.bedstead.enterprise.annotations.EnsureHasDelegate
19 import com.android.bedstead.harrier.AnnotationExecutorUtil
20 import com.android.bedstead.harrier.BedsteadServiceLocator
21 import com.android.bedstead.harrier.DeviceStateComponent
22 import com.android.bedstead.harrier.annotations.EnsureTestAppDoesNotHavePermission
23 import com.android.bedstead.harrier.annotations.EnsureTestAppHasAppOp
24 import com.android.bedstead.harrier.annotations.EnsureTestAppHasPermission
25 import com.android.bedstead.harrier.annotations.EnsureTestAppInstalled
26 import com.android.bedstead.harrier.annotations.FailureMode
27 import com.android.bedstead.harrier.annotations.enterprise.AdditionalQueryParameters
28 import com.android.bedstead.nene.TestApis.packages
29 import com.android.bedstead.nene.exceptions.NeneException
30 import com.android.bedstead.nene.users.UserReference
31 import com.android.bedstead.remotedpc.RemoteDpc
32 import com.android.bedstead.remotedpc.RemoteTestApp
33 import com.android.bedstead.testapp.TestApp
34 import com.android.bedstead.testapp.TestAppInstance
35 import com.android.bedstead.testapp.TestAppProvider
36 import com.android.queryable.annotations.Query
37 import com.google.errorprone.annotations.CanIgnoreReturnValue
38 import org.junit.Assume
39 
40 /**
41  * Manages test apps for device state tests.
42  *
43  * @param locator provides access to other dependencies.
44  */
45 class TestAppsComponent(locator: BedsteadServiceLocator) : DeviceStateComponent {
46 
47     private val testApps: MutableMap<String, TestAppInstance> = HashMap()
48     private val installedTestApps: MutableSet<TestAppInstance> = HashSet()
49     private val uninstalledTestApps: MutableSet<TestAppInstance> = HashSet()
50     val testAppProvider = TestAppProvider()
51     private val _additionalQueryParameters: MutableMap<String, Query> = mutableMapOf()
52     val additionalQueryParameters: Map<String, Query>
53         get() = _additionalQueryParameters
54 
55     /**
56      * See [EnsureTestAppHasPermission]
57      */
ensureTestAppHasPermissionnull58     fun ensureTestAppHasPermission(
59         testAppKey: String,
60         permissions: Array<String>,
61         minVersion: Int,
62         maxVersion: Int,
63         failureMode: FailureMode
64     ) {
65         checkTestAppExistsWithKey(testAppKey)
66         try {
67             testApps[testAppKey]!!.permissions()
68                 .withPermissionOnVersionBetween(minVersion, maxVersion, *permissions)
69         } catch (e: NeneException) {
70             if (failureMode == FailureMode.SKIP && e.message!!.contains("Cannot grant") ||
71                 e.message!!.contains("Error granting")
72             ) {
73                 AnnotationExecutorUtil.failOrSkip(e.message, FailureMode.SKIP)
74             } else {
75                 throw e
76             }
77         }
78     }
79 
80     /**
81      * See [EnsureTestAppDoesNotHavePermission]
82      */
ensureTestAppDoesNotHavePermissionnull83     fun ensureTestAppDoesNotHavePermission(
84         testAppKey: String,
85         permissions: Array<String>,
86         failureMode: FailureMode
87     ) {
88         checkTestAppExistsWithKey(testAppKey)
89         try {
90             testApps[testAppKey]!!.permissions().withoutPermission(*permissions)
91         } catch (e: NeneException) {
92             if (failureMode == FailureMode.SKIP) {
93                 AnnotationExecutorUtil.failOrSkip(e.message, FailureMode.SKIP)
94             } else {
95                 throw e
96             }
97         }
98     }
99 
100     /**
101      * See [EnsureTestAppHasAppOp]
102      */
ensureTestAppHasAppOpnull103     fun ensureTestAppHasAppOp(
104         testAppKey: String,
105         appOps: Array<String>,
106         minVersion: Int,
107         maxVersion: Int
108     ) {
109         checkTestAppExistsWithKey(testAppKey)
110         testApps[testAppKey]!!.permissions()
111             .withAppOpOnVersionBetween(minVersion, maxVersion, *appOps)
112     }
113 
checkTestAppExistsWithKeynull114     private fun checkTestAppExistsWithKey(testAppKey: String) {
115         if (!testApps.containsKey(testAppKey)) {
116             throw NeneException(
117                 "No testapp with key " + testAppKey + ". Use @EnsureTestAppInstalled." +
118                         " Valid Test apps: " + testApps
119             )
120         }
121     }
122 
123     /**
124      * See [EnsureTestAppInstalled]
125      */
126     @CanIgnoreReturnValue
ensureTestAppInstallednull127     fun ensureTestAppInstalled(testApp: TestApp, user: UserReference): TestAppInstance? {
128         return ensureTestAppInstalled(key = null, testApp, user)
129     }
130 
131     /**
132      * See [EnsureTestAppInstalled]
133      */
134     @CanIgnoreReturnValue
ensureTestAppInstallednull135     fun ensureTestAppInstalled(
136         key: String?,
137         testApp: TestApp,
138         user: UserReference
139     ): TestAppInstance? {
140         if (additionalQueryParameters.isNotEmpty()) {
141             Assume.assumeFalse(
142                 "b/276740719 - we don't support custom delegates",
143                 EnsureHasDelegate.DELEGATE_KEY == key
144             )
145         }
146         val pkg = packages().find(testApp.packageName())
147         val testAppInstance: TestAppInstance?
148         if (pkg != null && packages().find(testApp.packageName()).installedOnUser(user)) {
149             testAppInstance = testApp.instance(user)
150         } else {
151             // TODO: Consider if we want to record that we've started it so we can stop it after
152             //  if needed?
153             user.start()
154             testAppInstance = testApp.install(user)
155             installedTestApps.add(testAppInstance)
156         }
157         if (key != null) {
158             testApps[key] = testAppInstance
159         }
160         return testAppInstance
161     }
162 
163     /**
164      * Uninstalls a test app for the given user.
165      * If the test app is not installed, this method does nothing.
166      *
167      * @param testApp The test app to uninstall.
168      * @param user The user for whom the test app should be uninstalled.
169      */
ensureTestAppNotInstallednull170     fun ensureTestAppNotInstalled(testApp: TestApp, user: UserReference?) {
171         val pkg = packages().find(testApp.packageName())
172         if (pkg == null || !packages().find(testApp.packageName()).installedOnUser(user)) {
173             return
174         }
175         val instance = testApp.instance(user)
176         if (installedTestApps.contains(instance)) {
177             installedTestApps.remove(instance)
178         } else {
179             uninstalledTestApps.add(instance)
180         }
181         testApp.uninstall(user)
182     }
183 
184     /**
185      * Gets TestAppInstance for the given [key]
186      * @throws NeneException if there is no TestAppInstance for a given [key]
187      */
testAppnull188     fun testApp(key: String): TestAppInstance {
189         return testApps[key]
190             ?: throw NeneException("No testapp with given key. Use @EnsureTestAppInstalled")
191     }
192 
193     /**
194      * Saves [RemoteDpc] for the given [key]
195      */
addRemoteDpcTestAppnull196     fun addRemoteDpcTestApp(key: String, remoteDpc: RemoteDpc) {
197         testApps[key] = remoteDpc
198     }
199 
200     /**
201      * See [EnsureTestAppInstalled]
202      */
203     @CanIgnoreReturnValue
ensureTestAppInstallednull204     fun ensureTestAppInstalled(
205         key: String,
206         query: Query,
207         user: UserReference
208     ): TestAppInstance? {
209         val testApp: TestApp = testAppProvider.query(query).applyAnnotation(
210             additionalQueryParameters.getOrDefault(key, null)
211         ).get()
212         return ensureTestAppInstalled(
213             key,
214             testApp,
215             user
216         )
217     }
218 
prepareTestStatenull219     override fun prepareTestState() {
220         testAppProvider.snapshot()
221     }
222 
teardownNonShareableStatenull223     override fun teardownNonShareableState() {
224         testApps.clear()
225         testAppProvider.restore()
226         _additionalQueryParameters.clear()
227     }
228 
teardownShareableStatenull229     override fun teardownShareableState() {
230         for (installedTestApp in installedTestApps) {
231             installedTestApp.uninstall()
232         }
233         installedTestApps.clear()
234 
235         for (uninstalledTestApp in uninstalledTestApps) {
236             uninstalledTestApp.testApp().install(uninstalledTestApp.user())
237         }
238         uninstalledTestApps.clear()
239     }
240 
addQueryParametersnull241     fun addQueryParameters(annotation: AdditionalQueryParameters) {
242         _additionalQueryParameters[annotation.forTestApp] = annotation.query
243     }
244 }
245