From ed34602c3370b657f731aaee06bcb6ed8e5e758a Mon Sep 17 00:00:00 2001 From: jinliu9508 Date: Tue, 10 Dec 2024 12:24:44 -0500 Subject: [PATCH] add a test unit to simulate the ANR caused by operationRepo.enqueue while loading is not completed --- .../com/onesignal/common/ModelingTests.kt | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/ModelingTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/ModelingTests.kt index 1ee72ddb3a..d80439b2f1 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/ModelingTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/common/ModelingTests.kt @@ -4,6 +4,7 @@ import com.onesignal.common.events.EventProducer import com.onesignal.common.modeling.IModelChangedHandler import com.onesignal.common.modeling.IModelStoreChangeHandler import com.onesignal.common.modeling.ModelChangedArgs +import com.onesignal.core.internal.operations.Operation import com.onesignal.core.internal.operations.impl.OperationModelStore import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys import com.onesignal.core.internal.preferences.PreferenceStores @@ -15,11 +16,64 @@ import com.onesignal.user.internal.subscriptions.SubscriptionModel import com.onesignal.user.internal.subscriptions.SubscriptionModelStore import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk import org.json.JSONArray +import org.json.JSONObject import java.util.UUID class ModelingTests : FunSpec({ + test("ensure prolonged loading in the background thread does not block insertion in the main thread") { + // Given + val prefs = MockPreferencesService() + val operationModelStore = OperationModelStore(prefs) + val mockOperationModelStore = mockk() + every { mockOperationModelStore.list() } answers { operationModelStore.list() } + every { mockOperationModelStore.add(any()) } answers { operationModelStore.add(firstArg()) } + every { mockOperationModelStore.remove(any()) } answers { + val id = firstArg() + operationModelStore.remove(id) + } + every { mockOperationModelStore.create(any()) } answers { + // force load() to take at least 200 ms to complete + Thread.sleep(200) + operationModelStore.create(firstArg()) + } + every { mockOperationModelStore.loadOperations() } answers { + // TODO: simulate a prolonged loading process while synchronizing operationModelStore.models + } + + // add an arbitrary operation to the cache + val cachedOperation = LoginUserFromSubscriptionOperation() + val newOperation = LoginUserOperation() + cachedOperation.id = UUID.randomUUID().toString() + val jsonArray = JSONArray() + jsonArray.put(cachedOperation.toJSON()) + prefs.saveString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + "operations", jsonArray.toString()) + + // simulate a background thread to load operations + val backgroundThread = + Thread { + mockOperationModelStore.loadOperations() + } + + val mainThread = + Thread { + mockOperationModelStore.add(newOperation) + } + + backgroundThread.start() + mainThread.start() + + mainThread.join(100) + + // Then + // insertion from the main thread is done without blocking + mockOperationModelStore.list().count() shouldBe 1 + mockOperationModelStore.list().first() shouldBe newOperation + } + test("Deadlock related to Model.setOptAnyProperty") { // Given val modelStore = MockHelper.configModelStore()