Update ScreenshotRuleAsserter to support capturing a screenshot from a specified display ID
Test: Ran v2/wear-engprod/suites/test-mapping-presubmit-cloud-wear
Flag: EXEMPTED test only
Bug: 401504441
Change-Id: Ib82a42c0cba2b7d913c080cd470b78b4ee625dc7
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
index de510ac..bc70c81 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotAsserter.kt
@@ -18,12 +18,14 @@
import android.graphics.Bitmap
import android.graphics.Rect
+import android.view.Display.DEFAULT_DISPLAY
import androidx.test.runner.screenshot.Screenshot
import platform.test.screenshot.matchers.BitmapMatcher
import platform.test.screenshot.matchers.PixelPerfectMatcher
interface ScreenshotAsserter {
fun assertGoldenImage(goldenId: String, areas: List<Rect>)
+
fun assertGoldenImage(goldenId: String)
}
@@ -52,5 +54,31 @@
* The [Bitmap] produced by [captureStrategy] will be recycled immediately after assertions are
* completed. Therefore, do not retain references to created [Bitmap]s.
*/
- val captureStrategy: BitmapSupplier = { Screenshot.capture().bitmap }
-)
+ val captureStrategy: BitmapSupplier = DEFAULT_SCREENSHOT_CAPTURE,
+) {
+ var displayId: Int = DEFAULT_DISPLAY
+ private set(value) {
+ require(captureStrategy == DEFAULT_SCREENSHOT_CAPTURE) {
+ "display ID can only be set when default capture strategy is in use"
+ }
+ field = value
+ }
+
+ constructor(
+ matcher: BitmapMatcher = PixelPerfectMatcher(),
+ beforeScreenshot: () -> Unit = {},
+ afterScreenshot: () -> Unit = {},
+ displayId: Int,
+ ) : this(
+ matcher = matcher,
+ beforeScreenshot = beforeScreenshot,
+ afterScreenshot = afterScreenshot,
+ captureStrategy = DEFAULT_SCREENSHOT_CAPTURE,
+ ) {
+ this.displayId = displayId
+ }
+
+ companion object {
+ val DEFAULT_SCREENSHOT_CAPTURE: BitmapSupplier = { Screenshot.capture().bitmap }
+ }
+}
diff --git a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
index 73539a3..fee6d6e 100644
--- a/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
+++ b/libraries/screenshot/src/main/java/platform/test/screenshot/ScreenshotTestRule.kt
@@ -24,14 +24,16 @@
import android.graphics.Rect
import android.platform.uiautomatorhelpers.DeviceHelpers.shell
import android.provider.Settings.System
+import android.view.Display.DEFAULT_DISPLAY
import androidx.annotation.VisibleForTesting
+import androidx.core.graphics.createBitmap
import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.runner.screenshot.Screenshot
import com.android.internal.app.SimpleIconFactory
import java.io.FileNotFoundException
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+import platform.test.screenshot.ScreenshotAsserterConfig.Companion.DEFAULT_SCREENSHOT_CAPTURE
import platform.test.screenshot.matchers.BitmapMatcher
import platform.test.screenshot.matchers.MSSIMMatcher
import platform.test.screenshot.matchers.MatchResult
@@ -306,6 +308,7 @@
.setOnBeforeScreenshot(config.beforeScreenshot)
.setOnAfterScreenshot(config.afterScreenshot)
.setScreenshotProvider(config.captureStrategy)
+ .setDisplayId(config.displayId)
.build()
}
@@ -348,16 +351,26 @@
typealias BitmapSupplier = () -> Bitmap
-/** Implements a screenshot asserter based on the ScreenshotRule */
-class ScreenshotRuleAsserter private constructor(private val rule: ScreenshotTestRule) :
- ScreenshotAsserter {
- // use the most constraining matcher as default
- private var matcher: BitmapMatcher = PixelPerfectMatcher()
- private var beforeScreenshot: Runnable? = null
- private var afterScreenshot: Runnable? = null
+/** Implements a screenshot asserter based on the ScreenshotRule. */
+class ScreenshotRuleAsserter
+private constructor(
+ private val rule: ScreenshotTestRule,
+ private val beforeScreenshot: Runnable?,
+ private val afterScreenshot: Runnable?,
+ private val screenShotter: BitmapSupplier,
+ private val matcher: BitmapMatcher,
+ private val targetDisplayId: Int,
+) : ScreenshotAsserter {
+ init {
+ require(screenShotter == DEFAULT_SCREENSHOT_CAPTURE || targetDisplayId == DEFAULT_DISPLAY) {
+ "DEFAULT_SCREENSHOT_CAPTURE must be used when non default display ID is set."
+ }
+ }
- // use the instrumentation screenshot as default
- private var screenShotter: BitmapSupplier = { Screenshot.capture().bitmap }
+ private val displayScreenShotter: (Int) -> Bitmap = { _displayId ->
+ InstrumentationRegistry.getInstrumentation().uiAutomation.takeScreenshot(_displayId)
+ ?: createBitmap(0, 0)
+ }
private var pointerLocationSetting: Int
get() = shell("settings get system ${System.POINTER_LOCATION}").trim().toIntOrNull() ?: 0
@@ -379,7 +392,7 @@
runBeforeScreenshot()
var actual: Bitmap? = null
try {
- actual = screenShotter()
+ actual = captureScreenshot(targetDisplayId)
rule.assertBitmapAgainstGolden(actual, goldenId, matcher)
} finally {
actual?.recycle()
@@ -392,7 +405,7 @@
runBeforeScreenshot()
var actual: Bitmap? = null
try {
- actual = screenShotter()
+ actual = captureScreenshot(targetDisplayId)
rule.assertBitmapAgainstGolden(actual, goldenId, matcher, areas)
} finally {
actual?.recycle()
@@ -400,6 +413,13 @@
}
}
+ private fun captureScreenshot(displayId: Int): Bitmap =
+ if (screenShotter != DEFAULT_SCREENSHOT_CAPTURE) {
+ screenShotter()
+ } else {
+ displayScreenShotter(displayId)
+ }
+
private fun runBeforeScreenshot() {
// Disable Sensitive Content Redaction
shell("settings put secure disable_secure_windows 1")
@@ -424,25 +444,49 @@
@Deprecated("Use ScreenshotAsserterFactory instead")
class Builder(private val rule: ScreenshotTestRule) {
- private var asserter = ScreenshotRuleAsserter(rule)
+ private var matcher: BitmapMatcher? = null
+ private var screenshotProvider: BitmapSupplier? = null
+ private var beforeScreenshot: Runnable? = null
+ private var afterScreenshot: Runnable? = null
+ private var displayId: Int? = null
- fun withMatcher(matcher: BitmapMatcher): Builder = apply { asserter.matcher = matcher }
+ fun withMatcher(matcher: BitmapMatcher): Builder = apply { this.matcher = matcher }
/**
* The [Bitmap] produced by [screenshotProvider] will be recycled immediately after
* assertions are completed. Therefore, do not retain references to created [Bitmap]s.
*/
fun setScreenshotProvider(screenshotProvider: BitmapSupplier): Builder = apply {
- asserter.screenShotter = screenshotProvider
+ require(displayId == null || displayId == DEFAULT_DISPLAY) {
+ "Custom screenshot provider is unsupported when a display ID is specified."
+ }
+ this.screenshotProvider = screenshotProvider
}
- fun setOnBeforeScreenshot(run: Runnable): Builder = apply {
- asserter.beforeScreenshot = run
+ fun setOnBeforeScreenshot(run: Runnable): Builder = apply { this.beforeScreenshot = run }
+
+ fun setOnAfterScreenshot(run: Runnable): Builder = apply { this.afterScreenshot = run }
+
+ fun setDisplayId(displayId: Int): Builder = apply {
+ require(
+ displayId == DEFAULT_DISPLAY ||
+ screenshotProvider == null ||
+ screenshotProvider == DEFAULT_SCREENSHOT_CAPTURE
+ ) {
+ "Custom display ID is unsupported when a custom screenshot provider is used"
+ }
+ this.displayId = displayId
}
- fun setOnAfterScreenshot(run: Runnable): Builder = apply { asserter.afterScreenshot = run }
-
- fun build(): ScreenshotAsserter = asserter.also { asserter = ScreenshotRuleAsserter(rule) }
+ fun build(): ScreenshotAsserter =
+ ScreenshotRuleAsserter(
+ rule = rule,
+ beforeScreenshot = beforeScreenshot,
+ afterScreenshot = afterScreenshot,
+ screenShotter = screenshotProvider ?: DEFAULT_SCREENSHOT_CAPTURE,
+ matcher = matcher ?: PixelPerfectMatcher(),
+ targetDisplayId = displayId ?: DEFAULT_DISPLAY,
+ )
}
}