Status Update
Comments
cl...@google.com <cl...@google.com> #2
reemission of the same liveData is racy
kh...@gmail.com <kh...@gmail.com> #3
ad...@guardian.co.uk <ad...@guardian.co.uk> #4
cl...@google.com <cl...@google.com> #5
@Test
fun raceTest() {
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData)
emitSource(subLiveData) //crashes
}
subject.addObserver().apply {
testScope.advanceUntilIdle()
}
}
ad...@guardian.co.uk <ad...@guardian.co.uk> #6
cl...@google.com <cl...@google.com> #7
I actually have a WIP fix for it:
if your case is the one i found (emitting same LiveData multiple times, as shown in #5) you can work around it by adding a dummy transformation.
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData.map {it })
emitSource(subLiveData.map {it} )
}
kf...@gmail.com <kf...@gmail.com> #8
Branch: androidx-master-dev
commit af12e75e6b4110f48e44ca121466943909de8f06
Author: Yigit Boyar <yboyar@google.com>
Date: Tue Sep 03 12:58:11 2019
Fix coroutine livedata race condition
This CL fixes a bug in liveData builder where emitting same
LiveData source twice would make it crash because the second
emission registry could possibly happen before first one is
removed as source.
We fix it by using a suspending dispose function. It does feel
a bit hacky but we cannot make DisposableHandle.dispose async
and we do not want to block there. This does not mean that there
is a problem if developer disposes it manually since our emit
functions take care of making sure it disposes (and there is
no other way to add source to the underlying MediatorLiveData)
Bug: 140249349
Test: BuildLiveDataTest#raceTest_*
Change-Id: I0b464c242a583da4669af195cf2504e2adc4de40
M lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/current.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_current.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
M lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
M lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/BuildLiveDataTest.kt
cl...@google.com <cl...@google.com> #9
As a quick update, the core cause of this issue is that in 3.3.0
paging was commonized to support Kotlin Multiplatform.
As a result, when an android project depends on Paging 3.3, it pulls in the android implementation for the main source as well as android unit tests. Hence you see PagingLogger.android.kt
in the stacktrace.
at androidx.paging.PagingLogger.isLoggable(PagingLogger.android.kt:29)
This will need to be fixed on our end so that unit tests do not trigger logs.
le...@gmail.com <le...@gmail.com> #10
Thanks a lot.
cl...@google.com <cl...@google.com> #11
While we work on the fix, here are a few workarounds in the meantime. Either option works:
- Make your project KMP and put paging tests in jvmTest
- Use Robolectric in your androidTest to run the tests as is
- Move the tests to instrumented androidTest and run the tests with emulator
ad...@guardian.co.uk <ad...@guardian.co.uk> #12
Thanks Clara!
"While we work on the fix... " - Does this mean we can expect this to be resolved in near future to work with non-KMP projects in regular jvm tests?
Migrating to KMP is a larger task and beyond our scope. Introducing Roboelectric just for paging tests also feels excessive.
Moving the tests to androidTest
feels like a reasonable approach in the moment. The only issue with this is that instrumented tests are slow and expensive, and once we move these tests there, we're quite likely to forget about ever moving them back to jvm.
If there's a solution for jvm tests without KMP in sight, we'd rather continue holding back from upgrading to 3.3.x
rather than migrate to androidTests.
It there's no change expected anytime soon (as Ian suggested in another channel), then we'd rather to bite the bullet now and migrate to androidTest
.
cl...@google.com <cl...@google.com> #13
Yup I get where you're coming from. I'm looking into makings the logs no-op in android tests (or making it optional) to remove the burden from developers. Not sure how viable this is though but if a fix on our end is possible, it will be released in 3.3.4
.
ap...@google.com <ap...@google.com> #14
Project: platform/frameworks/support
Branch: androidx-main
Author: Clara Fok <
Link:
Disable Paging logs on android unit tests
Expand for full commit details
Disable Paging logs on android unit tests
Non-kmp projects pulling in Paging 3.3 or later will automatically pull in android dependencies for android unit tests. This is by nature of how multiplatform sources are resolved.
Paging logger is an android dependency that requires instrumented tests or robolectric. As such it throws in non-instrumented unit tests. This CL checks os BUILD id and disables logging if null. This does not affect instrumented tests or robolectric tests as the ID in either case is non-null.
Test: existing tests pass
Bug: 331684448
Relnote: "Android unit tests pulling in Paging 3.3 or later will no longer throw from PagingLogger"
Change-Id: Ia9400db995e7f0357cae2647cbd2cef9cf6c3398
Files:
- M
paging/paging-common/src/androidMain/kotlin/androidx/paging/PagingLogger.android.kt
Hash: 77041951f769c8e98a60da2dbe540505d98084ad
Date: Wed Oct 30 17:21:10 2024
cl...@google.com <cl...@google.com> #15
Fixed internally and will be available in paging 3.3.4
ad...@guardian.co.uk <ad...@guardian.co.uk> #16
--
This e-mail and all attachments are confidential and may also be
privileged. If you are not the named recipient, please notify the sender
and delete the e-mail and all attachments immediately. Do not disclose the
contents to another person. You may not use the information for any
purpose, or store, or copy, it in any way. Guardian News & Media Limited
is not liable for any computer viruses or other material transmitted with
or as part of this e-mail. You should employ virus checking software.
Guardian News & Media Limited is a member of Guardian Media Group plc.
Registered Office: PO Box 68164, Kings Place, 90 York Way, London, N1P 2AP.
Registered in England Number 908396
na...@google.com <na...@google.com> #17
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.paging:paging-common:3.3.4
androidx.paging:paging-common-android:3.3.4
androidx.paging:paging-common-iosarm64:3.3.4
androidx.paging:paging-common-iossimulatorarm64:3.3.4
androidx.paging:paging-common-iosx64:3.3.4
androidx.paging:paging-common-jvm:3.3.4
androidx.paging:paging-common-linuxarm64:3.3.4
androidx.paging:paging-common-linuxx64:3.3.4
androidx.paging:paging-common-macosarm64:3.3.4
androidx.paging:paging-common-macosx64:3.3.4
androidx.paging:paging-common-tvosarm64:3.3.4
androidx.paging:paging-common-tvossimulatorarm64:3.3.4
androidx.paging:paging-common-tvosx64:3.3.4
androidx.paging:paging-common-watchosarm32:3.3.4
androidx.paging:paging-common-watchosarm64:3.3.4
androidx.paging:paging-common-watchossimulatorarm64:3.3.4
androidx.paging:paging-common-watchosx64:3.3.4
Description
Version used: 3.2.1
Devices/Android versions reproduced on: it's in unit-test
here is the error log
```
java.lang.RuntimeException: Method isLoggable in android.util.Log not mocked. See
at android.util.Log.isLoggable(Log.java)
at androidx.paging.AsyncPagingDataDiffer$Companion$1.isLoggable(AsyncPagingDataDiffer.kt:433)
at androidx.paging.PagingDataDiffer$collectFrom$2$1.emit(PagingDataDiffer.kt:555)
at androidx.paging.PagingDataDiffer$collectFrom$2$1.emit(PagingDataDiffer.kt:140)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:87)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:66)
at androidx.paging.CachedPageEventFlow$downstreamFlow$1$2.emit(CachedPageEventFlow.kt:106)
at androidx.paging.CachedPageEventFlow$downstreamFlow$1$2.emit(CachedPageEventFlow.kt:103)
at kotlinx.coroutines.flow.FlowKt__LimitKt$takeWhile$lambda$6$$inlined$collectWhile$1.emit(Limit.kt:143)
at kotlinx.coroutines.flow.SubscribedFlowCollector.emit(Share.kt)
at kotlinx.coroutines.flow.SharedFlowImpl.collect$suspendImpl(SharedFlow.kt:382)
at kotlinx.coroutines.flow.SharedFlowImpl.collect(SharedFlow.kt)
at kotlinx.coroutines.flow.SubscribedSharedFlow.collect(Share.kt:409)
at kotlinx.coroutines.flow.FlowKt__LimitKt$takeWhile$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:125)
at androidx.paging.CachedPageEventFlow$downstreamFlow$1.invokeSuspend(CachedPageEventFlow.kt:103)
at androidx.paging.CachedPageEventFlow$downstreamFlow$1.invoke(CachedPageEventFlow.kt)
at androidx.paging.CachedPageEventFlow$downstreamFlow$1.invoke(CachedPageEventFlow.kt)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230)
at kotlinx.coroutines.flow.FlowKt__EmittersKt$onStart$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:121)
at kotlinx.coroutines.flow.FlowKt__EmittersKt$onCompletion$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:115)
at androidx.paging.PagingDataDiffer$collectFrom$2.invokeSuspend(PagingDataDiffer.kt:140)
at androidx.paging.PagingDataDiffer$collectFrom$2.invoke(PagingDataDiffer.kt)
at androidx.paging.PagingDataDiffer$collectFrom$2.invoke(PagingDataDiffer.kt)
at androidx.paging.SingleRunner$runInIsolation$2.invokeSuspend(SingleRunner.kt:59)
at androidx.paging.SingleRunner$runInIsolation$2.invoke(SingleRunner.kt)
at androidx.paging.SingleRunner$runInIsolation$2.invoke(SingleRunner.kt)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78)
at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
at androidx.paging.SingleRunner.runInIsolation(SingleRunner.kt:49)
at androidx.paging.SingleRunner.runInIsolation$default(SingleRunner.kt:44)
at androidx.paging.PagingDataDiffer.collectFrom(PagingDataDiffer.kt:138)
at androidx.paging.AsyncPagingDataDiffer.submitData(AsyncPagingDataDiffer.kt:229)
```