cashapp / molecule

Build a StateFlow stream using Jetpack Compose
https://cashapp.github.io/molecule/docs/1.x/
Apache License 2.0
1.87k stars 81 forks source link

Caused by: java.lang.UnsatisfiedLinkError: 'long android.os.Trace.nativeGetEnabledTags()' #321

Closed MedetZhakupov closed 11 months ago

MedetZhakupov commented 11 months ago

Getting above error when running unit tests with launchMolecule. Anyone has any clue what could go wrong?

app.cash.turbine.TurbineAssertionError: Expected item but found Error(UnsatisfiedLinkError) at app//app.cash.turbine.ChannelKt.unexpectedEvent(channel.kt:253) at app//app.cash.turbine.ChannelKt.awaitItem(channel.kt:192) at app//app.cash.turbine.ChannelTurbine$awaitItem$2.invokeSuspend(Turbine.kt:199) at app//app.cash.turbine.ChannelTurbine$awaitItem$2.invoke(Turbine.kt) at app//app.cash.turbine.ChannelTurbine$awaitItem$2.invoke(Turbine.kt) at app//app.cash.turbine.ChannelTurbine.withTurbineTimeout(Turbine.kt:112) at app//app.cash.turbine.ChannelTurbine.awaitItem(Turbine.kt:199) at app//app.cash.turbine.TurbineTestContextImpl.awaitItem(flow.kt) Caused by: java.lang.UnsatisfiedLinkError: 'long android.os.Trace.nativeGetEnabledTags()' at android.os.Trace.nativeGetEnabledTags(Native Method) at android.os.Trace.isTagEnabled(Trace.java:163) at android.os.Trace.beginSection(Trace.java:382) at androidx.compose.runtime.Trace.beginSection(ActualAndroid.android.kt:31) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:4673) at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3273) at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:588) at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1013) at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:520) at app.cash.molecule.MoleculeKt.launchMolecule(molecule.kt:163) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1$1.invokeSuspend(molecule.kt:64) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1$1.invoke(molecule.kt) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1$1.invoke(molecule.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:44) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) at kotlinx.coroutines.BuildersKt.launch(Unknown Source) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1.invokeSuspend(molecule.kt:63) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1.invoke(molecule.kt) at app.cash.molecule.MoleculeKt$immediateClockFlow$1$1.invoke(molecule.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78) at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264) at app.cash.molecule.MoleculeKt$immediateClockFlow$1.invokeSuspend(molecule.kt:59) at app.cash.molecule.MoleculeKt$immediateClockFlow$1.invoke(molecule.kt) at app.cash.molecule.MoleculeKt$immediateClockFlow$1.invoke(molecule.kt) at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61) at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230) at app.cash.turbine.FlowKt$collectIntoChannel$job$1.invokeSuspend(flow.kt:228) at app.cash.turbine.FlowKt$collectIntoChannel$job$1.invoke(flow.kt) at app.cash.turbine.FlowKt$collectIntoChannel$job$1.invoke(flow.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:44) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) at kotlinx.coroutines.BuildersKt.launch(Unknown Source) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47) at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source) at app.cash.turbine.FlowKt.collectIntoChannel(flow.kt:226) at app.cash.turbine.FlowKt$collectTurbineIn$job$1.invokeSuspend(flow.kt:216) at app.cash.turbine.FlowKt$collectTurbineIn$job$1.invoke(flow.kt) at app.cash.turbine.FlowKt$collectTurbineIn$job$1.invoke(flow.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startCoroutineUndispatched(Undispatched.kt:44) at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:112) at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56) at kotlinx.coroutines.BuildersKt.launch(Unknown Source) at app.cash.turbine.FlowKt.collectTurbineIn-5_5nbZA(flow.kt:215) at app.cash.turbine.FlowKt.access$collectTurbineIn-5_5nbZA(flow.kt:1) at app.cash.turbine.FlowKt$test$2.invokeSuspend(flow.kt:142) at app.cash.turbine.FlowKt$test$2.invoke(flow.kt) at app.cash.turbine.FlowKt$test$2.invoke(flow.kt) at app.cash.turbine.FlowKt$turbineScope$2$1.invokeSuspend(flow.kt:91) at app.cash.turbine.FlowKt$turbineScope$2$1.invoke(flow.kt) at app.cash.turbine.FlowKt$turbineScope$2$1.invoke(flow.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78) at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264) at app.cash.turbine.FlowKt$turbineScope$2$scopeFn$1.invokeSuspend(flow.kt:83) at app.cash.turbine.FlowKt$turbineScope$2$scopeFn$1.invoke(flow.kt) at app.cash.turbine.FlowKt$turbineScope$2$scopeFn$1.invoke(flow.kt) at app.cash.turbine.FlowKt$turbineScope$2.invokeSuspend(flow.kt:88) at app.cash.turbine.FlowKt$turbineScope$2.invoke(flow.kt) at app.cash.turbine.FlowKt$turbineScope$2.invoke(flow.kt) at app.cash.turbine.CoroutinesKt$reportTurbines$2.invokeSuspend(coroutines.kt:89) at app.cash.turbine.CoroutinesKt$reportTurbines$2.invoke(coroutines.kt) at app.cash.turbine.CoroutinesKt$reportTurbines$2.invoke(coroutines.kt) at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78) at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:167) at kotlinx.coroutines.BuildersKt.withContext(Unknown Source) at app.cash.turbine.CoroutinesKt.reportTurbines(coroutines.kt:88) at app.cash.turbine.FlowKt.turbineScope-k1IrOU0(flow.kt:80) at app.cash.turbine.FlowKt.turbineScope-k1IrOU0$default(flow.kt:75) at app.cash.turbine.FlowKt.test-C2H2yOE(flow.kt:141) at app.cash.turbine.FlowKt.test-C2H2yOE$default(flow.kt:136)

JakeWharton commented 11 months ago

Best guess based on the little information the trace conveys, you are running Molecule in an Android app and using Turbine to test it in a JVM test. You are using Robolectric, Unmock, or Paparazzi to get a subset of real Android code running on the JVM for the test. The Compose runtime that Molecule sets up calls into Trace, which then tries to call into a native method whose associated JNI is not loaded (hence the unsatified link error).

All of that to say, if you replace your test with a Trace.beginSection call you should see the same error. The problem is not with Molecule specifically, or its usage of Compose, but rather then environment of the test is somehow providing the real implementation of the Trace class without proper stubbing of the code that calls into native. So if you are using Robolectric, Unmock, or Paparazzi, please consider reaching out to them about the fact that calls to Trace are failing.

MedetZhakupov commented 11 months ago

Indeed we have paparazzi in the project. I will open issue in the paparazzi repo then. Thanks a lot @JakeWharton and the team for creating such a great library