Adds Dropshots and edge-to-edge tests

Change-Id: I4e0d5d0c9dfbf106acea951266c04139fab93c53
ben/dropshots
Jose Alcérreca 6 months ago
parent b85cf7d476
commit 8571212666

@ -189,7 +189,8 @@ jobs:
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
- name: Build projects and run instrumentation tests
- name: Build projects and run instrumentation tests including screenshots
id: dropshotsverify
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
@ -197,8 +198,43 @@ jobs:
disable-animations: true
disk-size: 6000M
heap-size: 600M
profile: pixel_5
ram-size: 4096M
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: ./gradlew connectedDemoDebugAndroidTest --daemon
- name: Prevent pushing new screenshots if this is a fork (instrumented)
id: checkfork_screenshots_instrumented
continue-on-error: false
if: steps.dropshotsverify.outcome == 'failure' && github.event.pull_request.head.repo.full_name != github.repository
run: |
echo "::error::Instrumented screenshot tests failed, please create a PR in your fork first." && exit 1
- name: Record new instrumented screenshots
id: screenshotsrecordinstrumented
if: steps.dropshotsverify.outcome == 'failure' && github.event_name == 'pull_request'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
disable-animations: true
disk-size: 6000M
heap-size: 600M
profile: pixel_5
force-avd-creation: false
ram-size: 4096M
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: adb shell rm -rf /storage/emulated/0/Download/screenshots && ./gradlew connectedDemoDebugAndroidTest -Pdropshots.record --stacktrace
- name: Push new screenshots if available
uses: stefanzweifel/git-auto-commit-action@4b8a201e31cadd9829df349894b28c54e6c19fe6
if: steps.screenshotsrecordinstrumented.outcome == 'success'
with:
file_pattern: '*/*.png'
disable_globbing: true
commit_message: "🤖 Updates instrumented screenshots"
- name: Run local tests (including Roborazzi) for the combined coverage report (only API 30)
if: matrix.api-level == 30
# There is no need to verify Roborazzi tests to generate coverage.

@ -126,9 +126,12 @@ dependencies {
androidTestImplementation(projects.core.dataTest)
androidTestImplementation(projects.core.datastoreTest)
androidTestImplementation(libs.androidx.test.espresso.core)
androidTestImplementation(libs.androidx.test.core)
androidTestImplementation(libs.androidx.navigation.testing)
androidTestImplementation(libs.androidx.compose.ui.test)
androidTestImplementation(libs.hilt.android.testing)
androidTestImplementation(libs.androidx.test.espresso.device)
androidTestImplementation(libs.androidx.test.uiautomator)
baselineProfile(projects.benchmarks)
}

@ -0,0 +1,113 @@
/*
* Copyright 2024 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
*
* https://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.google.samples.apps.nowinandroid.ui
import androidx.test.core.app.takeScreenshot
import androidx.test.espresso.device.common.executeShellCommand
import androidx.test.espresso.device.filter.RequiresDisplay
import androidx.test.espresso.device.sizeclass.HeightSizeClass.Companion.HeightSizeClassEnum
import androidx.test.espresso.device.sizeclass.WidthSizeClass.Companion.WidthSizeClassEnum
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.dropbox.dropshots.Dropshots
import com.google.samples.apps.nowinandroid.MainActivity
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
import dagger.hilt.android.testing.BindValue
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@HiltAndroidTest
class EdgeToEdgeTest {
/**
* Manages the components' state and is used to perform injection on your test
*/
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)
/**
* Create a temporary folder used to create a Data Store file. This guarantees that
* the file is removed in between each test, preventing a crash.
*/
@BindValue
@get:Rule(order = 1)
val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build()
/**
* Grant [android.Manifest.permission.POST_NOTIFICATIONS] permission.
*/
@get:Rule(order = 2)
val postNotificationsPermission = GrantPostNotificationsPermissionRule()
@get:Rule(order = 3)
val activityScenarioRule = ActivityScenarioRule(MainActivity::class.java)
@get:Rule(order = 4)
val dropshots = Dropshots()
@Before
fun setup() = hiltRule.inject()
@Before
fun enableDemoMode() {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
executeShellCommand("cmd overlay enable-exclusive com.android.internal.systemui.navbar.threebutton")
executeShellCommand("settings put global sysui_demo_allowed 1")
executeShellCommand("am broadcast -a com.android.systemui.demo -e command enter")
executeShellCommand("am broadcast -a com.android.systemui.demo -e command notifications -e visible false")
executeShellCommand("am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1234")
executeShellCommand("am broadcast -a com.android.systemui.demo -e command network -e wifi hide")
executeShellCommand("am broadcast -a com.android.systemui.demo -e command network -e mobile hide")
}
}
@After
fun resetDemoMode() {
executeShellCommand("am broadcast -a com.android.systemui.demo -e command exit")
}
@RequiresDisplay(WidthSizeClassEnum.COMPACT, HeightSizeClassEnum.MEDIUM)
@SdkSuppress(minSdkVersion = 26, maxSdkVersion = 26)
@Test
fun edgeToEdge_Phone_Api26() {
testEdgeToEdge("edgeToEdge_Phone_Api26")
}
@RequiresDisplay(WidthSizeClassEnum.COMPACT, HeightSizeClassEnum.MEDIUM)
@SdkSuppress(minSdkVersion = 30, maxSdkVersion = 30)
@Test
fun edgeToEdge_Phone_Api30() {
testEdgeToEdge("edgeToEdge_Phone_Api30")
}
@RequiresDisplay(WidthSizeClassEnum.EXPANDED, HeightSizeClassEnum.MEDIUM)
@SdkSuppress(minSdkVersion = 30, maxSdkVersion = 30)
@Test
fun edgeToEdge_Tablet_Api30() {
testEdgeToEdge("edgeToEdge_Tablet_Api30")
}
private fun testEdgeToEdge(screenshotFileName: String) {
dropshots.assertSnapshot(takeScreenshot(), screenshotFileName)
}
}

