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 4627ddee1..f2a089d44 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 @@ -2,6 +2,7 @@ package com.github.takahirom.roborazzi import com.dropbox.differ.ImageComparator import java.io.File +import kotlin.properties.Delegates fun interface EmptyCanvasFactory { operator fun invoke( @@ -84,6 +85,10 @@ fun processOutputImageAndReport( bufferedImageType = recordOptions.pixelBitConfig.toBufferedImageType() ) } + + // Only used by CaptureResult.Changed + var diffPercentage by Delegates.notNull() + val changed = if (height == goldenRoboCanvas.height && width == goldenRoboCanvas.width) { val comparisonResult: ImageComparator.ComparisonResult = newRoboCanvas.differ( @@ -91,10 +96,12 @@ fun processOutputImageAndReport( resizeScale = resizeScale, imageComparator = roborazziOptions.compareOptions.imageComparator ) + diffPercentage = comparisonResult.pixelDifferences.toFloat() / comparisonResult.pixelCount val changed = !roborazziOptions.compareOptions.resultValidator(comparisonResult) reportLog("${goldenFile.name} The differ result :$comparisonResult changed:$changed") changed } else { + diffPercentage = Float.NaN // diff. percentage is not defined if new canvas and golden canvas dimensions differ reportLog("${goldenFile.name} The image size is changed. actual = (${goldenRoboCanvas.width}, ${goldenRoboCanvas.height}), golden = (${newRoboCanvas.croppedWidth}, ${newRoboCanvas.croppedHeight})") true } @@ -147,6 +154,7 @@ fun processOutputImageAndReport( actualFile = actualFile.absolutePath, goldenFile = goldenFile.absolutePath, timestampNs = System.nanoTime(), + diffPercentage = diffPercentage, contextData = contextData, ) } else { diff --git a/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/CaptureResult.kt b/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/CaptureResult.kt index e9497a329..938f273e7 100644 --- a/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/CaptureResult.kt +++ b/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/CaptureResult.kt @@ -74,6 +74,8 @@ sealed interface CaptureResult { override val actualFile:@Contextual String, @SerialName("timestamp") override val timestampNs: Long, + @SerialName("diff_percentage") + val diffPercentage: Float, @SerialName("context_data") override val contextData: Map ) : CaptureResult { diff --git a/include-build/roborazzi-core/src/jvmTest/kotlin/io/github/takahirom/roborazzi/CaptureResultTest.kt b/include-build/roborazzi-core/src/jvmTest/kotlin/io/github/takahirom/roborazzi/CaptureResultTest.kt index 7e852d5a7..efcf8741c 100644 --- a/include-build/roborazzi-core/src/jvmTest/kotlin/io/github/takahirom/roborazzi/CaptureResultTest.kt +++ b/include-build/roborazzi-core/src/jvmTest/kotlin/io/github/takahirom/roborazzi/CaptureResultTest.kt @@ -1,16 +1,15 @@ package io.github.takahirom.roborazzi import com.github.takahirom.roborazzi.CaptureResult -import com.github.takahirom.roborazzi.CaptureResults.Companion.json import com.github.takahirom.roborazzi.CaptureResults +import com.github.takahirom.roborazzi.CaptureResults.Companion.json import com.github.takahirom.roborazzi.ResultSummary -import com.github.takahirom.roborazzi.absolutePath -import kotlinx.io.files.Path import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.boolean import kotlinx.serialization.json.double import kotlinx.serialization.json.doubleOrNull import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.float import kotlinx.serialization.json.int import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonArray @@ -47,6 +46,7 @@ class CaptureResultTest { goldenFile = "/golden_file", actualFile = "/actual_file", timestampNs = 123456789, + diffPercentage = 0.123f, contextData = mapOf("key" to Long.MAX_VALUE - 100), ), CaptureResult.Unchanged( @@ -95,6 +95,12 @@ class CaptureResultTest { expectedCaptureResult.timestampNs, actualJsonResult["timestamp"]?.jsonPrimitive?.long ) + if (expectedCaptureResult is CaptureResult.Changed) { + assertEquals( + expectedCaptureResult.diffPercentage, + actualJsonResult["diff_percentage"]?.jsonPrimitive?.float + ) + } assertEquals( expectedCaptureResult.contextData.entries.map { it.key to it.value }, (actualJsonResult["context_data"]?.jsonObject?.entries @@ -154,6 +160,7 @@ class CaptureResultTest { "actual_file_path": "actual_file", "golden_file_path": "golden_file", "timestamp": 123456789, + "diff_percentage": 0.123, "context_data": { "key": 9223372036854775707 } @@ -201,6 +208,7 @@ class CaptureResultTest { assertEquals("actual_file", actualChangedResult.actualFile) assertEquals("golden_file", actualChangedResult.goldenFile) assertEquals(123456789, actualChangedResult.timestampNs) + assertEquals(0.123f, actualChangedResult.diffPercentage) assertEquals(9223372036854775707, actualChangedResult.contextData["key"]) val actualUnchangedResult = actualCaptureResultList[3] as CaptureResult.Unchanged diff --git a/roborazzi-compose-ios/src/iosMain/kotlin/io/github/takahirom/roborazzi/RoborazziIos.kt b/roborazzi-compose-ios/src/iosMain/kotlin/io/github/takahirom/roborazzi/RoborazziIos.kt index be1bfda6f..f9054981d 100644 --- a/roborazzi-compose-ios/src/iosMain/kotlin/io/github/takahirom/roborazzi/RoborazziIos.kt +++ b/roborazzi-compose-ios/src/iosMain/kotlin/io/github/takahirom/roborazzi/RoborazziIos.kt @@ -539,6 +539,7 @@ fun SemanticsNodeInteraction.captureRoboImage( actualFile = actualFilePath, goldenFile = goldenFilePath, timestampNs = getNanoTime(), + diffPercentage = Float.NaN, contextData = emptyMap() ) writeJson(result, resultsDir, nameWithoutExtension) @@ -581,6 +582,7 @@ fun SemanticsNodeInteraction.captureRoboImage( actualFile = actualFilePath, goldenFile = goldenFilePath, timestampNs = getNanoTime(), + diffPercentage = Float.NaN, contextData = emptyMap() ) writeJson(result, resultsDir, nameWithoutExtension) @@ -623,6 +625,7 @@ fun SemanticsNodeInteraction.captureRoboImage( actualFile = goldenFilePath, goldenFile = goldenFilePath, timestampNs = getNanoTime(), + diffPercentage = Float.NaN, contextData = emptyMap() ) writeJson(result, resultsDir, nameWithoutExtension) @@ -665,6 +668,7 @@ fun SemanticsNodeInteraction.captureRoboImage( actualFile = goldenFilePath, goldenFile = goldenFilePath, timestampNs = getNanoTime(), + diffPercentage = Float.NaN, contextData = emptyMap() ) writeJson(result, resultsDir, nameWithoutExtension)