blob: 6ebd45b0d57d158ad42c3c6b73892ada27f49498 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Roman Elizarov7cf452e2017-01-29 21:58:33 +030017package kotlinx.coroutines.experimental.future
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030018
Roman Elizarov489cac22017-05-17 12:18:28 +030019import kotlinx.coroutines.experimental.*
Roman Elizarov19c1f2e2018-03-01 22:15:58 +030020import kotlinx.coroutines.experimental.CancellationException
Roman Elizarov9fe5f462018-02-21 19:05:52 +030021import org.hamcrest.core.*
22import org.junit.*
Roman Elizarov489cac22017-05-17 12:18:28 +030023import org.junit.Assert.*
Roman Elizarov19c1f2e2018-03-01 22:15:58 +030024import java.util.concurrent.*
Roman Elizarov9fe5f462018-02-21 19:05:52 +030025import java.util.concurrent.atomic.*
26import java.util.concurrent.locks.*
27import kotlin.concurrent.*
28import kotlin.coroutines.experimental.*
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030029
Roman Elizarov489cac22017-05-17 12:18:28 +030030class FutureTest : TestBase() {
Roman Elizarov45181042017-07-20 20:37:51 +030031 @Before
32 fun setup() {
33 ignoreLostThreads("ForkJoinPool.commonPool-worker-")
34 }
35
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030036 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +030037 fun testSimpleAwait() {
Roman Elizarov3754f952017-01-18 20:47:54 +030038 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +030039 CompletableFuture.supplyAsync {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030040 "O"
Denis Zharkovb4833122016-12-15 11:49:03 +030041 }.await() + "K"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030042 }
Roman Elizarov489cac22017-05-17 12:18:28 +030043 assertThat(future.get(), IsEqual("OK"))
44 }
45
46 @Test
47 fun testCompletedFuture() {
48 val toAwait = CompletableFuture<String>()
49 toAwait.complete("O")
50 val future = future {
51 toAwait.await() + "K"
52 }
53 assertThat(future.get(), IsEqual("OK"))
54 }
55
56 @Test
57 fun testCompletedCompletionStage() {
58 val completable = CompletableFuture<String>()
59 completable.complete("O")
60 val toAwait: CompletionStage<String> = completable
61 val future = future {
62 toAwait.await() + "K"
63 }
64 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030065 }
66
67 @Test
Roman Elizarovfc6461f2017-05-16 18:48:17 +030068 fun testWaitForFuture() {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030069 val toAwait = CompletableFuture<String>()
Roman Elizarov3754f952017-01-18 20:47:54 +030070 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +030071 toAwait.await() + "K"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030072 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030073 assertFalse(future.isDone)
74 toAwait.complete("O")
Roman Elizarov489cac22017-05-17 12:18:28 +030075 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +030076 }
77
78 @Test
Roman Elizarovfc6461f2017-05-16 18:48:17 +030079 fun testWaitForCompletionStage() {
80 val completable = CompletableFuture<String>()
81 val toAwait: CompletionStage<String> = completable
82 val future = future {
83 toAwait.await() + "K"
84 }
85 assertFalse(future.isDone)
86 completable.complete("O")
Roman Elizarov489cac22017-05-17 12:18:28 +030087 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovfc6461f2017-05-16 18:48:17 +030088 }
89
90 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +030091 fun testCompletedFutureExceptionally() {
Roman Elizarovee893442017-01-19 14:56:21 +030092 val toAwait = CompletableFuture<String>()
Roman Elizarov61216632018-05-18 11:22:51 +030093 toAwait.completeExceptionally(TestException("O"))
94 val future = future {
Roman Elizarovee893442017-01-19 14:56:21 +030095 try {
96 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +030097 } catch (e: TestException) {
Roman Elizarovee893442017-01-19 14:56:21 +030098 e.message!!
99 } + "K"
100 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300101 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovee893442017-01-19 14:56:21 +0300102 }
103
104 @Test
Roman Elizarov61216632018-05-18 11:22:51 +0300105 // Test fast-path of CompletionStage.await() extension
Roman Elizarov489cac22017-05-17 12:18:28 +0300106 fun testCompletedCompletionStageExceptionally() {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300107 val completable = CompletableFuture<String>()
108 val toAwait: CompletionStage<String> = completable
Roman Elizarov61216632018-05-18 11:22:51 +0300109 completable.completeExceptionally(TestException("O"))
110 val future = future {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300111 try {
112 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +0300113 } catch (e: TestException) {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300114 e.message!!
115 } + "K"
116 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300117 assertThat(future.get(), IsEqual("OK"))
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300118 }
119
120 @Test
Roman Elizarov61216632018-05-18 11:22:51 +0300121 // Test slow-path of CompletionStage.await() extension
122 fun testWaitForFutureWithException() = runTest {
123 expect(1)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300124 val toAwait = CompletableFuture<String>()
Roman Elizarov61216632018-05-18 11:22:51 +0300125 val future = future(coroutineContext, start = CoroutineStart.UNDISPATCHED) {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300126 try {
Roman Elizarov61216632018-05-18 11:22:51 +0300127 expect(2)
128 toAwait.await() // will suspend (slow path)
129 } catch (e: TestException) {
130 expect(4)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300131 e.message!!
132 } + "K"
133 }
Roman Elizarov61216632018-05-18 11:22:51 +0300134 expect(3)
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300135 assertFalse(future.isDone)
Roman Elizarov61216632018-05-18 11:22:51 +0300136 toAwait.completeExceptionally(TestException("O"))
137 yield() // to future coroutine
Roman Elizarov489cac22017-05-17 12:18:28 +0300138 assertThat(future.get(), IsEqual("OK"))
Roman Elizarov61216632018-05-18 11:22:51 +0300139 finish(5)
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300140 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300141
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300142 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +0300143 fun testWaitForCompletionStageWithException() {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300144 val completable = CompletableFuture<String>()
145 val toAwait: CompletionStage<String> = completable
Roman Elizarov61216632018-05-18 11:22:51 +0300146 val future = future {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300147 try {
148 toAwait.await()
Roman Elizarov61216632018-05-18 11:22:51 +0300149 } catch (e: TestException) {
Roman Elizarovfc6461f2017-05-16 18:48:17 +0300150 e.message!!
151 } + "K"
152 }
153 assertFalse(future.isDone)
Roman Elizarov61216632018-05-18 11:22:51 +0300154 completable.completeExceptionally(TestException("O"))
Roman Elizarov489cac22017-05-17 12:18:28 +0300155 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300156 }
157
158 @Test
159 fun testExceptionInsideCoroutine() {
Roman Elizarov3754f952017-01-18 20:47:54 +0300160 val future = future {
Denis Zharkovb4833122016-12-15 11:49:03 +0300161 if (CompletableFuture.supplyAsync { true }.await()) {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300162 throw IllegalStateException("OK")
163 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300164 "fail"
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300165 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300166 try {
167 future.get()
168 fail("'get' should've throw an exception")
169 } catch (e: ExecutionException) {
170 assertTrue(e.cause is IllegalStateException)
Roman Elizarov489cac22017-05-17 12:18:28 +0300171 assertThat(e.cause!!.message, IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300172 }
173 }
174
175 @Test
Roman Elizarov489cac22017-05-17 12:18:28 +0300176 fun testCompletedDeferredAsCompletableFuture() = runBlocking {
177 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +0300178 val deferred = async(coroutineContext, CoroutineStart.UNDISPATCHED) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300179 expect(2) // completed right away
180 "OK"
181 }
182 expect(3)
183 val future = deferred.asCompletableFuture()
184 assertThat(future.await(), IsEqual("OK"))
185 finish(4)
186 }
187
188 @Test
189 fun testWaitForDeferredAsCompletableFuture() = runBlocking {
190 expect(1)
Roman Elizarov43e3af72017-07-21 16:01:31 +0300191 val deferred = async(coroutineContext) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300192 expect(3) // will complete later
193 "OK"
194 }
195 expect(2)
196 val future = deferred.asCompletableFuture()
197 assertThat(future.await(), IsEqual("OK")) // await yields main thread to deferred coroutine
198 finish(4)
199 }
200
201 @Test
202 fun testCancellableAwaitFuture() = runBlocking {
203 expect(1)
204 val toAwait = CompletableFuture<String>()
Roman Elizarov43e3af72017-07-21 16:01:31 +0300205 val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) {
Roman Elizarov489cac22017-05-17 12:18:28 +0300206 expect(2)
207 try {
208 toAwait.await() // suspends
209 } catch (e: CancellationException) {
210 expect(5) // should throw cancellation exception
211 throw e
212 }
213 }
214 expect(3)
215 job.cancel() // cancel the job
216 toAwait.complete("fail") // too late, the waiting job was already cancelled
217 expect(4) // job processing of cancellation was scheduled, not executed yet
218 yield() // yield main thread to job
219 finish(6)
220 }
221
222 @Test
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300223 fun testContinuationWrapped() {
224 val depth = AtomicInteger()
Roman Elizarov3754f952017-01-18 20:47:54 +0300225 val future = future(wrapContinuation {
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300226 depth.andIncrement
227 it()
228 depth.andDecrement
229 }) {
Roman Elizarov12f961d2017-02-06 15:44:03 +0300230 assertEquals("Part before first suspension must be wrapped", 1, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300231 val result =
Denis Zharkovb4833122016-12-15 11:49:03 +0300232 CompletableFuture.supplyAsync {
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100233 while (depth.get() > 0);
Roman Elizarov12f961d2017-02-06 15:44:03 +0300234 assertEquals("Part inside suspension point should not be wrapped", 0, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300235 "OK"
Denis Zharkovb4833122016-12-15 11:49:03 +0300236 }.await()
Roman Elizarov12f961d2017-02-06 15:44:03 +0300237 assertEquals("Part after first suspension should be wrapped", 1, depth.get())
Denis Zharkovb4833122016-12-15 11:49:03 +0300238 CompletableFuture.supplyAsync {
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100239 while (depth.get() > 0);
Roman Elizarov12f961d2017-02-06 15:44:03 +0300240 assertEquals("Part inside suspension point should not be wrapped", 0, depth.get())
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300241 "ignored"
Denis Zharkovb4833122016-12-15 11:49:03 +0300242 }.await()
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300243 result
244 }
Roman Elizarov489cac22017-05-17 12:18:28 +0300245 assertThat(future.get(), IsEqual("OK"))
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300246 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300247
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100248 @Test
249 fun testCompletableFutureStageAsDeferred() = runBlocking {
250 val lock = ReentrantLock().apply { lock() }
251
252 val deferred: Deferred<Int> = CompletableFuture.supplyAsync {
253 lock.withLock { 42 }
254 }.asDeferred()
255
256 assertFalse(deferred.isCompleted)
257 lock.unlock()
258
259 assertEquals(42, deferred.await())
260 assertTrue(deferred.isCompleted)
261 }
262
263 @Test
264 fun testCompletedFutureAsDeferred() = runBlocking {
265 val deferred: Deferred<Int> = CompletableFuture.completedFuture(42).asDeferred()
266 assertEquals(42, deferred.await())
267 }
268
269 @Test
270 fun testFailedFutureAsDeferred() = runBlocking {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300271 val future = CompletableFuture<Int>().apply {
272 completeExceptionally(TestException("something went wrong"))
273 }
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100274 val deferred = future.asDeferred()
275
276 assertTrue(deferred.isCompletedExceptionally)
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300277 val completionException = deferred.getCompletionExceptionOrNull()!!
278 assertTrue(completionException is TestException)
279 assertEquals("something went wrong", completionException.message)
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100280
281 try {
282 deferred.await()
283 fail("deferred.await() should throw an exception")
284 } catch (e: Exception) {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300285 assertTrue(e is TestException)
286 assertEquals("something went wrong", e.message)
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100287 }
288 }
289
290 @Test
291 fun testCompletableFutureWithExceptionAsDeferred() = runBlocking {
292 val lock = ReentrantLock().apply { lock() }
293
294 val deferred: Deferred<Int> = CompletableFuture.supplyAsync {
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300295 lock.withLock { throw TestException("something went wrong") }
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100296 }.asDeferred()
297
298 assertFalse(deferred.isCompleted)
299 lock.unlock()
300
301 try {
302 deferred.await()
303 fail("deferred.await() should throw an exception")
304 } catch (e: Exception) {
305 assertTrue(deferred.isCompletedExceptionally)
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300306 assertTrue(e is CompletionException) // that's how supplyAsync wraps it
307 val cause = e.cause!!
308 assertTrue(cause is TestException)
309 assertEquals("something went wrong", cause.message)
310 assertSame(e, deferred.getCompletionExceptionOrNull()) // same exception is returns as thrown
Jonathan Cornaz7bd2c502018-02-28 15:59:21 +0100311 }
312 }
313
Roman Elizarov19c1f2e2018-03-01 22:15:58 +0300314 class TestException(message: String) : Exception(message)
315
Roman Elizarov3754f952017-01-18 20:47:54 +0300316 private fun wrapContinuation(wrapper: (() -> Unit) -> Unit): CoroutineDispatcher = object : CoroutineDispatcher() {
Roman Elizarov67891d82017-01-23 16:47:20 +0300317 override fun dispatch(context: CoroutineContext, block: Runnable) {
Roman Elizarov3754f952017-01-18 20:47:54 +0300318 wrapper {
319 block.run()
320 }
321 }
322 }
Denis Zharkov8e4e0e42016-06-22 18:27:19 +0300323}