Skip to content

Commit

Permalink
Merge pull request #211 from takahirom/takahirom/roborazzi-task-type-…
Browse files Browse the repository at this point in the history
…property-to-roborazzi-options/2023-11-13

Add RoborazziTaskType property to RoborazziOptions
  • Loading branch information
takahirom authored Dec 10, 2023
2 parents 27beffa + c670d1a commit c224134
Show file tree
Hide file tree
Showing 13 changed files with 316 additions and 85 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
.cxx
local.properties
.idea/vcs.xml
/tmp
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -81,5 +81,10 @@ object RoborazziContext {
}
}

@ExperimentalRoborazziApi
var RoborazziContext = RoborazziContextImpl()
@InternalRoborazziApi
set

@ExperimentalRoborazziApi
fun provideRoborazziContext() = RoborazziContext
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/**
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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" }
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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 -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()) {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand All @@ -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
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fun SemanticsNodeInteraction.captureRoboImage(
filePath: String = DefaultFileNameGenerator.generateFilePath("png"),
roborazziOptions: RoborazziOptions = provideRoborazziContext().options,
) {
if (!roborazziEnabled()) {
if (!roborazziOptions.taskType.isEnabled()) {
return
}
captureRoboImage(
Expand All @@ -32,7 +32,7 @@ fun SemanticsNodeInteraction.captureRoboImage(
file: File,
roborazziOptions: RoborazziOptions
) {
if (!roborazziEnabled()) {
if (!roborazziOptions.taskType.isEnabled()) {
return
}
val density = this.fetchSemanticsNode().layoutInfo.density
Expand All @@ -59,7 +59,7 @@ fun ImageBitmap.captureRoboImage(
filePath: String = DefaultFileNameGenerator.generateFilePath("png"),
roborazziOptions: RoborazziOptions = provideRoborazziContext().options,
) {
if (!roborazziEnabled()) {
if (!roborazziOptions.taskType.isEnabled()) {
return
}
captureRoboImage(
Expand All @@ -72,7 +72,7 @@ fun ImageBitmap.captureRoboImage(
file: File,
roborazziOptions: RoborazziOptions
) {
if (!roborazziEnabled()) {
if (!roborazziOptions.taskType.isEnabled()) {
return
}
val awtImage = this.toAwtImage()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit c224134

Please sign in to comment.