diff --git a/.gitignore b/.gitignore index cd3ef37d4..d0447a2dd 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ .cxx local.properties .idea/vcs.xml +/tmp \ No newline at end of file diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt index 127742d04..ab2802a65 100644 --- a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziContext.kt @@ -1,10 +1,10 @@ package com.github.takahirom.roborazzi -import java.io.File import org.junit.runner.Description +import java.io.File @ExperimentalRoborazziApi -object RoborazziContext { +class RoborazziContextImpl { private var ruleOverrideOutputDirectory: String? = null private var ruleOverrideRoborazziOptions: RoborazziOptions? = null @@ -81,5 +81,10 @@ object RoborazziContext { } } +@ExperimentalRoborazziApi +var RoborazziContext = RoborazziContextImpl() + @InternalRoborazziApi + set + @ExperimentalRoborazziApi fun provideRoborazziContext() = RoborazziContext \ No newline at end of file diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt index 53ee6cea0..9a9af1f48 100644 --- a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt @@ -7,33 +7,60 @@ import java.io.FileWriter const val DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH = "build/outputs/roborazzi" var ROBORAZZI_DEBUG = false + +@Deprecated( + message = "Use roborazziSystemPropertyTaskType()", + replaceWith = ReplaceWith("roborazziSystemPropertyTaskType().isEnabled()"), +) fun roborazziEnabled(): Boolean { - val isEnabled = roborazziRecordingEnabled() || - roborazziCompareEnabled() || - roborazziVerifyEnabled() - debugLog { - "roborazziEnabled: $isEnabled \n" + - "roborazziRecordingEnabled(): ${roborazziRecordingEnabled()}\n" + - "roborazziCompareEnabled(): ${roborazziCompareEnabled()}\n" + - "roborazziVerifyEnabled(): ${roborazziVerifyEnabled()}\n" + - "roborazziDefaultResizeScale(): ${roborazziDefaultResizeScale()}\n" + - "roborazziDefaultNamingStrategy(): ${roborazziDefaultNamingStrategy()}\n" + - "roborazziRecordFilePathStrategy(): ${roborazziRecordFilePathStrategy()}\n" + - "RoborazziContext: ${provideRoborazziContext()}\n" - } - return isEnabled + return roborazziSystemPropertyTaskType().isEnabled() } +@Deprecated( + message = "Use roborazziSystemPropertyTaskType()", + ReplaceWith("roborazziSystemPropertyTaskType().isRecord()") +) fun roborazziRecordingEnabled(): Boolean { - return System.getProperty("roborazzi.test.record") == "true" + return roborazziSystemPropertyTaskType().isRecording() } +@Deprecated( + message = "Use roborazziSystemPropertyTaskType()", + ReplaceWith("roborazziSystemPropertyTaskType().isComparing()") +) fun roborazziCompareEnabled(): Boolean { - return System.getProperty("roborazzi.test.compare") == "true" + return roborazziSystemPropertyTaskType().isComparing() } +@Deprecated( + message = "Use roborazziSystemPropertyTaskType()", + ReplaceWith("roborazziSystemPropertyTaskType().isVerifying()") +) fun roborazziVerifyEnabled(): Boolean { - return System.getProperty("roborazzi.test.verify") == "true" + return roborazziSystemPropertyTaskType().isVerifying() +} + +@ExperimentalRoborazziApi +fun roborazziSystemPropertyTaskType(): RoborazziTaskType { + val result = run { + val roborazziRecordingEnabled = System.getProperty("roborazzi.test.record") == "true" + val roborazziCompareEnabled = System.getProperty("roborazzi.test.compare") == "true" + val roborazziVerifyEnabled = System.getProperty("roborazzi.test.verify") == "true" + RoborazziTaskType.of( + isRecording = roborazziRecordingEnabled, + isComparing = roborazziCompareEnabled, + isVerifying = roborazziVerifyEnabled + ) + } + debugLog { + "roborazziSystemPropertyTaskType():\n" + + "roborazziTaskType: $result \n" + + "roborazziDefaultResizeScale(): ${roborazziDefaultResizeScale()}\n" + + "roborazziDefaultNamingStrategy(): ${roborazziDefaultNamingStrategy()}\n" + + "roborazziRecordFilePathStrategy(): ${roborazziRecordFilePathStrategy()}\n" + + "RoborazziContext: ${provideRoborazziContext()}\n" + } + return result } /** @@ -106,6 +133,12 @@ fun roborazziDefaultNamingStrategy(): DefaultFileNameGenerator.DefaultNamingStra } data class RoborazziOptions( + /** + * This option, taskType, is experimental. So the API may change. + * Please tell me your opinion about this option + * https://github.com/takahirom/roborazzi/issues/215 + */ + val taskType: RoborazziTaskType = roborazziSystemPropertyTaskType(), val captureType: CaptureType = if (canScreenshot()) CaptureType.Screenshot() else defaultCaptureType(), val reportOptions: ReportOptions = ReportOptions(), val compareOptions: CompareOptions = CompareOptions(), @@ -168,14 +201,20 @@ data class RoborazziOptions( @ExperimentalRoborazziApi interface CaptureResultReporter { - fun report(reportResult: CaptureResult) + fun report(captureResult: CaptureResult, roborazziTaskType: RoborazziTaskType) companion object { operator fun invoke(): CaptureResultReporter { - return if (roborazziVerifyEnabled()) { - VerifyCaptureResultReporter() + return DefaultCaptureResultReporter() + } + } + + class DefaultCaptureResultReporter : CaptureResultReporter { + override fun report(captureResult: CaptureResult, roborazziTaskType: RoborazziTaskType) { + if (roborazziTaskType.isVerifying()) { + VerifyCaptureResultReporter().report(captureResult, roborazziTaskType) } else { - JsonOutputCaptureResultReporter() + JsonOutputCaptureResultReporter().report(captureResult, roborazziTaskType) } } } @@ -186,18 +225,18 @@ data class RoborazziOptions( File(RoborazziReportConst.resultDirPath).mkdirs() } - override fun report(reportResult: CaptureResult) { + override fun report(captureResult: CaptureResult, roborazziTaskType: RoborazziTaskType) { val absolutePath = File(RoborazziReportConst.resultDirPath).absolutePath - val nameWithoutExtension = when (reportResult) { - is CaptureResult.Added -> reportResult.compareFile - is CaptureResult.Changed -> reportResult.goldenFile - is CaptureResult.Unchanged -> reportResult.goldenFile - is CaptureResult.Recorded -> reportResult.goldenFile + val nameWithoutExtension = when (captureResult) { + is CaptureResult.Added -> captureResult.compareFile + is CaptureResult.Changed -> captureResult.goldenFile + is CaptureResult.Unchanged -> captureResult.goldenFile + is CaptureResult.Recorded -> captureResult.goldenFile }.nameWithoutExtension val reportFileName = - "$absolutePath/${reportResult.timestampNs}_$nameWithoutExtension.json" + "$absolutePath/${captureResult.timestampNs}_$nameWithoutExtension.json" - val jsonResult = reportResult.toJson() + val jsonResult = captureResult.toJson() FileWriter(reportFileName).use { it.write(jsonResult.toString()) } debugLog { "JsonResult file($reportFileName) has been written" } } @@ -206,9 +245,9 @@ data class RoborazziOptions( @InternalRoborazziApi class VerifyCaptureResultReporter : CaptureResultReporter { private val jsonOutputCaptureResultReporter = JsonOutputCaptureResultReporter() - override fun report(reportResult: CaptureResult) { - jsonOutputCaptureResultReporter.report(reportResult) - val assertErrorOrNull = getAssertErrorOrNull(reportResult) + override fun report(captureResult: CaptureResult, roborazziTaskType: RoborazziTaskType) { + jsonOutputCaptureResultReporter.report(captureResult, roborazziTaskType) + val assertErrorOrNull = getAssertErrorOrNull(captureResult) if (assertErrorOrNull != null) { throw assertErrorOrNull } @@ -241,16 +280,16 @@ expect fun canScreenshot(): Boolean expect fun defaultCaptureType(): RoborazziOptions.CaptureType -private fun getAssertErrorOrNull(reportResult: CaptureResult): AssertionError? = - when (reportResult) { +private fun getAssertErrorOrNull(captureResult: CaptureResult): AssertionError? = + when (captureResult) { is CaptureResult.Added -> AssertionError( - "Roborazzi: The original file(${reportResult.goldenFile.absolutePath}) was not found.\n" + - "See the actual image at ${reportResult.actualFile.absolutePath}" + "Roborazzi: The original file(${captureResult.goldenFile.absolutePath}) was not found.\n" + + "See the actual image at ${captureResult.actualFile.absolutePath}" ) is CaptureResult.Changed -> AssertionError( - "Roborazzi: ${reportResult.goldenFile.absolutePath} is changed.\n" + - "See the compare image at ${reportResult.compareFile.absolutePath}" + "Roborazzi: ${captureResult.goldenFile.absolutePath} is changed.\n" + + "See the compare image at ${captureResult.compareFile.absolutePath}" ) is CaptureResult.Unchanged, is CaptureResult.Recorded -> { diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt new file mode 100644 index 000000000..32f11662c --- /dev/null +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt @@ -0,0 +1,56 @@ +package com.github.takahirom.roborazzi + +@ExperimentalRoborazziApi +enum class RoborazziTaskType { + None, + Record, + Compare, + Verify, + VerifyAndRecord, + CompareAndRecord; + + fun isEnabled(): Boolean { + return this != None + } + + fun isRecording(): Boolean { + return this == Record || this == VerifyAndRecord || this == CompareAndRecord + } + + fun isComparing(): Boolean { + return this == Compare || this == CompareAndRecord + } + + fun isVerifying(): Boolean { + return this == Verify || this == VerifyAndRecord + } + + fun convertVerifyingToComparing(): RoborazziTaskType { + return when (this) { + Verify -> Compare + VerifyAndRecord -> CompareAndRecord + else -> this + } + } + + fun isVerifyingAndRecording(): Boolean { + return this == VerifyAndRecord + } + + companion object { + fun of( + isRecording: Boolean, + isComparing: Boolean, + isVerifying: Boolean + ): RoborazziTaskType { + return when { + isRecording && isVerifying -> VerifyAndRecord + isRecording && isComparing -> CompareAndRecord + isRecording -> Record + isComparing -> Compare + isVerifying -> Verify + else -> None + } + } + } +} \ No newline at end of file diff --git a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/processOutputImageAndReport.kt b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/processOutputImageAndReport.kt index 6500b009d..71457e7a2 100644 --- a/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/processOutputImageAndReport.kt +++ b/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/processOutputImageAndReport.kt @@ -37,9 +37,19 @@ fun processOutputImageAndReport( canvasFactoryFromFile: CanvasFactoryFromFile, comparisonCanvasFactory: ComparisonCanvasFactory, ) { + val taskType = roborazziOptions.taskType debugLog { "processOutputImageAndReport(): " + - "goldenFile:${goldenFile.absolutePath}" + "taskType:" + taskType + + "\ngoldenFile:${goldenFile.absolutePath}" + } + if (taskType.isEnabled() && !roborazziSystemPropertyTaskType().isEnabled()) { + println( + "Roborazzi Warning:\n" + + "You have specified '$taskType' without the necessary plugin configuration like roborazzi.test.record=true or ./gradlew recordRoborazziDebug.\n" + + "This may complicate your screenshot testing process because the behavior is not changeable. And it doesn't allow Roborazzi plugin to generate test report.\n" + + "Please ensure proper setup in gradle.properties or via Gradle tasks for optimal functionality." + ) } val forbiddenFileSuffixes = listOf("_compare", "_actual") forbiddenFileSuffixes.forEach { @@ -49,7 +59,7 @@ fun processOutputImageAndReport( } val recordOptions = roborazziOptions.recordOptions val resizeScale = recordOptions.resizeScale - if (roborazziCompareEnabled() || roborazziVerifyEnabled()) { + if (taskType.isVerifying() || taskType.isComparing()) { val width = (newRoboCanvas.croppedWidth * resizeScale).toInt() val height = (newRoboCanvas.croppedHeight * resizeScale).toInt() val goldenRoboCanvas = if (goldenFile.exists()) { @@ -99,7 +109,7 @@ fun processOutputImageAndReport( } comparisonCanvas.release() - val actualFile = if (roborazziRecordingEnabled()) { + val actualFile = if (taskType.isRecording()) { // If record option is enabled, we should save the actual file as the golden file. goldenFile } else { @@ -144,7 +154,10 @@ fun processOutputImageAndReport( " changed: $changed\n" + " result: $result\n" } - roborazziOptions.reportOptions.captureResultReporter.report(result) + roborazziOptions.reportOptions.captureResultReporter.report( + captureResult = result, + roborazziTaskType = taskType + ) } else { // roborazzi.record is checked before newRoboCanvas.save(goldenFile, resizeScale) @@ -153,10 +166,11 @@ fun processOutputImageAndReport( " record goldenFile: $goldenFile\n" } roborazziOptions.reportOptions.captureResultReporter.report( - CaptureResult.Recorded( + captureResult = CaptureResult.Recorded( goldenFile = goldenFile, timestampNs = System.nanoTime() - ) + ), + roborazziTaskType = taskType ) } } diff --git a/roborazzi-compose-desktop/src/commonMain/kotlin/io/github/takahirom/roborazzi/RoborazziDesktop.kt b/roborazzi-compose-desktop/src/commonMain/kotlin/io/github/takahirom/roborazzi/RoborazziDesktop.kt index 7f6f422ab..16c93776a 100644 --- a/roborazzi-compose-desktop/src/commonMain/kotlin/io/github/takahirom/roborazzi/RoborazziDesktop.kt +++ b/roborazzi-compose-desktop/src/commonMain/kotlin/io/github/takahirom/roborazzi/RoborazziDesktop.kt @@ -17,7 +17,7 @@ fun SemanticsNodeInteraction.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) { + if (!roborazziOptions.taskType.isEnabled()) { return } captureRoboImage( @@ -32,7 +32,7 @@ fun SemanticsNodeInteraction.captureRoboImage( file: File, roborazziOptions: RoborazziOptions ) { - if (!roborazziEnabled()) { + if (!roborazziOptions.taskType.isEnabled()) { return } val density = this.fetchSemanticsNode().layoutInfo.density @@ -59,7 +59,7 @@ fun ImageBitmap.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) { + if (!roborazziOptions.taskType.isEnabled()) { return } captureRoboImage( @@ -72,7 +72,7 @@ fun ImageBitmap.captureRoboImage( file: File, roborazziOptions: RoborazziOptions ) { - if (!roborazziEnabled()) { + if (!roborazziOptions.taskType.isEnabled()) { return } val awtImage = this.toAwtImage() diff --git a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt index 440ae8025..c23fde369 100644 --- a/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt +++ b/roborazzi-compose/src/main/java/com/github/takahirom/roborazzi/RoborazziCompose.kt @@ -29,7 +29,7 @@ fun captureRoboImage( roborazziOptions: RoborazziOptions = provideRoborazziContext().options, content: @Composable () -> Unit, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return registerRoborazziActivityToRobolectricIfNeeded() val activityScenario = ActivityScenario.launch(RoborazziTransparentActivity::class.java) activityScenario.onActivity { activity -> diff --git a/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt b/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt index cf77455bb..43b3ad71e 100644 --- a/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt +++ b/roborazzi-junit-rule/src/main/java/com/github/takahirom/roborazzi/RoborazziRule.kt @@ -153,11 +153,11 @@ class RoborazziRule private constructor( } } val captureType = options.captureType - if (!roborazziEnabled()) { + if (!options.roborazziOptions.taskType.isEnabled()) { evaluate() return } - if (!roborazziRecordingEnabled() && options.captureType is CaptureType.Gif) { + if (!options.roborazziOptions.taskType.isRecording() && options.captureType is CaptureType.Gif) { // currently, gif compare is not supported evaluate() return diff --git a/roborazzi/src/main/java/com/github/takahirom/roborazzi/Roborazzi.kt b/roborazzi/src/main/java/com/github/takahirom/roborazzi/Roborazzi.kt index ead85fbcc..89acf5510 100644 --- a/roborazzi/src/main/java/com/github/takahirom/roborazzi/Roborazzi.kt +++ b/roborazzi/src/main/java/com/github/takahirom/roborazzi/Roborazzi.kt @@ -38,7 +38,7 @@ fun ViewInteraction.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureRoboImage( file = fileWithRecordFilePathStrategy(filePath), roborazziOptions = roborazziOptions @@ -49,7 +49,7 @@ fun ViewInteraction.captureRoboImage( file: File, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return perform(ImageCaptureViewAction(roborazziOptions) { canvas -> processOutputImageAndReportWithDefaults( canvas = canvas, @@ -64,7 +64,7 @@ fun View.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureRoboImage( file = fileWithRecordFilePathStrategy(filePath), roborazziOptions = roborazziOptions @@ -75,7 +75,7 @@ fun View.captureRoboImage( file: File, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return val targetView = this@captureRoboImage if (targetView.isAttachedToWindow) { @@ -117,7 +117,7 @@ fun Bitmap.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureRoboImage( file = fileWithRecordFilePathStrategy(filePath), roborazziOptions = roborazziOptions @@ -128,7 +128,7 @@ fun Bitmap.captureRoboImage( file: File, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return val image = this val canvas = AwtRoboCanvas( width = image.width, @@ -152,7 +152,7 @@ fun ViewInteraction.captureRoboGif( block: () -> Unit ) { // currently, gif compare is not supported - if (!roborazziRecordingEnabled()) return + if (!roborazziOptions.taskType.isRecording()) return captureRoboGif(fileWithRecordFilePathStrategy(filePath), roborazziOptions, block) } @@ -162,7 +162,7 @@ fun ViewInteraction.captureRoboGif( block: () -> Unit ) { // currently, gif compare is not supported - if (!roborazziRecordingEnabled()) return + if (!roborazziOptions.taskType.isRecording()) return captureAndroidView(roborazziOptions, block).apply { saveGif(file) clear() @@ -175,7 +175,7 @@ fun ViewInteraction.captureRoboLastImage( roborazziOptions: RoborazziOptions = provideRoborazziContext().options, block: () -> Unit ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureRoboLastImage(fileWithRecordFilePathStrategy(filePath), roborazziOptions, block) } @@ -184,7 +184,7 @@ fun ViewInteraction.captureRoboLastImage( roborazziOptions: RoborazziOptions = provideRoborazziContext().options, block: () -> Unit ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureAndroidView(roborazziOptions, block).apply { saveLastImage(file) clear() @@ -197,7 +197,7 @@ fun ViewInteraction.captureRoboAllImage( roborazziOptions: RoborazziOptions = provideRoborazziContext().options, block: () -> Unit ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureAndroidView(roborazziOptions, block).apply { saveAllImage(fileNameCreator) clear() @@ -210,7 +210,7 @@ fun SemanticsNodeInteraction.captureRoboImage( filePath: String = DefaultFileNameGenerator.generateFilePath("png"), roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return captureRoboImage(fileWithRecordFilePathStrategy(filePath), roborazziOptions) } @@ -218,7 +218,7 @@ fun SemanticsNodeInteraction.captureRoboImage( file: File, roborazziOptions: RoborazziOptions = provideRoborazziContext().options, ) { - if (!roborazziEnabled()) return + if (!roborazziOptions.taskType.isEnabled()) return capture( rootComponent = RoboComponent.Compose( node = this.fetchSemanticsNode("fail to captureRoboImage"), @@ -242,7 +242,7 @@ fun SemanticsNodeInteraction.captureRoboGif( block: () -> Unit ) { // currently, gif compare is not supported - if (!roborazziRecordingEnabled()) return + if (!roborazziOptions.taskType.isRecording()) return captureComposeNode( composeRule = composeRule, roborazziOptions = roborazziOptions, @@ -261,7 +261,7 @@ fun SemanticsNodeInteraction.captureRoboGif( block: () -> Unit ) { // currently, gif compare is not supported - if (!roborazziRecordingEnabled()) return + if (!roborazziOptions.taskType.isRecording()) return captureComposeNode(composeRule, roborazziOptions, block).apply { saveGif(file) clear() diff --git a/sample-android/build.gradle b/sample-android/build.gradle index 176f35506..bdee45751 100644 --- a/sample-android/build.gradle +++ b/sample-android/build.gradle @@ -43,6 +43,8 @@ android { it.all { // To run coverage report in Android Studio jvmArgs '-noverify' + // Set the max heap size for the tests JVM(s) + maxHeapSize = "4g" } } } diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/FilePathTest.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/FilePathTest.kt similarity index 64% rename from sample-android/src/test/java/com/github/takahirom/roborazzi/sample/FilePathTest.kt rename to sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/FilePathTest.kt index e3eff5403..c23522c6a 100644 --- a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/FilePathTest.kt +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/FilePathTest.kt @@ -1,10 +1,11 @@ -package com.github.takahirom.roborazzi.sample +package com.github.takahirom.roborazzi.sample.boxed import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.test.espresso.Espresso.onView import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.takahirom.roborazzi.* +import com.github.takahirom.roborazzi.sample.MainActivity import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -46,23 +47,8 @@ class FilePathTest { expectedOutput .exists() ) { - "File not found: ${expectedOutput.absolutePath} \n" + - File("build/outputs/roborazzi").listFiles()?.joinToString("\n") { it.absolutePath } + "File not found: ${expectedOutput.absolutePath} \n" } - - provideRoborazziContext().clearRuleOverrideOutputDirectory() } } - - private fun boxedEnvironment(block: () -> Unit) { - val originalRecord = System.getProperty("roborazzi.test.record") - val originalFilePathStrategy = System.getProperty("roborazzi.record.filePathStrategy") - block() - originalFilePathStrategy?.let { - System.setProperty("roborazzi.record.filePathStrategy", it) - } ?: System.clearProperty("roborazzi.record.filePathStrategy") - originalRecord?.let { - System.setProperty("roborazzi.test.record", it) - } ?: System.clearProperty("roborazzi.test.record") - } } diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/RoborazziTaskTest.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/RoborazziTaskTest.kt new file mode 100644 index 000000000..1c5fbf50b --- /dev/null +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/RoborazziTaskTest.kt @@ -0,0 +1,91 @@ +package com.github.takahirom.roborazzi.sample.boxed + +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.* +import com.github.takahirom.roborazzi.sample.MainActivity +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import org.robolectric.annotation.GraphicsMode +import java.io.File + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +@Config( + sdk = [30], + qualifiers = RobolectricDeviceQualifiers.NexusOne +) +class RoborazziTaskTest { + @get:Rule + val composeTestRule = createAndroidComposeRule() + + @Test( + expected = AssertionError::class + ) + fun roborazziOptionTask() { + boxedEnvironment { + ROBORAZZI_DEBUG = true + val prefix = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/${this::class.qualifiedName}.roborazziOptionTask" + val expectedOutput = + File("$prefix.png") + val expectedCompareOutput = File("${prefix}_compare.png") + expectedOutput.delete() + setupRoborazziSystemProperty( + compare = true + ) + + try { + onView(ViewMatchers.isRoot()) + .captureRoboImage( + roborazziOptions = RoborazziOptions( + taskType = RoborazziTaskType.Verify + ) + ) + } finally { + expectedCompareOutput.delete() + } + } + } + + @Test + fun roborazziOptionTaskConvertCompare() { + boxedEnvironment { + ROBORAZZI_DEBUG = true + val prefix = "$DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH/${this::class.qualifiedName}.roborazziOptionTaskConvertCompare" + val expectedCompareOutput = File("${prefix}_compare.png") + expectedCompareOutput.delete() + val expectedOutput = File("${prefix}.png") + expectedOutput.delete() + setupRoborazziSystemProperty( + record = true, + compare = true, + verify = true + ) + + onView(ViewMatchers.isRoot()) + .captureRoboImage( + roborazziOptions = RoborazziOptions( + taskType = roborazziSystemPropertyTaskType().convertVerifyingToComparing() + ) + ) + + assert( + expectedCompareOutput + .exists() + ) { + "File not found: ${expectedCompareOutput.absolutePath} \n" + } + assert( + expectedOutput + .exists() + ) { + "File not found: ${expectedOutput.absolutePath} \n" + } + expectedCompareOutput.delete() + } + } +} diff --git a/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/boxedEnvironment.kt b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/boxedEnvironment.kt new file mode 100644 index 000000000..95375a13f --- /dev/null +++ b/sample-android/src/test/java/com/github/takahirom/roborazzi/sample/boxed/boxedEnvironment.kt @@ -0,0 +1,37 @@ +package com.github.takahirom.roborazzi.sample.boxed + +import com.github.takahirom.roborazzi.RoborazziContext +import com.github.takahirom.roborazzi.RoborazziContextImpl +import com.github.takahirom.roborazzi.provideRoborazziContext + +fun boxedEnvironment(block: () -> Unit) { + val originalProperties = System.getProperties().filter { it.key.toString().startsWith("roborazzi") }.toList() + originalProperties.forEach { System.clearProperty(it.first.toString()) } + val context = provideRoborazziContext() + RoborazziContext = RoborazziContextImpl() + try { + block() + } finally { + RoborazziContext = context + originalProperties.forEach { System.setProperty(it.first.toString(), it.second.toString()) } + } +} + +fun setupRoborazziSystemProperty( + record: Boolean = false, + compare: Boolean = false, + verify: Boolean = false, +) { + System.setProperty( + "roborazzi.test.record", + record.toString() + ) + System.setProperty( + "roborazzi.test.compare", + compare.toString() + ) + System.setProperty( + "roborazzi.test.verify", + verify.toString() + ) +} \ No newline at end of file