Skip to content

Commit eb284ef

Browse files
authored
Merge pull request #2076 from OneSignal/fix/op-repo-loadSavedOperations-dup-bug
Follow Up to PR #2068: ensure internalEnqueue can't add the same operation
2 parents 18a388c + 6e9b690 commit eb284ef

File tree

2 files changed

+38
-10
lines changed

2 files changed

+38
-10
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal class OperationRepo(
4848
)
4949

5050
private val executorsMap: Map<String, IOperationExecutor>
51-
private val queue = mutableListOf<OperationQueueItem>()
51+
internal val queue = mutableListOf<OperationQueueItem>()
5252
private val waiter = WaiterWithValue<LoopWaiterMessage>()
5353
private var paused = false
5454
private var coroutineScope = CoroutineScope(newSingleThreadContext(name = "OpRepo"))
@@ -143,6 +143,12 @@ internal class OperationRepo(
143143
addToStore: Boolean,
144144
index: Int? = null,
145145
) {
146+
val hasExisting = queue.any { it.operation.id == queueItem.operation.id }
147+
if (hasExisting) {
148+
Logging.debug("OperationRepo: internalEnqueue - operation.id: ${queueItem.operation.id} already exists in the queue.")
149+
return
150+
}
151+
146152
synchronized(queue) {
147153
if (index != null) {
148154
queue.add(index, queueItem)
@@ -402,7 +408,7 @@ internal class OperationRepo(
402408
* NOTE: Sometimes the loading might take longer than expected due to I/O reads from disk
403409
* Any I/O implies executing time will vary greatly.
404410
*/
405-
private fun loadSavedOperations() {
411+
internal fun loadSavedOperations() {
406412
_operationModelStore.loadOperations()
407413
for (operation in _operationModelStore.list().withIndex()) {
408414
internalEnqueue(

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

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,24 @@ import kotlinx.coroutines.delay
2727
import kotlinx.coroutines.launch
2828
import kotlinx.coroutines.withTimeoutOrNull
2929
import kotlinx.coroutines.yield
30+
import java.util.UUID
3031

3132
// Mocks used by every test in this file
3233
private class Mocks {
3334
val configModelStore = MockHelper.configModelStore()
3435

3536
val operationModelStore: OperationModelStore =
3637
run {
38+
val operationStoreList = mutableListOf<Operation>()
3739
val mockOperationModelStore = mockk<OperationModelStore>()
3840
every { mockOperationModelStore.loadOperations() } just runs
39-
every { mockOperationModelStore.list() } returns listOf()
40-
every { mockOperationModelStore.add(any()) } just runs
41-
every { mockOperationModelStore.remove(any()) } just runs
41+
every { mockOperationModelStore.list() } returns operationStoreList
42+
every { mockOperationModelStore.add(any()) } answers { operationStoreList.add(firstArg<Operation>()) }
43+
every { mockOperationModelStore.remove(any()) } answers {
44+
val id = firstArg<String>()
45+
val op = operationStoreList.firstOrNull { it.id == id }
46+
operationStoreList.remove(op)
47+
}
4248
mockOperationModelStore
4349
}
4450

@@ -117,9 +123,9 @@ class OperationRepoTests : FunSpec({
117123
test("enqueue operation executes and is removed when executed") {
118124
// Given
119125
val mocks = Mocks()
120-
121126
val operationIdSlot = slot<String>()
122127
val operation = mockOperation(operationIdSlot = operationIdSlot)
128+
val opId = operation.id
123129

124130
// When
125131
mocks.operationRepo.start()
@@ -136,7 +142,7 @@ class OperationRepoTests : FunSpec({
136142
it[0] shouldBe operation
137143
},
138144
)
139-
mocks.operationModelStore.remove("operationId")
145+
mocks.operationModelStore.remove(opId)
140146
}
141147
}
142148

@@ -151,6 +157,7 @@ class OperationRepoTests : FunSpec({
151157

152158
val operationIdSlot = slot<String>()
153159
val operation = mockOperation(operationIdSlot = operationIdSlot)
160+
val opId = operation.id
154161

155162
// When
156163
opRepo.start()
@@ -174,7 +181,7 @@ class OperationRepoTests : FunSpec({
174181
it[0] shouldBe operation
175182
},
176183
)
177-
mocks.operationModelStore.remove("operationId")
184+
mocks.operationModelStore.remove(opId)
178185
}
179186
}
180187

@@ -210,6 +217,7 @@ class OperationRepoTests : FunSpec({
210217

211218
val operationIdSlot = slot<String>()
212219
val operation = mockOperation(operationIdSlot = operationIdSlot)
220+
val opId = operation.id
213221

214222
// When
215223
mocks.operationRepo.start()
@@ -226,7 +234,7 @@ class OperationRepoTests : FunSpec({
226234
it[0] shouldBe operation
227235
},
228236
)
229-
mocks.operationModelStore.remove("operationId")
237+
mocks.operationModelStore.remove(opId)
230238
}
231239
}
232240

@@ -608,10 +616,24 @@ class OperationRepoTests : FunSpec({
608616
spyListener.onOperationRepoLoaded()
609617
}
610618
}
619+
620+
test("ensure loadSavedOperations doesn't duplicate existing OperationItems") {
621+
// Given
622+
val mocks = Mocks()
623+
val op = mockOperation()
624+
mocks.operationRepo.enqueue(op)
625+
626+
// When
627+
mocks.operationRepo.loadSavedOperations()
628+
629+
// Then
630+
mocks.operationRepo.queue.size shouldBe 1
631+
mocks.operationRepo.queue.first().operation shouldBe op
632+
}
611633
}) {
612634
companion object {
613635
private fun mockOperation(
614-
id: String = "operationId",
636+
id: String = UUID.randomUUID().toString(),
615637
name: String = "DUMMY_OPERATION",
616638
canStartExecute: Boolean = true,
617639
groupComparisonType: GroupComparisonType = GroupComparisonType.NONE,

0 commit comments

Comments
 (0)