Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1257711e authored by Nataniel Borges's avatar Nataniel Borges Committed by Android (Google) Code Review
Browse files

Merge changes from topics "media-projection", "merge-specs", "single-assertion"

* changes:
  Use MediaProjection for screen recording
  Split app pair tests into individual assertions
  Compatibilize tests with new runner
parents c89c2f8e 9d110213
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@
    <uses-permission android:name="android.permission.DUMP" />
    <!-- Run layers trace -->
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
    <!-- Capture screen recording -->
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
    <!-- Workaround grant runtime permission exception from b/152733071 -->
    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
    <uses-permission android:name="android.permission.READ_LOGS"/>
+0 −2
Original line number Diff line number Diff line
@@ -11,8 +11,6 @@
        <option name="force-skip-system-props" value="true" />
        <!-- set WM tracing verbose level to all -->
        <option name="run-command" value="cmd window tracing level all" />
        <!-- inform WM to log all transactions -->
        <option name="run-command" value="cmd window tracing transaction" />
        <!-- restart launcher to activate TAPL -->
        <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
    </target_preparer>
+34 −61
Original line number Diff line number Diff line
@@ -18,59 +18,59 @@ package com.android.wm.shell.flicker

import android.graphics.Region
import android.view.Surface
import com.android.server.wm.flicker.dsl.EventLogAssertion
import com.android.server.wm.flicker.dsl.LayersAssertion
import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.FlickerTestBase.Companion.DOCKED_STACK_DIVIDER

