1 /* 2 * Copyright 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.input.screenshot 18 19 import android.content.Context 20 import android.graphics.Bitmap 21 import android.os.Build 22 import androidx.activity.ComponentActivity 23 import androidx.compose.foundation.Image 24 import androidx.compose.ui.platform.ViewRootForTest 25 import androidx.compose.ui.test.junit4.createAndroidComposeRule 26 import androidx.compose.ui.test.onRoot 27 import androidx.compose.ui.graphics.asImageBitmap 28 import org.junit.rules.RuleChain 29 import org.junit.rules.TestRule 30 import org.junit.runner.Description 31 import org.junit.runners.model.Statement 32 import platform.test.screenshot.DeviceEmulationRule 33 import platform.test.screenshot.DeviceEmulationSpec 34 import platform.test.screenshot.MaterialYouColorsRule 35 import platform.test.screenshot.ScreenshotTestRule 36 import platform.test.screenshot.getEmulatedDevicePathConfig 37 38 /** A rule for Input screenshot diff tests. */ 39 class InputScreenshotTestRule( 40 emulationSpec: DeviceEmulationSpec, 41 assetsPathRelativeToBuildRoot: String 42 ) : TestRule { 43 private val colorsRule = MaterialYouColorsRule() 44 private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) 45 private val screenshotRule = 46 ScreenshotTestRule( 47 InputGoldenPathManager( 48 getEmulatedDevicePathConfig(emulationSpec), 49 assetsPathRelativeToBuildRoot 50 ) 51 ) 52 private val composeRule = createAndroidComposeRule<ComponentActivity>() 53 private val roboRule = 54 RuleChain.outerRule(deviceEmulationRule) 55 .around(screenshotRule) 56 .around(composeRule) 57 private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule) 58 private val matcher = UnitTestBitmapMatcher 59 private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false 60 applynull61 override fun apply(base: Statement, description: Description): Statement { 62 val ruleToApply = if (isRobolectric) roboRule else delegateRule 63 return ruleToApply.apply(base, description) 64 } 65 66 /** 67 * Compare [content] with the golden image identified by [goldenIdentifier]. 68 */ screenshotTestnull69 fun screenshotTest( 70 goldenIdentifier: String, 71 content: (Context) -> Bitmap, 72 ) { 73 // Make sure that the activity draws full screen and fits the whole display. 74 val activity = composeRule.activity 75 activity.mainExecutor.execute { activity.window.setDecorFitsSystemWindows(false) } 76 77 // Set the content using the AndroidComposeRule to make sure that the Activity is set up 78 // correctly. 79 composeRule.setContent { 80 Image( 81 bitmap = content(activity).asImageBitmap(), 82 contentDescription = null, 83 ) 84 } 85 composeRule.waitForIdle() 86 87 val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view 88 screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher) 89 } 90 } 91