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 com.google.common.base.Throwables;
     20 
     21 import junit.framework.TestCase;
     22 
     23 import java.lang.Thread.UncaughtExceptionHandler;
     24 import java.util.concurrent.CountDownLatch;
     25 import java.util.concurrent.ExecutionException;
     26 import java.util.concurrent.Executor;
     27 import java.util.concurrent.Future;
     28 import java.util.concurrent.TimeUnit;
     29 import java.util.concurrent.TimeoutException;
     30 
     31 /**
     32  * Unit test for {@link AbstractExecutionThreadService}.
     33  *
     34  * @author Jesse Wilson
     35  */
     36 public class AbstractExecutionThreadServiceTest extends TestCase {
     37 
     38   private final CountDownLatch enterRun = new CountDownLatch(1);
     39   private final CountDownLatch exitRun = new CountDownLatch(1);
     40 
     41   private Thread executionThread;
     42   private Throwable thrownByExecutionThread;
     43   private final Executor executor = new Executor() {
     44     @Override
     45     public void execute(Runnable command) {
     46       executionThread = new Thread(command);
     47       executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
     48         @Override
     49         public void uncaughtException(Thread thread, Throwable e) {
     50           thrownByExecutionThread = e;
     51         }
     52       });
     53       executionThread.start();
     54     }
     55   };
     56 
     57   public void testServiceStartStop() throws Exception {
     58     WaitOnRunService service = new WaitOnRunService();
     59     assertFalse(service.startUpCalled);
     60 
     61     service.start().get();
     62     assertTrue(service.startUpCalled);
     63     assertEquals(Service.State.RUNNING, service.state());
     64 
     65     enterRun.await(); // to avoid stopping the service until run() is invoked
     66 
     67     service.stop().get();
     68     assertTrue(service.shutDownCalled);
     69     assertEquals(Service.State.TERMINATED, service.state());
     70     executionThread.join();
     71     assertNull(thrownByExecutionThread);
     72   }
     73 
     74   public void testServiceStartStopIdempotence() throws Exception {
     75     WaitOnRunService service = new WaitOnRunService();
     76 
     77     service.start();
     78     service.start();
     79     service.startAndWait();
     80     assertEquals(Service.State.RUNNING, service.state());
     81     service.startAndWait();
     82     assertEquals(Service.State.RUNNING, service.state());
     83 
     84     enterRun.await(); // to avoid stopping the service until run() is invoked
     85 
     86     service.stop();
     87     service.stop();
     88     service.stopAndWait();
     89     assertEquals(Service.State.TERMINATED, service.state());
     90     service.stopAndWait();
     91     assertEquals(Service.State.TERMINATED, service.state());
     92 
     93     assertEquals(Service.State.RUNNING, service.start().get());
     94     assertEquals(Service.State.RUNNING, service.startAndWait());
     95     assertEquals(Service.State.TERMINATED, service.stop().get());
     96     assertEquals(Service.State.TERMINATED, service.stopAndWait());
     97 
     98     executionThread.join();
     99     assertNull(thrownByExecutionThread);
    100   }
    101 
    102   public void testServiceExitingOnItsOwn() throws Exception {
    103     WaitOnRunService service = new WaitOnRunService();
    104     service.expectedShutdownState = Service.State.RUNNING;
    105 
    106     service.start().get();
    107     assertTrue(service.startUpCalled);
    108     assertEquals(Service.State.RUNNING, service.state());
    109 
    110     exitRun.countDown(); // the service will exit voluntarily
    111     executionThread.join();
    112 
    113     assertTrue(service.shutDownCalled);
    114     assertEquals(Service.State.TERMINATED, service.state());
    115     assertNull(thrownByExecutionThread);
    116 
    117     service.stop().get(); // no-op
    118     assertEquals(Service.State.TERMINATED, service.state());
    119     assertTrue(service.shutDownCalled);
    120   }
    121 
    122   private class WaitOnRunService extends AbstractExecutionThreadService {
    123     private boolean startUpCalled = false;
    124     private boolean runCalled = false;
    125     private boolean shutDownCalled = false;
    126     private State expectedShutdownState = State.STOPPING;
    127 
    128     @Override protected void startUp() {
    129       assertFalse(startUpCalled);
    130       assertFalse(runCalled);
    131       assertFalse(shutDownCalled);
    132       startUpCalled = true;
    133       assertEquals(State.STARTING, state());
    134     }
    135 
    136     @Override protected void run() {
    137       assertTrue(startUpCalled);
    138       assertFalse(runCalled);
    139       assertFalse(shutDownCalled);
    140       runCalled = true;
    141       assertEquals(State.RUNNING, state());
    142 
    143       enterRun.countDown();
    144       try {
    145         exitRun.await();
    146       } catch (InterruptedException e) {
    147         throw Throwables.propagate(e);
    148       }
    149     }
    150 
    151     @Override protected void shutDown() {
    152       assertTrue(startUpCalled);
    153       assertTrue(runCalled);
    154       assertFalse(shutDownCalled);
    155       shutDownCalled = true;
    156       assertEquals(expectedShutdownState, state());
    157     }
    158 
    159     @Override protected void triggerShutdown() {
    160       exitRun.countDown();
    161     }
    162 
    163     @Override protected Executor executor() {
    164       return executor;
    165     }
    166   }
    167 
    168   public void testServiceThrowOnStartUp() throws Exception {
    169     ThrowOnStartUpService service = new ThrowOnStartUpService();
    170     assertFalse(service.startUpCalled);
    171 
    172     Future<Service.State> startupFuture = service.start();
    173     try {
    174       startupFuture.get();
    175       fail();
    176     } catch (ExecutionException expected) {
    177       assertEquals("kaboom!", expected.getCause().getMessage());
    178     }
    179     executionThread.join();
    180 
    181     assertTrue(service.startUpCalled);
    182     assertEquals(Service.State.FAILED, service.state());
    183     assertTrue(thrownByExecutionThread.getMessage().equals("kaboom!"));
    184   }
    185 
    186   private class ThrowOnStartUpService extends AbstractExecutionThreadService {
    187     private boolean startUpCalled = false;
    188 
    189     @Override protected void startUp() {
    190       startUpCalled = true;
    191       throw new UnsupportedOperationException("kaboom!");
    192     }
    193 
    194     @Override protected void run() {
    195       throw new AssertionError("run() should not be called");
    196     }
    197 
    198     @Override protected Executor executor() {
    199       return executor;
    200     }
    201   }
    202 
    203   public void testServiceThrowOnRun() throws Exception {
    204     ThrowOnRunService service = new ThrowOnRunService();
    205 
    206     service.start().get();
    207 
    208     executionThread.join();
    209     assertTrue(service.shutDownCalled);
    210     assertEquals(Service.State.FAILED, service.state());
    211     assertEquals("kaboom!", thrownByExecutionThread.getMessage());
    212   }
    213 
    214   public void testServiceThrowOnRunAndThenAgainOnShutDown() throws Exception {
    215     ThrowOnRunService service = new ThrowOnRunService();
    216     service.throwOnShutDown = true;
    217 
    218     service.start().get();
    219     executionThread.join();
    220 
    221     assertTrue(service.shutDownCalled);
    222     assertEquals(Service.State.FAILED, service.state());
    223     assertEquals("kaboom!", thrownByExecutionThread.getMessage());
    224   }
    225 
    226   private class ThrowOnRunService extends AbstractExecutionThreadService {
    227     private boolean shutDownCalled = false;
    228     private boolean throwOnShutDown = false;
    229 
    230     @Override protected void run() {
    231       throw new UnsupportedOperationException("kaboom!");
    232     }
    233 
    234     @Override protected void shutDown() {
    235       shutDownCalled = true;
    236       if (throwOnShutDown) {
    237         throw new UnsupportedOperationException("double kaboom!");
    238       }
    239     }
    240 
    241     @Override protected Executor executor() {
    242       return executor;
    243     }
    244   }
    245 
    246   public void testServiceThrowOnShutDown() throws Exception {
    247     ThrowOnShutDown service = new ThrowOnShutDown();
    248 
    249     service.start().get();
    250     assertEquals(Service.State.RUNNING, service.state());
    251 
    252     service.stop();
    253     enterRun.countDown();
    254     executionThread.join();
    255 
    256     assertEquals(Service.State.FAILED, service.state());
    257     assertEquals("kaboom!", thrownByExecutionThread.getMessage());
    258   }
    259 
    260   private class ThrowOnShutDown extends AbstractExecutionThreadService {
    261     @Override protected void run() {
    262       try {
    263         enterRun.await();
    264       } catch (InterruptedException e) {
    265         throw Throwables.propagate(e);
    266       }
    267     }
    268 
    269     @Override protected void shutDown() {
    270       throw new UnsupportedOperationException("kaboom!");
    271     }
    272 
    273     @Override protected Executor executor() {
    274       return executor;
    275     }
    276   }
    277 
    278   public void testServiceTimeoutOnStartUp() throws Exception {
    279     TimeoutOnStartUp service = new TimeoutOnStartUp();
    280 
    281     try {
    282       service.start().get(1, TimeUnit.MILLISECONDS);
    283       fail();
    284     } catch (TimeoutException e) {
    285       assertTrue(e.getMessage().contains(Service.State.STARTING.toString()));
    286     }
    287   }
    288 
    289   private class TimeoutOnStartUp extends AbstractExecutionThreadService {
    290     @Override protected Executor executor() {
    291       return new Executor() {
    292         @Override public void execute(Runnable command) {
    293         }
    294       };
    295     }
    296 
    297     @Override
    298     protected void run() throws Exception {
    299     }
    300   }
    301 
    302 }
    303