Home | History | Annotate | Download | only in concurrent
      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 
     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 
     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 
     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 
     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 
     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 
    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 
    123     @Override protected void doStart() {
    124       assertFalse(running);
    125       running = true;
    126       notifyStarted();
    127     }
    128 
    129     @Override protected void doStop() {
    130       assertTrue(running);
    131       running = false;
    132       notifyStopped();
    133     }
    134   }
    135 
    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 
    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 
    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 
    205     @Override protected void doStart() {
    206       assertFalse(doStartCalled);
    207       doStartCalled = true;
    208     }
    209 
    210     @Override protected void doStop() {
    211       assertFalse(doStopCalled);
    212       doStopCalled = true;
    213     }
    214   }
    215 
    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 
    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 
    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 
    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      */
    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 
    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 
    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 
    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 
    334   private static void throwIfSet(Throwable t) throws Throwable {
    335     if (t != null) {
    336       throw t;
    337     }
    338   }
    339 
    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 
    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 
    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 
    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 {
    387     @Override protected void doStart() {
    388       notifyFailed(EXCEPTION);
    389     }
    390 
    391     @Override protected void doStop() {
    392       fail();
    393     }
    394   }
    395 
    396   private static class RunThrowingService extends AbstractService {
    397     @Override protected void doStart() {
    398       notifyStarted();
    399       notifyFailed(EXCEPTION);
    400     }
    401 
    402     @Override protected void doStop() {
    403       fail();
    404     }
    405   }
    406 
    407   private static class StopThrowingService extends AbstractService {
    408     @Override protected void doStart() {
    409       notifyStarted();
    410     }
    411 
    412     @Override protected void doStop() {
    413       notifyFailed(EXCEPTION);
    414     }
    415   }
    416 
    417   private static final Exception EXCEPTION = new Exception();
    418 }
    419