• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static java.lang.Thread.currentThread;
20 import static java.util.concurrent.TimeUnit.SECONDS;
21 
22 import junit.framework.Assert;
23 import junit.framework.TestCase;
24 
25 import java.lang.Thread.UncaughtExceptionHandler;
26 import java.util.concurrent.CountDownLatch;
27 import java.util.concurrent.Future;
28 
29 /**
30  * Unit test for {@link AbstractService}.
31  *
32  * @author Jesse Wilson
33  */
34 public class AbstractServiceTest extends TestCase {
35 
36   private Thread executionThread;
37   private Throwable thrownByExecutionThread;
38 
testNoOpServiceStartStop()39   public void testNoOpServiceStartStop() {
40     NoOpService service = new NoOpService();
41     Assert.assertEquals(Service.State.NEW, service.state());
42     assertFalse(service.isRunning());
43     assertFalse(service.running);
44 
45     service.start();
46     assertEquals(Service.State.RUNNING, service.state());
47     assertTrue(service.isRunning());
48     assertTrue(service.running);
49 
50     service.stop();
51     assertEquals(Service.State.TERMINATED, service.state());
52     assertFalse(service.isRunning());
53     assertFalse(service.running);
54   }
55 
testNoOpServiceStartAndWaitStopAndWait()56   public void testNoOpServiceStartAndWaitStopAndWait() throws Exception {
57     NoOpService service = new NoOpService();
58 
59     service.start().get();
60     assertEquals(Service.State.RUNNING, service.state());
61 
62     service.stop().get();
63     assertEquals(Service.State.TERMINATED, service.state());
64   }
65 
testNoOpServiceStartStopIdempotence()66   public void testNoOpServiceStartStopIdempotence() throws Exception {
67     NoOpService service = new NoOpService();
68 
69     service.start();
70     service.start();
71     assertEquals(Service.State.RUNNING, service.state());
72 
73     service.stop();
74     service.stop();
75     assertEquals(Service.State.TERMINATED, service.state());
76   }
77 
testNoOpServiceStartStopIdempotenceAfterWait()78   public void testNoOpServiceStartStopIdempotenceAfterWait() throws Exception {
79     NoOpService service = new NoOpService();
80 
81     service.start().get();
82     service.start();
83     assertEquals(Service.State.RUNNING, service.state());
84 
85     service.stop().get();
86     service.stop();
87     assertEquals(Service.State.TERMINATED, service.state());
88   }
89 
testNoOpServiceStartStopIdempotenceDoubleWait()90   public void testNoOpServiceStartStopIdempotenceDoubleWait() throws Exception {
91     NoOpService service = new NoOpService();
92 
93     service.start().get();
94     service.start().get();
95     assertEquals(Service.State.RUNNING, service.state());
96 
97     service.stop().get();
98     service.stop().get();
99     assertEquals(Service.State.TERMINATED, service.state());
100   }
101 
testNoOpServiceStartStopAndWaitUninterruptible()102   public void testNoOpServiceStartStopAndWaitUninterruptible()
103       throws Exception {
104     NoOpService service = new NoOpService();
105 
106     currentThread().interrupt();
107     try {
108       service.startAndWait();
109       assertEquals(Service.State.RUNNING, service.state());
110 
111       service.stopAndWait();
112       assertEquals(Service.State.TERMINATED, service.state());
113 
114       assertTrue(currentThread().isInterrupted());
115     } finally {
116       Thread.interrupted(); // clear interrupt for future tests
117     }
118   }
119 
120   private static class NoOpService extends AbstractService {
121     boolean running = false;
122 
doStart()123     @Override protected void doStart() {
124       assertFalse(running);
125       running = true;
126       notifyStarted();
127     }
128 
doStop()129     @Override protected void doStop() {
130       assertTrue(running);
131       running = false;
132       notifyStopped();
133     }
134   }
135 
testManualServiceStartStop()136   public void testManualServiceStartStop() {
137     ManualSwitchedService service = new ManualSwitchedService();
138 
139     service.start();
140     assertEquals(Service.State.STARTING, service.state());
141     assertFalse(service.isRunning());
142     assertTrue(service.doStartCalled);
143 
144     service.notifyStarted(); // usually this would be invoked by another thread
145     assertEquals(Service.State.RUNNING, service.state());
146     assertTrue(service.isRunning());
147 
148     service.stop();
149     assertEquals(Service.State.STOPPING, service.state());
150     assertFalse(service.isRunning());
151     assertTrue(service.doStopCalled);
152 
153     service.notifyStopped(); // usually this would be invoked by another thread
154     assertEquals(Service.State.TERMINATED, service.state());
155     assertFalse(service.isRunning());
156   }
157 
testManualServiceStopWhileStarting()158   public void testManualServiceStopWhileStarting() {
159     ManualSwitchedService service = new ManualSwitchedService();
160 
161     service.start();
162     assertEquals(Service.State.STARTING, service.state());
163     assertFalse(service.isRunning());
164     assertTrue(service.doStartCalled);
165 
166     service.stop();
167     assertEquals(Service.State.STOPPING, service.state());
168     assertFalse(service.isRunning());
169     assertFalse(service.doStopCalled);
170 
171     service.notifyStarted();
172     assertEquals(Service.State.STOPPING, service.state());
173     assertFalse(service.isRunning());
174     assertTrue(service.doStopCalled);
175 
176     service.notifyStopped();
177     assertEquals(Service.State.TERMINATED, service.state());
178     assertFalse(service.isRunning());
179   }
180 
testManualServiceUnrequestedStop()181   public void testManualServiceUnrequestedStop() {
182     ManualSwitchedService service = new ManualSwitchedService();
183 
184     service.start();
185 
186     service.notifyStarted();
187     assertEquals(Service.State.RUNNING, service.state());
188     assertTrue(service.isRunning());
189     assertFalse(service.doStopCalled);
190 
191     service.notifyStopped();
192     assertEquals(Service.State.TERMINATED, service.state());
193     assertFalse(service.isRunning());
194     assertFalse(service.doStopCalled);
195   }
196 
197   /**
198    * The user of this service should call {@link #notifyStarted} and {@link
199    * #notifyStopped} after calling {@link #start} and {@link #stop}.
200    */
201   private static class ManualSwitchedService extends AbstractService {
202     boolean doStartCalled = false;
203     boolean doStopCalled = false;
204 
doStart()205     @Override protected void doStart() {
206       assertFalse(doStartCalled);
207       doStartCalled = true;
208     }
209 
doStop()210     @Override protected void doStop() {
211       assertFalse(doStopCalled);
212       doStopCalled = true;
213     }
214   }
215 
testThreadedServiceStartAndWaitStopAndWait()216   public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable {
217     ThreadedService service = new ThreadedService();
218 
219     service.start().get();
220     assertEquals(Service.State.RUNNING, service.state());
221 
222     service.awaitRunChecks();
223 
224     service.stop().get();
225     assertEquals(Service.State.TERMINATED, service.state());
226 
227     throwIfSet(thrownByExecutionThread);
228   }
229 
testThreadedServiceStartStopIdempotence()230   public void testThreadedServiceStartStopIdempotence() throws Throwable {
231     ThreadedService service = new ThreadedService();
232 
233     service.start();
234     service.start().get();
235     assertEquals(Service.State.RUNNING, service.state());
236 
237     service.awaitRunChecks();
238 
239     service.stop();
240     service.stop().get();
241     assertEquals(Service.State.TERMINATED, service.state());
242 
243     throwIfSet(thrownByExecutionThread);
244   }
245 
testThreadedServiceStartStopIdempotenceAfterWait()246   public void testThreadedServiceStartStopIdempotenceAfterWait()
247       throws Throwable {
248     ThreadedService service = new ThreadedService();
249 
250     service.start().get();
251     service.start();
252     assertEquals(Service.State.RUNNING, service.state());
253 
254     service.awaitRunChecks();
255 
256     service.stop().get();
257     service.stop();
258     assertEquals(Service.State.TERMINATED, service.state());
259 
260     executionThread.join();
261 
262     throwIfSet(thrownByExecutionThread);
263   }
264 
testThreadedServiceStartStopIdempotenceDoubleWait()265   public void testThreadedServiceStartStopIdempotenceDoubleWait()
266       throws Throwable {
267     ThreadedService service = new ThreadedService();
268 
269     service.start().get();
270     service.start().get();
271     assertEquals(Service.State.RUNNING, service.state());
272 
273     service.awaitRunChecks();
274 
275     service.stop().get();
276     service.stop().get();
277     assertEquals(Service.State.TERMINATED, service.state());
278 
279     throwIfSet(thrownByExecutionThread);
280   }
281 
282   private class ThreadedService extends AbstractService {
283     final CountDownLatch hasConfirmedIsRunning = new CountDownLatch(1);
284 
285     /*
286      * The main test thread tries to stop() the service shortly after
287      * confirming that it is running. Meanwhile, the service itself is trying
288      * to confirm that it is running. If the main thread's stop() call happens
289      * before it has the chance, the test will fail. To avoid this, the main
290      * thread calls this method, which waits until the service has performed
291      * its own "running" check.
292      */
awaitRunChecks()293     void awaitRunChecks() throws InterruptedException {
294       assertTrue("Service thread hasn't finished its checks. "
295           + "Exception status (possibly stale): " + thrownByExecutionThread,
296           hasConfirmedIsRunning.await(10, SECONDS));
297     }
298 
doStart()299     @Override protected void doStart() {
300       assertEquals(State.STARTING, state());
301       invokeOnExecutionThreadForTest(new Runnable() {
302         @Override public void run() {
303           assertEquals(State.STARTING, state());
304           notifyStarted();
305           assertEquals(State.RUNNING, state());
306           hasConfirmedIsRunning.countDown();
307         }
308       });
309     }
310 
doStop()311     @Override protected void doStop() {
312       assertEquals(State.STOPPING, state());
313       invokeOnExecutionThreadForTest(new Runnable() {
314         @Override public void run() {
315           assertEquals(State.STOPPING, state());
316           notifyStopped();
317           assertEquals(State.TERMINATED, state());
318         }
319       });
320     }
321   }
322 
invokeOnExecutionThreadForTest(Runnable runnable)323   private void invokeOnExecutionThreadForTest(Runnable runnable) {
324     executionThread = new Thread(runnable);
325     executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
326       @Override
327       public void uncaughtException(Thread thread, Throwable e) {
328         thrownByExecutionThread = e;
329       }
330     });
331     executionThread.start();
332   }
333 
throwIfSet(Throwable t)334   private static void throwIfSet(Throwable t) throws Throwable {
335     if (t != null) {
336       throw t;
337     }
338   }
339 
testStopUnstartedService()340   public void testStopUnstartedService() throws Exception {
341     NoOpService service = new NoOpService();
342     Future<Service.State> stopResult = service.stop();
343     assertEquals(Service.State.TERMINATED, service.state());
344     assertEquals(Service.State.TERMINATED, stopResult.get());
345 
346     Future<Service.State> startResult = service.start();
347     assertEquals(Service.State.TERMINATED, service.state());
348     assertEquals(Service.State.TERMINATED, startResult.get());
349   }
350 
testThrowingServiceStartAndWait()351   public void testThrowingServiceStartAndWait() throws Exception {
352     StartThrowingService service = new StartThrowingService();
353 
354     try {
355       service.startAndWait();
356       fail();
357     } catch (UncheckedExecutionException e) {
358       assertEquals(EXCEPTION, e.getCause());
359     }
360   }
361 
testThrowingServiceStopAndWait_stopThrowing()362   public void testThrowingServiceStopAndWait_stopThrowing() throws Exception {
363     StopThrowingService service = new StopThrowingService();
364 
365     service.startAndWait();
366     try {
367       service.stopAndWait();
368       fail();
369     } catch (UncheckedExecutionException e) {
370       assertEquals(EXCEPTION, e.getCause());
371     }
372   }
373 
testThrowingServiceStopAndWait_runThrowing()374   public void testThrowingServiceStopAndWait_runThrowing() throws Exception {
375     RunThrowingService service = new RunThrowingService();
376 
377     service.startAndWait();
378     try {
379       service.stopAndWait();
380       fail();
381     } catch (UncheckedExecutionException e) {
382       assertEquals(EXCEPTION, e.getCause().getCause());
383     }
384   }
385 
386   private static class StartThrowingService extends AbstractService {
doStart()387     @Override protected void doStart() {
388       notifyFailed(EXCEPTION);
389     }
390 
doStop()391     @Override protected void doStop() {
392       fail();
393     }
394   }
395 
396   private static class RunThrowingService extends AbstractService {
doStart()397     @Override protected void doStart() {
398       notifyStarted();
399       notifyFailed(EXCEPTION);
400     }
401 
doStop()402     @Override protected void doStop() {
403       fail();
404     }
405   }
406 
407   private static class StopThrowingService extends AbstractService {
doStart()408     @Override protected void doStart() {
409       notifyStarted();
410     }
411 
doStop()412     @Override protected void doStop() {
413       notifyFailed(EXCEPTION);
414     }
415   }
416 
417   private static final Exception EXCEPTION = new Exception();
418 }
419