View Javadoc
1   /*
2    * Copyright (C) 2008 The Guava Authors
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  
17  /*
18   * Portions of this file are modified versions of
19   * http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/tck/AbstractExecutorServiceTest.java?revision=1.30
20   * which contained the following notice:
21   *
22   * Written by Doug Lea with assistance from members of JCP JSR-166
23   * Expert Group and released to the public domain, as explained at
24   * http://creativecommons.org/publicdomain/zero/1.0/
25   * Other contributors include Andrew Wright, Jeffrey Hayes,
26   * Pat Fisher, Mike Judd.
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   * Tests for MoreExecutors.
80   *
81   * @author Kyle Littlefield (klittle)
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               // WAIT #1
184               barrier.await(1, TimeUnit.SECONDS);
185 
186               // WAIT #2
187               barrier.await(1, TimeUnit.SECONDS);
188               assertTrue(executor.isShutdown());
189               assertFalse(executor.isTerminated());
190 
191               // WAIT #3
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     // WAIT #1
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       // good to go
218     }
219     assertFalse(executor.isTerminated());
220 
221     // WAIT #2
222     barrier.await(1, TimeUnit.SECONDS);
223     assertFalse(executor.awaitTermination(20, TimeUnit.MILLISECONDS));
224 
225     // WAIT #3
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       // good to go
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") // guaranteed by invokeAll contract
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      * TODO(cpovirk): move ForwardingTestCase somewhere common, and use it to
285      * test the forwarded methods
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      * Wait not just until the Future's value is set (as in future.get()) but
314      * also until ListeningScheduledExecutorService's wrapper task is done
315      * executing listeners, as detected by yielding control to afterExecute.
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    * invokeAny(null) throws NPE
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    * invokeAny(empty collection) throws IAE
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    * invokeAny(c) throws NPE if c has null elements
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    * invokeAny(c) throws ExecutionException if no task in c completes
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    * invokeAny(c) returns result of some task in c if at least one completes
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     // Executors#defaultThreadFactory() may return a new instance each time.
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   /* Half of a 1-second timeout in nanoseconds */
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     // we need to keep this in a flag because t.isInterrupted() returns false after t.join()
675     final AtomicBoolean interrupted = new AtomicBoolean();
676     // we need to use another thread because it will be interrupted and thus using
677     // the current one, owned by JUnit, would make the test fail
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 }