@@ -7,11 +7,14 @@ import io.mockk.mockk
77import io.mockk.mockkObject
88import io.mockk.unmockkAll
99import kotlinx.coroutines.Dispatchers
10+ import kotlinx.coroutines.Job
1011import kotlinx.coroutines.delay
1112import kotlinx.coroutines.launch
1213import kotlinx.coroutines.runBlocking
1314import org.junit.After
14- import org.junit.Assert.*
15+ import org.junit.Assert.assertEquals
16+ import org.junit.Assert.assertFalse
17+ import org.junit.Assert.assertTrue
1518import org.junit.Before
1619import org.junit.Test
1720import java.security.cert.X509Certificate
@@ -53,6 +56,40 @@ class UserDecisionRegistryTest {
5356 })
5457 }
5558
59+ @Test
60+ fun testCheck_MultipleDecisionsForSameCert_allCancelled () {
61+ val canSendFeedback = Semaphore (0 )
62+ val getUserDecision: suspend (X509Certificate ) -> Boolean = mockk {
63+ coEvery { this @mockk(testCert) } coAnswers {
64+ canSendFeedback.acquire() // block call until released
65+ true
66+ }
67+ }
68+ val results = Collections .synchronizedList(mutableListOf<Boolean >())
69+ val jobs = mutableListOf<Job >()
70+ runBlocking(Dispatchers .Default ) {
71+ // launch 5 getUserDecision calls (each will be blocked by the semaphore)
72+ repeat(5 ) {
73+ jobs + = launch {
74+ results + = registry.check(testCert, this , getUserDecision)
75+ }
76+ }
77+ delay(1000 ) // wait a bit for all getUserDecision calls to be launched and blocked
78+
79+ // Cancel all jobs
80+ jobs.forEach { it.cancel() }
81+
82+ canSendFeedback.release() // now unblock
83+ }
84+
85+ // pendingDecisions should be empty
86+ synchronized(registry.pendingDecisions) {
87+ assertFalse(registry.pendingDecisions.containsKey(testCert))
88+ }
89+ assertEquals(0 , results.size) // should be 0 results
90+ coVerify(exactly = 1 ) { getUserDecision(testCert) } // getUserDecision should be called only once
91+ }
92+
5693 @Test
5794 fun testCheck_MultipleDecisionsForSameCert_Negative () {
5895 val canSendFeedback = Semaphore (0 )
0 commit comments