Skip to content

Commit fc48c72

Browse files
committed
add a test unit to simulate the ANR caused by operationRepo.enqueue while loading is not completed
1 parent 2c94030 commit fc48c72

File tree

1 file changed

+64
-0
lines changed

1 file changed

+64
-0
lines changed

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import com.onesignal.common.threading.WaiterWithValue
55
import com.onesignal.core.internal.operations.impl.OperationModelStore
66
import com.onesignal.core.internal.operations.impl.OperationRepo
77
import com.onesignal.core.internal.operations.impl.OperationRepo.OperationQueueItem
8+
import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys
9+
import com.onesignal.core.internal.preferences.PreferenceStores
810
import com.onesignal.core.internal.time.impl.Time
911
import com.onesignal.debug.LogLevel
1012
import com.onesignal.debug.internal.logging.Logging
1113
import com.onesignal.mocks.MockHelper
14+
import com.onesignal.mocks.MockPreferencesService
1215
import com.onesignal.user.internal.operations.ExecutorMocks.Companion.getNewRecordState
16+
import com.onesignal.user.internal.operations.LoginUserOperation
1317
import io.kotest.core.spec.style.FunSpec
1418
import io.kotest.matchers.shouldBe
1519
import io.mockk.CapturingSlot
@@ -28,6 +32,7 @@ import kotlinx.coroutines.launch
2832
import kotlinx.coroutines.withTimeout
2933
import kotlinx.coroutines.withTimeoutOrNull
3034
import kotlinx.coroutines.yield
35+
import org.json.JSONArray
3136
import java.util.UUID
3237

3338
// Mocks used by every test in this file
@@ -76,6 +81,65 @@ class OperationRepoTests : FunSpec({
7681
Logging.logLevel = LogLevel.NONE
7782
}
7883

84+
test("ensure loading in the background thread does not block enqueue") {
85+
// Given
86+
val prefs = MockPreferencesService()
87+
val mocks = Mocks()
88+
val operationModelStore: OperationModelStore = spyk(OperationModelStore(prefs))
89+
val operationRepo =
90+
spyk(
91+
OperationRepo(
92+
listOf(mocks.executor),
93+
operationModelStore,
94+
mocks.configModelStore,
95+
Time(),
96+
getNewRecordState(mocks.configModelStore),
97+
),
98+
)
99+
100+
val cachedOperation = LoginUserOperation()
101+
val newOperation = LoginUserOperation()
102+
val jsonArray = JSONArray()
103+
104+
// cache the operation
105+
jsonArray.put(cachedOperation.toJSON())
106+
prefs.saveString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + "operations", jsonArray.toString())
107+
108+
cachedOperation.id = UUID.randomUUID().toString()
109+
newOperation.id = UUID.randomUUID().toString()
110+
every { operationModelStore.create(any()) } answers {
111+
// simulate a prolonged loading from cache
112+
Thread.sleep(1000)
113+
cachedOperation
114+
}
115+
116+
// simulate a background thread to load operations
117+
val backgroundThread =
118+
Thread {
119+
operationRepo.loadSavedOperations()
120+
}
121+
122+
val mainThread =
123+
Thread {
124+
operationRepo.enqueue(newOperation)
125+
}
126+
127+
// When
128+
backgroundThread.start()
129+
mainThread.start()
130+
131+
// Then
132+
// insertion from the main thread is done without blocking
133+
mainThread.join(500)
134+
operationRepo.queue.size shouldBe 1
135+
mainThread.state shouldBe Thread.State.TERMINATED
136+
137+
// after loading is completed, the cached operation should be at the beginning of the queue
138+
backgroundThread.join()
139+
operationRepo.queue.size shouldBe 2
140+
operationRepo.queue.first().operation shouldBe cachedOperation
141+
}
142+
79143
test("containsInstanceOf") {
80144
// Given
81145
val operationRepo = Mocks().operationRepo

0 commit comments

Comments
 (0)