-
Notifications
You must be signed in to change notification settings - Fork 0
/
PromiseTests.kt
199 lines (179 loc) · 6.14 KB
/
PromiseTests.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package pt.isel.pc.problemsets.set1
import org.junit.jupiter.api.Test
import pt.isel.pc.problemsets.utils.MultiThreadTestHelper
import java.util.concurrent.CancellationException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds
internal class PromiseTests {
// tests without concurrency stress
@Test
fun `A promise is not done nor cancelled when created`() {
val promise = Promise<String>()
assertFalse(promise.isDone)
assertFalse(promise.isCancelled)
}
@Test
fun `A promise is done when resolved and should retrieve the result`() {
val value = "value"
val promise = Promise<String>()
promise.resolve(value)
assertFalse(promise.isCancelled)
assertTrue(promise.isDone)
assertEquals(value, promise.get(0, TimeUnit.MILLISECONDS))
}
@Test
fun `A promise is not done once marked as started`() {
val promise = Promise<String>()
promise.start()
assertFalse(promise.isCancelled)
assertFalse(promise.isDone)
}
@Test
fun `A started promise cannot be cancelled`() {
val promise = Promise<String>()
promise.start()
assertFalse(promise.cancel(true))
assertFalse(promise.isCancelled)
assertFalse(promise.isDone)
}
@Test
fun `A promise is done when rejected`() {
val promise = Promise<String>()
promise.reject(Exception())
assertFalse(promise.isCancelled)
assertTrue(promise.isDone)
}
@Test
fun `Cannot start a promise twice`() {
val promise = Promise<String>()
promise.start()
assertFailsWith<IllegalStateException> {
promise.start()
}
}
@Test
fun `Cannot resolve a promise twice`() {
val value = "value"
val promise = Promise<String>()
promise.resolve(value)
assertFailsWith<IllegalStateException> {
promise.resolve(value)
}
}
@Test
fun `Cannot reject a promise twice`() {
val promise = Promise<String>()
promise.reject(Exception())
assertFailsWith<IllegalStateException> {
promise.reject(Exception())
}
}
@Test
fun `A promise is done when cancelled`() {
val promise = Promise<String>()
promise.cancel(true)
assertTrue(promise.isCancelled)
assertTrue(promise.isDone)
}
@Test
fun `Can only cancel a promise in the pending state`() {
val promiseA = Promise<String>()
assertTrue(promiseA.cancel(true))
assertFailsWith<CancellationException> {
promiseA.get()
}
val promiseB = Promise<String>()
promiseB.start()
assertFalse(promiseB.cancel(true))
assertFalse(promiseB.isCancelled)
}
@Test
fun `A promise cannot be cancelled after it is resolved`() {
val value = "value"
val promise = Promise<String>()
promise.resolve(value)
assertFalse(promise.cancel(true))
assertFalse(promise.isCancelled)
assertTrue(promise.isDone)
assertEquals(value, promise.get(0, TimeUnit.MILLISECONDS))
}
@Test
fun `ExecutionException should be thrown if the value of a rejected promise is retrieved`() {
val promise = Promise<String>()
promise.reject(Exception())
assertFailsWith<ExecutionException> {
promise.get(0, TimeUnit.MILLISECONDS)
}
}
@Test
fun `A thread does not want to wait for the result of a promise`() {
val promise = Promise<String>()
assertFailsWith<TimeoutException> {
promise.get(0, TimeUnit.MILLISECONDS)
}
}
@Test
fun `TimeoutException should be thrown if the timeout exceed when waiting for a promise result`() {
val promise = Promise<String>()
val testHelper = MultiThreadTestHelper(5.seconds)
val timeout = 3000L
testHelper.createAndStartThread {
assertFailsWith<TimeoutException> {
promise.get(timeout, TimeUnit.MILLISECONDS)
}
}
// Make sure that the thread waiting exceeds the timeout
Thread.sleep(timeout + 1000)
promise.resolve("value")
testHelper.join()
}
@Test
fun `Nullable promise`() {
val promise = Promise<Int?>()
promise.resolve(null)
assertTrue(promise.isDone)
assertFalse(promise.isCancelled)
assertNull(promise.get())
}
@Test
fun `InterruptedException should be thrown if a thread waiting for the result is interrupted`() {
val promise = Promise<String>()
val testHelper = MultiThreadTestHelper(5.seconds)
val th1 = testHelper.createAndStartThread {
assertFailsWith<InterruptedException> { promise.get() }
}
// Make sure that the thread is waiting
Thread.sleep(2000)
th1.interrupt()
testHelper.join()
}
// tests with concurrency stress
@Test
fun `All threads waiting for a promise result should be see it when its completed`() {
val testHelper = MultiThreadTestHelper(10.seconds)
val promise = Promise<Int>()
val resolvedValue = 43
val nOfThreads = 100
val expectedValues = List(nOfThreads) { resolvedValue }
val retrievedValues: ConcurrentHashMap<Int, Int> = ConcurrentHashMap()
testHelper.createAndStartMultipleThreads(nOfThreads) { it, willingToWait ->
while (!willingToWait()) {
retrievedValues.computeIfAbsent(it) { _ -> promise.get() }
}
}
// Make sure that all threads are waiting
Thread.sleep(4000)
promise.resolve(resolvedValue)
testHelper.join()
assertEquals(expectedValues.size, retrievedValues.size)
assertEquals(expectedValues, retrievedValues.entries.map { it.value })
}
}