Skip to content

Commit

Permalink
Add Fix workflow (Splitties#6)
Browse files Browse the repository at this point in the history
Add 2% tolerance to screenshot testing for subtle differences between Linux and macOS
Add a workflow to update the screenshots from GitHub Actions
  • Loading branch information
yschimke committed Feb 24, 2024
1 parent 883134e commit 9adf5ad
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 10 deletions.
30 changes: 28 additions & 2 deletions .github/workflows/build_workflow.main.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

import io.github.typesafegithub.workflows.actions.actions.CheckoutV4
import io.github.typesafegithub.workflows.actions.actions.SetupJavaV4
import io.github.typesafegithub.workflows.actions.actions.UploadArtifactV4
import io.github.typesafegithub.workflows.actions.gradle.GradleBuildActionV3
import io.github.typesafegithub.workflows.domain.RunnerType.UbuntuLatest
import io.github.typesafegithub.workflows.domain.triggers.PullRequest
import io.github.typesafegithub.workflows.domain.triggers.Push
import io.github.typesafegithub.workflows.dsl.expressions.expr
import io.github.typesafegithub.workflows.dsl.workflow
import io.github.typesafegithub.workflows.yaml.writeToFile

Expand All @@ -19,17 +21,41 @@ workflow(
),
sourceFile = __FILE__.toPath(),
) {
job(id = "build-and-test", runsOn = UbuntuLatest) {
job(
id = "build-and-test",
runsOn = UbuntuLatest
) {
uses(name = "Check out", action = CheckoutV4())
uses(
name = "Setup Java",
action = SetupJavaV4(distribution = SetupJavaV4.Distribution.Temurin, javaVersion = "17")
action = SetupJavaV4(
distribution = SetupJavaV4.Distribution.Temurin,
javaVersion = "17"
)
)
uses(
name = "Build",
action = GradleBuildActionV3(
arguments = "test",
)
)
uses(
name = "Upload reports",
action = UploadArtifactV4(
name = "Roborazzi",
path = listOf("shared/build/outputs/roborazzi"),
retentionDays = UploadArtifactV4.RetentionPeriod.Default,
),
`if` = expr { always() }
)
uses(
name = "Upload test reports",
action = UploadArtifactV4(
name = "Junit",
path = listOf("**/build/reports/tests"),
retentionDays = UploadArtifactV4.RetentionPeriod.Default,
),
`if` = expr { always() }
)
}
}.writeToFile()
16 changes: 16 additions & 0 deletions .github/workflows/build_workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,19 @@ jobs:
uses: 'gradle/gradle-build-action@v3'
with:
arguments: 'test'
- id: 'step-3'
name: 'Upload reports'
uses: 'actions/upload-artifact@v4'
with:
name: 'Roborazzi'
path: 'shared/build/outputs/roborazzi'
retention-days: '0'
if: '${{ always() }}'
- id: 'step-4'
name: 'Upload test reports'
uses: 'actions/upload-artifact@v4'
with:
name: 'Junit'
path: '**/build/reports/tests'
retention-days: '0'
if: '${{ always() }}'
50 changes: 50 additions & 0 deletions .github/workflows/fix_workflow.main.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env kotlin

@file:DependsOn("io.github.typesafegithub:github-workflows-kt:1.11.0")

import io.github.typesafegithub.workflows.actions.actions.CheckoutV4
import io.github.typesafegithub.workflows.actions.actions.SetupJavaV4
import io.github.typesafegithub.workflows.actions.gradle.GradleBuildActionV3
import io.github.typesafegithub.workflows.actions.stefanzweifel.GitAutoCommitActionV5
import io.github.typesafegithub.workflows.domain.RunnerType.UbuntuLatest
import io.github.typesafegithub.workflows.domain.triggers.WorkflowDispatch
import io.github.typesafegithub.workflows.dsl.expressions.expr
import io.github.typesafegithub.workflows.dsl.workflow
import io.github.typesafegithub.workflows.yaml.writeToFile

workflow(
name = "Fix workflow",
on = listOf(
WorkflowDispatch(),
),
sourceFile = __FILE__.toPath(),
) {
job(
id = "fix-branch",
runsOn = UbuntuLatest,
// Ensure no auto-commits are ever made on the main/default branch.
// This is an extra security measure, especially since we don't control what the
// "stefanzweifel/git-auto-commit-action@v5" GitHub Action could theoretically do.
`if` = expr { github.ref_name + " != " + github.eventWorkflowDispatch.repository.default_branch }
) {
uses(name = "Check out", action = CheckoutV4())
uses(
name = "Setup Java",
action = SetupJavaV4(distribution = SetupJavaV4.Distribution.Temurin, javaVersion = "17")
)
uses(
name = "Record Screenshots",
action = GradleBuildActionV3(
arguments = "verifyAndRecordRoborazziDebug",
)
)
uses(
name = "Commit Screenshots",
action = GitAutoCommitActionV5(
filePattern = "**/src/test/screenshots/*.png",
disableGlobbing = true,
commitMessage = "🤖 Updates screenshots"
)
)
}
}.writeToFile()
48 changes: 48 additions & 0 deletions .github/workflows/fix_workflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This file was generated using Kotlin DSL (.github/workflows/fix_workflow.main.kts).
# If you want to modify the workflow, please change the Kotlin file and regenerate this YAML file.
# Generated with https://github.com/typesafegithub/github-workflows-kt

name: 'Fix workflow'
on:
workflow_dispatch: {}
jobs:
check_yaml_consistency:
name: 'Check YAML consistency'
runs-on: 'ubuntu-latest'
steps:
- id: 'step-0'
name: 'Check out'
uses: 'actions/checkout@v4'
- id: 'step-1'
name: 'Execute script'
run: 'rm ''.github/workflows/fix_workflow.yaml'' && ''.github/workflows/fix_workflow.main.kts'''
- id: 'step-2'
name: 'Consistency check'
run: 'git diff --exit-code ''.github/workflows/fix_workflow.yaml'''
fix-branch:
runs-on: 'ubuntu-latest'
needs:
- 'check_yaml_consistency'
if: '${{ github.ref_name != github.event.repository.default_branch }}'
steps:
- id: 'step-0'
name: 'Check out'
uses: 'actions/checkout@v4'
- id: 'step-1'
name: 'Setup Java'
uses: 'actions/setup-java@v4'
with:
java-version: '17'
distribution: 'temurin'
- id: 'step-2'
name: 'Record Screenshots'
uses: 'gradle/gradle-build-action@v3'
with:
arguments: 'verifyAndRecordRoborazziDebug'
- id: 'step-3'
name: 'Commit Screenshots'
uses: 'stefanzweifel/git-auto-commit-action@v5'
with:
commit_message: '🤖 Updates screenshots'
file_pattern: '**/src/test/screenshots/*.png'
disable_globbing: 'true'
1 change: 1 addition & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = versionFor(AndroidX.compose.compiler)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,67 @@
@file:OptIn(ExperimentalRoborazziApi::class)

package org.splitties.compose.oclock.sample.watchfaces

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.takahirom.roborazzi.ExperimentalRoborazziApi
import com.github.takahirom.roborazzi.RoborazziOptions
import com.github.takahirom.roborazzi.ThresholdValidator
import com.github.takahirom.roborazzi.captureRoboImage
import com.louiscad.composeoclockplayground.shared.BuildConfig
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Assume
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.ParameterizedRobolectricTestRunner
import org.junit.rules.TestRule
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import org.splitties.compose.oclock.OClockRootCanvas
import java.io.File

@Config(
sdk = [33],
qualifiers = "w227dp-h227dp-small-notlong-round-watch-xhdpi-keyshidden-nonav",
)
@GraphicsMode(GraphicsMode.Mode.NATIVE)
abstract class ClockScreenshotTest {
@get:Rule
@get:Rule(order = 0)
val debugOnly = TestRule { base, _ ->
Assume.assumeTrue("Screenshot tests not supported for release", BuildConfig.DEBUG)

base
}

@get:Rule(order = 1)
val composeRule = createComposeRule()

abstract val device: WearDevice

// generous to allow for mac/linux differences
open val tolerance = 0.02f

open val roborazziOptions: RoborazziOptions
get() = RoborazziOptions(
compareOptions = RoborazziOptions.CompareOptions(
resultValidator = ThresholdValidator(tolerance)
)
)

@Before
fun check() {
// Robolectric RNG not supported on Windows
// https://github.com/robolectric/robolectric/issues/8312
assumeFalse(System.getProperty("os.name")?.startsWith("Windows") ?: false)
assumeFalse("Robolectric RNG not supported on Windows", System.getProperty("os.name")?.startsWith("Windows") ?: false)
}

fun runTest(isAmbient: Boolean = false, clock: @Composable () -> Unit) {
val filePath =
File("src/test/screenshots/${this.javaClass.simpleName}_${device.id}${if (isAmbient) "_ambient" else ""}.png")

RuntimeEnvironment.setQualifiers("+w${device.dp}dp-h${device.dp}dp")

composeRule.setContent {
Expand All @@ -50,6 +73,6 @@ abstract class ClockScreenshotTest {
}

composeRule.onRoot()
.captureRoboImage("src/test/screenshots/${this.javaClass.simpleName}_${device.id}${if (isAmbient) "_ambient" else ""}.png")
.captureRoboImage(filePath = filePath.path, roborazziOptions = roborazziOptions)
}
}

0 comments on commit 9adf5ad

Please sign in to comment.