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 package com.google.common.util.concurrent;
30
31 import static com.google.common.collect.Iterables.getOnlyElement;
32 import static com.google.common.truth.Truth.assertThat;
33 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
34 import static com.google.common.util.concurrent.MoreExecutors.invokeAnyImpl;
35 import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
36 import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
37 import static com.google.common.util.concurrent.MoreExecutors.renamingDecorator;
38 import static com.google.common.util.concurrent.MoreExecutors.shutdownAndAwaitTermination;
39 import static java.util.concurrent.TimeUnit.NANOSECONDS;
40 import static java.util.concurrent.TimeUnit.SECONDS;
41 import static org.mockito.Mockito.mock;
42 import static org.mockito.Mockito.times;
43 import static org.mockito.Mockito.verify;
44 import static org.mockito.Mockito.when;
45
46 import com.google.common.base.Suppliers;
47 import com.google.common.base.Throwables;
48 import com.google.common.collect.ImmutableList;
49 import com.google.common.collect.Lists;
50 import com.google.common.testing.ClassSanityTester;
51 import com.google.common.util.concurrent.MoreExecutors.Application;
52
53 import org.mockito.InOrder;
54 import org.mockito.Mockito;
55
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
59 import java.util.concurrent.ArrayBlockingQueue;
60 import java.util.concurrent.BlockingQueue;
61 import java.util.concurrent.Callable;
62 import java.util.concurrent.CountDownLatch;
63 import java.util.concurrent.CyclicBarrier;
64 import java.util.concurrent.ExecutionException;
65 import java.util.concurrent.Executor;
66 import java.util.concurrent.ExecutorService;
67 import java.util.concurrent.Executors;
68 import java.util.concurrent.Future;
69 import java.util.concurrent.RejectedExecutionException;
70 import java.util.concurrent.ScheduledFuture;
71 import java.util.concurrent.ScheduledThreadPoolExecutor;
72 import java.util.concurrent.ThreadFactory;
73 import java.util.concurrent.ThreadPoolExecutor;
74 import java.util.concurrent.TimeUnit;
75 import java.util.concurrent.atomic.AtomicBoolean;
76 import java.util.concurrent.atomic.AtomicReference;
77
78
79
80
81
82
83 public class MoreExecutorsTest extends JSR166TestCase {
84
85 private static final Runnable EMPTY_RUNNABLE = new Runnable() {
86 @Override public void run() {}
87 };
88
89 public void testDirectExecutorServiceServiceInThreadExecution()
90 throws Exception {
91 final ListeningExecutorService executor = newDirectExecutorService();
92 final ThreadLocal<Integer> threadLocalCount = new ThreadLocal<Integer>() {
93 @Override
94 protected Integer initialValue() {
95 return 0;
96 }
97 };
98 final AtomicReference<Throwable> throwableFromOtherThread =
99 new AtomicReference<Throwable>(null);
100 final Runnable incrementTask =
101 new Runnable() {
102 @Override
103 public void run() {
104 threadLocalCount.set(threadLocalCount.get() + 1);
105 }
106 };
107
108 Thread otherThread = new Thread(
109 new Runnable() {
110 @Override
111 public void run() {
112 try {
113 Future<?> future = executor.submit(incrementTask);
114 assertTrue(future.isDone());
115 assertEquals(1, threadLocalCount.get().intValue());
116 } catch (Throwable t) {
117 throwableFromOtherThread.set(t);
118 }
119 }
120 });
121
122 otherThread.start();
123
124 ListenableFuture<?> future = executor.submit(incrementTask);
125 assertTrue(future.isDone());
126 assertListenerRunImmediately(future);
127 assertEquals(1, threadLocalCount.get().intValue());
128 otherThread.join(1000);
129 assertEquals(Thread.State.TERMINATED, otherThread.getState());
130 Throwable throwable = throwableFromOtherThread.get();
131 assertNull("Throwable from other thread: "
132 + (throwable == null ? null : Throwables.getStackTraceAsString(throwable)),
133 throwableFromOtherThread.get());
134 }
135
136 public void testDirectExecutorServiceInvokeAll() throws Exception {
137 final ExecutorService executor = newDirectExecutorService();
138 final ThreadLocal<Integer> threadLocalCount = new ThreadLocal<Integer>() {
139 @Override
140 protected Integer initialValue() {
141 return 0;
142 }
143 };
144
145 final Callable<Integer> incrementTask = new Callable<Integer>() {
146 @Override
147 public Integer call() {
148 int i = threadLocalCount.get();
149 threadLocalCount.set(i + 1);
150 return i;
151 }
152 };
153
154 List<Future<Integer>> futures =
155 executor.invokeAll(Collections.nCopies(10, incrementTask));
156
157 for (int i = 0; i < 10; i++) {
158 Future<Integer> future = futures.get(i);
159 assertTrue("Task should have been run before being returned", future.isDone());
160 assertEquals(i, future.get().intValue());
161 }
162
163 assertEquals(10, threadLocalCount.get().intValue());
164 }
165
166 public void testDirectExecutorServiceServiceTermination()
167 throws Exception {
168 final ExecutorService executor = newDirectExecutorService();
169 final CyclicBarrier barrier = new CyclicBarrier(2);
170 final AtomicReference<Throwable> throwableFromOtherThread =
171 new AtomicReference<Throwable>(null);
172 final Runnable doNothingRunnable = new Runnable() {
173 @Override public void run() {
174 }};
175
176 Thread otherThread = new Thread(new Runnable() {
177 @Override
178 public void run() {
179 try {
180 Future<?> future = executor.submit(new Callable<Void>() {
181 @Override
182 public Void call() throws Exception {
183
184 barrier.await(1, TimeUnit.SECONDS);
185
186
187 barrier.await(1, TimeUnit.SECONDS);
188 assertTrue(executor.isShutdown());
189 assertFalse(executor.isTerminated());
190
191
192 barrier.await(1, TimeUnit.SECONDS);
193 return null;
194 }
195 });
196 assertTrue(future.isDone());
197 assertTrue(executor.isShutdown());
198 assertTrue(executor.isTerminated());
199 } catch (Throwable t) {
200 throwableFromOtherThread.set(t);
201 }
202 }});
203
204 otherThread.start();
205
206
207 barrier.await(1, TimeUnit.SECONDS);
208 assertFalse(executor.isShutdown());
209 assertFalse(executor.isTerminated());
210
211 executor.shutdown();
212 assertTrue(executor.isShutdown());
213 try {
214 executor.submit(doNothingRunnable);
215 fail("Should have encountered RejectedExecutionException");
216 } catch (RejectedExecutionException ex) {
217
218 }
219 assertFalse(executor.isTerminated());
220
221
222 barrier.await(1, TimeUnit.SECONDS);
223 assertFalse(executor.awaitTermination(20, TimeUnit.MILLISECONDS));
224
225
226 barrier.await(1, TimeUnit.SECONDS);
227 assertTrue(executor.awaitTermination(1, TimeUnit.SECONDS));
228 assertTrue(executor.awaitTermination(0, TimeUnit.SECONDS));
229 assertTrue(executor.isShutdown());
230 try {
231 executor.submit(doNothingRunnable);
232 fail("Should have encountered RejectedExecutionException");
233 } catch (RejectedExecutionException ex) {
234
235 }
236 assertTrue(executor.isTerminated());
237
238 otherThread.join(1000);
239 assertEquals(Thread.State.TERMINATED, otherThread.getState());
240 Throwable throwable = throwableFromOtherThread.get();
241 assertNull("Throwable from other thread: "
242 + (throwable == null ? null : Throwables.getStackTraceAsString(throwable)),
243 throwableFromOtherThread.get());
244 }
245
246 public void testDirectExecutorService_shutdownNow() {
247 ExecutorService executor = newDirectExecutorService();
248 assertEquals(ImmutableList.of(), executor.shutdownNow());
249 assertTrue(executor.isShutdown());
250 }
251
252 public void testExecuteAfterShutdown() {
253 ExecutorService executor = newDirectExecutorService();
254 executor.shutdown();
255 try {
256 executor.execute(EMPTY_RUNNABLE);
257 fail();
258 } catch (RejectedExecutionException expected) {}
259 }
260
261 public <T> void testListeningExecutorServiceInvokeAllJavadocCodeCompiles()
262 throws Exception {
263 ListeningExecutorService executor = newDirectExecutorService();
264 List<Callable<T>> tasks = ImmutableList.of();
265 @SuppressWarnings("unchecked")
266 List<ListenableFuture<T>> futures = (List) executor.invokeAll(tasks);
267 }
268
269 public void testListeningDecorator() throws Exception {
270 ListeningExecutorService service =
271 listeningDecorator(newDirectExecutorService());
272 assertSame(service, listeningDecorator(service));
273 List<Callable<String>> callables =
274 ImmutableList.of(Callables.returning("x"));
275 List<Future<String>> results;
276
277 results = service.invokeAll(callables);
278 assertThat(getOnlyElement(results)).isA(ListenableFutureTask.class);
279
280 results = service.invokeAll(callables, 1, SECONDS);
281 assertThat(getOnlyElement(results)).isA(ListenableFutureTask.class);
282
283
284
285
286
287 }
288
289 public void testListeningDecorator_noWrapExecuteTask() {
290 ExecutorService delegate = mock(ExecutorService.class);
291 ListeningExecutorService service = listeningDecorator(delegate);
292 Runnable task = new Runnable() {
293 @Override
294 public void run() {}
295 };
296 service.execute(task);
297 verify(delegate).execute(task);
298 }
299
300 public void testListeningDecorator_scheduleSuccess() throws Exception {
301 final CountDownLatch completed = new CountDownLatch(1);
302 ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1) {
303 @Override
304 protected void afterExecute(Runnable r, Throwable t) {
305 completed.countDown();
306 }
307 };
308 ListeningScheduledExecutorService service = listeningDecorator(delegate);
309 ListenableFuture<?> future =
310 service.schedule(Callables.returning(null), 1, TimeUnit.MILLISECONDS);
311
312
313
314
315
316
317 completed.await();
318 assertTrue(future.isDone());
319 assertListenerRunImmediately(future);
320 assertEquals(0, delegate.getQueue().size());
321 }
322
323 public void testListeningDecorator_scheduleFailure() throws Exception {
324 ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1);
325 ListeningScheduledExecutorService service = listeningDecorator(delegate);
326 RuntimeException ex = new RuntimeException();
327 ListenableFuture<?> future =
328 service.schedule(new ThrowingRunnable(0, ex), 1, TimeUnit.MILLISECONDS);
329 assertExecutionException(future, ex);
330 assertEquals(0, delegate.getQueue().size());
331 }
332
333 public void testListeningDecorator_schedulePeriodic() throws Exception {
334 ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1);
335 ListeningScheduledExecutorService service = listeningDecorator(delegate);
336 RuntimeException ex = new RuntimeException();
337
338 ListenableFuture<?> future;
339
340 ThrowingRunnable runnable = new ThrowingRunnable(5, ex);
341 future = service.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.MILLISECONDS);
342 assertExecutionException(future, ex);
343 assertEquals(5, runnable.count);
344 assertEquals(0, delegate.getQueue().size());
345
346 runnable = new ThrowingRunnable(5, ex);
347 future = service.scheduleWithFixedDelay(runnable, 1, 1, TimeUnit.MILLISECONDS);
348 assertExecutionException(future, ex);
349 assertEquals(5, runnable.count);
350 assertEquals(0, delegate.getQueue().size());
351 }
352
353 public void testListeningDecorator_cancelled() throws Exception {
354 ScheduledThreadPoolExecutor delegate = new ScheduledThreadPoolExecutor(1);
355 BlockingQueue<?> delegateQueue = delegate.getQueue();
356 ListeningScheduledExecutorService service = listeningDecorator(delegate);
357 ListenableFuture<?> future;
358 ScheduledFuture<?> delegateFuture;
359
360 Runnable runnable = new Runnable() {
361 @Override public void run() {}
362 };
363
364 future = service.schedule(runnable, 5, TimeUnit.MINUTES);
365 future.cancel(true);
366 assertTrue(future.isCancelled());
367 delegateFuture = (ScheduledFuture<?>) delegateQueue.element();
368 assertTrue(delegateFuture.isCancelled());
369
370 delegateQueue.clear();
371
372 future = service.scheduleAtFixedRate(runnable, 5, 5, TimeUnit.MINUTES);
373 future.cancel(true);
374 assertTrue(future.isCancelled());
375 delegateFuture = (ScheduledFuture<?>) delegateQueue.element();
376 assertTrue(delegateFuture.isCancelled());
377
378 delegateQueue.clear();
379
380 future = service.scheduleWithFixedDelay(runnable, 5, 5, TimeUnit.MINUTES);
381 future.cancel(true);
382 assertTrue(future.isCancelled());
383 delegateFuture = (ScheduledFuture<?>) delegateQueue.element();
384 assertTrue(delegateFuture.isCancelled());
385 }
386
387 private static final class ThrowingRunnable implements Runnable {
388 final int throwAfterCount;
389 final RuntimeException thrown;
390 int count;
391
392 ThrowingRunnable(int throwAfterCount, RuntimeException thrown) {
393 this.throwAfterCount = throwAfterCount;
394 this.thrown = thrown;
395 }
396
397 @Override
398 public void run() {
399 if (++count >= throwAfterCount) {
400 throw thrown;
401 }
402 }
403 }
404
405 private static void assertExecutionException(Future<?> future, Exception expectedCause)
406 throws Exception {
407 try {
408 future.get();
409 fail("Expected ExecutionException");
410 } catch (ExecutionException e) {
411 assertSame(expectedCause, e.getCause());
412 }
413 }
414
415
416
417
418 public void testInvokeAnyImpl_nullTasks() throws Exception {
419 ListeningExecutorService e = newDirectExecutorService();
420 try {
421 invokeAnyImpl(e, null, false, 0);
422 shouldThrow();
423 } catch (NullPointerException success) {
424 } finally {
425 joinPool(e);
426 }
427 }
428
429
430
431
432 public void testInvokeAnyImpl_emptyTasks() throws Exception {
433 ListeningExecutorService e = newDirectExecutorService();
434 try {
435 invokeAnyImpl(e, new ArrayList<Callable<String>>(), false, 0);
436 shouldThrow();
437 } catch (IllegalArgumentException success) {
438 } finally {
439 joinPool(e);
440 }
441 }
442
443
444
445
446 public void testInvokeAnyImpl_nullElement() throws Exception {
447 ListeningExecutorService e = newDirectExecutorService();
448 List<Callable<Integer>> l = new ArrayList<Callable<Integer>>();
449 l.add(new Callable<Integer>() {
450 @Override public Integer call() {
451 throw new ArithmeticException("/ by zero");
452 }
453 });
454 l.add(null);
455 try {
456 invokeAnyImpl(e, l, false, 0);
457 shouldThrow();
458 } catch (NullPointerException success) {
459 } finally {
460 joinPool(e);
461 }
462 }
463
464
465
466
467 public void testInvokeAnyImpl_noTaskCompletes() throws Exception {
468 ListeningExecutorService e = newDirectExecutorService();
469 List<Callable<String>> l = new ArrayList<Callable<String>>();
470 l.add(new NPETask());
471 try {
472 invokeAnyImpl(e, l, false, 0);
473 shouldThrow();
474 } catch (ExecutionException success) {
475 assertTrue(success.getCause() instanceof NullPointerException);
476 } finally {
477 joinPool(e);
478 }
479 }
480
481
482
483
484 public void testInvokeAnyImpl() throws Exception {
485 ListeningExecutorService e = newDirectExecutorService();
486 try {
487 List<Callable<String>> l = new ArrayList<Callable<String>>();
488 l.add(new StringTask());
489 l.add(new StringTask());
490 String result = invokeAnyImpl(e, l, false, 0);
491 assertSame(TEST_STRING, result);
492 } finally {
493 joinPool(e);
494 }
495 }
496
497 private static void assertListenerRunImmediately(ListenableFuture<?> future) {
498 CountingRunnable listener = new CountingRunnable();
499 future.addListener(listener, directExecutor());
500 assertEquals(1, listener.count);
501 }
502
503 private static final class CountingRunnable implements Runnable {
504 int count;
505
506 @Override
507 public void run() {
508 count++;
509 }
510 }
511
512 public void testAddDelayedShutdownHook_success() throws InterruptedException {
513 TestApplication application = new TestApplication();
514 ExecutorService service = mock(ExecutorService.class);
515 application.addDelayedShutdownHook(service, 2, TimeUnit.SECONDS);
516 verify(service, Mockito.never()).shutdown();
517 application.shutdown();
518 InOrder shutdownFirst = Mockito.inOrder(service);
519 shutdownFirst.verify(service).shutdown();
520 shutdownFirst.verify(service).awaitTermination(2, TimeUnit.SECONDS);
521 }
522
523 public void testAddDelayedShutdownHook_interrupted() throws InterruptedException {
524 TestApplication application = new TestApplication();
525 ExecutorService service = mock(ExecutorService.class);
526 application.addDelayedShutdownHook(service, 2, TimeUnit.SECONDS);
527 when(service.awaitTermination(2, TimeUnit.SECONDS)).thenThrow(new InterruptedException());
528 application.shutdown();
529 verify(service).shutdown();
530 }
531
532 public void testGetExitingExcutorService_executorSetToUseDaemonThreads() {
533 TestApplication application = new TestApplication();
534 ThreadPoolExecutor executor = new ThreadPoolExecutor(
535 1, 2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
536 assertNotNull(application.getExitingExecutorService(executor));
537 assertTrue(executor.getThreadFactory().newThread(EMPTY_RUNNABLE).isDaemon());
538 }
539
540 public void testGetExitingExcutorService_executorDelegatesToOriginal() {
541 TestApplication application = new TestApplication();
542 ThreadPoolExecutor executor = mock(ThreadPoolExecutor.class);
543 ThreadFactory threadFactory = mock(ThreadFactory.class);
544 when(executor.getThreadFactory()).thenReturn(threadFactory);
545 application.getExitingExecutorService(executor).execute(EMPTY_RUNNABLE);
546 verify(executor).execute(EMPTY_RUNNABLE);
547 }
548
549 public void testGetExitingExcutorService_shutdownHookRegistered() throws InterruptedException {
550 TestApplication application = new TestApplication();
551 ThreadPoolExecutor executor = mock(ThreadPoolExecutor.class);
552 ThreadFactory threadFactory = mock(ThreadFactory.class);
553 when(executor.getThreadFactory()).thenReturn(threadFactory);
554 application.getExitingExecutorService(executor);
555 application.shutdown();
556 verify(executor).shutdown();
557 }
558
559 public void testGetExitingScheduledExcutorService_executorSetToUseDaemonThreads() {
560 TestApplication application = new TestApplication();
561 ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
562 assertNotNull(application.getExitingScheduledExecutorService(executor));
563 assertTrue(executor.getThreadFactory().newThread(EMPTY_RUNNABLE).isDaemon());
564 }
565
566 public void testGetExitingScheduledExcutorService_executorDelegatesToOriginal() {
567 TestApplication application = new TestApplication();
568 ScheduledThreadPoolExecutor executor = mock(ScheduledThreadPoolExecutor.class);
569 ThreadFactory threadFactory = mock(ThreadFactory.class);
570 when(executor.getThreadFactory()).thenReturn(threadFactory);
571 application.getExitingScheduledExecutorService(executor).execute(EMPTY_RUNNABLE);
572 verify(executor).execute(EMPTY_RUNNABLE);
573 }
574
575 public void testGetScheduledExitingExcutorService_shutdownHookRegistered()
576 throws InterruptedException {
577 TestApplication application = new TestApplication();
578 ScheduledThreadPoolExecutor executor = mock(ScheduledThreadPoolExecutor.class);
579 ThreadFactory threadFactory = mock(ThreadFactory.class);
580 when(executor.getThreadFactory()).thenReturn(threadFactory);
581 application.getExitingScheduledExecutorService(executor);
582 application.shutdown();
583 verify(executor).shutdown();
584 }
585
586 public void testPlatformThreadFactory_default() {
587 ThreadFactory factory = MoreExecutors.platformThreadFactory();
588 assertNotNull(factory);
589
590 assertEquals(factory.getClass(), Executors.defaultThreadFactory().getClass());
591 }
592
593 public void testThreadRenaming() {
594 Executor renamingExecutor = renamingDecorator(newDirectExecutorService(),
595 Suppliers.ofInstance("FooBar"));
596 String oldName = Thread.currentThread().getName();
597 renamingExecutor.execute(new Runnable() {
598 @Override public void run() {
599 assertEquals("FooBar", Thread.currentThread().getName());
600 }});
601 assertEquals(oldName, Thread.currentThread().getName());
602 }
603
604 public void testExecutors_nullCheck() throws Exception {
605 new ClassSanityTester()
606 .setDefault(RateLimiter.class, RateLimiter.create(1.0))
607 .forAllPublicStaticMethods(MoreExecutors.class)
608 .thatReturn(Executor.class)
609 .testNulls();
610 }
611
612 private static class TestApplication extends Application {
613 private final List<Thread> hooks = Lists.newArrayList();
614
615 @Override synchronized void addShutdownHook(Thread hook) {
616 hooks.add(hook);
617 }
618
619 synchronized void shutdown() throws InterruptedException {
620 for (Thread hook : hooks) {
621 hook.start();
622 }
623 for (Thread hook : hooks) {
624 hook.join();
625 }
626 }
627 }
628
629
630 private static final long HALF_SECOND_NANOS = NANOSECONDS.convert(1L, SECONDS) / 2;
631
632 public void testShutdownAndAwaitTermination_immediateShutdown() throws Exception {
633 ExecutorService service = Executors.newSingleThreadExecutor();
634 assertTrue(shutdownAndAwaitTermination(service, 1L, SECONDS));
635 assertTrue(service.isTerminated());
636 }
637
638 public void testShutdownAndAwaitTermination_immediateShutdownInternal() throws Exception {
639 ExecutorService service = mock(ExecutorService.class);
640 when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS)).thenReturn(true);
641 when(service.isTerminated()).thenReturn(true);
642 assertTrue(shutdownAndAwaitTermination(service, 1L, SECONDS));
643 verify(service).shutdown();
644 verify(service).awaitTermination(HALF_SECOND_NANOS, NANOSECONDS);
645 }
646
647 public void testShutdownAndAwaitTermination_forcedShutDownInternal() throws Exception {
648 ExecutorService service = mock(ExecutorService.class);
649 when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS))
650 .thenReturn(false).thenReturn(true);
651 when(service.isTerminated()).thenReturn(true);
652 assertTrue(shutdownAndAwaitTermination(service, 1L, SECONDS));
653 verify(service).shutdown();
654 verify(service, times(2)).awaitTermination(HALF_SECOND_NANOS, NANOSECONDS);
655 verify(service).shutdownNow();
656 }
657
658 public void testShutdownAndAwaitTermination_nonTerminationInternal() throws Exception {
659 ExecutorService service = mock(ExecutorService.class);
660 when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS))
661 .thenReturn(false).thenReturn(false);
662 assertFalse(shutdownAndAwaitTermination(service, 1L, SECONDS));
663 verify(service).shutdown();
664 verify(service, times(2)).awaitTermination(HALF_SECOND_NANOS, NANOSECONDS);
665 verify(service).shutdownNow();
666 }
667
668 public void testShutdownAndAwaitTermination_interruptedInternal() throws Exception {
669 final ExecutorService service = mock(ExecutorService.class);
670 when(service.awaitTermination(HALF_SECOND_NANOS, NANOSECONDS))
671 .thenThrow(new InterruptedException());
672
673 final AtomicBoolean terminated = new AtomicBoolean();
674
675 final AtomicBoolean interrupted = new AtomicBoolean();
676
677
678 Thread thread = new Thread(new Runnable() {
679 @Override
680 public void run() {
681 terminated.set(shutdownAndAwaitTermination(service, 1L, SECONDS));
682 interrupted.set(Thread.currentThread().isInterrupted());
683 }
684 });
685 thread.start();
686 thread.join();
687 verify(service).shutdown();
688 verify(service).awaitTermination(HALF_SECOND_NANOS, NANOSECONDS);
689 verify(service).shutdownNow();
690 assertTrue(interrupted.get());
691 assertFalse(terminated.get());
692 }
693 }