11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2009 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.util.concurrent;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.lang.Thread.currentThread;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.util.concurrent.TimeUnit.SECONDS;
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
227dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableList;
237dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Iterables;
247dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Lists;
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.Service.Listener;
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.Service.State;
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
280888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport junit.framework.TestCase;
290888a09821a98ac0680fad765217302858e70fa4Paul Duffin
301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.Thread.UncaughtExceptionHandler;
317dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.List;
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.CountDownLatch;
337dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.TimeUnit;
340888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.concurrent.atomic.AtomicInteger;
350888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.concurrent.atomic.AtomicReference;
367dd252788645e940eada959bdde927426e2531c9Paul Duffin
377dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.concurrent.GuardedBy;
387dd252788645e940eada959bdde927426e2531c9Paul Duffin
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unit test for {@link AbstractService}.
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Jesse Wilson
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class AbstractServiceTest extends TestCase {
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
460888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final long LONG_TIMEOUT_MILLIS = 2500;
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Thread executionThread;
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Throwable thrownByExecutionThread;
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
507dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNoOpServiceStartStop() throws Exception {
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
527dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
537dd252788645e940eada959bdde927426e2531c9Paul Duffin
547dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.NEW, service.state());
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
597dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.running);
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
647dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
677dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
687dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
697dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
707dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
717dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
727dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
737dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartAndWaitStopAndWait() throws Exception {
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
790888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
807dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
837dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
860888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStartAsyncAndAwaitStopAsyncAndAwait() throws Exception {
870888a09821a98ac0680fad765217302858e70fa4Paul Duffin    NoOpService service = new NoOpService();
880888a09821a98ac0680fad765217302858e70fa4Paul Duffin
890888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
910888a09821a98ac0680fad765217302858e70fa4Paul Duffin
920888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
930888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.TERMINATED, service.state());
940888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
950888a09821a98ac0680fad765217302858e70fa4Paul Duffin
960888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotence() throws Exception {
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
987dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
990888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1007dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1020888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1030888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1047dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1057dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
1067dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
1077dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
1087dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
1097dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
1107dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
1117dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1140888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotenceAfterWait() throws Exception {
1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1170888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1200888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1217dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1240888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotenceDoubleWait() throws Exception {
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1270888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1287dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1300888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopAndWaitUninterruptible()
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Exception {
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    currentThread().interrupt();
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
1410888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.RUNNING, service.state());
1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1440888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
1457dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.TERMINATED, service.state());
1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(currentThread().isInterrupted());
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } finally {
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Thread.interrupted(); // clear interrupt for future tests
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class NoOpService extends AbstractService {
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean running = false;
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(running);
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = true;
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStarted();
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(running);
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = false;
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStopped();
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1697dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStartStop() throws Exception {
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
1717dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1730888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
1747dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STARTING, service.state());
1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted(); // usually this would be invoked by another thread
1797dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1837dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped(); // usually this would be invoked by another thread
1887dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1907dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
1917dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
1927dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
1937dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
1947dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
1957dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
1967dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
1977dd252788645e940eada959bdde927426e2531c9Paul Duffin
1981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2007dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceNotifyStoppedWhileRunning() throws Exception {
2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
2027dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2040888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2057dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
2067dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStopped();
2077dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.isRunning());
2097dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStopCalled);
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin
2117dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
2127dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
2137dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
2147dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
2157dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
2167dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
2177dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2187dd252788645e940eada959bdde927426e2531c9Paul Duffin
2197dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStopWhileStarting() throws Exception {
2207dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin
2230888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2247dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STARTING, service.state());
2251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2297dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
2301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
2321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
2347dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
2351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
2397dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2407dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.isRunning());
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
2427dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
2437dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
2447dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
2457dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
2467dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
2477dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2487dd252788645e940eada959bdde927426e2531c9Paul Duffin
2490888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
2500888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * This tests for a bug where if {@link Service#stopAsync()} was called while the service was
2510888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * {@link State#STARTING} more than once, the {@link Listener#stopping(State)} callback would get
2520888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * called multiple times.
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
2540888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testManualServiceStopMultipleTimesWhileStarting() throws Exception {
2550888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final AtomicInteger stopppingCount = new AtomicInteger();
2570888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.addListener(new Listener() {
2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void stopping(State from) {
2590888a09821a98ac0680fad765217302858e70fa4Paul Duffin        stopppingCount.incrementAndGet();
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2610888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }, MoreExecutors.sameThreadExecutor());
2620888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2640888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(1, stopppingCount.get());
2660888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(1, stopppingCount.get());
2680888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2690888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2707dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStopWhileNew() throws Exception {
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin
2740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2757dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2777dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStartCalled);
2787dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStopCalled);
2797dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.TERMINATED), listener.getStateHistory());
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileStarting() throws Exception {
2837dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2850888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2867dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.FAILED), listener.getStateHistory());
2887dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileRunning() throws Exception {
2917dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2930888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2947dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
2967dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.FAILED),
2977dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
2987dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2997dd252788645e940eada959bdde927426e2531c9Paul Duffin
3007dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileStopping() throws Exception {
3017dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
3027dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
3030888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3047dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
3050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
3067dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
3077dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.STOPPING, State.FAILED),
3087dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
3091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testManualServiceUnrequestedStop() {
3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
3131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3140888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
3177dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
3181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
3191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
3227dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
3231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
3241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
3251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
3281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * The user of this service should call {@link #notifyStarted} and {@link
3290888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * #notifyStopped} after calling {@link #startAsync} and {@link #stopAsync}.
3301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
3311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class ManualSwitchedService extends AbstractService {
3321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStartCalled = false;
3331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStopCalled = false;
3341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
3361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStartCalled);
3371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStartCalled = true;
3381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
3411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStopCalled);
3421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStopCalled = true;
3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3460888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testAwaitTerminated() throws Exception {
3470888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final NoOpService service = new NoOpService();
3480888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Thread waiter = new Thread() {
3490888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void run() {
3500888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitTerminated();
3510888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3520888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
3530888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.start();
3540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
3550888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
3560888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
3570888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.join(LONG_TIMEOUT_MILLIS);  // ensure that the await in the other thread is triggered
3580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertFalse(waiter.isAlive());
3590888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
3600888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3610888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testAwaitTerminated_FailedService() throws Exception {
3620888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final ManualSwitchedService service = new ManualSwitchedService();
3630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final AtomicReference<Throwable> exception = Atomics.newReference();
3640888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Thread waiter = new Thread() {
3650888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void run() {
3660888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
3670888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitTerminated();
3680888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail("Expected an IllegalStateException");
3690888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (Throwable t) {
3700888a09821a98ac0680fad765217302858e70fa4Paul Duffin          exception.set(t);
3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
3720888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3730888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
3740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.start();
3750888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3760888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.notifyStarted();
3770888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.notifyFailed(EXCEPTION);
3790888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.FAILED, service.state());
3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.join(LONG_TIMEOUT_MILLIS);
3810888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertFalse(waiter.isAlive());
3820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertTrue(exception.get() instanceof IllegalStateException);
3830888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(EXCEPTION, exception.get().getCause());
3840888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
3850888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable {
3871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
3887dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
3890888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
3907dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
3911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
3931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
3957dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
3961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
3987dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
3997dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
4007dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
4017dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
4027dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
4037dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
4047dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
4051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4070888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotence() throws Throwable {
4081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4117dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
4160888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4177dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4220888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotenceAfterWait()
4231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
4241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4260888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4277dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
4337dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.join();
4361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4400888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotenceDoubleWait()
4411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
4421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4440888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4457dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4490888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4500888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4517dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4567dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailureIdempotence() {
4577dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
4587dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener.record(service);
4590888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
4607dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(new Exception("1"));
4617dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(new Exception("2"));
4620888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("1", service.failureCause().getMessage());
4637dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
4640888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
4657dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
4660888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
4677dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals("1", e.getCause().getMessage());
4687dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4697dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4707dd252788645e940eada959bdde927426e2531c9Paul Duffin
4711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private class ThreadedService extends AbstractService {
4721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch hasConfirmedIsRunning = new CountDownLatch(1);
4731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    /*
4751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * The main test thread tries to stop() the service shortly after
4761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * confirming that it is running. Meanwhile, the service itself is trying
4771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * to confirm that it is running. If the main thread's stop() call happens
4781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * before it has the chance, the test will fail. To avoid this, the main
4791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * thread calls this method, which waits until the service has performed
4801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * its own "running" check.
4811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     */
4821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    void awaitRunChecks() throws InterruptedException {
4831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue("Service thread hasn't finished its checks. "
4841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          + "Exception status (possibly stale): " + thrownByExecutionThread,
4851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.await(10, SECONDS));
4861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
4891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STARTING, state());
4901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
4911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
4921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STARTING, state());
4931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStarted();
4941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.RUNNING, state());
4951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.countDown();
4961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
4971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
4981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
5011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STOPPING, state());
5021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
5031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
5041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STOPPING, state());
5051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStopped();
5061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.TERMINATED, state());
5071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
5081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
5091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
5101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void invokeOnExecutionThreadForTest(Runnable runnable) {
5131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread = new Thread(runnable);
5141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
5151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      @Override
5161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      public void uncaughtException(Thread thread, Throwable e) {
5171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        thrownByExecutionThread = e;
5181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
5191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    });
5201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.start();
5211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static void throwIfSet(Throwable t) throws Throwable {
5241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    if (t != null) {
5251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throw t;
5261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
5271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testStopUnstartedService() throws Exception {
5301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
5317dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5330888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
5347dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
5357dd252788645e940eada959bdde927426e2531c9Paul Duffin
5360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    try {
5370888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync();
5380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      fail();
5390888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException expected) {}
5407dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, Iterables.getOnlyElement(listener.getStateHistory()));
5417dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5427dd252788645e940eada959bdde927426e2531c9Paul Duffin
5437dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailingServiceStartAndWait() throws Exception {
5447dd252788645e940eada959bdde927426e2531c9Paul Duffin    StartFailingService service = new StartFailingService();
5457dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5467dd252788645e940eada959bdde927426e2531c9Paul Duffin
5477dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5480888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
5497dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5500888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5510888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5527dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5537dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5547dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5557dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5567dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5577dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
5587dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
5597dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5607dd252788645e940eada959bdde927426e2531c9Paul Duffin
5617dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailingServiceStopAndWait_stopFailing() throws Exception {
5627dd252788645e940eada959bdde927426e2531c9Paul Duffin    StopFailingService service = new StopFailingService();
5637dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5647dd252788645e940eada959bdde927426e2531c9Paul Duffin
5650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
5667dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5670888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
5687dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5690888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5717dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5727dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5737dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5747dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5757dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5767dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
5777dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
5787dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
5797dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
5807dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5817dd252788645e940eada959bdde927426e2531c9Paul Duffin
5820888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testFailingServiceStopAndWait_runFailing() throws Exception {
5837dd252788645e940eada959bdde927426e2531c9Paul Duffin    RunFailingService service = new RunFailingService();
5847dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5857dd252788645e940eada959bdde927426e2531c9Paul Duffin
5860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
5877dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5880888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
5897dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5910888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5920888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5937dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5947dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5957dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5967dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5977dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
5987dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
5997dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStartAndWait() throws Exception {
6031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StartThrowingService service = new StartThrowingService();
6047dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
6070888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
6081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
6090888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6100888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6117dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(service.exception, e.getCause());
6121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6137dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6147dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6157dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6167dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6177dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_stopThrowing() throws Exception {
6211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StopThrowingService service = new StopThrowingService();
6227dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6240888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
6251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
6260888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
6271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
6280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6290888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6307dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(service.exception, e.getCause());
6311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6327dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6337dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6347dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6357dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
6367dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
6377dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6387dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_runThrowing() throws Exception {
6421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    RunThrowingService service = new RunThrowingService();
6437dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6450888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
6463c77433663281544363151bf284b0240dfd22a42Paul Duffin    try {
6470888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitTerminated();
6483c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
6490888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6500888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6510888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, e.getCause());
6521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6537dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6547dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6557dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6567dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
6577dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6587dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6617dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailureCause_throwsIfNotFailed() {
6627dd252788645e940eada959bdde927426e2531c9Paul Duffin    StopFailingService service = new StopFailingService();
6637dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6647dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.failureCause();
6657dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6667dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException e) {
6677dd252788645e940eada959bdde927426e2531c9Paul Duffin      // expected
6687dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6690888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
6707dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6717dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.failureCause();
6727dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6737dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException e) {
6747dd252788645e940eada959bdde927426e2531c9Paul Duffin      // expected
6757dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6767dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6770888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
6787dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6790888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6807dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
6817dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
6827dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6837dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
6847dd252788645e940eada959bdde927426e2531c9Paul Duffin
6857dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testAddListenerAfterFailureDoesntCauseDeadlock() throws InterruptedException {
6867dd252788645e940eada959bdde927426e2531c9Paul Duffin    final StartFailingService service = new StartFailingService();
6870888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
6887dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.FAILED, service.state());
6897dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.addListener(new RecordingListener(service), MoreExecutors.sameThreadExecutor());
6907dd252788645e940eada959bdde927426e2531c9Paul Duffin    Thread thread = new Thread() {
6917dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void run() {
6920888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // Internally stopAsync() grabs a lock, this could be any such method on AbstractService.
6930888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync();
6947dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
6957dd252788645e940eada959bdde927426e2531c9Paul Duffin    };
6967dd252788645e940eada959bdde927426e2531c9Paul Duffin    thread.start();
6970888a09821a98ac0680fad765217302858e70fa4Paul Duffin    thread.join(LONG_TIMEOUT_MILLIS);
6987dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(thread + " is deadlocked", thread.isAlive());
6997dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7007dd252788645e940eada959bdde927426e2531c9Paul Duffin
7017dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testListenerDoesntDeadlockOnStartAndWaitFromRunning() throws Exception {
7027dd252788645e940eada959bdde927426e2531c9Paul Duffin    final NoOpThreadedService service = new NoOpThreadedService();
7037dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.addListener(new Listener() {
7047dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void running() {
7050888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitRunning();
7067dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7077dd252788645e940eada959bdde927426e2531c9Paul Duffin    }, MoreExecutors.sameThreadExecutor());
7080888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning(LONG_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
7090888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
7107dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7117dd252788645e940eada959bdde927426e2531c9Paul Duffin
7127dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testListenerDoesntDeadlockOnStopAndWaitFromTerminated() throws Exception {
7137dd252788645e940eada959bdde927426e2531c9Paul Duffin    final NoOpThreadedService service = new NoOpThreadedService();
7147dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.addListener(new Listener() {
7157dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void terminated(State from) {
7160888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync().awaitTerminated();
7177dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7187dd252788645e940eada959bdde927426e2531c9Paul Duffin    }, MoreExecutors.sameThreadExecutor());
7190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
7207dd252788645e940eada959bdde927426e2531c9Paul Duffin
7217dd252788645e940eada959bdde927426e2531c9Paul Duffin    Thread thread = new Thread() {
7227dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void run() {
7230888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync().awaitTerminated();
7247dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7257dd252788645e940eada959bdde927426e2531c9Paul Duffin    };
7267dd252788645e940eada959bdde927426e2531c9Paul Duffin    thread.start();
7270888a09821a98ac0680fad765217302858e70fa4Paul Duffin    thread.join(LONG_TIMEOUT_MILLIS);
7287dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(thread + " is deadlocked", thread.isAlive());
7297dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7307dd252788645e940eada959bdde927426e2531c9Paul Duffin
7317dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class NoOpThreadedService extends AbstractExecutionThreadService {
7320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final CountDownLatch latch = new CountDownLatch(1);
7330888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void run() throws Exception {
7340888a09821a98ac0680fad765217302858e70fa4Paul Duffin      latch.await();
7350888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
7360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void triggerShutdown() {
7370888a09821a98ac0680fad765217302858e70fa4Paul Duffin      latch.countDown();
7380888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
7397dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7407dd252788645e940eada959bdde927426e2531c9Paul Duffin
7417dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StartFailingService extends AbstractService {
7423c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
743dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7443c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7453c77433663281544363151bf284b0240dfd22a42Paul Duffin
7463c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
7473c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
7483c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7493c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7503c77433663281544363151bf284b0240dfd22a42Paul Duffin
7517dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class RunFailingService extends AbstractService {
7523c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
7533c77433663281544363151bf284b0240dfd22a42Paul Duffin      notifyStarted();
754dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7553c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7563c77433663281544363151bf284b0240dfd22a42Paul Duffin
7573c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
7583c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
7593c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7603c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7613c77433663281544363151bf284b0240dfd22a42Paul Duffin
7627dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StopFailingService extends AbstractService {
7633c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
7643c77433663281544363151bf284b0240dfd22a42Paul Duffin      notifyStarted();
7653c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7663c77433663281544363151bf284b0240dfd22a42Paul Duffin
7673c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
768dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7693c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7703c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7713c77433663281544363151bf284b0240dfd22a42Paul Duffin
7727dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StartThrowingService extends AbstractService {
7737dd252788645e940eada959bdde927426e2531c9Paul Duffin
7747dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
7757dd252788645e940eada959bdde927426e2531c9Paul Duffin
7767dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
7777dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
7787dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7797dd252788645e940eada959bdde927426e2531c9Paul Duffin
7807dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
7817dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
7827dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7837dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7847dd252788645e940eada959bdde927426e2531c9Paul Duffin
7857dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class RunThrowingService extends AbstractService {
7867dd252788645e940eada959bdde927426e2531c9Paul Duffin
7877dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
7887dd252788645e940eada959bdde927426e2531c9Paul Duffin
7897dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
7907dd252788645e940eada959bdde927426e2531c9Paul Duffin      notifyStarted();
7917dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
7927dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7937dd252788645e940eada959bdde927426e2531c9Paul Duffin
7947dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
7957dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
7967dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7977dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7987dd252788645e940eada959bdde927426e2531c9Paul Duffin
7997dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StopThrowingService extends AbstractService {
8007dd252788645e940eada959bdde927426e2531c9Paul Duffin
8017dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
8027dd252788645e940eada959bdde927426e2531c9Paul Duffin
8037dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
8047dd252788645e940eada959bdde927426e2531c9Paul Duffin      notifyStarted();
8057dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8067dd252788645e940eada959bdde927426e2531c9Paul Duffin
8077dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
8087dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
8097dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8107dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
8117dd252788645e940eada959bdde927426e2531c9Paul Duffin
8120888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static class RecordingListener extends Listener {
8137dd252788645e940eada959bdde927426e2531c9Paul Duffin    static RecordingListener record(Service service) {
8147dd252788645e940eada959bdde927426e2531c9Paul Duffin      RecordingListener listener = new RecordingListener(service);
8157dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.addListener(listener, MoreExecutors.sameThreadExecutor());
8167dd252788645e940eada959bdde927426e2531c9Paul Duffin      return listener;
8177dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8187dd252788645e940eada959bdde927426e2531c9Paul Duffin
8197dd252788645e940eada959bdde927426e2531c9Paul Duffin    final Service service;
8207dd252788645e940eada959bdde927426e2531c9Paul Duffin
8217dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener(Service service) {
8227dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.service = service;
8237dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8247dd252788645e940eada959bdde927426e2531c9Paul Duffin
8257dd252788645e940eada959bdde927426e2531c9Paul Duffin    @GuardedBy("this")
8267dd252788645e940eada959bdde927426e2531c9Paul Duffin    final List<State> stateHistory = Lists.newArrayList();
8277dd252788645e940eada959bdde927426e2531c9Paul Duffin    final CountDownLatch completionLatch = new CountDownLatch(1);
8287dd252788645e940eada959bdde927426e2531c9Paul Duffin
8297dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableList<State> getStateHistory() throws Exception {
8307dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.await();
8317dd252788645e940eada959bdde927426e2531c9Paul Duffin      synchronized (this) {
8327dd252788645e940eada959bdde927426e2531c9Paul Duffin        return ImmutableList.copyOf(stateHistory);
8337dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8347dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8357dd252788645e940eada959bdde927426e2531c9Paul Duffin
8367dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void starting() {
8377dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertTrue(stateHistory.isEmpty());
8387dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(State.NEW, service.state());
8397dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.STARTING);
8407dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8417dd252788645e940eada959bdde927426e2531c9Paul Duffin
8427dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void running() {
8437dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.STARTING, Iterables.getOnlyElement(stateHistory));
8447dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.RUNNING);
8450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
8467dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(State.STARTING, service.state());
8477dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8487dd252788645e940eada959bdde927426e2531c9Paul Duffin
8497dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void stopping(State from) {
8507dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory));
8517dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.STOPPING);
8527dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.STARTING) {
8530888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
8540888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8550888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail();
8560888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException expected) {
8570888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertNull(expected.getCause());
8580888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertTrue(expected.getMessage().equals(
8590888a09821a98ac0680fad765217302858e70fa4Paul Duffin              "Expected the service to be RUNNING, but was STOPPING"));
8600888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
8617dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8627dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(from, service.state());
8637dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8647dd252788645e940eada959bdde927426e2531c9Paul Duffin
8657dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void terminated(State from) {
8667dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory, State.NEW));
8677dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.TERMINATED);
8687dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.TERMINATED, service.state());
8697dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.NEW) {
8700888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
8710888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8720888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail();
8730888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException expected) {
8740888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertNull(expected.getCause());
8750888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertTrue(expected.getMessage().equals(
8760888a09821a98ac0680fad765217302858e70fa4Paul Duffin              "Expected the service to be RUNNING, but was TERMINATED"));
8770888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
8787dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8797dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.countDown();
8807dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8817dd252788645e940eada959bdde927426e2531c9Paul Duffin
8827dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void failed(State from, Throwable failure) {
8837dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory));
8847dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.FAILED);
8857dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.FAILED, service.state());
8860888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(failure, service.failureCause());
8877dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.STARTING) {
8887dd252788645e940eada959bdde927426e2531c9Paul Duffin        try {
8890888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8907dd252788645e940eada959bdde927426e2531c9Paul Duffin          fail();
8910888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException e) {
8927dd252788645e940eada959bdde927426e2531c9Paul Duffin          assertEquals(failure, e.getCause());
8937dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
8947dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8957dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
8960888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitTerminated();
8977dd252788645e940eada959bdde927426e2531c9Paul Duffin        fail();
8980888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalStateException e) {
8990888a09821a98ac0680fad765217302858e70fa4Paul Duffin        assertEquals(failure, e.getCause());
9007dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
9017dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.countDown();
9027dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
9037dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9047dd252788645e940eada959bdde927426e2531c9Paul Duffin
9057dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyStartedWhenNotStarting() {
9067dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9077dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9087dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyStarted();
9097dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9107dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9117dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9127dd252788645e940eada959bdde927426e2531c9Paul Duffin
9137dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyStoppedWhenNotRunning() {
9147dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9157dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9167dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyStopped();
9177dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9187dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9197dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9207dd252788645e940eada959bdde927426e2531c9Paul Duffin
9217dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyFailedWhenNotStarted() {
9227dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9237dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9247dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyFailed(new Exception());
9257dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9267dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9277dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9287dd252788645e940eada959bdde927426e2531c9Paul Duffin
9297dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyFailedWhenTerminated() {
9307dd252788645e940eada959bdde927426e2531c9Paul Duffin    NoOpService service = new NoOpService();
9310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
9320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
9337dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9347dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyFailed(new Exception());
9357dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9367dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9377dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9387dd252788645e940eada959bdde927426e2531c9Paul Duffin
9397dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class DefaultService extends AbstractService {
9407dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {}
9417dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {}
9427dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9437dd252788645e940eada959bdde927426e2531c9Paul Duffin
9441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final Exception EXCEPTION = new Exception();
9451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
946