Skip to content

Commit d4878c2

Browse files
authored
Remove last continuation in list when cancelled (#53)
* Add test to check whether pendingDecisions are empty after cancellation * Remove last continuation from pendingDecisions when cancelled * Synchronize both check and removal of empty decisions list
1 parent 43b2d63 commit d4878c2

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

lib/src/androidTest/java/at/bitfire/cert4android/UserDecisionRegistryTest.kt

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
package at.bitfire.cert4android
22

3+
import androidx.core.app.NotificationManagerCompat
34
import androidx.test.platform.app.InstrumentationRegistry
45
import io.mockk.every
6+
import io.mockk.just
7+
import io.mockk.mockk
58
import io.mockk.mockkObject
9+
import io.mockk.runs
610
import io.mockk.unmockkAll
711
import io.mockk.verify
812
import kotlinx.coroutines.Dispatchers
13+
import kotlinx.coroutines.delay
914
import kotlinx.coroutines.launch
1015
import kotlinx.coroutines.runBlocking
1116
import org.junit.After
12-
import org.junit.Assert.*
17+
import org.junit.Assert.assertEquals
18+
import org.junit.Assert.assertFalse
19+
import org.junit.Assert.assertTrue
1320
import org.junit.Before
1421
import org.junit.Test
1522
import java.util.Collections
@@ -111,6 +118,37 @@ class UserDecisionRegistryTest {
111118
verify(exactly = 1) { registry.requestDecision(any(), any(), any()) }
112119
}
113120

121+
@Test
122+
fun testCheck_MultipleDecisionsForSameCert_cancel() {
123+
val canSendFeedback = Semaphore(0)
124+
val nm = mockk<NotificationManagerCompat>()
125+
every { nm.cancel(any(), any()) } just runs
126+
every { NotificationUtils.createChannels(any()) } returns nm
127+
every { registry.requestDecision(testCert, any(), any()) } answers {
128+
thread {
129+
canSendFeedback.acquire()
130+
registry.onUserDecision(testCert, false)
131+
}
132+
}
133+
val results = Collections.synchronizedList(mutableListOf<Boolean>())
134+
runBlocking {
135+
repeat(5) {
136+
val job = launch(Dispatchers.Default) {
137+
results += registry.check(testCert, true)
138+
}
139+
delay(1000)
140+
job.cancel() // Cancel the job
141+
delay(1000)
142+
}
143+
canSendFeedback.release()
144+
}
145+
synchronized(registry.pendingDecisions) {
146+
assertFalse(registry.pendingDecisions.containsKey(testCert))
147+
}
148+
assertEquals(0, results.size)
149+
verify(exactly = 5) { registry.requestDecision(any(), any(), any()) }
150+
}
151+
114152
@Test
115153
fun testCheck_UserDecisionImpossible() {
116154
every { NotificationUtils.notificationsPermitted(any()) } returns false

lib/src/main/java/at/bitfire/cert4android/UserDecisionRegistry.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,15 @@ class UserDecisionRegistry private constructor(
5353
// User decision possible → remember request in pendingDecisions so that a later decision will be applied to this request
5454

5555
cont.invokeOnCancellation {
56-
// remove from pending decisions on cancellation
5756
synchronized(pendingDecisions) {
58-
pendingDecisions[cert]?.remove(cont)
57+
val decisionsList = pendingDecisions[cert]
58+
59+
// remove from pending decisions on cancellation
60+
decisionsList?.remove(cont)
61+
62+
// Remove decisions list if empty
63+
if (decisionsList?.isEmpty() == true)
64+
pendingDecisions -= cert
5965
}
6066

6167
val nm = NotificationUtils.createChannels(context)

0 commit comments

Comments
 (0)