Skip to content

Commit

Permalink
Use our own HandlerDispatcher for ComposeUi tests (cashapp#1306)
Browse files Browse the repository at this point in the history
  • Loading branch information
gamepro65 authored Feb 26, 2024
1 parent 5d158e6 commit 7276226
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 5 deletions.
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ agp = "8.2.2"
androidTools = "31.2.2" # == 23.0.0 + agp version
bytebuddy = "1.14.12"
composeCompiler = "1.5.8"
coroutines = "1.8.0"
javaTarget = "11"
jcodec = "0.2.5"
kotlin = "1.9.22"
Expand Down Expand Up @@ -37,7 +38,8 @@ jcodec-core = { module = "org.jcodec:jcodec", version.ref = "jcodec" }
jcodec-javase = { module = "org.jcodec:jcodec-javase", version.ref = "jcodec" }

kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version = "1.8.0" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }

ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ android {

dependencies {
implementation libs.composeUi.material

testImplementation libs.kotlinx.coroutines.test
}

apply from: '../guava-fix.gradle'
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ import app.cash.paparazzi.Paparazzi
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.resetMain

@OptIn(ExperimentalCoroutinesApi::class)
class CoroutineDelayMainTest {
@get:Rule
val paparazzi = Paparazzi()

init {
// coroutines-test installs a TestMainDispatcher which is incompatible with ComposeUi
Dispatchers.resetMain()
}

@Test fun delayUsesMainDispatcher() {
var start = 0L
var end = 0L
Expand Down
13 changes: 9 additions & 4 deletions paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import android.animation.AnimationHandler
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
import android.os.Handler
import android.os.Handler_Delegate
import android.os.SystemClock_Delegate
import android.util.AttributeSet
Expand Down Expand Up @@ -85,7 +86,7 @@ import java.awt.image.BufferedImage
import java.util.Date
import java.util.EnumSet
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.asCoroutineDispatcher

@OptIn(ExperimentalComposeUiApi::class, InternalComposeUiApi::class)
public class Paparazzi @JvmOverloads constructor(
Expand Down Expand Up @@ -303,9 +304,9 @@ public class Paparazzi @JvmOverloads constructor(

// By default, Compose UI uses its own implementation of CoroutineDispatcher, `AndroidUiDispatcher`.
// Since this dispatcher does not provide its own implementation of Delay, it will default to using DefaultDelay which runs
// async to our test Handler. By initializing Recomposer with Dispatchers.Main, Delay will now be backed by our test Handler,
// synchronizing expected behavior.
WindowRecomposerPolicy.setFactory { it.createLifecycleAwareWindowRecomposer(Dispatchers.Main) }
// async to our test Handler. By initializing Recomposer with our own HandlerDispatcher, Delay will now be backed
// by our test Handler, synchronizing expected behavior.
WindowRecomposerPolicy.setFactory { it.createLifecycleAwareWindowRecomposer(MAIN_DISPATCHER) }
}

if (hasLifecycleOwnerRuntime) {
Expand Down Expand Up @@ -647,6 +648,10 @@ public class Paparazzi @JvmOverloads constructor(

internal lateinit var sessionParamsBuilder: SessionParamsBuilder

private val MAIN_DISPATCHER by lazy {
Handler.getMain().asCoroutineDispatcher("Paparazzi-Main")
}

private val isVerifying: Boolean =
System.getProperty("paparazzi.test.verify")?.toBoolean() == true

Expand Down

0 comments on commit 7276226

Please sign in to comment.