blob: 9a694a3bb23698aade3c5f2e3f3a29ab2d47a374 [file] [log] [blame]
/*
* Copyright (C) 2021 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.server.wm.flicker.layers
import androidx.test.filters.FlakyTest
import com.android.server.wm.InitRule
import com.android.server.wm.flicker.TestComponents
import com.android.server.wm.flicker.assertFailureFact
import com.android.server.wm.flicker.assertThrows
import com.android.server.wm.flicker.readLayerTraceFromFile
import com.android.server.wm.traces.common.Cache
import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher
import com.android.server.wm.traces.common.layers.LayersTrace
import com.android.server.wm.traces.common.region.Region
import com.android.server.wm.traces.common.subjects.FlickerSubjectException
import com.android.server.wm.traces.common.subjects.layers.LayersTraceSubject
import com.google.common.truth.Truth
import org.junit.Before
import org.junit.ClassRule
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runners.MethodSorters
/**
* Contains [LayersTraceSubject] tests. To run this test: `atest
* FlickerLibTest:LayersTraceSubjectTest`
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class LayersTraceSubjectTest {
@Before
fun before() {
Cache.clear()
}
@Test
fun exceptionContainsDebugInfo() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_launch_split_screen.pb", legacyTrace = true)
val error =
assertThrows<AssertionError> { LayersTraceSubject(layersTraceEntries).isEmpty() }
Truth.assertThat(error).hasMessageThat().contains("Trace start")
Truth.assertThat(error).hasMessageThat().contains("Trace end")
}
@Test
fun testCanDetectEmptyRegionFromLayerTrace() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_emptyregion.pb", legacyTrace = true)
val failure =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(layersTraceEntries)
.visibleRegion()
.coversAtLeast(DISPLAY_REGION)
.forAllEntries()
error("Assertion should not have passed")
}
assertFailureFact(failure, "Region to test").contains(DISPLAY_REGION.toString())
assertFailureFact(failure, "Uncovered region").contains("SkRegion((0,1440,1440,2880))")
}
@Test
fun testCanInspectBeginning() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_launch_split_screen.pb", legacyTrace = true)
LayersTraceSubject(layersTraceEntries)
.first()
.isVisible(ComponentNameMatcher.NAV_BAR)
.notContains(TestComponents.DOCKER_STACK_DIVIDER)
.isVisible(TestComponents.LAUNCHER)
}
@Test
fun testCanInspectEnd() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_launch_split_screen.pb", legacyTrace = true)
LayersTraceSubject(layersTraceEntries)
.last()
.isVisible(ComponentNameMatcher.NAV_BAR)
.isVisible(TestComponents.DOCKER_STACK_DIVIDER)
}
@Test
fun testAssertionsOnRange() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_launch_split_screen.pb", legacyTrace = true)
LayersTraceSubject(layersTraceEntries)
.isVisible(ComponentNameMatcher.NAV_BAR)
.isInvisible(TestComponents.DOCKER_STACK_DIVIDER)
.forSystemUpTimeRange(90480846872160L, 90480994138424L)
LayersTraceSubject(layersTraceEntries)
.isVisible(ComponentNameMatcher.NAV_BAR)
.isVisible(TestComponents.DOCKER_STACK_DIVIDER)
.forSystemUpTimeRange(90491795074136L, 90493757372977L)
}
@Test
fun testCanDetectChangingAssertions() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_launch_split_screen.pb", legacyTrace = true)
LayersTraceSubject(layersTraceEntries)
.isVisible(ComponentNameMatcher.NAV_BAR)
.notContains(TestComponents.DOCKER_STACK_DIVIDER)
.then()
.isVisible(ComponentNameMatcher.NAV_BAR)
.isInvisible(TestComponents.DOCKER_STACK_DIVIDER)
.then()
.isVisible(ComponentNameMatcher.NAV_BAR)
.isVisible(TestComponents.DOCKER_STACK_DIVIDER)
.forAllEntries()
}
@FlakyTest
@Test
fun testCanDetectIncorrectVisibilityFromLayerTrace() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_invalid_layer_visibility.pb", legacyTrace = true)
val error =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(layersTraceEntries)
.isVisible(TestComponents.SIMPLE_APP)
.then()
.isInvisible(TestComponents.SIMPLE_APP)
.forAllEntries()
}
Truth.assertThat(error)
.hasMessageThat()
.contains("layers_trace_invalid_layer_visibility.pb")
Truth.assertThat(error).hasMessageThat().contains("2d22h13m14s303ms")
Truth.assertThat(error).hasMessageThat().contains("!isVisible")
Truth.assertThat(error)
.hasMessageThat()
.contains(
"com.android.server.wm.flicker.testapp/" +
"com.android.server.wm.flicker.testapp.SimpleActivity#0 is visible"
)
}
@Test
fun testCanDetectInvalidVisibleLayerForMoreThanOneConsecutiveEntry() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_invalid_visible_layers.pb", legacyTrace = true)
val error =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(layersTraceEntries)
.visibleLayersShownMoreThanOneConsecutiveEntry()
.forAllEntries()
error("Assertion should not have passed")
}
Truth.assertThat(error).hasMessageThat().contains("2d18h35m56s397ms")
Truth.assertThat(error).hasMessageThat().contains("StatusBar#0")
Truth.assertThat(error).hasMessageThat().contains("is not visible for 2 entries")
}
private fun testCanDetectVisibleLayersMoreThanOneConsecutiveEntry(trace: LayersTrace) {
LayersTraceSubject(trace).visibleLayersShownMoreThanOneConsecutiveEntry().forAllEntries()
}
@Test
fun testCanDetectVisibleLayersMoreThanOneConsecutiveEntry() {
testCanDetectVisibleLayersMoreThanOneConsecutiveEntry(
readLayerTraceFromFile("layers_trace_snapshot_visible.pb", legacyTrace = true)
)
}
@Test
fun testCanIgnoreLayerEqualNameInVisibleLayersMoreThanOneConsecutiveEntry() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_invalid_visible_layers.pb", legacyTrace = true)
LayersTraceSubject(layersTraceEntries)
.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(ComponentNameMatcher.STATUS_BAR))
.forAllEntries()
}
@Test
fun testCanIgnoreLayerShorterNameInVisibleLayersMoreThanOneConsecutiveEntry() {
val layersTraceEntries =
readLayerTraceFromFile("one_visible_layer_launcher_trace.pb", legacyTrace = true)
val launcherComponent =
ComponentNameMatcher(
"com.google.android.apps.nexuslauncher",
"com.google.android.apps.nexuslauncher.NexusLauncherActivity#1"
)
LayersTraceSubject(layersTraceEntries)
.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherComponent))
.forAllEntries()
}
private fun detectRootLayer(fileName: String, legacyTrace: Boolean = false) {
val layersTrace = readLayerTraceFromFile(fileName, legacyTrace = legacyTrace)
for (entry in layersTrace.entries) {
val rootLayers = entry.children
Truth.assertWithMessage("Does not have any root layer")
.that(rootLayers.size)
.isGreaterThan(0)
val firstParentId = rootLayers.first().parentId
Truth.assertWithMessage("Has multiple root layers")
.that(rootLayers.all { it.parentId == firstParentId })
.isTrue()
}
}
@Test
fun testCanDetectRootLayer() {
detectRootLayer("layers_trace_root.pb", legacyTrace = true)
}
@Test
fun testCanDetectRootLayerAOSP() {
detectRootLayer("layers_trace_root_aosp.pb", legacyTrace = true)
}
@Test
fun canTestLayerOccludedBySplashScreenLayerIsNotVisible() {
val trace = readLayerTraceFromFile("layers_trace_occluded.pb", legacyTrace = true)
val entry =
LayersTraceSubject(trace)
.getEntryBySystemUpTime(1700382131522L, byElapsedTimestamp = true)
entry.isInvisible(TestComponents.SIMPLE_APP)
entry.isVisible(ComponentNameMatcher.SPLASH_SCREEN)
}
@Test
fun testCanDetectLayerExpanding() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_openchrome.pb", legacyTrace = true)
val animation =
LayersTraceSubject(layersTraceEntries).layers("animation-leash of app_transition#0")
// Obtain the area of each layer and checks if the next area is
// greater or equal to the previous one
val areas =
animation.map {
val region = it.layer?.visibleRegion ?: Region()
val area = region.width * region.height
area
}
val expanding = areas.zipWithNext { currentArea, nextArea -> nextArea >= currentArea }
Truth.assertWithMessage("Animation leash should be expanding")
.that(expanding.all { it })
.isTrue()
}
@Test
fun checkVisibleRegionAppMinusPipLayer() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_pip_wmshell.pb", legacyTrace = true)
val subject = LayersTraceSubject(layersTraceEntries).last()
try {
subject.visibleRegion(TestComponents.FIXED_APP).coversExactly(DISPLAY_REGION_ROTATED)
error(
"Layer is partially covered by a Pip layer and should not cover the device screen"
)
} catch (e: AssertionError) {
val pipRegion = subject.visibleRegion(TestComponents.PIP_APP).region
val expectedWithoutPip = DISPLAY_REGION_ROTATED.minus(pipRegion)
subject.visibleRegion(TestComponents.FIXED_APP).coversExactly(expectedWithoutPip)
}
}
@Test
fun checkVisibleRegionAppPlusPipLayer() {
val layersTraceEntries =
readLayerTraceFromFile("layers_trace_pip_wmshell.pb", legacyTrace = true)
val subject = LayersTraceSubject(layersTraceEntries).last()
val pipRegion = subject.visibleRegion(TestComponents.PIP_APP).region
subject
.visibleRegion(TestComponents.FIXED_APP)
.plus(pipRegion)
.coversExactly(DISPLAY_REGION_ROTATED)
}
@Test
fun checkCanDetectSplashScreen() {
val trace = readLayerTraceFromFile("layers_trace_splashscreen.pb", legacyTrace = true)
val newLayer =
ComponentNameMatcher(
"com.android.server.wm.flicker.testapp",
"com.android.server.wm.flicker.testapp.SimpleActivity"
)
LayersTraceSubject(trace)
.isVisible(TestComponents.LAUNCHER)
.then()
.isSplashScreenVisibleFor(TestComponents.SIMPLE_APP, isOptional = false)
.then()
.isVisible(TestComponents.SIMPLE_APP)
.forAllEntries()
val failure =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(trace)
.isVisible(TestComponents.LAUNCHER)
.then()
.isVisible(TestComponents.SIMPLE_APP)
.forAllEntries()
}
Truth.assertThat(failure).hasMessageThat().contains("Is Invisible")
}
@Test
fun checkCanDetectMissingSplashScreen() {
val trace = readLayerTraceFromFile("layers_trace_splashscreen.pb", legacyTrace = true)
val newLayer =
ComponentNameMatcher(
"com.android.server.wm.flicker.testapp",
"com.android.server.wm.flicker.testapp.SimpleActivity"
)
// No splashscreen because no matching activity record
var failure =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(trace)
.first()
.isSplashScreenVisibleFor(TestComponents.SIMPLE_APP)
}
Truth.assertThat(failure).hasMessageThat().contains("Could not find Activity Record layer")
// No splashscreen for target activity record
failure =
assertThrows<FlickerSubjectException> {
LayersTraceSubject(trace).first().isSplashScreenVisibleFor(TestComponents.LAUNCHER)
}
Truth.assertThat(failure).hasMessageThat().contains("No splash screen visible")
}
companion object {
private val DISPLAY_REGION = Region.from(0, 0, 1440, 2880)
private val DISPLAY_REGION_ROTATED = Region.from(0, 0, 2160, 1080)
private const val SHELL_APP_PACKAGE = "com.android.wm.shell.flicker.testapp"
private val FIXED_APP =
ComponentNameMatcher(SHELL_APP_PACKAGE, "$SHELL_APP_PACKAGE.FixedActivity")
private val PIP_APP =
ComponentNameMatcher(SHELL_APP_PACKAGE, "$SHELL_APP_PACKAGE.PipActivity")
@ClassRule @JvmField val initRule = InitRule()
}
}