Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make image extension changeable #527

Merged
merged 21 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/CompareScreenshotComment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
shell: bash
run: |
# Find all the files ending with _compare.png
mapfile -t files_to_add < <(find . -type f -name "*_compare.png")
mapfile -t files_to_add < <(find . -type f -name "*_compare.*")

# Check for invalid file names and add only valid ones
exist_valid_files="false"
Expand All @@ -79,7 +79,7 @@ jobs:
BRANCH_NAME: companion_${{ github.event.workflow_run.head_branch }}
run: |
# Find all the files ending with _compare.png
files_to_add=$(find . -type f -name "*_compare.png")
files_to_add=$(find . -type f -name "*_compare.*")

# Check for invalid file names and add only valid ones
for file in $files_to_add; do
Expand All @@ -99,7 +99,7 @@ jobs:
shell: bash
run: |
# Find all the files ending with _compare.png in roborazzi folder
files=$(find . -type f -name "*_compare.png" | grep "roborazzi/" | grep -E "^[a-zA-Z0-9_./-]+$")
files=$(find . -type f -name "*_compare.*" | grep "roborazzi/" | grep -E "^[a-zA-Z0-9_./-]+$")
delimiter="$(openssl rand -hex 8)"
{
echo "reports<<${delimiter}"
Expand Down
128 changes: 38 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ fun captureRoboGifSample() {

<img width="350" src="https://user-images.githubusercontent.com/1386930/226362212-35d34c9e-6df1-4671-8949-10fad7ad98c9.gif" />

### Automatically generate gif with test rule
### Generate gif with test rule

> **Note**
> You **don't need to use RoborazziRule** if you're using captureRoboImage().
Expand Down Expand Up @@ -732,7 +732,7 @@ class RuleTestWithOnlyFail {
}
```

### Automatically generate Jetpack Compose gif with test rule
### Generate Jetpack Compose gif with test rule

Test target

Expand Down Expand Up @@ -869,94 +869,6 @@ class RoborazziRule private constructor(
}
```

### Roborazzi options

```kotlin
data class RoborazziOptions(
val captureType: CaptureType = if (isNativeGraphicsEnabled()) CaptureType.Screenshot() else CaptureType.Dump(),
val compareOptions: CompareOptions = CompareOptions(),
val recordOptions: RecordOptions = RecordOptions(),
) {
sealed interface CaptureType {
class Screenshot : CaptureType

data class Dump(
val takeScreenShot: Boolean = isNativeGraphicsEnabled(),
val basicSize: Int = 600,
val depthSlideSize: Int = 30,
val query: ((RoboComponent) -> Boolean)? = null,
val explanation: ((RoboComponent) -> String?) = DefaultExplanation,
) : CaptureType {
companion object {
val DefaultExplanation: ((RoboComponent) -> String) = {
it.text
}
val AccessibilityExplanation: ((RoboComponent) -> String) = {
it.accessibilityText
}
}
}
}

data class CompareOptions(
val roborazziCompareReporter: RoborazziCompareReporter = RoborazziCompareReporter(),
val resultValidator: (result: ImageComparator.ComparisonResult) -> Boolean,
) {
constructor(
roborazziCompareReporter: RoborazziCompareReporter = RoborazziCompareReporter(),
/**
* This value determines the threshold of pixel change at which the diff image is output or not.
* The value should be between 0 and 1
*/
changeThreshold: Float = 0.01F,
) : this(roborazziCompareReporter, ThresholdValidator(changeThreshold))
}

interface RoborazziCompareReporter {
fun report(compareReportCaptureResult: CompareReportCaptureResult)

companion object {
operator fun invoke(): RoborazziCompareReporter {
...
}
}

class JsonOutputRoborazziCompareReporter : RoborazziCompareReporter {
...

override fun report(compareReportCaptureResult: CompareReportCaptureResult) {
...
}
}

class VerifyRoborazziCompareReporter : RoborazziCompareReporter {
override fun report(compareReportCaptureResult: CompareReportCaptureResult) {
...
}
}
}

data class RecordOptions(
val resizeScale: Double = roborazziDefaultResizeScale(),
val applyDeviceCrop: Boolean = false,
val pixelBitConfig: PixelBitConfig = PixelBitConfig.Argb8888,
)

enum class PixelBitConfig {
Argb8888,
Rgb565;

fun toBitmapConfig(): Bitmap.Config {
...
}

fun toBufferedImageType(): Int {
...
}
}
}
```

#### Image comparator custom settings
When comparing images, you may encounter differences due to minor changes related to antialiasing. You can use the options below to avoid this.
```kotlin
Expand All @@ -977,12 +889,48 @@ val roborazziRule = RoborazziRule(
)
```

### Experimental WebP support and other image formats

You can set `roborazzi.record.image.extension` to `webp` in your `gradle.properties` file to generate WebP images.

```kotlin
roborazzi.record.image.extension=webp
```

WebP is a lossy image format by default, which can make managing image differences challenging. To address this, we provide a lossless WebP image comparison feature.
To enable WebP support, add `testImplementation("io.github.darkxanter:webp-imageio:0.3.3")` to your `build.gradle.kts` file.

```kotlin
onView(ViewMatchers.withId(R.id.textview_first))
.captureRoboImage(
roborazziOptions = RoborazziOptions(
recordOptions = RoborazziOptions.RecordOptions(
imageIoFormat = LosslessWebPImageIoFormat(),
),
)
)
```

You can also use other image formats by implementing your own `AwtImageWriter` and `AwtImageLoader`.

```kotlin
data class JvmImageIoFormat(
val awtImageWriter: AwtImageWriter,
val awtImageLoader: AwtImageLoader
) : ImageIoFormat

```

### Dump mode

If you are having trouble debugging your test, try Dump mode as follows.

![image](https://user-images.githubusercontent.com/1386930/226364158-a07a0fb0-d8e7-46b7-a495-8dd217faaadb.png)

### Roborazzi options

Please check out [RoborazziOptions](https://github.com/takahirom/roborazzi/blob/main/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt) for available Roborazzi options.

</div>
<div name="topic_preview_support">

Expand Down
128 changes: 38 additions & 90 deletions docs/topics/how_to_use.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ fun captureRoboGifSample() {

<img width="350" src="https://user-images.githubusercontent.com/1386930/226362212-35d34c9e-6df1-4671-8949-10fad7ad98c9.gif" />

### Automatically generate gif with test rule
### Generate gif with test rule

> **Note**
> You **don't need to use RoborazziRule** if you're using captureRoboImage().
Expand Down Expand Up @@ -409,7 +409,7 @@ class RuleTestWithOnlyFail {
}
```

### Automatically generate Jetpack Compose gif with test rule
### Generate Jetpack Compose gif with test rule

Test target

Expand Down Expand Up @@ -546,94 +546,6 @@ class RoborazziRule private constructor(
}
```

### Roborazzi options

```kotlin
data class RoborazziOptions(
val captureType: CaptureType = if (isNativeGraphicsEnabled()) CaptureType.Screenshot() else CaptureType.Dump(),
val compareOptions: CompareOptions = CompareOptions(),
val recordOptions: RecordOptions = RecordOptions(),
) {
sealed interface CaptureType {
class Screenshot : CaptureType

data class Dump(
val takeScreenShot: Boolean = isNativeGraphicsEnabled(),
val basicSize: Int = 600,
val depthSlideSize: Int = 30,
val query: ((RoboComponent) -> Boolean)? = null,
val explanation: ((RoboComponent) -> String?) = DefaultExplanation,
) : CaptureType {
companion object {
val DefaultExplanation: ((RoboComponent) -> String) = {
it.text
}
val AccessibilityExplanation: ((RoboComponent) -> String) = {
it.accessibilityText
}
}
}
}

data class CompareOptions(
val roborazziCompareReporter: RoborazziCompareReporter = RoborazziCompareReporter(),
val resultValidator: (result: ImageComparator.ComparisonResult) -> Boolean,
) {
constructor(
roborazziCompareReporter: RoborazziCompareReporter = RoborazziCompareReporter(),
/**
* This value determines the threshold of pixel change at which the diff image is output or not.
* The value should be between 0 and 1
*/
changeThreshold: Float = 0.01F,
) : this(roborazziCompareReporter, ThresholdValidator(changeThreshold))
}

interface RoborazziCompareReporter {
fun report(compareReportCaptureResult: CompareReportCaptureResult)

companion object {
operator fun invoke(): RoborazziCompareReporter {
...
}
}

class JsonOutputRoborazziCompareReporter : RoborazziCompareReporter {
...

override fun report(compareReportCaptureResult: CompareReportCaptureResult) {
...
}
}

class VerifyRoborazziCompareReporter : RoborazziCompareReporter {
override fun report(compareReportCaptureResult: CompareReportCaptureResult) {
...
}
}
}

data class RecordOptions(
val resizeScale: Double = roborazziDefaultResizeScale(),
val applyDeviceCrop: Boolean = false,
val pixelBitConfig: PixelBitConfig = PixelBitConfig.Argb8888,
)

enum class PixelBitConfig {
Argb8888,
Rgb565;

fun toBitmapConfig(): Bitmap.Config {
...
}

fun toBufferedImageType(): Int {
...
}
}
}
```

#### Image comparator custom settings
When comparing images, you may encounter differences due to minor changes related to antialiasing. You can use the options below to avoid this.
```kotlin
Expand All @@ -654,8 +566,44 @@ val roborazziRule = RoborazziRule(
)
```

### Experimental WebP support and other image formats

You can set `roborazzi.record.image.extension` to `webp` in your `gradle.properties` file to generate WebP images.

```kotlin
roborazzi.record.image.extension=webp
```

WebP is a lossy image format by default, which can make managing image differences challenging. To address this, we provide a lossless WebP image comparison feature.
To enable WebP support, add `testImplementation("io.github.darkxanter:webp-imageio:0.3.3")` to your `build.gradle.kts` file.

```kotlin
onView(ViewMatchers.withId(R.id.textview_first))
.captureRoboImage(
roborazziOptions = RoborazziOptions(
recordOptions = RoborazziOptions.RecordOptions(
imageIoFormat = LosslessWebPImageIoFormat(),
),
)
)
```

You can also use other image formats by implementing your own `AwtImageWriter` and `AwtImageLoader`.

```kotlin
data class JvmImageIoFormat(
val awtImageWriter: AwtImageWriter,
val awtImageLoader: AwtImageLoader
) : ImageIoFormat

```

### Dump mode

If you are having trouble debugging your test, try Dump mode as follows.

![image](https://user-images.githubusercontent.com/1386930/226364158-a07a0fb0-d8e7-46b7-a495-8dd217faaadb.png)

### Roborazzi options

Please check out [RoborazziOptions](https://github.com/takahirom/roborazzi/blob/main/include-build/roborazzi-core/src/commonJvmMain/kotlin/com/github/takahirom/roborazzi/RoborazziOptions.kt) for available Roborazzi options.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ kotlinx-io = "0.3.3"
webjar-material-design-icons = "4.0.0"
webjar-materialize = "1.0.0"
webjars-locator-lite = "0.0.6"
webpImageio = "0.3.3"

composable-preview-scanner = "0.4.0"

Expand Down Expand Up @@ -110,3 +111,4 @@ kotlinx-io-core = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.re
webjars-material-design-icons = { module = "org.webjars:material-design-icons", version.ref = "webjar-material-design-icons" }
webjars-materialize = { module = "org.webjars:materializecss", version.ref = "webjar-materialize" }
webjars-locator-lite = { module = "org.webjars:webjars-locator-lite", version.ref = "webjars-locator-lite" }
webp-imageio = { module = "io.github.darkxanter:webp-imageio", version.ref = "webpImageio" }
3 changes: 3 additions & 0 deletions include-build/roborazzi-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ kotlin {
commonJvmMain {
dependencies {
implementation libs.junit
// The library is a little bit heavy, so we use compileOnly here.
// Users need to add this library to their dependencies.
compileOnly(libs.webp.imageio)
}
}
commonJvmTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object DefaultFileNameGenerator {
}

@InternalRoborazziApi
fun generateFilePath(extension: String): String {
fun generateFilePath(extension: String = provideRoborazziContext().imageExtension): String {
val roborazziContext = provideRoborazziContext()
val fileCreator = roborazziContext.fileProvider
val description = roborazziContext.description
Expand Down
Loading
Loading