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