1 /*
2  * Copyright (C) 2023 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 
17 package com.android.server.wm.flicker.activityembedding.open
18 
19 import android.graphics.Rect
20 import android.platform.test.annotations.Presubmit
21 import android.tools.flicker.junit.FlickerParametersRunnerFactory
22 import android.tools.flicker.legacy.FlickerBuilder
23 import android.tools.flicker.legacy.LegacyFlickerTest
24 import android.tools.flicker.legacy.LegacyFlickerTestFactory
25 import android.tools.flicker.subject.region.RegionSubject
26 import androidx.test.filters.RequiresDevice
27 import com.android.server.wm.flicker.activityembedding.ActivityEmbeddingTestBase
28 import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
29 import org.junit.FixMethodOrder
30 import org.junit.Test
31 import org.junit.runner.RunWith
32 import org.junit.runners.MethodSorters
33 import org.junit.runners.Parameterized
34 
35 /**
36  * Test launching a trampoline activity and resulting in a split state.
37  *
38  * Setup: Launch Activity A in fullscreen.
39  *
40  * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes
41  * itself, end up in split A|B.
42  *
43  * To run this test: `atest FlickerTestsActivityEmbedding:OpenTrampolineActivityTest`
44  */
45 @RequiresDevice
46 @RunWith(Parameterized::class)
47 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
48 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
49 class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) {
<lambda>null50     override val transition: FlickerBuilder.() -> Unit = {
51         setup {
52             tapl.setExpectedRotationCheckEnabled(false)
53             testApp.launchViaIntent(wmHelper)
54             startDisplayBounds =
55                 wmHelper.currentState.layerState.physicalDisplayBounds
56                     ?: error("Can't get display bounds")
57         }
58         transitions { testApp.launchTrampolineActivity(wmHelper) }
59         teardown {
60             tapl.goHome()
61             testApp.exit(wmHelper)
62         }
63     }
64 
65     /** Trampoline activity should finish itself before the end of this test. */
66     @Presubmit
67     @Test
trampolineActivityFinishesnull68     fun trampolineActivityFinishes() {
69         flicker.assertWmEnd {
70             notContains(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT)
71         }
72     }
73 
74     @Presubmit
75     @Test
trampolineLayerNeverVisiblenull76     fun trampolineLayerNeverVisible() {
77         flicker.assertLayers {
78             isInvisible(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT)
79         }
80     }
81 
82     /** Main activity is always visible throughout this test. */
83     @Presubmit
84     @Test
mainActivityWindowAlwaysVisiblenull85     fun mainActivityWindowAlwaysVisible() {
86         flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
87     }
88 
89     // TODO(b/289140963): After this is fixed, assert the main Activity window is visible
90     //  throughout the test instead.
91     /** Main activity layer is visible before and after the transition. */
92     @Presubmit
93     @Test
mainActivityLayerAlwaysVisiblenull94     fun mainActivityLayerAlwaysVisible() {
95         flicker.assertLayersStart { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
96         flicker.assertLayersEnd { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
97     }
98 
99     /** Secondary activity is launched from the trampoline activity. */
100     @Presubmit
101     @Test
secondaryActivityWindowLaunchedFromTrampolinenull102     fun secondaryActivityWindowLaunchedFromTrampoline() {
103         flicker.assertWm {
104             notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
105                 .then()
106                 .isAppWindowInvisible(
107                     ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
108                     isOptional = true
109                 )
110                 .then()
111                 .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
112         }
113     }
114 
115     /** Secondary activity is launched from the trampoline activity. */
116     @Presubmit
117     @Test
secondaryActivityLayerLaunchedFromTrampolinenull118     fun secondaryActivityLayerLaunchedFromTrampoline() {
119         flicker.assertLayers {
120             isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
121                 .then()
122                 .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
123         }
124     }
125 
126     /** Main activity should go from fullscreen to being a split with secondary activity. */
127     @Presubmit
128     @Test
mainActivityWindowGoesFromFullscreenToSplitnull129     fun mainActivityWindowGoesFromFullscreenToSplit() {
130         flicker.assertWm {
131             this.invoke("mainActivityStartsInFullscreen") {
132                     it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
133                         .coversExactly(startDisplayBounds)
134                 }
135                 // Begin of transition.
136                 .then()
137                 .isAppWindowInvisible(
138                     ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
139                     isOptional = true
140                 )
141                 .then()
142                 .invoke("mainAndSecondaryInSplit") {
143                     val mainActivityRegion =
144                         RegionSubject(
145                             it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
146                                 .region,
147                             it.timestamp
148                         )
149                     val secondaryActivityRegion =
150                         RegionSubject(
151                             it.visibleRegion(
152                                     ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT
153                                 )
154                                 .region,
155                             it.timestamp
156                         )
157                     check { "height" }
158                         .that(mainActivityRegion.region.bounds.height())
159                         .isEqual(secondaryActivityRegion.region.bounds.height())
160                     check { "width" }
161                         .that(mainActivityRegion.region.bounds.width())
162                         .isEqual(secondaryActivityRegion.region.bounds.width())
163                     mainActivityRegion
164                         .plus(secondaryActivityRegion.region)
165                         .coversExactly(startDisplayBounds)
166                 }
167         }
168     }
169 
170     /** Main activity should go from fullscreen to being a split with secondary activity. */
171     @Test
mainActivityLayerGoesFromFullscreenToSplitnull172     fun mainActivityLayerGoesFromFullscreenToSplit() {
173         flicker.assertLayers {
174             this.invoke("mainActivityStartsInFullscreen") {
175                     it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
176                         .coversExactly(startDisplayBounds)
177                 }
178                 .then()
179                 .isInvisible(
180                     ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT,
181                     isOptional = true
182                 )
183                 .then()
184                 .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
185         }
186         flicker.assertLayersEnd {
187             val leftLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
188             val rightLayerRegion =
189                 visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
190             // Compare dimensions of two splits, given we're using default split attributes,
191             // both activities take up the same visible size on the display.
192             check { "height" }
193                 .that(leftLayerRegion.region.bounds.height())
194                 .isEqual(rightLayerRegion.region.bounds.height())
195             check { "width" }
196                 .that(leftLayerRegion.region.bounds.width())
197                 .isEqual(rightLayerRegion.region.bounds.width())
198             leftLayerRegion.notOverlaps(rightLayerRegion.region)
199             // Layers of two activities sum to be fullscreen size on display.
200             leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
201         }
202     }
203 
204     @Test
visibleLayersShownMoreThanOneConsecutiveEntrynull205     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
206         super.visibleLayersShownMoreThanOneConsecutiveEntry()
207     }
208 
209     companion object {
210         /** {@inheritDoc} */
211         private var startDisplayBounds = Rect()
212 
213         /**
214          * Creates the test configurations.
215          *
216          * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
217          * navigation modes.
218          */
219         @Parameterized.Parameters(name = "{0}")
220         @JvmStatic
getParamsnull221         fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
222     }
223 }
224