Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 1 | /* |
Roman Elizarov | 1f74a2d | 2018-06-29 19:19:45 +0300 | [diff] [blame^] | 2 | * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 3 | */ |
| 4 | |
Roman Elizarov | 7cf452e | 2017-01-29 21:58:33 +0300 | [diff] [blame] | 5 | package kotlinx.coroutines.experimental.future |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 6 | |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 7 | import kotlinx.coroutines.experimental.* |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 8 | import kotlinx.coroutines.experimental.CancellationException |
Roman Elizarov | 9fe5f46 | 2018-02-21 19:05:52 +0300 | [diff] [blame] | 9 | import org.hamcrest.core.* |
| 10 | import org.junit.* |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 11 | import org.junit.Assert.* |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 12 | import java.util.concurrent.* |
Roman Elizarov | 9fe5f46 | 2018-02-21 19:05:52 +0300 | [diff] [blame] | 13 | import java.util.concurrent.atomic.* |
| 14 | import java.util.concurrent.locks.* |
| 15 | import kotlin.concurrent.* |
| 16 | import kotlin.coroutines.experimental.* |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 17 | |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 18 | class FutureTest : TestBase() { |
Roman Elizarov | 4518104 | 2017-07-20 20:37:51 +0300 | [diff] [blame] | 19 | @Before |
| 20 | fun setup() { |
| 21 | ignoreLostThreads("ForkJoinPool.commonPool-worker-") |
| 22 | } |
| 23 | |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 24 | @Test |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 25 | fun testSimpleAwait() { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 26 | val future = future { |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 27 | CompletableFuture.supplyAsync { |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 28 | "O" |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 29 | }.await() + "K" |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 30 | } |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 31 | 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 Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | @Test |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 56 | fun testWaitForFuture() { |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 57 | val toAwait = CompletableFuture<String>() |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 58 | val future = future { |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 59 | toAwait.await() + "K" |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 60 | } |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 61 | assertFalse(future.isDone) |
| 62 | toAwait.complete("O") |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 63 | assertThat(future.get(), IsEqual("OK")) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | @Test |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 67 | 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 Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 75 | assertThat(future.get(), IsEqual("OK")) |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 76 | } |
| 77 | |
| 78 | @Test |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 79 | fun testCompletedFutureExceptionally() { |
Roman Elizarov | ee89344 | 2017-01-19 14:56:21 +0300 | [diff] [blame] | 80 | val toAwait = CompletableFuture<String>() |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 81 | toAwait.completeExceptionally(TestException("O")) |
| 82 | val future = future { |
Roman Elizarov | ee89344 | 2017-01-19 14:56:21 +0300 | [diff] [blame] | 83 | try { |
| 84 | toAwait.await() |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 85 | } catch (e: TestException) { |
Roman Elizarov | ee89344 | 2017-01-19 14:56:21 +0300 | [diff] [blame] | 86 | e.message!! |
| 87 | } + "K" |
| 88 | } |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 89 | assertThat(future.get(), IsEqual("OK")) |
Roman Elizarov | ee89344 | 2017-01-19 14:56:21 +0300 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | @Test |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 93 | // Test fast-path of CompletionStage.await() extension |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 94 | fun testCompletedCompletionStageExceptionally() { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 95 | val completable = CompletableFuture<String>() |
| 96 | val toAwait: CompletionStage<String> = completable |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 97 | completable.completeExceptionally(TestException("O")) |
| 98 | val future = future { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 99 | try { |
| 100 | toAwait.await() |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 101 | } catch (e: TestException) { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 102 | e.message!! |
| 103 | } + "K" |
| 104 | } |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 105 | assertThat(future.get(), IsEqual("OK")) |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | @Test |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 109 | // Test slow-path of CompletionStage.await() extension |
| 110 | fun testWaitForFutureWithException() = runTest { |
| 111 | expect(1) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 112 | val toAwait = CompletableFuture<String>() |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 113 | val future = future(coroutineContext, start = CoroutineStart.UNDISPATCHED) { |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 114 | try { |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 115 | expect(2) |
| 116 | toAwait.await() // will suspend (slow path) |
| 117 | } catch (e: TestException) { |
| 118 | expect(4) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 119 | e.message!! |
| 120 | } + "K" |
| 121 | } |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 122 | expect(3) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 123 | assertFalse(future.isDone) |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 124 | toAwait.completeExceptionally(TestException("O")) |
| 125 | yield() // to future coroutine |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 126 | assertThat(future.get(), IsEqual("OK")) |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 127 | finish(5) |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 128 | } |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 129 | |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 130 | @Test |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 131 | fun testWaitForCompletionStageWithException() { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 132 | val completable = CompletableFuture<String>() |
| 133 | val toAwait: CompletionStage<String> = completable |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 134 | val future = future { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 135 | try { |
| 136 | toAwait.await() |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 137 | } catch (e: TestException) { |
Roman Elizarov | fc6461f | 2017-05-16 18:48:17 +0300 | [diff] [blame] | 138 | e.message!! |
| 139 | } + "K" |
| 140 | } |
| 141 | assertFalse(future.isDone) |
Roman Elizarov | 6121663 | 2018-05-18 11:22:51 +0300 | [diff] [blame] | 142 | completable.completeExceptionally(TestException("O")) |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 143 | assertThat(future.get(), IsEqual("OK")) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | @Test |
| 147 | fun testExceptionInsideCoroutine() { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 148 | val future = future { |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 149 | if (CompletableFuture.supplyAsync { true }.await()) { |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 150 | throw IllegalStateException("OK") |
| 151 | } |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 152 | "fail" |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 153 | } |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 154 | try { |
| 155 | future.get() |
| 156 | fail("'get' should've throw an exception") |
| 157 | } catch (e: ExecutionException) { |
| 158 | assertTrue(e.cause is IllegalStateException) |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 159 | assertThat(e.cause!!.message, IsEqual("OK")) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 160 | } |
| 161 | } |
| 162 | |
| 163 | @Test |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 164 | fun testCompletedDeferredAsCompletableFuture() = runBlocking { |
| 165 | expect(1) |
Roman Elizarov | 43e3af7 | 2017-07-21 16:01:31 +0300 | [diff] [blame] | 166 | val deferred = async(coroutineContext, CoroutineStart.UNDISPATCHED) { |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 167 | 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 Elizarov | 43e3af7 | 2017-07-21 16:01:31 +0300 | [diff] [blame] | 179 | val deferred = async(coroutineContext) { |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 180 | 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 Elizarov | 43e3af7 | 2017-07-21 16:01:31 +0300 | [diff] [blame] | 193 | val job = launch(coroutineContext, CoroutineStart.UNDISPATCHED) { |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 194 | 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 Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 211 | fun testContinuationWrapped() { |
| 212 | val depth = AtomicInteger() |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 213 | val future = future(wrapContinuation { |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 214 | depth.andIncrement |
| 215 | it() |
| 216 | depth.andDecrement |
| 217 | }) { |
Roman Elizarov | 12f961d | 2017-02-06 15:44:03 +0300 | [diff] [blame] | 218 | assertEquals("Part before first suspension must be wrapped", 1, depth.get()) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 219 | val result = |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 220 | CompletableFuture.supplyAsync { |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 221 | while (depth.get() > 0); |
Roman Elizarov | 12f961d | 2017-02-06 15:44:03 +0300 | [diff] [blame] | 222 | assertEquals("Part inside suspension point should not be wrapped", 0, depth.get()) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 223 | "OK" |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 224 | }.await() |
Roman Elizarov | 12f961d | 2017-02-06 15:44:03 +0300 | [diff] [blame] | 225 | assertEquals("Part after first suspension should be wrapped", 1, depth.get()) |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 226 | CompletableFuture.supplyAsync { |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 227 | while (depth.get() > 0); |
Roman Elizarov | 12f961d | 2017-02-06 15:44:03 +0300 | [diff] [blame] | 228 | assertEquals("Part inside suspension point should not be wrapped", 0, depth.get()) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 229 | "ignored" |
Denis Zharkov | b483312 | 2016-12-15 11:49:03 +0300 | [diff] [blame] | 230 | }.await() |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 231 | result |
| 232 | } |
Roman Elizarov | 489cac2 | 2017-05-17 12:18:28 +0300 | [diff] [blame] | 233 | assertThat(future.get(), IsEqual("OK")) |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 234 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 235 | |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 236 | @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 Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 259 | val future = CompletableFuture<Int>().apply { |
| 260 | completeExceptionally(TestException("something went wrong")) |
| 261 | } |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 262 | val deferred = future.asDeferred() |
| 263 | |
| 264 | assertTrue(deferred.isCompletedExceptionally) |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 265 | val completionException = deferred.getCompletionExceptionOrNull()!! |
| 266 | assertTrue(completionException is TestException) |
| 267 | assertEquals("something went wrong", completionException.message) |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 268 | |
| 269 | try { |
| 270 | deferred.await() |
| 271 | fail("deferred.await() should throw an exception") |
| 272 | } catch (e: Exception) { |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 273 | assertTrue(e is TestException) |
| 274 | assertEquals("something went wrong", e.message) |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 275 | } |
| 276 | } |
| 277 | |
| 278 | @Test |
| 279 | fun testCompletableFutureWithExceptionAsDeferred() = runBlocking { |
| 280 | val lock = ReentrantLock().apply { lock() } |
| 281 | |
| 282 | val deferred: Deferred<Int> = CompletableFuture.supplyAsync { |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 283 | lock.withLock { throw TestException("something went wrong") } |
Jonathan Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 284 | }.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 Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 294 | 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 Cornaz | 7bd2c50 | 2018-02-28 15:59:21 +0100 | [diff] [blame] | 299 | } |
| 300 | } |
| 301 | |
Roman Elizarov | 19c1f2e | 2018-03-01 22:15:58 +0300 | [diff] [blame] | 302 | class TestException(message: String) : Exception(message) |
| 303 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 304 | private fun wrapContinuation(wrapper: (() -> Unit) -> Unit): CoroutineDispatcher = object : CoroutineDispatcher() { |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame] | 305 | override fun dispatch(context: CoroutineContext, block: Runnable) { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 306 | wrapper { |
| 307 | block.run() |
| 308 | } |
| 309 | } |
| 310 | } |
Denis Zharkov | 8e4e0e4 | 2016-06-22 18:27:19 +0300 | [diff] [blame] | 311 | } |