blob: a2e59f73c0b4f73d979dfa6cce4918ebcd326334 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
Roman Elizarov1f74a2d2018-06-29 19:19:45 +03002 * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
Roman Elizarovf16fd272017-02-07 11:26:00 +03003 */
4
Roman Elizarov7cf452e2017-01-29 21:58:33 +03005package kotlinx.coroutines.experimental.future
Denis Zharkov8e4e0e42016-06-22 18:27:19 +03006
Roman Elizarov489cac22017-05-17 12:18:28 +03007import kotlinx.coroutines.experimental.*
Roman Elizarov19c1f2e2018-03-01 22:15:58 +03008import kotlinx.coroutines.experimental.CancellationException
Roman Elizarov9fe5f462018-02-21 19:05:52 +03009import org.hamcrest.core.*
10import org.junit.*
Roman Elizarov489cac22017-05-17 12:18:28 +030011import org.junit.Assert.*
Roman Elizarov19c1f2e2018-03-01 22:15:58 +030012import java.util.concurrent.*
Roman Elizarov9fe5f462018-02-21 19:05:52 +030013import java.util.concurrent.atomic.*
14import java.util.concurrent.locks.*
15import kotlin.concurrent.*
16import kotlin.coroutines.experimental.*
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030017
Roman Elizarov489cac22017-05-17 12:18:28 +030018class FutureTest : TestBase() {
Roman Elizarov45181042017-07-20 20:37:51 +030019 @Before
20 fun setup() {
21 ignoreLostThreads("ForkJoinPool.commonPool-worker-")
22 }
23
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030024 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +030025 fun testSimpleAwait() {
Roman Elizarov3754f952017-01-18 20:47:54 +030026 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +030027 CompletableFuture.supplyAsync {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030028 "O"
Denis Zharkovb4833122016-12-15 11:49:03 +030029 }.await() + "K"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030030 }
Roman Elizarov489cac22017-05-17 12:18:28 +030031 assertThat(future.get(), IsEqual("OK"))
32 }
33
34 @Test
35 fun testCompletedFuture() {
36 val toAwait = CompletableFuture<String>()
37 toAwait.complete("O")
38 val future = future {
39 toAwait.await() + "K"
40 }
41 assertThat(future.get(), IsEqual("OK"))
42 }
43
44 @Test
45 fun testCompletedCompletionStage() {
46 val completable = CompletableFuture<String>()
47 completable.complete("O")
48 val toAwait: CompletionStage<String> = completable
49 val future = future {
50 toAwait.await() + "K"
51 }
52 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030053 }
54
55 @Test
Roman Elizarovfc6461f2017-05-16 18:48:17 +030056 fun testWaitForFuture() {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030057 val toAwait = CompletableFuture<String>()
Roman Elizarov3754f952017-01-18 20:47:54 +030058 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +030059 toAwait.await() + "K"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030060 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030061 assertFalse(future.isDone)
62 toAwait.complete("O")
Roman Elizarov489cac22017-05-17 12:18:28 +030063 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030064 }
65
66 @Test
Roman Elizarovfc6461f2017-05-16 18:48:17 +030067 fun testWaitForCompletionStage() {
68 val completable = CompletableFuture<String>()
69 val toAwait: CompletionStage<String> = completable
70 val future = future {
71 toAwait.await() + "K"
72 }
73 assertFalse(future.isDone)
74 completable.complete("O")
Roman Elizarov489cac22017-05-17 12:18:28 +030075 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovfc6461f2017-05-16 18:48:17 +030076 }
77
78 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +030079 fun testCompletedFutureExceptionally() {
Roman Elizarovee893442017-01-19 14:56:21 +030080 val toAwait = CompletableFuture<String>()
Roman Elizarov61216632018-05-18 11:22:51 +030081 toAwait.completeExceptionally(TestException("O"))
82 val future = future {
Roman Elizarovee893442017-01-19 14:56:21 +030083 try {
84 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +030085 } catch (e: TestException) {
Roman Elizarovee893442017-01-19 14:56:21 +030086 e.message!!
87 } + "K"
88 }
Roman Elizarov489cac22017-05-17 12:18:28 +030089 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovee893442017-01-19 14:56:21 +030090 }
91
92 @Test
Roman Elizarov61216632018-05-18 11:22:51 +030093 // Test fast-path of CompletionStage.await() extension
Roman Elizarov489cac22017-05-17 12:18:28 +030094 fun testCompletedCompletionStageExceptionally() {
Roman Elizarovfc6461f2017-05-16 18:48:17 +030095 val completable = CompletableFuture<String>()
96 val toAwait: CompletionStage<String> = completable
Roman Elizarov61216632018-05-18 11:22:51 +030097 completable.completeExceptionally(TestException("O"))
98 val future = future {
Roman Elizarovfc6461f2017-05-16 18:48:17 +030099 try {
100 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +0300101 } catch (e: TestException) {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300102 e.message!!
103 } + "K"
104 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300105 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300106 }
107
108 @Test
Roman Elizarov61216632018-05-18 11:22:51 +0300109 // Test slow-path of CompletionStage.await() extension
110 fun testWaitForFutureWithException() = runTest {
111 expect(1)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300112 val toAwait = CompletableFuture<String>()
Roman Elizarov61216632018-05-18 11:22:51 +0300113 val future = future(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300114 try {
Roman Elizarov61216632018-05-18 11:22:51 +0300115 expect(2)
116 toAwait.await() // will suspend (slow path)
117 } catch (e: TestException) {
118 expect(4)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300119 e.message!!
120 } + "K"
121 }
Roman Elizarov61216632018-05-18 11:22:51 +0300122 expect(3)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300123 assertFalse(future.isDone)
Roman Elizarov61216632018-05-18 11:22:51 +0300124 toAwait.completeExceptionally(TestException("O"))
125 yield() // to future coroutine
Roman Elizarov489cac22017-05-17 12:18:28 +0300126 assertThat(future.get(), IsEqual("OK"))
Roman Elizarov61216632018-05-18 11:22:51 +0300127 finish(5)
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300128 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300129
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300130 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +0300131 fun testWaitForCompletionStageWithException() {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300132 val completable = CompletableFuture<String>()
133 val toAwait: CompletionStage<String> = completable
Roman Elizarov61216632018-05-18 11:22:51 +0300134 val future = future {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300135 try {
136 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +0300137 } catch (e: TestException) {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300138 e.message!!
139 } + "K"
140 }
141 assertFalse(future.isDone)
Roman Elizarov61216632018-05-18 11:22:51 +0300142 completable.completeExceptionally(TestException("O"))
Roman Elizarov489cac22017-05-17 12:18:28 +0300143 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300144 }
145
146 @Test
147 fun testExceptionInsideCoroutine() {
Roman Elizarov3754f952017-01-18 20:47:54 +0300148 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +0300149 if (CompletableFuture.supplyAsync { true }.await()) {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300150 throw IllegalStateException("OK")
151 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300152 "fail"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300153 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300154 try {
155 future.get()
156 fail("'get' should've throw an exception")
157 } catch (e: ExecutionException) {
158 assertTrue(e.cause is IllegalStateException)
Roman Elizarov489cac22017-05-17 12:18:28 +0300159 assertThat(e.cause!!.message, IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300160 }
161 }
162
163 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +0300164 fun testCompletedDeferredAsCompletableFuture() = runBlocking {
165 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +0300166 val deferred = async(coroutineContext, CoroutineStart.UNDISPATCHED) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300167 expect(2) // completed right away
168 "OK"
169 }
170 expect(3)
171 val future = deferred.asCompletableFuture()
172 assertThat(future.await(), IsEqual("OK"))
173 finish(4)
174 }
175
176 @Test
177 fun testWaitForDeferredAsCompletableFuture() = runBlocking {
178 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +0300179 val deferred = async(coroutineContext) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300180 expect(3) // will complete later
181 "OK"
182 }
183 expect(2)
184 val future = deferred.asCompletableFuture()
185 assertThat(future.await(), IsEqual("OK")) // await yields main thread to deferred coroutine
186 finish(4)
187 }
188
189 @Test
190 fun testCancellableAwaitFuture() = runBlocking {
191 expect(1)
192 val toAwait = CompletableFuture<String>()
Roman Elizarov43e3af72017-07-21 16:01:31 +0300193 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300194 expect(2)
195 try {
196 toAwait.await() // suspends
197 } catch (e: CancellationException) {
198 expect(5) // should throw cancellation exception
199 throw e
200 }
201 }
202 expect(3)
203 job.cancel() // cancel the job
204 toAwait.complete("fail") // too late, the waiting job was already cancelled
205 expect(4) // job processing of cancellation was scheduled, not executed yet
206 yield() // yield main thread to job
207 finish(6)
208 }
209
210 @Test
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300211 fun testContinuationWrapped() {
212 val depth = AtomicInteger()
Roman Elizarov3754f952017-01-18 20:47:54 +0300213 val future = future(wrapContinuation {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300214 depth.andIncrement
215 it()
216 depth.andDecrement
217 }) {
Roman Elizarov12f961d2017-02-06 15:44:03 +0300218 assertEquals("Part before first suspension must be wrapped", 1, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300219 val result =
Denis Zharkovb4833122016-12-15 11:49:03 +0300220 CompletableFuture.supplyAsync {
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100221 while (depth.get() > 0);
Roman Elizarov12f961d2017-02-06 15:44:03 +0300222 assertEquals("Part inside suspension point should not be wrapped", 0, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300223 "OK"
Denis Zharkovb4833122016-12-15 11:49:03 +0300224 }.await()
Roman Elizarov12f961d2017-02-06 15:44:03 +0300225 assertEquals("Part after first suspension should be wrapped", 1, depth.get())
Denis Zharkovb4833122016-12-15 11:49:03 +0300226 CompletableFuture.supplyAsync {
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100227 while (depth.get() > 0);
Roman Elizarov12f961d2017-02-06 15:44:03 +0300228 assertEquals("Part inside suspension point should not be wrapped", 0, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300229 "ignored"
Denis Zharkovb4833122016-12-15 11:49:03 +0300230 }.await()
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300231 result
232 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300233 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300234 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300235
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100236 @Test
237 fun testCompletableFutureStageAsDeferred() = runBlocking {
238 val lock = ReentrantLock().apply { lock() }
239
240 val deferred: Deferred<Int> = CompletableFuture.supplyAsync {
241 lock.withLock { 42 }
242 }.asDeferred()
243
244 assertFalse(deferred.isCompleted)
245 lock.unlock()
246
247 assertEquals(42, deferred.await())
248 assertTrue(deferred.isCompleted)
249 }
250
251 @Test
252 fun testCompletedFutureAsDeferred() = runBlocking {
253 val deferred: Deferred<Int> = CompletableFuture.completedFuture(42).asDeferred()
254 assertEquals(42, deferred.await())
255 }
256
257 @Test
258 fun testFailedFutureAsDeferred() = runBlocking {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300259 val future = CompletableFuture<Int>().apply {
260 completeExceptionally(TestException("something went wrong"))
261 }
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100262 val deferred = future.asDeferred()
263
264 assertTrue(deferred.isCompletedExceptionally)
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300265 val completionException = deferred.getCompletionExceptionOrNull()!!
266 assertTrue(completionException is TestException)
267 assertEquals("something went wrong", completionException.message)
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100268
269 try {
270 deferred.await()
271 fail("deferred.await() should throw an exception")
272 } catch (e: Exception) {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300273 assertTrue(e is TestException)
274 assertEquals("something went wrong", e.message)
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100275 }
276 }
277
278 @Test
279 fun testCompletableFutureWithExceptionAsDeferred() = runBlocking {
280 val lock = ReentrantLock().apply { lock() }
281
282 val deferred: Deferred<Int> = CompletableFuture.supplyAsync {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300283 lock.withLock { throw TestException("something went wrong") }
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100284 }.asDeferred()
285
286 assertFalse(deferred.isCompleted)
287 lock.unlock()
288
289 try {
290 deferred.await()
291 fail("deferred.await() should throw an exception")
292 } catch (e: Exception) {
293 assertTrue(deferred.isCompletedExceptionally)
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300294 assertTrue(e is CompletionException) // that's how supplyAsync wraps it
295 val cause = e.cause!!
296 assertTrue(cause is TestException)
297 assertEquals("something went wrong", cause.message)
298 assertSame(e, deferred.getCompletionExceptionOrNull()) // same exception is returns as thrown
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100299 }
300 }
301
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300302 class TestException(message: String) : Exception(message)
303
Roman Elizarov3754f952017-01-18 20:47:54 +0300304 private fun wrapContinuation(wrapper: (() -> Unit) -> Unit): CoroutineDispatcher = object : CoroutineDispatcher() {
Roman Elizarov67891d82017-01-23 16:47:20 +0300305 override fun dispatch(context: CoroutineContext, block: Runnable) {
Roman Elizarov3754f952017-01-18 20:47:54 +0300306 wrapper {
307 block.run()
308 }
309 }
310 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300311}