Skip to content
This repository was archived by the owner on Aug 8, 2022. It is now read-only.

Commit c0aecaa

Browse files
authored
Merge pull request #209 from Shopify/206-generate-diffs
Add ability for Testify to generate companion diff images for failed tests
2 parents 2c0cde7 + 7d2bd2e commit c0aecaa

10 files changed

Lines changed: 88 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
### Library
66

7+
#### Added
8+
9+
- Added `TestifyFeatures.GenerateDiffs`. When enabled, will output a `.diff.png` alongside existing baseline images. These images are high-contrast images where each difference, regardless of how minor, are indicated in red against a black background. See the `generateDiffs` test in `ScreenshotRuleExampleTests` for an example. Diff images will be pulled from the device when running `screenshotPull`.
10+
711
#### Changes
812

913
- Optional constructor argument enableReporter added to ScreenshotRule. Allows you to specify whether to run the reporter for this test rule.

Library/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,3 @@ android {
7878

7979
apply from: "bintray.build.gradle"
8080
apply from: '../ktlint.gradle'
81-

Library/src/main/java/com/shopify/testify/ScreenshotRule.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import com.shopify.testify.internal.modification.HidePasswordViewModification
7575
import com.shopify.testify.internal.modification.HideScrollbarsViewModification
7676
import com.shopify.testify.internal.modification.HideTextSuggestionsViewModification
7777
import com.shopify.testify.internal.modification.SoftwareRenderViewModification
78+
import com.shopify.testify.internal.processor.diff.HighContrastDiff
7879
import com.shopify.testify.internal.output.OutputFileUtility
7980
import com.shopify.testify.report.ReportSession
8081
import com.shopify.testify.report.Reporter
@@ -544,6 +545,13 @@ open class ScreenshotRule<T : Activity> @JvmOverloads constructor(
544545
screenshotUtility.deleteBitmap(activity, outputFileName)
545546
)
546547
} else {
548+
if (TestifyFeatures.GenerateDiffs.isEnabled(activity)) {
549+
HighContrastDiff()
550+
.name(outputFileName)
551+
.baseline(baselineBitmap)
552+
.current(currentBitmap)
553+
.generate(context = activity)
554+
}
547555
if (isRecordMode()) {
548556
instrumentationPrintln(
549557
"\n\t" + 27.toChar() + "[36mRecording baseline for " + testName +

Library/src/main/java/com/shopify/testify/ScreenshotUtility.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ScreenshotUtility {
5959
return options
6060
}
6161

62-
private fun saveBitmapToFile(context: Context, bitmap: Bitmap?, outputFilePath: String): Boolean {
62+
fun saveBitmapToFile(context: Context, bitmap: Bitmap?, outputFilePath: String): Boolean {
6363
if (bitmap == null) {
6464
return false
6565
}

Library/src/main/java/com/shopify/testify/TestifyFeatures.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum class TestifyFeatures(internal val tags: List<String>, private val defaultV
3232
ExampleDisabledFeature(listOf("testify-disabled")),
3333

3434
Reporter(listOf("testify-reporter")),
35+
GenerateDiffs(listOf("testify-generate-diffs"), defaultValue = false),
3536
Locale(listOf("testify-experimental-locale"), defaultValue = true),
3637
CanvasCapture(listOf("testify-canvas-capture")),
3738
PixelCopyCapture(listOf("testify-experimental-capture", "testify-pixelcopy-capture"));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.shopify.testify.internal.processor.diff
2+
3+
import android.content.Context
4+
import android.graphics.Bitmap
5+
import android.graphics.Color
6+
import com.shopify.testify.ScreenshotUtility
7+
import com.shopify.testify.internal.output.OutputFileUtility
8+
import com.shopify.testify.internal.processor.ParallelPixelProcessor
9+
import com.shopify.testify.internal.processor.createBitmap
10+
11+
class HighContrastDiff {
12+
13+
private lateinit var fileName: String
14+
private lateinit var baselineBitmap: Bitmap
15+
private lateinit var currentBitmap: Bitmap
16+
17+
fun generate(context: Context) {
18+
val transformResult = ParallelPixelProcessor
19+
.create()
20+
.baseline(baselineBitmap)
21+
.current(currentBitmap)
22+
.transform { baselinePixel, currentPixel ->
23+
if (baselinePixel == currentPixel) {
24+
Color.BLACK
25+
} else {
26+
Color.RED
27+
}
28+
}
29+
30+
val screenshotUtility = ScreenshotUtility()
31+
screenshotUtility.saveBitmapToFile(
32+
context = context,
33+
bitmap = transformResult.createBitmap(),
34+
outputFilePath = OutputFileUtility().getOutputFilePath(context, "$fileName.diff")
35+
)
36+
}
37+
38+
fun name(outputFileName: String): HighContrastDiff {
39+
fileName = outputFileName
40+
return this
41+
}
42+
43+
fun baseline(baselineBitmap: Bitmap): HighContrastDiff {
44+
this.baselineBitmap = baselineBitmap
45+
return this
46+
}
47+
48+
fun current(currentBitmap: Bitmap): HighContrastDiff {
49+
this.currentBitmap = currentBitmap
50+
return this
51+
}
52+
}
0 Bytes
Binary file not shown.
16.4 KB
Loading
191 KB
Loading

Sample/src/androidTest/java/com/shopify/testify/sample/ScreenshotRuleExampleTests.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.shopify.testify.TestifyFeatures
3737
import com.shopify.testify.annotation.ScreenshotInstrumentation
3838
import com.shopify.testify.annotation.TestifyLayout
3939
import com.shopify.testify.extensions.boundingBox
40+
import com.shopify.testify.internal.exception.ScreenshotIsDifferentException
4041
import com.shopify.testify.sample.test.TestHarnessActivity
4142
import com.shopify.testify.sample.test.TestHarnessActivity.Companion.EXTRA_TITLE
4243
import com.shopify.testify.sample.test.clientDetailsView
@@ -54,7 +55,8 @@ class ScreenshotRuleExampleTests {
5455
* [R.id.harness_root] is the topmost/root view in the hierarchy. Testify will load views in
5556
* this root.
5657
*/
57-
@get:Rule var rule = ScreenshotRule(
58+
@get:Rule
59+
var rule = ScreenshotRule(
5860
activityClass = TestHarnessActivity::class.java,
5961
launchActivity = false,
6062
rootViewId = R.id.harness_root
@@ -227,6 +229,25 @@ class ScreenshotRuleExampleTests {
227229
.assertSame()
228230
}
229231

232+
233+
@TestifyLayout(R.layout.view_client_details)
234+
@ScreenshotInstrumentation
235+
@Test(expected = ScreenshotIsDifferentException::class)
236+
fun generateDiffs() {
237+
TestifyFeatures.GenerateDiffs.setEnabled(true)
238+
rule.setViewModifications { harnessRoot ->
239+
rule.activity.getViewState(name = "A Name").let {
240+
val state = it.copy(
241+
name = "A Different Name",
242+
avatar = R.drawable.avatar2,
243+
heading = harnessRoot.context.getString(R.string.client_since, "2019")
244+
)
245+
harnessRoot.clientDetailsView.render(state)
246+
rule.activity.title = state.name
247+
}
248+
}.assertSame()
249+
}
250+
230251
/**
231252
* Demonstrates Testify's ability to take a screenshot of a single view.
232253
*

0 commit comments

Comments
 (0)