@JvmOverloads
fun LayersAssertion.appPairsDividerIsVisible(
fun LayersAssertionBuilder.appPairsDividerIsVisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("appPairsDividerIsVisible", bugId, enabled) {
        this.showsLayer(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
        this.isVisible(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
    }
}

@JvmOverloads
fun LayersAssertion.appPairsDividerIsInvisible(
fun LayersAssertionBuilder.appPairsDividerIsInvisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("appPairsDividerIsInVisible", bugId, enabled) {
        this.hasNotLayer(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
        this.notExists(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
    }
}

@JvmOverloads
fun LayersAssertion.appPairsDividerBecomesVisible(
fun LayersAssertionBuilder.appPairsDividerBecomesVisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    all("dividerLayerBecomesVisible") {
        this.hidesLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
    all("dividerLayerBecomesVisible", bugId, enabled) {
        this.hidesLayer(DOCKED_STACK_DIVIDER)
                .then()
                .showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
                .showsLayer(DOCKED_STACK_DIVIDER)
    }
}

@JvmOverloads
fun LayersAssertion.dockedStackDividerIsVisible(
fun LayersAssertionBuilder.dockedStackDividerIsVisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("dockedStackDividerIsVisible", bugId, enabled) {
        this.showsLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
        this.isVisible(DOCKED_STACK_DIVIDER)
    }
}

@JvmOverloads
fun LayersAssertion.dockedStackDividerBecomesVisible(
fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    all("dividerLayerBecomesVisible") {
    all("dividerLayerBecomesVisible", bugId, enabled) {
        this.hidesLayer(DOCKED_STACK_DIVIDER)
                .then()
                .showsLayer(DOCKED_STACK_DIVIDER)
@@ -78,11 +78,11 @@ fun LayersAssertion.dockedStackDividerBecomesVisible(
}

@JvmOverloads
fun LayersAssertion.dockedStackDividerBecomesInvisible(
fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    all("dividerLayerBecomesInvisible") {
    all("dividerLayerBecomesInvisible", bugId, enabled) {
        this.showsLayer(DOCKED_STACK_DIVIDER)
                .then()
                .hidesLayer(DOCKED_STACK_DIVIDER)
@@ -90,71 +90,63 @@ fun LayersAssertion.dockedStackDividerBecomesInvisible(
}

@JvmOverloads
fun LayersAssertion.dockedStackDividerIsInvisible(
fun LayersAssertionBuilder.dockedStackDividerIsInvisible(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("dockedStackDividerIsInvisible", bugId, enabled) {
        this.hasNotLayer(FlickerTestBase.DOCKED_STACK_DIVIDER)
        this.notExists(DOCKED_STACK_DIVIDER)
    }
}

@JvmOverloads
fun LayersAssertion.appPairsPrimaryBoundsIsVisible(
fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
    rotation: Int,
    primaryLayerName: String,
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("PrimaryAppBounds", bugId, enabled) {
        val entry = this.trace.entries.firstOrNull()
                ?: throw IllegalStateException("Trace is empty")
        val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
    }
}

@JvmOverloads
fun LayersAssertion.appPairsSecondaryBoundsIsVisible(
fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
    rotation: Int,
    secondaryLayerName: String,
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("SecondaryAppBounds", bugId, enabled) {
        val entry = this.trace.entries.firstOrNull()
                ?: throw IllegalStateException("Trace is empty")
        val dividerRegion = entry.getVisibleBounds(FlickerTestBase.APP_PAIR_SPLIT_DIVIDER)
        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
    }
}

@JvmOverloads
fun LayersAssertion.dockedStackPrimaryBoundsIsVisible(
fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
    rotation: Int,
    primaryLayerName: String,
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("PrimaryAppBounds", bugId, enabled) {
        val entry = this.trace.entries.firstOrNull()
                ?: throw IllegalStateException("Trace is empty")
        val dividerRegion = entry.getVisibleBounds(FlickerTestBase.DOCKED_STACK_DIVIDER)
        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
        this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
    }
}

@JvmOverloads
fun LayersAssertion.dockedStackSecondaryBoundsIsVisible(
fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
    rotation: Int,
    secondaryLayerName: String,
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    end("SecondaryAppBounds", bugId, enabled) {
        val entry = this.trace.entries.firstOrNull()
                ?: throw IllegalStateException("Trace is empty")
        val dividerRegion = entry.getVisibleBounds(FlickerTestBase.DOCKED_STACK_DIVIDER)
        val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
        this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
    }
}
@@ -162,11 +154,11 @@ fun LayersAssertion.dockedStackSecondaryBoundsIsVisible(
fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region {
    val displayBounds = WindowUtils.getDisplayBounds(rotation)
    return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
        Region(0, 0, displayBounds.getBounds().right,
                dividerRegion.getBounds().bottom - WindowUtils.dockedStackDividerInset)
        Region(0, 0, displayBounds.bounds.right,
                dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset)
    } else {
        Region(0, 0, dividerRegion.getBounds().left,
                dividerRegion.getBounds().right - WindowUtils.dockedStackDividerInset)
        Region(0, 0, dividerRegion.bounds.left,
                dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset)
    }
}

@@ -174,31 +166,12 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region {
    val displayBounds = WindowUtils.getDisplayBounds(rotation)
    return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
        Region(0,
                dividerRegion.getBounds().bottom - WindowUtils.dockedStackDividerInset,
                displayBounds.getBounds().right,
                displayBounds.getBounds().bottom - WindowUtils.dockedStackDividerInset)
                dividerRegion.bounds.bottom - WindowUtils.dockedStackDividerInset,
                displayBounds.bounds.right,
                displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
    } else {
        Region(dividerRegion.getBounds().right, 0,
                displayBounds.getBounds().right,
                displayBounds.getBounds().bottom - WindowUtils.dockedStackDividerInset)
    }
}

fun EventLogAssertion.focusChanges(
    vararg windows: String,
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    all(enabled = enabled, bugId = bugId) {
        this.focusChanges(windows)
    }
}

fun EventLogAssertion.focusDoesNotChange(
    bugId: Int = 0,
    enabled: Boolean = bugId == 0
) {
    all(enabled = enabled, bugId = bugId) {
        this.focusDoesNotChange()
        Region(dividerRegion.bounds.right, 0,
                displayBounds.bounds.right,
                displayBounds.bounds.bottom - WindowUtils.dockedStackDividerInset)
    }
}
+0 −24
Original line number Diff line number Diff line
@@ -17,28 +17,4 @@
package com.android.wm.shell.flicker

const val IME_WINDOW_NAME = "InputMethod"
const val PIP_MENU_WINDOW_NAME = "PipMenuActivity"

const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
const val TEST_APP_PACKAGE_NAME = "com.android.wm.shell.flicker.testapp"

// Test App > Pip Activity
const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp"
const val TEST_APP_PIP_MENU_ACTION_NO_OP = "No-Op"
const val TEST_APP_PIP_MENU_ACTION_ON = "On"
const val TEST_APP_PIP_MENU_ACTION_OFF = "Off"
const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear"

// Test App > Ime Activity
const val TEST_APP_IME_ACTIVITY_LABEL = "ImeApp"
const val TEST_APP_IME_ACTIVITY_ACTION_OPEN_IME =
        "com.android.wm.shell.flicker.testapp.action.OPEN_IME"
const val TEST_APP_IME_ACTIVITY_ACTION_CLOSE_IME =
        "com.android.wm.shell.flicker.testapp.action.CLOSE_IME"
// Test App > Test Activity
const val TEST_APP_FIXED_ACTIVITY_LABEL = "FixedApp"

// Test App > SplitScreen Activity
const val TEST_APP_SPLITSCREEN_PRIMARY_LABEL = "SplitScreenPrimaryApp"
const val TEST_APP_SPLITSCREEN_SECONDARY_LABEL = "SplitScreenSecondaryApp"
const val TEST_APP_NONRESIZEABLE_LABEL = "NonResizeableApp"
+0 −232
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.wm.shell.flicker.apppairs

import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.wm.shell.flicker.appPairsDividerIsInvisible
import com.android.wm.shell.flicker.appPairsDividerIsVisible
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.TEST_REPETITIONS
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized

/**
 * Test AppPairs launch.
 * To run this test: `atest WMShellFlickerTests:AppPairsTest`
 */
@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class AppPairsTest(
    rotationName: String,
    rotation: Int
) : AppPairsTestBase(rotationName, rotation) {
    private val appPairsSetup: FlickerBuilder
        get() = FlickerBuilder(instrumentation).apply {
            val testLaunchActivity = "launch_appPairs_primary_secondary_activities"
            withTestName {
                testLaunchActivity
            }
            setup {
                eachRun {
                    uiDevice.wakeUpAndGoToHomeScreen()
                    primaryApp.launchViaIntent()
                    secondaryApp.launchViaIntent()
                    nonResizeableApp.launchViaIntent()
                    updateTasksId()
                }
            }
            teardown {
                eachRun {
                    executeShellCommand(composePairsCommand(
                            primaryTaskId, secondaryTaskId, false /* pair */))
                    executeShellCommand(composePairsCommand(
                            primaryTaskId, nonResizeableTaskId, false /* pair */))
                    primaryApp.exit()
                    secondaryApp.exit()
                    nonResizeableApp.exit()
                }
            }
            assertions {
                layersTrace {
                    navBarLayerIsAlwaysVisible()
                    statusBarLayerIsAlwaysVisible()
                }
                windowManagerTrace {
                    navBarWindowIsAlwaysVisible()
                    statusBarWindowIsAlwaysVisible()
                }
            }
        }

    @Test
    fun testAppPairs_pairPrimaryAndSecondaryApps() {
        val testTag = "testAppPairs_pairPrimaryAndSecondaryApps"
        runWithFlicker(appPairsSetup) {
            withTestName { testTag }
            repeat {
                TEST_REPETITIONS
            }
            transitions {
                // TODO pair apps through normal UX flow
                executeShellCommand(composePairsCommand(
                        primaryTaskId, secondaryTaskId, true /* pair */))
                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
            }
            assertions {
                layersTrace {
                    appPairsDividerIsVisible()
                    end("appsEndingBounds", enabled = false) {
                        val entry = this.trace.entries.firstOrNull()
                                ?: throw IllegalStateException("Trace is empty")
                        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
                        this.hasVisibleRegion(primaryApp.defaultWindowName,
                                appPairsHelper.getPrimaryBounds(dividerRegion))
                                .and()
                                .hasVisibleRegion(secondaryApp.defaultWindowName,
                                        appPairsHelper.getSecondaryBounds(dividerRegion))
                    }
                }
                windowManagerTrace {
                    end {
                        showsAppWindow(primaryApp.defaultWindowName)
                                .and()
                                .showsAppWindow(secondaryApp.defaultWindowName)
                    }
                }
            }
        }
    }

    @Test
    fun testAppPairs_unpairPrimaryAndSecondary() {
        val testTag = "testAppPairs_unpairPrimaryAndSecondary"
        runWithFlicker(appPairsSetup) {
            withTestName { testTag }
            repeat {
                TEST_REPETITIONS
            }
            setup {
                executeShellCommand(composePairsCommand(
                        primaryTaskId, secondaryTaskId, true /* pair */))
                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
            }
            transitions {
                // TODO pair apps through normal UX flow
                executeShellCommand(composePairsCommand(
                        primaryTaskId, secondaryTaskId, false /* pair */))
                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
            }
            assertions {
                layersTrace {
                    appPairsDividerIsInvisible()
                    start("appsStartingBounds", enabled = false) {
                        val entry = this.trace.entries.firstOrNull()
                                ?: throw IllegalStateException("Trace is empty")
                        val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
                        this.hasVisibleRegion(primaryApp.defaultWindowName,
                                appPairsHelper.getPrimaryBounds(dividerRegion))
                                .and()
                                .hasVisibleRegion(secondaryApp.defaultWindowName,
                                        appPairsHelper.getSecondaryBounds(dividerRegion))
                    }
                    end("appsEndingBounds", enabled = false) {
                        this.hasNotLayer(primaryApp.defaultWindowName)
                                .and()
                                .hasNotLayer(secondaryApp.defaultWindowName)
                    }
                }
                windowManagerTrace {
                    end {
                        hidesAppWindow(primaryApp.defaultWindowName)
                                .and()
                                .hidesAppWindow(secondaryApp.defaultWindowName)
                    }
                }
            }
        }
    }

    @Test
    fun testAppPairs_canNotPairNonResizeableApps() {
        val testTag = "testAppPairs_canNotPairNonResizeableApps"
        runWithFlicker(appPairsSetup) {
            withTestName { testTag }
            repeat {
                TEST_REPETITIONS
            }
            transitions {
                // TODO pair apps through normal UX flow
                executeShellCommand(composePairsCommand(
                    primaryTaskId, nonResizeableTaskId, true /* pair */))
                SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
            }

            assertions {
                layersTrace {
                    appPairsDividerIsInvisible()
                }
                windowManagerTrace {
                    end {
                        showsAppWindow(nonResizeableApp.defaultWindowName)
                            .and()
                            .hidesAppWindow(primaryApp.defaultWindowName)
                    }
                }
            }
        }
    }

    fun updateTasksId() {
        if (primaryAppComponent != null) {
            primaryTaskId = getTaskIdForActivity(
                    primaryAppComponent.packageName, primaryAppComponent.className).toString()
        }
        if (secondaryAppComponent != null) {
            secondaryTaskId = getTaskIdForActivity(
                    secondaryAppComponent.packageName, secondaryAppComponent.className).toString()
        }
        if (nonResizeableAppComponent != null) {
            nonResizeableTaskId = getTaskIdForActivity(
                    nonResizeableAppComponent.packageName,
                    nonResizeableAppComponent.className).toString()
        }
    }

    companion object {
        @Parameterized.Parameters(name = "{0}")
        @JvmStatic
        fun getParams(): Collection<Array<Any>> {
            val supportedRotations = intArrayOf(Surface.ROTATION_0)
            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
        }
    }
}
 No newline at end of file
Loading