195 });
196 assertThrows(WrongThreadException.class, () -> {
197 scope2.fork(() -> null);
198 });
199 return null;
200 });
201 future.get();
202 }
203 }
204 }
205
206 /**
207 * Test fork after join completes.
208 */
209 @ParameterizedTest
210 @MethodSource("factories")
211 void testForkAfterJoin(ThreadFactory factory) throws Exception {
212 try (var scope = new StructuredTaskScope<String>(null, factory)) {
213 // round 1
214 var subtask1 = scope.fork(() -> "foo");
215 assertThrows(IllegalStateException.class, subtask1::get);
216 scope.join();
217 assertEquals("foo", subtask1.get());
218
219 // round 2
220 var subtask2 = scope.fork(() -> "bar");
221 assertEquals("foo", subtask1.get());
222 assertThrows(IllegalStateException.class, subtask2::get);
223 scope.join();
224 assertEquals("foo", subtask1.get());
225 assertEquals("bar", subtask2.get());
226
227 // round 3
228 var subtask3 = scope.fork(() -> "baz");
229 assertEquals("foo", subtask1.get());
230 assertEquals("bar", subtask2.get());
231 assertThrows(IllegalStateException.class, subtask3::get);
232 scope.join();
233 assertEquals("foo", subtask1.get());
234 assertEquals("bar", subtask2.get());
235 assertEquals("baz", subtask3.get());
236 }
237 }
238
239 /**
240 * Test fork after join throws.
241 */
242 @ParameterizedTest
243 @MethodSource("factories")
244 void testForkAfterJoinThrows(ThreadFactory factory) throws Exception {
245 try (var scope = new StructuredTaskScope<String>(null, factory)) {
246 var latch = new CountDownLatch(1);
247 var subtask1 = scope.fork(() -> {
248 latch.await();
249 return "foo";
250 });
251
252 // join throws
253 Thread.currentThread().interrupt();
254 assertThrows(InterruptedException.class, scope::join);
255
256 // allow subtask1 to finish
257 latch.countDown();
258
259 // continue to fork
260 var subtask2 = scope.fork(() -> "bar");
261 assertThrows(IllegalStateException.class, subtask1::get);
262 assertThrows(IllegalStateException.class, subtask2::get);
263 scope.join();
264 assertEquals("foo", subtask1.get());
265 assertEquals("bar", subtask2.get());
266 }
267 }
268
269 /**
270 * Test fork after scope is shutdown.
271 */
272 @ParameterizedTest
273 @MethodSource("factories")
274 void testForkAfterShutdown(ThreadFactory factory) throws Exception {
275 var executed = new AtomicBoolean();
276 try (var scope = new StructuredTaskScope<Object>(null, factory)) {
277 scope.shutdown();
278 Subtask<String> subtask = scope.fork(() -> {
279 executed.set(true);
280 return null;
281 });
282 scope.join();
1257 // shutdown
1258 scope.shutdown();
1259 assertTrue(scope.toString().contains("duke"));
1260
1261 // closed
1262 scope.join();
1263 scope.close();
1264 assertTrue(scope.toString().contains("duke"));
1265 }
1266 }
1267
1268 /**
1269 * Test Subtask with task that completes successfully.
1270 */
1271 @ParameterizedTest
1272 @MethodSource("factories")
1273 void testSubtaskWhenSuccess(ThreadFactory factory) throws Exception {
1274 try (var scope = new StructuredTaskScope<String>(null, factory)) {
1275 Callable<String> task = () -> "foo";
1276 Subtask<String> subtask = scope.fork(task);
1277
1278 // before join, owner thread
1279 assertEquals(task, subtask.task());
1280 assertThrows(IllegalStateException.class, subtask::get);
1281 assertThrows(IllegalStateException.class, subtask::exception);
1282
1283 scope.join();
1284
1285 // after join
1286 assertEquals(task, subtask.task());
1287 assertEquals(Subtask.State.SUCCESS, subtask.state());
1288 assertEquals("foo", subtask.get());
1289 assertThrows(IllegalStateException.class, subtask::exception);
1290 }
1291 }
1292
1293 /**
1294 * Test Subtask with task that fails.
1295 */
1296 @ParameterizedTest
1297 @MethodSource("factories")
1298 void testSubtaskWhenFailed(ThreadFactory factory) throws Exception {
1299 try (var scope = new StructuredTaskScope<String>(null, factory)) {
1300 Callable<String> task = () -> { throw new FooException(); };
1301 Subtask<String> subtask = scope.fork(task);
1302
1303 // before join, owner thread
1304 assertEquals(task, subtask.task());
1305 assertThrows(IllegalStateException.class, subtask::get);
1306 assertThrows(IllegalStateException.class, subtask::exception);
1307
1308 scope.join();
1309
1310 // after join
1311 assertEquals(task, subtask.task());
1312 assertEquals(Subtask.State.FAILED, subtask.state());
1313 assertThrows(IllegalStateException.class, subtask::get);
1314 assertTrue(subtask.exception() instanceof FooException);
1315 }
1316 }
1317
1318 /**
1319 * Test Subtask with a task that has not completed.
1320 */
1321 @ParameterizedTest
1322 @MethodSource("factories")
1323 void testSubtaskWhenNotCompleted(ThreadFactory factory) throws Exception {
1324 try (var scope = new StructuredTaskScope<Object>(null, factory)) {
1325 Callable<Void> task = () -> {
1326 Thread.sleep(Duration.ofDays(1));
1327 return null;
|
195 });
196 assertThrows(WrongThreadException.class, () -> {
197 scope2.fork(() -> null);
198 });
199 return null;
200 });
201 future.get();
202 }
203 }
204 }
205
206 /**
207 * Test fork after join completes.
208 */
209 @ParameterizedTest
210 @MethodSource("factories")
211 void testForkAfterJoin(ThreadFactory factory) throws Exception {
212 try (var scope = new StructuredTaskScope<String>(null, factory)) {
213 // round 1
214 var subtask1 = scope.fork(() -> "foo");
215 scope.join();
216 assertEquals("foo", subtask1.get());
217
218 // round 2
219 var subtask2 = scope.fork(() -> "bar");
220 assertEquals("foo", subtask1.get());
221 scope.join();
222 assertEquals("foo", subtask1.get());
223 assertEquals("bar", subtask2.get());
224
225 // round 3
226 var subtask3 = scope.fork(() -> "baz");
227 assertEquals("foo", subtask1.get());
228 assertEquals("bar", subtask2.get());
229 scope.join();
230 assertEquals("foo", subtask1.get());
231 assertEquals("bar", subtask2.get());
232 assertEquals("baz", subtask3.get());
233 }
234 }
235
236 /**
237 * Test fork after join throws.
238 */
239 @ParameterizedTest
240 @MethodSource("factories")
241 void testForkAfterJoinThrows(ThreadFactory factory) throws Exception {
242 try (var scope = new StructuredTaskScope<String>(null, factory)) {
243 var latch = new CountDownLatch(1);
244 var subtask1 = scope.fork(() -> {
245 latch.await();
246 return "foo";
247 });
248
249 // join throws
250 Thread.currentThread().interrupt();
251 assertThrows(InterruptedException.class, scope::join);
252
253 // allow subtask1 to finish
254 latch.countDown();
255
256 // continue to fork
257 var subtask2 = scope.fork(() -> "bar");
258 scope.join();
259 assertEquals("foo", subtask1.get());
260 assertEquals("bar", subtask2.get());
261 }
262 }
263
264 /**
265 * Test fork after scope is shutdown.
266 */
267 @ParameterizedTest
268 @MethodSource("factories")
269 void testForkAfterShutdown(ThreadFactory factory) throws Exception {
270 var executed = new AtomicBoolean();
271 try (var scope = new StructuredTaskScope<Object>(null, factory)) {
272 scope.shutdown();
273 Subtask<String> subtask = scope.fork(() -> {
274 executed.set(true);
275 return null;
276 });
277 scope.join();
1252 // shutdown
1253 scope.shutdown();
1254 assertTrue(scope.toString().contains("duke"));
1255
1256 // closed
1257 scope.join();
1258 scope.close();
1259 assertTrue(scope.toString().contains("duke"));
1260 }
1261 }
1262
1263 /**
1264 * Test Subtask with task that completes successfully.
1265 */
1266 @ParameterizedTest
1267 @MethodSource("factories")
1268 void testSubtaskWhenSuccess(ThreadFactory factory) throws Exception {
1269 try (var scope = new StructuredTaskScope<String>(null, factory)) {
1270 Callable<String> task = () -> "foo";
1271 Subtask<String> subtask = scope.fork(task);
1272 assertEquals(task, subtask.task());
1273 scope.join();
1274
1275 // after join
1276 assertEquals(task, subtask.task());
1277 assertEquals(Subtask.State.SUCCESS, subtask.state());
1278 assertEquals("foo", subtask.get());
1279 assertThrows(IllegalStateException.class, subtask::exception);
1280 }
1281 }
1282
1283 /**
1284 * Test Subtask with task that fails.
1285 */
1286 @ParameterizedTest
1287 @MethodSource("factories")
1288 void testSubtaskWhenFailed(ThreadFactory factory) throws Exception {
1289 try (var scope = new StructuredTaskScope<String>(null, factory)) {
1290 Callable<String> task = () -> { throw new FooException(); };
1291 Subtask<String> subtask = scope.fork(task);
1292 assertEquals(task, subtask.task());
1293 scope.join();
1294
1295 // after join
1296 assertEquals(task, subtask.task());
1297 assertEquals(Subtask.State.FAILED, subtask.state());
1298 assertThrows(IllegalStateException.class, subtask::get);
1299 assertTrue(subtask.exception() instanceof FooException);
1300 }
1301 }
1302
1303 /**
1304 * Test Subtask with a task that has not completed.
1305 */
1306 @ParameterizedTest
1307 @MethodSource("factories")
1308 void testSubtaskWhenNotCompleted(ThreadFactory factory) throws Exception {
1309 try (var scope = new StructuredTaskScope<Object>(null, factory)) {
1310 Callable<Void> task = () -> {
1311 Thread.sleep(Duration.ofDays(1));
1312 return null;
|