blob: ad581b24f02cb1ddc2865de01c08eac52f4a0f80 [file] [log] [blame]
/*
* 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.server.wm.flicker
import android.graphics.Region
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
import com.google.common.truth.Truth
/** Truth subject for [WindowManagerTrace] objects. */
class WmTraceSubject private constructor(
fm: FailureMetadata,
subject: WindowManagerTrace
) : Subject<WmTraceSubject, WindowManagerTrace>(fm, subject) {
private val assertionsChecker = AssertionsChecker<WindowManagerTraceEntry>()
private var newAssertion = true
private fun addAssertion(name: String, assertion: TraceAssertion<WindowManagerTraceEntry>) {
if (newAssertion) {
assertionsChecker.add(assertion, name)
} else {
assertionsChecker.append(assertion, name)
}
}
/**
* Run the assertions for all trace entries
*/
fun forAllEntries() {
test()
}
/**
* Run the assertions for all trace entries within the specified time range
*/
fun forRange(startTime: Long, endTime: Long) {
assertionsChecker.filterByRange(startTime, endTime)
test()
}
/**
* Signal that the last assertion set is complete. The next assertion added will start a new
* set of assertions.
*
* E.g.: checkA().then().checkB()
*
* Will produce two sets of assertions (checkA) and (checkB) and checkB will only be checked
* after checkA passes.
*/
fun then(): WmTraceSubject {
newAssertion = true
assertionsChecker.checkChangingAssertions()
return this
}
/**
* Signal that the last assertion set is not complete. The next assertion added will be
* appended to the current set of assertions.
*
* E.g.: checkA().and().checkB()
*
* Will produce one sets of assertions (checkA, checkB) and the assertion will only pass is both
* checkA and checkB pass.
*/
fun and(): WmTraceSubject {
newAssertion = false
return this
}
/**
* Ignores the first entries in the trace, until the first assertion passes. If it reaches the
* end of the trace without passing any assertion, return a failure with the name/reason from
* the first assertion
*
* @return
*/
fun skipUntilFirstAssertion(): WmTraceSubject {
assertionsChecker.skipUntilFirstAssertion()
return this
}
/**
* Run the assertions only in the first trace entry
*/
fun inTheBeginning() {
if (actual().entries.isEmpty()) {
fail("No entries found.")
}
assertionsChecker.checkFirstEntry()
test()
}
/**
* Run the assertions only in the last trace entry
*/
fun atTheEnd() {
if (actual().entries.isEmpty()) {
fail("No entries found.")
}
assertionsChecker.checkLastEntry()
test()
}
private fun test() {
val failures = assertionsChecker.test(actual().entries)
if (failures.isNotEmpty()) {
val failureTracePath = actual()!!.source
val failureLogs = failures.joinToString("\n") { it.toString() }
var tracePath = ""
if (failureTracePath.isPresent) {
tracePath = """
WindowManager Trace can be found in: ${failureTracePath.get().toAbsolutePath()}
Checksum: ${actual()!!.sourceChecksum}
""".trimIndent()
}
fail(tracePath + failureLogs)
}
}
/**
* Checks if the non-app window with title containing [partialWindowTitle] exists above the app
* windows and is visible
*
* @param partialWindowTitle window title to search
*/
fun showsAboveAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("showsAboveAppWindow($partialWindowTitle)") {
p: WindowManagerTraceEntry -> p.isAboveAppWindow(partialWindowTitle)
}
return this
}
/**
* Checks if the non-app window with title containing [partialWindowTitle] exists above the app
* windows and is invisible
*
* @param partialWindowTitle window title to search
*/
fun hidesAboveAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("hidesAboveAppWindow($partialWindowTitle)") {
it.isAboveAppWindow(partialWindowTitle, isVisible = false)
}
return this
}
/**
* Checks if the non-app window with title containing [partialWindowTitle] exists below the app
* windows and is visible
*
* @param partialWindowTitle window title to search
*/
fun showsBelowAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("showsBelowAppWindow($partialWindowTitle)") {
it.isBelowAppWindow(partialWindowTitle)
}
return this
}
/**
* Checks if the non-app window with title containing [partialWindowTitle] exists below the app
* windows and is invisible
*
* @param partialWindowTitle window title to search
*/
fun hidesBelowAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("hidesBelowAppWindow($partialWindowTitle)") {
it.isBelowAppWindow(partialWindowTitle, isVisible = false)
}
return this
}
/**
* Checks if non-app window with title containing the [partialWindowTitle] exists above or
* below the app windows and is visible
*
* @param partialWindowTitle window title to search
*/
fun showsNonAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("showsNonAppWindow($partialWindowTitle)") {
it.hasNonAppWindow(partialWindowTitle)
}
return this
}
/**
* Checks if non-app window with title containing the [partialWindowTitle] exists above or
* below the app windows and is invisible
*
* @param partialWindowTitle window title to search
*/
fun hidesNonAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("hidesNonAppWindow($partialWindowTitle)") {
it.hasNonAppWindow(partialWindowTitle, isVisible = false)
}
return this
}
/**
* Checks if app window with title containing the [partialWindowTitle] is on top
*
* @param partialWindowTitle window title to search
*/
fun showsAppWindowOnTop(partialWindowTitle: String): WmTraceSubject {
addAssertion("showsAppWindowOnTop($partialWindowTitle)") {
var result = it.isAppWindowVisible(partialWindowTitle)
if (result.passed()) {
result = it.isVisibleAppWindowOnTop(partialWindowTitle)
}
result
}
return this
}
/**
* Checks if app window with title containing the [partialWindowTitle] is not on top
*
* @param partialWindowTitle window title to search
*/
fun appWindowNotOnTop(partialWindowTitle: String): WmTraceSubject {
addAssertion("hidesAppWindowOnTop($partialWindowTitle)") { entry: WindowManagerTraceEntry ->
var result = entry.isAppWindowVisible(partialWindowTitle).negate()
if (result.failed()) {
result = entry.isVisibleAppWindowOnTop(partialWindowTitle).negate()
}
result
}
return this
}
/**
* Checks if app window with title containing the [partialWindowTitle] is visible
*
* @param partialWindowTitle window title to search
*/
fun showsAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("showsAppWindow($partialWindowTitle)") {
it.isAppWindowVisible(partialWindowTitle)
}
return this
}
/**
* Checks if app window with title containing the [partialWindowTitle] is invisible
*
* @param partialWindowTitle window title to search
*/
fun hidesAppWindow(partialWindowTitle: String): WmTraceSubject {
addAssertion("hidesAppWindow($partialWindowTitle)") {
it.isAppWindowVisible(partialWindowTitle).negate()
}
return this
}
fun coversAtLeastRegion(partialWindowTitle: String, region: Region) = apply {
addAssertion("coversAtLeastRegion($partialWindowTitle, $region)") {
it.coversAtLeastRegion(partialWindowTitle, region)
}
}
fun coversAtMostRegion(partialWindowTitle: String, region: Region) = apply {
addAssertion("coversAtMostRegion($partialWindowTitle, $region)") {
it.coversAtMostRegion(partialWindowTitle, region)
}
}
companion object {
// Boiler-plate Subject.Factory for WmTraceSubject
private val FACTORY = Factory { fm: FailureMetadata, subject: WindowManagerTrace ->
WmTraceSubject(fm, subject)
}
// User-defined entry point
@JvmStatic
fun assertThat(entry: WindowManagerTrace): WmTraceSubject {
return Truth.assertAbout(FACTORY).that(entry)
}
// User-defined entry point
@JvmStatic
fun assertThat(result: TransitionResult): WmTraceSubject {
val entries = WindowManagerTrace.parseFrom(
result.windowManagerTrace,
result.windowManagerTracePath,
result.windowManagerTraceChecksum)
return Truth.assertWithMessage(result.toString()).about(FACTORY).that(entries)
}
// Static method for getting the subject factory (for use with assertAbout())
@JvmStatic
fun entries(): Factory<WmTraceSubject, WindowManagerTrace> {
return FACTORY
}
}
}