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
193ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffinimport static com.google.common.util.concurrent.MoreExecutors.directExecutor;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.lang.Thread.currentThread;
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.util.concurrent.TimeUnit.SECONDS;
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
237dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.ImmutableList;
247dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Iterables;
257dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.collect.Lists;
267dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.Service.Listener;
277dd252788645e940eada959bdde927426e2531c9Paul Duffinimport com.google.common.util.concurrent.Service.State;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
290888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport junit.framework.TestCase;
300888a09821a98ac0680fad765217302858e70fa4Paul Duffin
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.Thread.UncaughtExceptionHandler;
327dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.List;
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.CountDownLatch;
347dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.TimeUnit;
350888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.concurrent.atomic.AtomicInteger;
360888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.concurrent.atomic.AtomicReference;
377dd252788645e940eada959bdde927426e2531c9Paul Duffin
387dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.concurrent.GuardedBy;
397dd252788645e940eada959bdde927426e2531c9Paul Duffin
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unit test for {@link AbstractService}.
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Jesse Wilson
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class AbstractServiceTest extends TestCase {
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
470888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final long LONG_TIMEOUT_MILLIS = 2500;
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Thread executionThread;
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Throwable thrownByExecutionThread;
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
517dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNoOpServiceStartStop() throws Exception {
521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
537dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
547dd252788645e940eada959bdde927426e2531c9Paul Duffin
557dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.NEW, service.state());
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
590888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
607dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.running);
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
640888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
657dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
687dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
697dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
707dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
717dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
727dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
737dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
747dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartAndWaitStopAndWait() throws Exception {
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
800888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
817dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
830888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
847dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
870888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStartAsyncAndAwaitStopAsyncAndAwait() throws Exception {
880888a09821a98ac0680fad765217302858e70fa4Paul Duffin    NoOpService service = new NoOpService();
890888a09821a98ac0680fad765217302858e70fa4Paul Duffin
900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
920888a09821a98ac0680fad765217302858e70fa4Paul Duffin
930888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.TERMINATED, service.state());
950888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
960888a09821a98ac0680fad765217302858e70fa4Paul Duffin
970888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotence() throws Exception {
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
997dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
1000888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1017dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1030888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1040888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1057dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1067dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
1077dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
1087dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
1097dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
1107dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
1117dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
1127dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1150888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotenceAfterWait() throws Exception {
1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1180888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1200888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1210888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1227dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1250888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testNoOpServiceStopIdempotenceDoubleWait() throws Exception {
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
1297dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1310888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopAndWaitUninterruptible()
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Exception {
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    currentThread().interrupt();
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
1420888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
1437dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.RUNNING, service.state());
1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
1467dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.TERMINATED, service.state());
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(currentThread().isInterrupted());
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } finally {
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Thread.interrupted(); // clear interrupt for future tests
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class NoOpService extends AbstractService {
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean running = false;
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(running);
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = true;
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStarted();
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(running);
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = false;
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStopped();
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1707dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStartStop() throws Exception {
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
1727dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
1757dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STARTING, service.state());
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted(); // usually this would be invoked by another thread
1807dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1830888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
1847dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped(); // usually this would be invoked by another thread
1897dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1917dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
1927dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
1937dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
1947dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
1957dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
1967dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
1977dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
1987dd252788645e940eada959bdde927426e2531c9Paul Duffin
1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2017dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceNotifyStoppedWhileRunning() throws Exception {
2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
2037dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2067dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
2077dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStopped();
2087dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2097dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.isRunning());
2107dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStopCalled);
2117dd252788645e940eada959bdde927426e2531c9Paul Duffin
2127dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
2137dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
2147dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
2157dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
2167dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
2177dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
2187dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2197dd252788645e940eada959bdde927426e2531c9Paul Duffin
2207dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStopWhileStarting() throws Exception {
2217dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2227dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2237dd252788645e940eada959bdde927426e2531c9Paul Duffin
2240888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2257dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STARTING, service.state());
2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
2281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2307dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
2311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
2357dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.STOPPING, service.state());
2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
2407dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2417dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.isRunning());
2427dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
2437dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
2447dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
2457dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
2467dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
2477dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
2487dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2497dd252788645e940eada959bdde927426e2531c9Paul Duffin
2500888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
2510888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * This tests for a bug where if {@link Service#stopAsync()} was called while the service was
2520888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * {@link State#STARTING} more than once, the {@link Listener#stopping(State)} callback would get
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * called multiple times.
2540888a09821a98ac0680fad765217302858e70fa4Paul Duffin   */
2550888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testManualServiceStopMultipleTimesWhileStarting() throws Exception {
2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2570888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final AtomicInteger stopppingCount = new AtomicInteger();
2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.addListener(new Listener() {
2590888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void stopping(State from) {
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin        stopppingCount.incrementAndGet();
2610888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2623ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    }, directExecutor());
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2640888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2660888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(1, stopppingCount.get());
2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2680888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(1, stopppingCount.get());
2690888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2700888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2717dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceStopWhileNew() throws Exception {
2727dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2737dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2747dd252788645e940eada959bdde927426e2531c9Paul Duffin
2750888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
2767dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
2771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
2787dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStartCalled);
2797dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(service.doStopCalled);
2807dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.TERMINATED), listener.getStateHistory());
2817dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2827dd252788645e940eada959bdde927426e2531c9Paul Duffin
2837dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileStarting() throws Exception {
2847dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2857dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2877dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
2887dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.FAILED), listener.getStateHistory());
2897dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
2907dd252788645e940eada959bdde927426e2531c9Paul Duffin
2917dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileRunning() throws Exception {
2927dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
2937dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
2940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
2967dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
2977dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.FAILED),
2987dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
2997dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3007dd252788645e940eada959bdde927426e2531c9Paul Duffin
3017dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailWhileStopping() throws Exception {
3027dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
3037dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
3040888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3057dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyStarted();
3060888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
3077dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(EXCEPTION);
3087dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(ImmutableList.of(State.STARTING, State.RUNNING, State.STOPPING, State.FAILED),
3097dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testManualServiceUnrequestedStop() {
3131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
3141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3150888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
3187dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
3191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
3237dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
3241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
3251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
3261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
3291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * The user of this service should call {@link #notifyStarted} and {@link
3300888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * #notifyStopped} after calling {@link #startAsync} and {@link #stopAsync}.
3311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
3321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class ManualSwitchedService extends AbstractService {
3331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStartCalled = false;
3341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStopCalled = false;
3351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
3371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStartCalled);
3381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStartCalled = true;
3391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
3421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStopCalled);
3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStopCalled = true;
3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3470888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testAwaitTerminated() throws Exception {
3480888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final NoOpService service = new NoOpService();
3490888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Thread waiter = new Thread() {
3500888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void run() {
3510888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitTerminated();
3520888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3530888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
3540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.start();
3550888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
3560888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
3570888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
3580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.join(LONG_TIMEOUT_MILLIS);  // ensure that the await in the other thread is triggered
3590888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertFalse(waiter.isAlive());
3600888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
3610888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3620888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testAwaitTerminated_FailedService() throws Exception {
3630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final ManualSwitchedService service = new ManualSwitchedService();
3640888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final AtomicReference<Throwable> exception = Atomics.newReference();
3650888a09821a98ac0680fad765217302858e70fa4Paul Duffin    Thread waiter = new Thread() {
3660888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override public void run() {
3670888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
3680888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitTerminated();
3690888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail("Expected an IllegalStateException");
3700888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (Throwable t) {
3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin          exception.set(t);
3720888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
3730888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
3740888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
3750888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.start();
3760888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
3770888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.notifyStarted();
3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.RUNNING, service.state());
3790888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.notifyFailed(EXCEPTION);
3800888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(State.FAILED, service.state());
3810888a09821a98ac0680fad765217302858e70fa4Paul Duffin    waiter.join(LONG_TIMEOUT_MILLIS);
3820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertFalse(waiter.isAlive());
3830888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertTrue(exception.get() instanceof IllegalStateException);
3840888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals(EXCEPTION, exception.get().getCause());
3850888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
3860888a09821a98ac0680fad765217302858e70fa4Paul Duffin
3871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable {
3881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
3897dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
3900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
3917dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
3921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
3941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3950888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
3967dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
3971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
3997dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
4007dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
4017dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
4027dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
4037dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
4047dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.TERMINATED),
4057dd252788645e940eada959bdde927426e2531c9Paul Duffin            listener.getStateHistory());
4061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4080888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotence() throws Throwable {
4091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4110888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4127dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4160888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
4170888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4187dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4230888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotenceAfterWait()
4241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
4251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4270888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4287dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4330888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
4347dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.join();
4371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4410888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testThreadedServiceStopIdempotenceDoubleWait()
4421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
4431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
4441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4450888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
4467dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.RUNNING, service.state());
4471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
4491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4500888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4510888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
4527dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
4531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
4551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4577dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testManualServiceFailureIdempotence() {
4587dd252788645e940eada959bdde927426e2531c9Paul Duffin    ManualSwitchedService service = new ManualSwitchedService();
4597dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener.record(service);
4600888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
4617dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(new Exception("1"));
4627dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.notifyFailed(new Exception("2"));
4630888a09821a98ac0680fad765217302858e70fa4Paul Duffin    assertEquals("1", service.failureCause().getMessage());
4647dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
4650888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
4667dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
4670888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
4687dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals("1", e.getCause().getMessage());
4697dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4707dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4717dd252788645e940eada959bdde927426e2531c9Paul Duffin
4721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private class ThreadedService extends AbstractService {
4731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch hasConfirmedIsRunning = new CountDownLatch(1);
4741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    /*
4761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * The main test thread tries to stop() the service shortly after
4771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * confirming that it is running. Meanwhile, the service itself is trying
4781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * to confirm that it is running. If the main thread's stop() call happens
4791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * before it has the chance, the test will fail. To avoid this, the main
4801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * thread calls this method, which waits until the service has performed
4811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * its own "running" check.
4821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     */
4831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    void awaitRunChecks() throws InterruptedException {
4841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue("Service thread hasn't finished its checks. "
4851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          + "Exception status (possibly stale): " + thrownByExecutionThread,
4861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.await(10, SECONDS));
4871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
4901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STARTING, state());
4911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
4921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
4931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STARTING, state());
4941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStarted();
4951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.RUNNING, state());
4961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.countDown();
4971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
4981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
4991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
5001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
5021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STOPPING, state());
5031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
5041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
5051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STOPPING, state());
5061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStopped();
5071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.TERMINATED, state());
5081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
5091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
5101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
5111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void invokeOnExecutionThreadForTest(Runnable runnable) {
5141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread = new Thread(runnable);
5151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
5161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      @Override
5171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      public void uncaughtException(Thread thread, Throwable e) {
5181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        thrownByExecutionThread = e;
5191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
5201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    });
5211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.start();
5221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static void throwIfSet(Throwable t) throws Throwable {
5251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    if (t != null) {
5261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throw t;
5271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
5281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
5291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testStopUnstartedService() throws Exception {
5311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
5327dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
5340888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
5357dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, service.state());
5367dd252788645e940eada959bdde927426e2531c9Paul Duffin
5370888a09821a98ac0680fad765217302858e70fa4Paul Duffin    try {
5380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync();
5390888a09821a98ac0680fad765217302858e70fa4Paul Duffin      fail();
5400888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException expected) {}
5417dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.TERMINATED, Iterables.getOnlyElement(listener.getStateHistory()));
5427dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5437dd252788645e940eada959bdde927426e2531c9Paul Duffin
5447dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailingServiceStartAndWait() throws Exception {
5457dd252788645e940eada959bdde927426e2531c9Paul Duffin    StartFailingService service = new StartFailingService();
5467dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5477dd252788645e940eada959bdde927426e2531c9Paul Duffin
5487dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5490888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
5507dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5510888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5520888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5537dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5547dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5557dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5567dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5577dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5587dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
5597dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
5607dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5617dd252788645e940eada959bdde927426e2531c9Paul Duffin
5627dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailingServiceStopAndWait_stopFailing() throws Exception {
5637dd252788645e940eada959bdde927426e2531c9Paul Duffin    StopFailingService service = new StopFailingService();
5647dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5657dd252788645e940eada959bdde927426e2531c9Paul Duffin
5660888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
5677dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5680888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
5697dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5700888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5710888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5727dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5737dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5747dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5757dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5767dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5777dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
5787dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
5797dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
5807dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
5817dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
5827dd252788645e940eada959bdde927426e2531c9Paul Duffin
5830888a09821a98ac0680fad765217302858e70fa4Paul Duffin  public void testFailingServiceStopAndWait_runFailing() throws Exception {
5847dd252788645e940eada959bdde927426e2531c9Paul Duffin    RunFailingService service = new RunFailingService();
5857dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
5867dd252788645e940eada959bdde927426e2531c9Paul Duffin
5870888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
5887dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
5890888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
5907dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
5910888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
5920888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
5930888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(EXCEPTION, e.getCause());
5947dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5957dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
5967dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
5977dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
5987dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
5997dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6007dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStartAndWait() throws Exception {
6041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StartThrowingService service = new StartThrowingService();
6057dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
6080888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.startAsync().awaitRunning();
6091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
6100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6110888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6127dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(service.exception, e.getCause());
6131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6147dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6157dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6167dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6177dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6187dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_stopThrowing() throws Exception {
6221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StopThrowingService service = new StopThrowingService();
6237dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6250888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
6261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
6270888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
6281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
6290888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6300888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6317dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(service.exception, e.getCause());
6321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6337dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6347dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6357dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6367dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
6377dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STOPPING,
6387dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6397dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_runThrowing() throws Exception {
6431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    RunThrowingService service = new RunThrowingService();
6447dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener listener = RecordingListener.record(service);
6451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6460888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
6473c77433663281544363151bf284b0240dfd22a42Paul Duffin    try {
6480888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitTerminated();
6493c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
6500888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6510888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, service.failureCause());
6520888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(service.exception, e.getCause());
6531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
6547dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(
6557dd252788645e940eada959bdde927426e2531c9Paul Duffin        ImmutableList.of(
6567dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.STARTING,
6577dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.RUNNING,
6587dd252788645e940eada959bdde927426e2531c9Paul Duffin            State.FAILED),
6597dd252788645e940eada959bdde927426e2531c9Paul Duffin        listener.getStateHistory());
6601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
6611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
6627dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testFailureCause_throwsIfNotFailed() {
6637dd252788645e940eada959bdde927426e2531c9Paul Duffin    StopFailingService service = new StopFailingService();
6647dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6657dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.failureCause();
6667dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6677dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException e) {
6687dd252788645e940eada959bdde927426e2531c9Paul Duffin      // expected
6697dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6700888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
6717dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6727dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.failureCause();
6737dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6747dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException e) {
6757dd252788645e940eada959bdde927426e2531c9Paul Duffin      // expected
6767dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6777dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
6780888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.stopAsync().awaitTerminated();
6797dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
6800888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } catch (IllegalStateException e) {
6817dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, service.failureCause());
6827dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(EXCEPTION, e.getCause());
6837dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
6847dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
6857dd252788645e940eada959bdde927426e2531c9Paul Duffin
6867dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testAddListenerAfterFailureDoesntCauseDeadlock() throws InterruptedException {
6877dd252788645e940eada959bdde927426e2531c9Paul Duffin    final StartFailingService service = new StartFailingService();
6880888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync();
6897dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertEquals(State.FAILED, service.state());
6903ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    service.addListener(new RecordingListener(service), directExecutor());
6917dd252788645e940eada959bdde927426e2531c9Paul Duffin    Thread thread = new Thread() {
6927dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void run() {
6930888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // Internally stopAsync() grabs a lock, this could be any such method on AbstractService.
6940888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync();
6957dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
6967dd252788645e940eada959bdde927426e2531c9Paul Duffin    };
6977dd252788645e940eada959bdde927426e2531c9Paul Duffin    thread.start();
6980888a09821a98ac0680fad765217302858e70fa4Paul Duffin    thread.join(LONG_TIMEOUT_MILLIS);
6997dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(thread + " is deadlocked", thread.isAlive());
7007dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7017dd252788645e940eada959bdde927426e2531c9Paul Duffin
7027dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testListenerDoesntDeadlockOnStartAndWaitFromRunning() throws Exception {
7037dd252788645e940eada959bdde927426e2531c9Paul Duffin    final NoOpThreadedService service = new NoOpThreadedService();
7047dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.addListener(new Listener() {
7057dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void running() {
7060888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitRunning();
7077dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7083ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    }, directExecutor());
7090888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning(LONG_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
7100888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync();
7117dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7127dd252788645e940eada959bdde927426e2531c9Paul Duffin
7137dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testListenerDoesntDeadlockOnStopAndWaitFromTerminated() throws Exception {
7147dd252788645e940eada959bdde927426e2531c9Paul Duffin    final NoOpThreadedService service = new NoOpThreadedService();
7157dd252788645e940eada959bdde927426e2531c9Paul Duffin    service.addListener(new Listener() {
7167dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void terminated(State from) {
7170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync().awaitTerminated();
7187dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7193ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin    }, directExecutor());
7200888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
7217dd252788645e940eada959bdde927426e2531c9Paul Duffin
7227dd252788645e940eada959bdde927426e2531c9Paul Duffin    Thread thread = new Thread() {
7237dd252788645e940eada959bdde927426e2531c9Paul Duffin      @Override public void run() {
7240888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.stopAsync().awaitTerminated();
7257dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
7267dd252788645e940eada959bdde927426e2531c9Paul Duffin    };
7277dd252788645e940eada959bdde927426e2531c9Paul Duffin    thread.start();
7280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    thread.join(LONG_TIMEOUT_MILLIS);
7297dd252788645e940eada959bdde927426e2531c9Paul Duffin    assertFalse(thread + " is deadlocked", thread.isAlive());
7307dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7317dd252788645e940eada959bdde927426e2531c9Paul Duffin
7327dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class NoOpThreadedService extends AbstractExecutionThreadService {
7330888a09821a98ac0680fad765217302858e70fa4Paul Duffin    final CountDownLatch latch = new CountDownLatch(1);
7340888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void run() throws Exception {
7350888a09821a98ac0680fad765217302858e70fa4Paul Duffin      latch.await();
7360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
7370888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override protected void triggerShutdown() {
7380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      latch.countDown();
7390888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
7407dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7417dd252788645e940eada959bdde927426e2531c9Paul Duffin
7427dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StartFailingService extends AbstractService {
7433c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
744dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7453c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7463c77433663281544363151bf284b0240dfd22a42Paul Duffin
7473c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
7483c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
7493c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7503c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7513c77433663281544363151bf284b0240dfd22a42Paul Duffin
7527dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class RunFailingService extends AbstractService {
7533c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
7543c77433663281544363151bf284b0240dfd22a42Paul Duffin      notifyStarted();
755dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7563c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7573c77433663281544363151bf284b0240dfd22a42Paul Duffin
7583c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
7593c77433663281544363151bf284b0240dfd22a42Paul Duffin      fail();
7603c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7613c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7623c77433663281544363151bf284b0240dfd22a42Paul Duffin
7637dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StopFailingService extends AbstractService {
7643c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStart() {
7653c77433663281544363151bf284b0240dfd22a42Paul Duffin      notifyStarted();
7663c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7673c77433663281544363151bf284b0240dfd22a42Paul Duffin
7683c77433663281544363151bf284b0240dfd22a42Paul Duffin    @Override protected void doStop() {
769dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin      notifyFailed(EXCEPTION);
7703c77433663281544363151bf284b0240dfd22a42Paul Duffin    }
7713c77433663281544363151bf284b0240dfd22a42Paul Duffin  }
7723c77433663281544363151bf284b0240dfd22a42Paul Duffin
7737dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StartThrowingService extends AbstractService {
7747dd252788645e940eada959bdde927426e2531c9Paul Duffin
7757dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
7767dd252788645e940eada959bdde927426e2531c9Paul Duffin
7777dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
7787dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
7797dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7807dd252788645e940eada959bdde927426e2531c9Paul Duffin
7817dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
7827dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
7837dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7847dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7857dd252788645e940eada959bdde927426e2531c9Paul Duffin
7867dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class RunThrowingService extends AbstractService {
7877dd252788645e940eada959bdde927426e2531c9Paul Duffin
7887dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
7897dd252788645e940eada959bdde927426e2531c9Paul Duffin
7907dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
7917dd252788645e940eada959bdde927426e2531c9Paul Duffin      notifyStarted();
7927dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
7937dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7947dd252788645e940eada959bdde927426e2531c9Paul Duffin
7957dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
7967dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
7977dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
7987dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
7997dd252788645e940eada959bdde927426e2531c9Paul Duffin
8007dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class StopThrowingService extends AbstractService {
8017dd252788645e940eada959bdde927426e2531c9Paul Duffin
8027dd252788645e940eada959bdde927426e2531c9Paul Duffin    final RuntimeException exception = new RuntimeException("deliberate");
8037dd252788645e940eada959bdde927426e2531c9Paul Duffin
8047dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {
8057dd252788645e940eada959bdde927426e2531c9Paul Duffin      notifyStarted();
8067dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8077dd252788645e940eada959bdde927426e2531c9Paul Duffin
8087dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {
8097dd252788645e940eada959bdde927426e2531c9Paul Duffin      throw exception;
8107dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8117dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
8127dd252788645e940eada959bdde927426e2531c9Paul Duffin
8130888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static class RecordingListener extends Listener {
8147dd252788645e940eada959bdde927426e2531c9Paul Duffin    static RecordingListener record(Service service) {
8157dd252788645e940eada959bdde927426e2531c9Paul Duffin      RecordingListener listener = new RecordingListener(service);
8163ecfa412eddc4b084663f38d562537b86b9734d5Paul Duffin      service.addListener(listener, directExecutor());
8177dd252788645e940eada959bdde927426e2531c9Paul Duffin      return listener;
8187dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8197dd252788645e940eada959bdde927426e2531c9Paul Duffin
8207dd252788645e940eada959bdde927426e2531c9Paul Duffin    final Service service;
8217dd252788645e940eada959bdde927426e2531c9Paul Duffin
8227dd252788645e940eada959bdde927426e2531c9Paul Duffin    RecordingListener(Service service) {
8237dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.service = service;
8247dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8257dd252788645e940eada959bdde927426e2531c9Paul Duffin
8267dd252788645e940eada959bdde927426e2531c9Paul Duffin    @GuardedBy("this")
8277dd252788645e940eada959bdde927426e2531c9Paul Duffin    final List<State> stateHistory = Lists.newArrayList();
8287dd252788645e940eada959bdde927426e2531c9Paul Duffin    final CountDownLatch completionLatch = new CountDownLatch(1);
8297dd252788645e940eada959bdde927426e2531c9Paul Duffin
8307dd252788645e940eada959bdde927426e2531c9Paul Duffin    ImmutableList<State> getStateHistory() throws Exception {
8317dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.await();
8327dd252788645e940eada959bdde927426e2531c9Paul Duffin      synchronized (this) {
8337dd252788645e940eada959bdde927426e2531c9Paul Duffin        return ImmutableList.copyOf(stateHistory);
8347dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8357dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8367dd252788645e940eada959bdde927426e2531c9Paul Duffin
8377dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void starting() {
8387dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertTrue(stateHistory.isEmpty());
8397dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(State.NEW, service.state());
8407dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.STARTING);
8417dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8427dd252788645e940eada959bdde927426e2531c9Paul Duffin
8437dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void running() {
8447dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.STARTING, Iterables.getOnlyElement(stateHistory));
8457dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.RUNNING);
8460888a09821a98ac0680fad765217302858e70fa4Paul Duffin      service.awaitRunning();
8477dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(State.STARTING, service.state());
8487dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8497dd252788645e940eada959bdde927426e2531c9Paul Duffin
8507dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void stopping(State from) {
8517dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory));
8527dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.STOPPING);
8537dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.STARTING) {
8540888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
8550888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8560888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail();
8570888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException expected) {
8580888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertNull(expected.getCause());
8590888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertTrue(expected.getMessage().equals(
8600888a09821a98ac0680fad765217302858e70fa4Paul Duffin              "Expected the service to be RUNNING, but was STOPPING"));
8610888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
8627dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8637dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertNotSame(from, service.state());
8647dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8657dd252788645e940eada959bdde927426e2531c9Paul Duffin
8667dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void terminated(State from) {
8677dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory, State.NEW));
8687dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.TERMINATED);
8697dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.TERMINATED, service.state());
8707dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.NEW) {
8710888a09821a98ac0680fad765217302858e70fa4Paul Duffin        try {
8720888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8730888a09821a98ac0680fad765217302858e70fa4Paul Duffin          fail();
8740888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException expected) {
8750888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertNull(expected.getCause());
8760888a09821a98ac0680fad765217302858e70fa4Paul Duffin          assertTrue(expected.getMessage().equals(
8770888a09821a98ac0680fad765217302858e70fa4Paul Duffin              "Expected the service to be RUNNING, but was TERMINATED"));
8780888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
8797dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8807dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.countDown();
8817dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
8827dd252788645e940eada959bdde927426e2531c9Paul Duffin
8837dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override public synchronized void failed(State from, Throwable failure) {
8847dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(from, Iterables.getLast(stateHistory));
8857dd252788645e940eada959bdde927426e2531c9Paul Duffin      stateHistory.add(State.FAILED);
8867dd252788645e940eada959bdde927426e2531c9Paul Duffin      assertEquals(State.FAILED, service.state());
8870888a09821a98ac0680fad765217302858e70fa4Paul Duffin      assertEquals(failure, service.failureCause());
8887dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (from == State.STARTING) {
8897dd252788645e940eada959bdde927426e2531c9Paul Duffin        try {
8900888a09821a98ac0680fad765217302858e70fa4Paul Duffin          service.awaitRunning();
8917dd252788645e940eada959bdde927426e2531c9Paul Duffin          fail();
8920888a09821a98ac0680fad765217302858e70fa4Paul Duffin        } catch (IllegalStateException e) {
8937dd252788645e940eada959bdde927426e2531c9Paul Duffin          assertEquals(failure, e.getCause());
8947dd252788645e940eada959bdde927426e2531c9Paul Duffin        }
8957dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
8967dd252788645e940eada959bdde927426e2531c9Paul Duffin      try {
8970888a09821a98ac0680fad765217302858e70fa4Paul Duffin        service.awaitTerminated();
8987dd252788645e940eada959bdde927426e2531c9Paul Duffin        fail();
8990888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (IllegalStateException e) {
9000888a09821a98ac0680fad765217302858e70fa4Paul Duffin        assertEquals(failure, e.getCause());
9017dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
9027dd252788645e940eada959bdde927426e2531c9Paul Duffin      completionLatch.countDown();
9037dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
9047dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9057dd252788645e940eada959bdde927426e2531c9Paul Duffin
9067dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyStartedWhenNotStarting() {
9077dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9087dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9097dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyStarted();
9107dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9117dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9127dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9137dd252788645e940eada959bdde927426e2531c9Paul Duffin
9147dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyStoppedWhenNotRunning() {
9157dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9167dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9177dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyStopped();
9187dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9197dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9207dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9217dd252788645e940eada959bdde927426e2531c9Paul Duffin
9227dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyFailedWhenNotStarted() {
9237dd252788645e940eada959bdde927426e2531c9Paul Duffin    AbstractService service = new DefaultService();
9247dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9257dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyFailed(new Exception());
9267dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9277dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9287dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9297dd252788645e940eada959bdde927426e2531c9Paul Duffin
9307dd252788645e940eada959bdde927426e2531c9Paul Duffin  public void testNotifyFailedWhenTerminated() {
9317dd252788645e940eada959bdde927426e2531c9Paul Duffin    NoOpService service = new NoOpService();
9320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.startAsync().awaitRunning();
9330888a09821a98ac0680fad765217302858e70fa4Paul Duffin    service.stopAsync().awaitTerminated();
9347dd252788645e940eada959bdde927426e2531c9Paul Duffin    try {
9357dd252788645e940eada959bdde927426e2531c9Paul Duffin      service.notifyFailed(new Exception());
9367dd252788645e940eada959bdde927426e2531c9Paul Duffin      fail();
9377dd252788645e940eada959bdde927426e2531c9Paul Duffin    } catch (IllegalStateException expected) {}
9387dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9397dd252788645e940eada959bdde927426e2531c9Paul Duffin
9407dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static class DefaultService extends AbstractService {
9417dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStart() {}
9427dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Override protected void doStop() {}
9437dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
9447dd252788645e940eada959bdde927426e2531c9Paul Duffin
9451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final Exception EXCEPTION = new Exception();
9461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
947