@ -34,6 +34,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
apply("org.jetbrains.kotlin.android")
apply("nowinandroid.android.lint")
apply("com.dropbox.dependency-guard")
apply("com.dropbox.dropshots")
}
extensions.configure<ApplicationExtension> {

@ -39,6 +39,7 @@ plugins {
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.dependencyGuard) apply false
alias(libs.plugins.dropshots) apply false
alias(libs.plugins.firebase.crashlytics) apply false
alias(libs.plugins.firebase.perf) apply false
alias(libs.plugins.gms) apply false
@ -58,4 +59,4 @@ tasks.register("printModulePaths") {
println(this.path)
}
}
}
}

@ -37,7 +37,7 @@ kotlin.code.style=official
# Disable build features that are enabled by default,
# https://developer.android.com/build/releases/gradle-plugin#default-changes
android.defaults.buildfeatures.resvalues=false
#android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
# Run Roborazzi screenshot tests with the local tests

@ -16,22 +16,24 @@ androidxCore = "1.12.0"
androidxCoreSplashscreen = "1.0.1"
androidxDataStore = "1.0.0"
androidxEspresso = "3.5.1"
androidxEspressoDevice = "1.0.0-beta01"
androidxHiltNavigationCompose = "1.2.0"
androidxLifecycle = "2.7.0"
androidxMacroBenchmark = "1.2.4"
androidxMetrics = "1.0.0-alpha04"
androidxNavigation = "2.8.0-alpha06"
androidxProfileinstaller = "1.3.1"
androidxTestCore = "1.5.0"
androidxTestCore = "1.6.0-beta01"
androidxTestExt = "1.1.5"
androidxTestRules = "1.5.0"
androidxTestRunner = "1.5.2"
androidxTracing = "1.3.0-alpha02"
androidxUiAutomator = "2.2.0"
androidxUiAutomator = "2.3.0"
androidxWindowManager = "1.3.0-alpha03"
androidxWork = "2.9.0"
coil = "2.6.0"
dependencyGuard = "0.5.0"
dropshots = "0.4.1"
firebaseBom = "32.4.0"
firebaseCrashlyticsPlugin = "2.9.9"
firebasePerfPlugin = "1.4.2"
@ -96,6 +98,7 @@ androidx-navigation-testing = { group = "androidx.navigation", name = "navigatio
androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" }
androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" }
androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" }
androidx-test-espresso-device = { group = "androidx.test.espresso", name = "espresso-device", version.ref = "androidxEspressoDevice" }
androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" }
androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" }
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" }
@ -156,6 +159,7 @@ android-library = { id = "com.android.library", version.ref = "androidGradlePlug
android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" }
baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidxMacroBenchmark"}
dependencyGuard = { id = "com.dropbox.dependency-guard", version.ref = "dependencyGuard" }
dropshots = { id = "com.dropbox.dropshots", version.ref = "dropshots" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" }
firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerfPlugin" }
gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" }

Loading…
Cancel
Save