1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.util.concurrent;
18
19 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
20 import static java.lang.Thread.currentThread;
21 import static java.util.concurrent.TimeUnit.SECONDS;
22
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.Iterables;
25 import com.google.common.collect.Lists;
26 import com.google.common.util.concurrent.Service.Listener;
27 import com.google.common.util.concurrent.Service.State;
28
29 import junit.framework.TestCase;
30
31 import java.lang.Thread.UncaughtExceptionHandler;
32 import java.util.List;
33 import java.util.concurrent.CountDownLatch;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicInteger;
36 import java.util.concurrent.atomic.AtomicReference;
37
38 import javax.annotation.concurrent.GuardedBy;
39
40
41
42
43
44
45 public class AbstractServiceTest extends TestCase {
46
47 private static final long LONG_TIMEOUT_MILLIS = 2500;
48 private Thread executionThread;
49 private Throwable thrownByExecutionThread;
50
51 public void testNoOpServiceStartStop() throws Exception {
52 NoOpService service = new NoOpService();
53 RecordingListener listener = RecordingListener.record(service);
54
55 assertEquals(State.NEW, service.state());
56 assertFalse(service.isRunning());
57 assertFalse(service.running);
58
59 service.startAsync();
60 assertEquals(State.RUNNING, service.state());
61 assertTrue(service.isRunning());
62 assertTrue(service.running);
63
64 service.stopAsync();
65 assertEquals(State.TERMINATED, service.state());
66 assertFalse(service.isRunning());
67 assertFalse(service.running);
68 assertEquals(
69 ImmutableList.of(
70 State.STARTING,
71 State.RUNNING,
72 State.STOPPING,
73 State.TERMINATED),
74 listener.getStateHistory());
75 }
76
77 public void testNoOpServiceStartAndWaitStopAndWait() throws Exception {
78 NoOpService service = new NoOpService();
79
80 service.startAsync().awaitRunning();
81 assertEquals(State.RUNNING, service.state());
82
83 service.stopAsync().awaitTerminated();
84 assertEquals(State.TERMINATED, service.state());
85 }
86
87 public void testNoOpServiceStartAsyncAndAwaitStopAsyncAndAwait() throws Exception {
88 NoOpService service = new NoOpService();
89
90 service.startAsync().awaitRunning();
91 assertEquals(State.RUNNING, service.state());
92
93 service.stopAsync().awaitTerminated();
94 assertEquals(State.TERMINATED, service.state());
95 }
96
97 public void testNoOpServiceStopIdempotence() throws Exception {
98 NoOpService service = new NoOpService();
99 RecordingListener listener = RecordingListener.record(service);
100 service.startAsync().awaitRunning();
101 assertEquals(State.RUNNING, service.state());
102
103 service.stopAsync();
104 service.stopAsync();
105 assertEquals(State.TERMINATED, service.state());
106 assertEquals(
107 ImmutableList.of(
108 State.STARTING,
109 State.RUNNING,
110 State.STOPPING,
111 State.TERMINATED),
112 listener.getStateHistory());
113 }
114
115 public void testNoOpServiceStopIdempotenceAfterWait() throws Exception {
116 NoOpService service = new NoOpService();
117
118 service.startAsync().awaitRunning();
119
120 service.stopAsync().awaitTerminated();
121 service.stopAsync();
122 assertEquals(State.TERMINATED, service.state());
123 }
124
125 public void testNoOpServiceStopIdempotenceDoubleWait() throws Exception {
126 NoOpService service = new NoOpService();
127
128 service.startAsync().awaitRunning();
129 assertEquals(State.RUNNING, service.state());
130
131 service.stopAsync().awaitTerminated();
132 service.stopAsync().awaitTerminated();
133 assertEquals(State.TERMINATED, service.state());
134 }
135
136 public void testNoOpServiceStartStopAndWaitUninterruptible()
137 throws Exception {
138 NoOpService service = new NoOpService();
139
140 currentThread().interrupt();
141 try {
142 service.startAsync().awaitRunning();
143 assertEquals(State.RUNNING, service.state());
144
145 service.stopAsync().awaitTerminated();
146 assertEquals(State.TERMINATED, service.state());
147
148 assertTrue(currentThread().isInterrupted());
149 } finally {
150 Thread.interrupted();
151 }
152 }
153
154 private static class NoOpService extends AbstractService {
155 boolean running = false;
156
157 @Override protected void doStart() {
158 assertFalse(running);
159 running = true;
160 notifyStarted();
161 }
162
163 @Override protected void doStop() {
164 assertTrue(running);
165 running = false;
166 notifyStopped();
167 }
168 }
169
170 public void testManualServiceStartStop() throws Exception {
171 ManualSwitchedService service = new ManualSwitchedService();
172 RecordingListener listener = RecordingListener.record(service);
173
174 service.startAsync();
175 assertEquals(State.STARTING, service.state());
176 assertFalse(service.isRunning());
177 assertTrue(service.doStartCalled);
178
179 service.notifyStarted();
180 assertEquals(State.RUNNING, service.state());
181 assertTrue(service.isRunning());
182
183 service.stopAsync();
184 assertEquals(State.STOPPING, service.state());
185 assertFalse(service.isRunning());
186 assertTrue(service.doStopCalled);
187
188 service.notifyStopped();
189 assertEquals(State.TERMINATED, service.state());
190 assertFalse(service.isRunning());
191 assertEquals(
192 ImmutableList.of(
193 State.STARTING,
194 State.RUNNING,
195 State.STOPPING,
196 State.TERMINATED),
197 listener.getStateHistory());
198
199 }
200
201 public void testManualServiceNotifyStoppedWhileRunning() throws Exception {
202 ManualSwitchedService service = new ManualSwitchedService();
203 RecordingListener listener = RecordingListener.record(service);
204
205 service.startAsync();
206 service.notifyStarted();
207 service.notifyStopped();
208 assertEquals(State.TERMINATED, service.state());
209 assertFalse(service.isRunning());
210 assertFalse(service.doStopCalled);
211
212 assertEquals(
213 ImmutableList.of(
214 State.STARTING,
215 State.RUNNING,
216 State.TERMINATED),
217 listener.getStateHistory());
218 }
219
220 public void testManualServiceStopWhileStarting() throws Exception {
221 ManualSwitchedService service = new ManualSwitchedService();
222 RecordingListener listener = RecordingListener.record(service);
223
224 service.startAsync();
225 assertEquals(State.STARTING, service.state());
226 assertFalse(service.isRunning());
227 assertTrue(service.doStartCalled);
228
229 service.stopAsync();
230 assertEquals(State.STOPPING, service.state());
231 assertFalse(service.isRunning());
232 assertFalse(service.doStopCalled);
233
234 service.notifyStarted();
235 assertEquals(State.STOPPING, service.state());
236 assertFalse(service.isRunning());
237 assertTrue(service.doStopCalled);
238
239 service.notifyStopped();
240 assertEquals(State.TERMINATED, service.state());
241 assertFalse(service.isRunning());
242 assertEquals(
243 ImmutableList.of(
244 State.STARTING,
245 State.STOPPING,
246 State.TERMINATED),
247 listener.getStateHistory());
248 }
249
250
251
252
253
254
255 public void testManualServiceStopMultipleTimesWhileStarting() throws Exception {
256 ManualSwitchedService service = new ManualSwitchedService();
257 final AtomicInteger stopppingCount = new AtomicInteger();
258 service.addListener(new Listener() {
259 @Override public void stopping(State from) {
260 stopppingCount.incrementAndGet();
261 }
262 }, directExecutor());
263
264 service.startAsync();
265 service.stopAsync();
266 assertEquals(1, stopppingCount.get());
267 service.stopAsync();
268 assertEquals(1, stopppingCount.get());
269 }
270
271 public void testManualServiceStopWhileNew() throws Exception {
272 ManualSwitchedService service = new ManualSwitchedService();
273 RecordingListener listener = RecordingListener.record(service);
274
275 service.stopAsync();
276 assertEquals(State.TERMINATED, service.state());
277 assertFalse(service.isRunning());
278 assertFalse(service.doStartCalled);
279 assertFalse(service.doStopCalled);
280 assertEquals(ImmutableList.of(State.TERMINATED), listener.getStateHistory());
281 }
282
283 public void testManualServiceFailWhileStarting() throws Exception {
284 ManualSwitchedService service = new ManualSwitchedService();
285 RecordingListener listener = RecordingListener.record(service);
286 service.startAsync();
287 service.notifyFailed(EXCEPTION);
288 assertEquals(ImmutableList.of(State.STARTING, State.FAILED), listener.getStateHistory());
289 }
290
291 public void testManualServiceFailWhileRunning() throws Exception {
292 ManualSwitchedService service = new ManualSwitchedService();
293 RecordingListener listener = RecordingListener.record(service);
294 service.startAsync();
295 service.notifyStarted();
296 service.notifyFailed(EXCEPTION);
297 assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.FAILED),
298 listener.getStateHistory());
299 }
300
301 public void testManualServiceFailWhileStopping() throws Exception {
302 ManualSwitchedService service = new ManualSwitchedService();
303 RecordingListener listener = RecordingListener.record(service);
304 service.startAsync();
305 service.notifyStarted();
306 service.stopAsync();
307 service.notifyFailed(EXCEPTION);
308 assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.STOPPING, State.FAILED),
309 listener.getStateHistory());
310 }
311
312 public void testManualServiceUnrequestedStop() {
313 ManualSwitchedService service = new ManualSwitchedService();
314
315 service.startAsync();
316
317 service.notifyStarted();
318 assertEquals(State.RUNNING, service.state());
319 assertTrue(service.isRunning());
320 assertFalse(service.doStopCalled);
321
322 service.notifyStopped();
323 assertEquals(State.TERMINATED, service.state());
324 assertFalse(service.isRunning());
325 assertFalse(service.doStopCalled);
326 }
327
328
329
330
331
332 private static class ManualSwitchedService extends AbstractService {
333 boolean doStartCalled = false;
334 boolean doStopCalled = false;
335
336 @Override protected void doStart() {
337 assertFalse(doStartCalled);
338 doStartCalled = true;
339 }
340
341 @Override protected void doStop() {
342 assertFalse(doStopCalled);
343 doStopCalled = true;
344 }
345 }
346
347 public void testAwaitTerminated() throws Exception {
348 final NoOpService service = new NoOpService();
349 Thread waiter = new Thread() {
350 @Override public void run() {
351 service.awaitTerminated();
352 }
353 };
354 waiter.start();
355 service.startAsync().awaitRunning();
356 assertEquals(State.RUNNING, service.state());
357 service.stopAsync();
358 waiter.join(LONG_TIMEOUT_MILLIS);
359 assertFalse(waiter.isAlive());
360 }
361
362 public void testAwaitTerminated_FailedService() throws Exception {
363 final ManualSwitchedService service = new ManualSwitchedService();
364 final AtomicReference<Throwable> exception = Atomics.newReference();
365 Thread waiter = new Thread() {
366 @Override public void run() {
367 try {
368 service.awaitTerminated();
369 fail("Expected an IllegalStateException");
370 } catch (Throwable t) {
371 exception.set(t);
372 }
373 }
374 };
375 waiter.start();
376 service.startAsync();
377 service.notifyStarted();
378 assertEquals(State.RUNNING, service.state());
379 service.notifyFailed(EXCEPTION);
380 assertEquals(State.FAILED, service.state());
381 waiter.join(LONG_TIMEOUT_MILLIS);
382 assertFalse(waiter.isAlive());
383 assertTrue(exception.get() instanceof IllegalStateException);
384 assertEquals(EXCEPTION, exception.get().getCause());
385 }
386
387 public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable {
388 ThreadedService service = new ThreadedService();
389 RecordingListener listener = RecordingListener.record(service);
390 service.startAsync().awaitRunning();
391 assertEquals(State.RUNNING, service.state());
392
393 service.awaitRunChecks();
394
395 service.stopAsync().awaitTerminated();
396 assertEquals(State.TERMINATED, service.state());
397
398 throwIfSet(thrownByExecutionThread);
399 assertEquals(
400 ImmutableList.of(
401 State.STARTING,
402 State.RUNNING,
403 State.STOPPING,
404 State.TERMINATED),
405 listener.getStateHistory());
406 }
407
408 public void testThreadedServiceStopIdempotence() throws Throwable {
409 ThreadedService service = new ThreadedService();
410
411 service.startAsync().awaitRunning();
412 assertEquals(State.RUNNING, service.state());
413
414 service.awaitRunChecks();
415
416 service.stopAsync();
417 service.stopAsync().awaitTerminated();
418 assertEquals(State.TERMINATED, service.state());
419
420 throwIfSet(thrownByExecutionThread);
421 }
422
423 public void testThreadedServiceStopIdempotenceAfterWait()
424 throws Throwable {
425 ThreadedService service = new ThreadedService();
426
427 service.startAsync().awaitRunning();
428 assertEquals(State.RUNNING, service.state());
429
430 service.awaitRunChecks();
431
432 service.stopAsync().awaitTerminated();
433 service.stopAsync();
434 assertEquals(State.TERMINATED, service.state());
435
436 executionThread.join();
437
438 throwIfSet(thrownByExecutionThread);
439 }
440
441 public void testThreadedServiceStopIdempotenceDoubleWait()
442 throws Throwable {
443 ThreadedService service = new ThreadedService();
444
445 service.startAsync().awaitRunning();
446 assertEquals(State.RUNNING, service.state());
447
448 service.awaitRunChecks();
449
450 service.stopAsync().awaitTerminated();
451 service.stopAsync().awaitTerminated();
452 assertEquals(State.TERMINATED, service.state());
453
454 throwIfSet(thrownByExecutionThread);
455 }
456
457 public void testManualServiceFailureIdempotence() {
458 ManualSwitchedService service = new ManualSwitchedService();
459 RecordingListener.record(service);
460 service.startAsync();
461 service.notifyFailed(new Exception("1"));
462 service.notifyFailed(new Exception("2"));
463 assertEquals("1", service.failureCause().getMessage());
464 try {
465 service.awaitRunning();
466 fail();
467 } catch (IllegalStateException e) {
468 assertEquals("1", e.getCause().getMessage());
469 }
470 }
471
472 private class ThreadedService extends AbstractService {
473 final CountDownLatch hasConfirmedIsRunning = new CountDownLatch(1);
474
475
476
477
478
479
480
481
482
483 void awaitRunChecks() throws InterruptedException {
484 assertTrue("Service thread hasn't finished its checks. "
485 + "Exception status (possibly stale): " + thrownByExecutionThread,
486 hasConfirmedIsRunning.await(10, SECONDS));
487 }
488
489 @Override protected void doStart() {
490 assertEquals(State.STARTING, state());
491 invokeOnExecutionThreadForTest(new Runnable() {
492 @Override public void run() {
493 assertEquals(State.STARTING, state());
494 notifyStarted();
495 assertEquals(State.RUNNING, state());
496 hasConfirmedIsRunning.countDown();
497 }
498 });
499 }
500
501 @Override protected void doStop() {
502 assertEquals(State.STOPPING, state());
503 invokeOnExecutionThreadForTest(new Runnable() {
504 @Override public void run() {
505 assertEquals(State.STOPPING, state());
506 notifyStopped();
507 assertEquals(State.TERMINATED, state());
508 }
509 });
510 }
511 }
512
513 private void invokeOnExecutionThreadForTest(Runnable runnable) {
514 executionThread = new Thread(runnable);
515 executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
516 @Override
517 public void uncaughtException(Thread thread, Throwable e) {
518 thrownByExecutionThread = e;
519 }
520 });
521 executionThread.start();
522 }
523
524 private static void throwIfSet(Throwable t) throws Throwable {
525 if (t != null) {
526 throw t;
527 }
528 }
529
530 public void testStopUnstartedService() throws Exception {
531 NoOpService service = new NoOpService();
532 RecordingListener listener = RecordingListener.record(service);
533
534 service.stopAsync();
535 assertEquals(State.TERMINATED, service.state());
536
537 try {
538 service.startAsync();
539 fail();
540 } catch (IllegalStateException expected) {}
541 assertEquals(State.TERMINATED, Iterables.getOnlyElement(listener.getStateHistory()));
542 }
543
544 public void testFailingServiceStartAndWait() throws Exception {
545 StartFailingService service = new StartFailingService();
546 RecordingListener listener = RecordingListener.record(service);
547
548 try {
549 service.startAsync().awaitRunning();
550 fail();
551 } catch (IllegalStateException e) {
552 assertEquals(EXCEPTION, service.failureCause());
553 assertEquals(EXCEPTION, e.getCause());
554 }
555 assertEquals(
556 ImmutableList.of(
557 State.STARTING,
558 State.FAILED),
559 listener.getStateHistory());
560 }
561
562 public void testFailingServiceStopAndWait_stopFailing() throws Exception {
563 StopFailingService service = new StopFailingService();
564 RecordingListener listener = RecordingListener.record(service);
565
566 service.startAsync().awaitRunning();
567 try {
568 service.stopAsync().awaitTerminated();
569 fail();
570 } catch (IllegalStateException e) {
571 assertEquals(EXCEPTION, service.failureCause());
572 assertEquals(EXCEPTION, e.getCause());
573 }
574 assertEquals(
575 ImmutableList.of(
576 State.STARTING,
577 State.RUNNING,
578 State.STOPPING,
579 State.FAILED),
580 listener.getStateHistory());
581 }
582
583 public void testFailingServiceStopAndWait_runFailing() throws Exception {
584 RunFailingService service = new RunFailingService();
585 RecordingListener listener = RecordingListener.record(service);
586
587 service.startAsync();
588 try {
589 service.awaitRunning();
590 fail();
591 } catch (IllegalStateException e) {
592 assertEquals(EXCEPTION, service.failureCause());
593 assertEquals(EXCEPTION, e.getCause());
594 }
595 assertEquals(
596 ImmutableList.of(
597 State.STARTING,
598 State.RUNNING,
599 State.FAILED),
600 listener.getStateHistory());
601 }
602
603 public void testThrowingServiceStartAndWait() throws Exception {
604 StartThrowingService service = new StartThrowingService();
605 RecordingListener listener = RecordingListener.record(service);
606
607 try {
608 service.startAsync().awaitRunning();
609 fail();
610 } catch (IllegalStateException e) {
611 assertEquals(service.exception, service.failureCause());
612 assertEquals(service.exception, e.getCause());
613 }
614 assertEquals(
615 ImmutableList.of(
616 State.STARTING,
617 State.FAILED),
618 listener.getStateHistory());
619 }
620
621 public void testThrowingServiceStopAndWait_stopThrowing() throws Exception {
622 StopThrowingService service = new StopThrowingService();
623 RecordingListener listener = RecordingListener.record(service);
624
625 service.startAsync().awaitRunning();
626 try {
627 service.stopAsync().awaitTerminated();
628 fail();
629 } catch (IllegalStateException e) {
630 assertEquals(service.exception, service.failureCause());
631 assertEquals(service.exception, e.getCause());
632 }
633 assertEquals(
634 ImmutableList.of(
635 State.STARTING,
636 State.RUNNING,
637 State.STOPPING,
638 State.FAILED),
639 listener.getStateHistory());
640 }
641
642 public void testThrowingServiceStopAndWait_runThrowing() throws Exception {
643 RunThrowingService service = new RunThrowingService();
644 RecordingListener listener = RecordingListener.record(service);
645
646 service.startAsync();
647 try {
648 service.awaitTerminated();
649 fail();
650 } catch (IllegalStateException e) {
651 assertEquals(service.exception, service.failureCause());
652 assertEquals(service.exception, e.getCause());
653 }
654 assertEquals(
655 ImmutableList.of(
656 State.STARTING,
657 State.RUNNING,
658 State.FAILED),
659 listener.getStateHistory());
660 }
661
662 public void testFailureCause_throwsIfNotFailed() {
663 StopFailingService service = new StopFailingService();
664 try {
665 service.failureCause();
666 fail();
667 } catch (IllegalStateException e) {
668
669 }
670 service.startAsync().awaitRunning();
671 try {
672 service.failureCause();
673 fail();
674 } catch (IllegalStateException e) {
675
676 }
677 try {
678 service.stopAsync().awaitTerminated();
679 fail();
680 } catch (IllegalStateException e) {
681 assertEquals(EXCEPTION, service.failureCause());
682 assertEquals(EXCEPTION, e.getCause());
683 }
684 }
685
686 public void testAddListenerAfterFailureDoesntCauseDeadlock() throws InterruptedException {
687 final StartFailingService service = new StartFailingService();
688 service.startAsync();
689 assertEquals(State.FAILED, service.state());
690 service.addListener(new RecordingListener(service), directExecutor());
691 Thread thread = new Thread() {
692 @Override public void run() {
693
694 service.stopAsync();
695 }
696 };
697 thread.start();
698 thread.join(LONG_TIMEOUT_MILLIS);
699 assertFalse(thread + " is deadlocked", thread.isAlive());
700 }
701
702 public void testListenerDoesntDeadlockOnStartAndWaitFromRunning() throws Exception {
703 final NoOpThreadedService service = new NoOpThreadedService();
704 service.addListener(new Listener() {
705 @Override public void running() {
706 service.awaitRunning();
707 }
708 }, directExecutor());
709 service.startAsync().awaitRunning(LONG_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
710 service.stopAsync();
711 }
712
713 public void testListenerDoesntDeadlockOnStopAndWaitFromTerminated() throws Exception {
714 final NoOpThreadedService service = new NoOpThreadedService();
715 service.addListener(new Listener() {
716 @Override public void terminated(State from) {
717 service.stopAsync().awaitTerminated();
718 }
719 }, directExecutor());
720 service.startAsync().awaitRunning();
721
722 Thread thread = new Thread() {
723 @Override public void run() {
724 service.stopAsync().awaitTerminated();
725 }
726 };
727 thread.start();
728 thread.join(LONG_TIMEOUT_MILLIS);
729 assertFalse(thread + " is deadlocked", thread.isAlive());
730 }
731
732 private static class NoOpThreadedService extends AbstractExecutionThreadService {
733 final CountDownLatch latch = new CountDownLatch(1);
734 @Override protected void run() throws Exception {
735 latch.await();
736 }
737 @Override protected void triggerShutdown() {
738 latch.countDown();
739 }
740 }
741
742 private static class StartFailingService extends AbstractService {
743 @Override protected void doStart() {
744 notifyFailed(EXCEPTION);
745 }
746
747 @Override protected void doStop() {
748 fail();
749 }
750 }
751
752 private static class RunFailingService extends AbstractService {
753 @Override protected void doStart() {
754 notifyStarted();
755 notifyFailed(EXCEPTION);
756 }
757
758 @Override protected void doStop() {
759 fail();
760 }
761 }
762
763 private static class StopFailingService extends AbstractService {
764 @Override protected void doStart() {
765 notifyStarted();
766 }
767
768 @Override protected void doStop() {
769 notifyFailed(EXCEPTION);
770 }
771 }
772
773 private static class StartThrowingService extends AbstractService {
774
775 final RuntimeException exception = new RuntimeException("deliberate");
776
777 @Override protected void doStart() {
778 throw exception;
779 }
780
781 @Override protected void doStop() {
782 fail();
783 }
784 }
785
786 private static class RunThrowingService extends AbstractService {
787
788 final RuntimeException exception = new RuntimeException("deliberate");
789
790 @Override protected void doStart() {
791 notifyStarted();
792 throw exception;
793 }
794
795 @Override protected void doStop() {
796 fail();
797 }
798 }
799
800 private static class StopThrowingService extends AbstractService {
801
802 final RuntimeException exception = new RuntimeException("deliberate");
803
804 @Override protected void doStart() {
805 notifyStarted();
806 }
807
808 @Override protected void doStop() {
809 throw exception;
810 }
811 }
812
813 private static class RecordingListener extends Listener {
814 static RecordingListener record(Service service) {
815 RecordingListener listener = new RecordingListener(service);
816 service.addListener(listener, directExecutor());
817 return listener;
818 }
819
820 final Service service;
821
822 RecordingListener(Service service) {
823 this.service = service;
824 }
825
826 @GuardedBy("this")
827 final List<State> stateHistory = Lists.newArrayList();
828 final CountDownLatch completionLatch = new CountDownLatch(1);
829
830 ImmutableList<State> getStateHistory() throws Exception {
831 completionLatch.await();
832 synchronized (this) {
833 return ImmutableList.copyOf(stateHistory);
834 }
835 }
836
837 @Override public synchronized void starting() {
838 assertTrue(stateHistory.isEmpty());
839 assertNotSame(State.NEW, service.state());
840 stateHistory.add(State.STARTING);
841 }
842
843 @Override public synchronized void running() {
844 assertEquals(State.STARTING, Iterables.getOnlyElement(stateHistory));
845 stateHistory.add(State.RUNNING);
846 service.awaitRunning();
847 assertNotSame(State.STARTING, service.state());
848 }
849
850 @Override public synchronized void stopping(State from) {
851 assertEquals(from, Iterables.getLast(stateHistory));
852 stateHistory.add(State.STOPPING);
853 if (from == State.STARTING) {
854 try {
855 service.awaitRunning();
856 fail();
857 } catch (IllegalStateException expected) {
858 assertNull(expected.getCause());
859 assertTrue(expected.getMessage().equals(
860 "Expected the service to be RUNNING, but was STOPPING"));
861 }
862 }
863 assertNotSame(from, service.state());
864 }
865
866 @Override public synchronized void terminated(State from) {
867 assertEquals(from, Iterables.getLast(stateHistory, State.NEW));
868 stateHistory.add(State.TERMINATED);
869 assertEquals(State.TERMINATED, service.state());
870 if (from == State.NEW) {
871 try {
872 service.awaitRunning();
873 fail();
874 } catch (IllegalStateException expected) {
875 assertNull(expected.getCause());
876 assertTrue(expected.getMessage().equals(
877 "Expected the service to be RUNNING, but was TERMINATED"));
878 }
879 }
880 completionLatch.countDown();
881 }
882
883 @Override public synchronized void failed(State from, Throwable failure) {
884 assertEquals(from, Iterables.getLast(stateHistory));
885 stateHistory.add(State.FAILED);
886 assertEquals(State.FAILED, service.state());
887 assertEquals(failure, service.failureCause());
888 if (from == State.STARTING) {
889 try {
890 service.awaitRunning();
891 fail();
892 } catch (IllegalStateException e) {
893 assertEquals(failure, e.getCause());
894 }
895 }
896 try {
897 service.awaitTerminated();
898 fail();
899 } catch (IllegalStateException e) {
900 assertEquals(failure, e.getCause());
901 }
902 completionLatch.countDown();
903 }
904 }
905
906 public void testNotifyStartedWhenNotStarting() {
907 AbstractService service = new DefaultService();
908 try {
909 service.notifyStarted();
910 fail();
911 } catch (IllegalStateException expected) {}
912 }
913
914 public void testNotifyStoppedWhenNotRunning() {
915 AbstractService service = new DefaultService();
916 try {
917 service.notifyStopped();
918 fail();
919 } catch (IllegalStateException expected) {}
920 }
921
922 public void testNotifyFailedWhenNotStarted() {
923 AbstractService service = new DefaultService();
924 try {
925 service.notifyFailed(new Exception());
926 fail();
927 } catch (IllegalStateException expected) {}
928 }
929
930 public void testNotifyFailedWhenTerminated() {
931 NoOpService service = new NoOpService();
932 service.startAsync().awaitRunning();
933 service.stopAsync().awaitTerminated();
934 try {
935 service.notifyFailed(new Exception());
936 fail();
937 } catch (IllegalStateException expected) {}
938 }
939
940 private static class DefaultService extends AbstractService {
941 @Override protected void doStart() {}
942 @Override protected void doStop() {}
943 }
944
945 private static final Exception EXCEPTION = new Exception();
946 }