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 } 694