/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.concurrent.TimeUnit; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.CountingChore; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.DoNothingChore; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.FailInitialChore; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SampleStopper; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SleepingChore; import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SlowChore; import org.apache.hadoop.hbase.testclassification.SmallTests; import org.junit.Test; import org.junit.experimental.categories.Category; @Category(SmallTests.class) public class TestChoreService { /** * A few ScheduledChore samples that are useful for testing with ChoreService */ public static class ScheduledChoreSamples { /** * Straight forward stopper implementation that is used by default when one is not provided */ public static class SampleStopper implements Stoppable { private boolean stopped = false; @Override public void stop(String why) { stopped = true; } @Override public boolean isStopped() { return stopped; } } /** * Sleeps for longer than the scheduled period. This chore always misses its scheduled periodic * executions */ public static class SlowChore extends ScheduledChore { public SlowChore(String name, int period) { this(name, new SampleStopper(), period); } public SlowChore(String name, Stoppable stopper, int period) { super(name, stopper, period); } @Override protected boolean initialChore() { try { Thread.sleep(getPeriod() * 2); } catch (InterruptedException e) { e.printStackTrace(); } return true; } @Override protected void chore() { try { Thread.sleep(getPeriod() * 2); } catch (InterruptedException e) { //e.printStackTrace(); } } } /** * Lightweight ScheduledChore used primarily to fill the scheduling queue in tests */ public static class DoNothingChore extends ScheduledChore { public DoNothingChore(String name, int period) { super(name, new SampleStopper(), period); } public DoNothingChore(String name, Stoppable stopper, int period) { super(name, stopper, period); } @Override protected void chore() { // DO NOTHING } } public static class SleepingChore extends ScheduledChore { private int sleepTime; public SleepingChore(String name, int chorePeriod, int sleepTime) { this(name, new SampleStopper(), chorePeriod, sleepTime); } public SleepingChore(String name, Stoppable stopper, int period, int sleepTime) { super(name, stopper, period); this.sleepTime = sleepTime; } @Override protected boolean initialChore() { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } return true; } @Override protected void chore() { try { Thread.sleep(sleepTime); } catch (Exception e) { System.err.println(e.getStackTrace()); } } } public static class CountingChore extends ScheduledChore { private int countOfChoreCalls; private boolean outputOnTicks = false; public CountingChore(String name, int period) { this(name, new SampleStopper(), period); } public CountingChore(String name, Stoppable stopper, int period) { this(name, stopper, period, false); } public CountingChore(String name, Stoppable stopper, int period, final boolean outputOnTicks) { super(name, stopper, period); this.countOfChoreCalls = 0; this.outputOnTicks = outputOnTicks; } @Override protected boolean initialChore() { countOfChoreCalls++; if (outputOnTicks) outputTickCount(); return true; } @Override protected void chore() { countOfChoreCalls++; if (outputOnTicks) outputTickCount(); } private void outputTickCount() { System.out.println("Chore: " + getName() + ". Count of chore calls: " + countOfChoreCalls); } public int getCountOfChoreCalls() { return countOfChoreCalls; } public boolean isOutputtingOnTicks() { return outputOnTicks; } public void setOutputOnTicks(boolean o) { outputOnTicks = o; } } /** * A Chore that will try to execute the initial chore a few times before succeeding. Once the * initial chore is complete the chore cancels itself */ public static class FailInitialChore extends ScheduledChore { private int numberOfFailures; private int failureThreshold; /** * @param failThreshold Number of times the Chore fails when trying to execute initialChore * before succeeding. */ public FailInitialChore(String name, int period, int failThreshold) { this(name, new SampleStopper(), period, failThreshold); } public FailInitialChore(String name, Stoppable stopper, int period, int failThreshold) { super(name, stopper, period); numberOfFailures = 0; failureThreshold = failThreshold; } @Override protected boolean initialChore() { if (numberOfFailures < failureThreshold) { numberOfFailures++; return false; } else { return true; } } @Override protected void chore() { assertTrue(numberOfFailures == failureThreshold); cancel(false); } } } @Test (timeout=20000) public void testInitialChorePrecedence() throws InterruptedException { ChoreService service = new ChoreService("testInitialChorePrecedence"); final int period = 100; final int failureThreshold = 5; try { ScheduledChore chore = new FailInitialChore("chore", period, failureThreshold); service.scheduleChore(chore); int loopCount = 0; boolean brokeOutOfLoop = false; while (!chore.isInitialChoreComplete() && chore.isScheduled()) { Thread.sleep(failureThreshold * period); loopCount++; if (loopCount > 3) { brokeOutOfLoop = true; break; } } assertFalse(brokeOutOfLoop); } finally { shutdownService(service); } } @Test (timeout=20000) public void testCancelChore() throws InterruptedException { final int period = 100; ScheduledChore chore1 = new DoNothingChore("chore1", period); ChoreService service = new ChoreService("testCancelChore"); try { service.scheduleChore(chore1); assertTrue(chore1.isScheduled()); chore1.cancel(true); assertFalse(chore1.isScheduled()); assertTrue(service.getNumberOfScheduledChores() == 0); } finally { shutdownService(service); } } @Test (timeout=20000) public void testScheduledChoreConstruction() { final String NAME = "chore"; final int PERIOD = 100; final long VALID_DELAY = 0; final long INVALID_DELAY = -100; final TimeUnit UNIT = TimeUnit.NANOSECONDS; ScheduledChore chore1 = new ScheduledChore(NAME, new SampleStopper(), PERIOD, VALID_DELAY, UNIT) { @Override protected void chore() { // DO NOTHING } }; assertEquals("Name construction failed", chore1.getName(), NAME); assertEquals("Period construction failed", chore1.getPeriod(), PERIOD); assertEquals("Initial Delay construction failed", chore1.getInitialDelay(), VALID_DELAY); assertEquals("TimeUnit construction failed", chore1.getTimeUnit(), UNIT); ScheduledChore invalidDelayChore = new ScheduledChore(NAME, new SampleStopper(), PERIOD, INVALID_DELAY, UNIT) { @Override protected void chore() { // DO NOTHING } }; assertEquals("Initial Delay should be set to 0 when invalid", 0, invalidDelayChore.getInitialDelay()); } @Test (timeout=20000) public void testChoreServiceConstruction() throws InterruptedException { final int corePoolSize = 10; final int defaultCorePoolSize = ChoreService.MIN_CORE_POOL_SIZE; ChoreService customInit = new ChoreService("testChoreServiceConstruction_custom", corePoolSize, false); try { assertEquals(corePoolSize, customInit.getCorePoolSize()); } finally { shutdownService(customInit); } ChoreService defaultInit = new ChoreService("testChoreServiceConstruction_default"); try { assertEquals(defaultCorePoolSize, defaultInit.getCorePoolSize()); } finally { shutdownService(defaultInit); } ChoreService invalidInit = new ChoreService("testChoreServiceConstruction_invalid", -10, false); try { assertEquals(defaultCorePoolSize, invalidInit.getCorePoolSize()); } finally { shutdownService(invalidInit); } } @Test (timeout=20000) public void testFrequencyOfChores() throws InterruptedException { final int period = 100; // Small delta that acts as time buffer (allowing chores to complete if running slowly) final int delta = 5; ChoreService service = new ChoreService("testFrequencyOfChores"); CountingChore chore = new CountingChore("countingChore", period); try { service.scheduleChore(chore); Thread.sleep(10 * period + delta); assertTrue(chore.getCountOfChoreCalls() == 11); Thread.sleep(10 * period); assertTrue(chore.getCountOfChoreCalls() == 21); } finally { shutdownService(service); } } public void shutdownService(ChoreService service) throws InterruptedException { service.shutdown(); while (!service.isTerminated()) { Thread.sleep(100); } } @Test (timeout=20000) public void testForceTrigger() throws InterruptedException { final int period = 100; final int delta = 5; ChoreService service = new ChoreService("testForceTrigger"); final CountingChore chore = new CountingChore("countingChore", period); try { service.scheduleChore(chore); Thread.sleep(10 * period + delta); assertTrue(chore.getCountOfChoreCalls() == 11); // Force five runs of the chore to occur, sleeping between triggers to ensure the // chore has time to run chore.triggerNow(); Thread.sleep(delta); chore.triggerNow(); Thread.sleep(delta); chore.triggerNow(); Thread.sleep(delta); chore.triggerNow(); Thread.sleep(delta); chore.triggerNow(); Thread.sleep(delta); assertTrue("" + chore.getCountOfChoreCalls(), chore.getCountOfChoreCalls() == 16); Thread.sleep(10 * period + delta); // Be loosey-goosey. It used to be '26' but it was a big flakey relying on timing. assertTrue("" + chore.getCountOfChoreCalls(), chore.getCountOfChoreCalls() > 16); } finally { shutdownService(service); } } @Test (timeout=20000) public void testCorePoolIncrease() throws InterruptedException { final int initialCorePoolSize = 3; ChoreService service = new ChoreService("testCorePoolIncrease", initialCorePoolSize, false); try { assertEquals("Should have a core pool of size: " + initialCorePoolSize, initialCorePoolSize, service.getCorePoolSize()); final int slowChorePeriod = 100; SlowChore slowChore1 = new SlowChore("slowChore1", slowChorePeriod); SlowChore slowChore2 = new SlowChore("slowChore2", slowChorePeriod); SlowChore slowChore3 = new SlowChore("slowChore3", slowChorePeriod); service.scheduleChore(slowChore1); service.scheduleChore(slowChore2); service.scheduleChore(slowChore3); Thread.sleep(slowChorePeriod * 10); assertEquals("Should not create more pools than scheduled chores", 3, service.getCorePoolSize()); SlowChore slowChore4 = new SlowChore("slowChore4", slowChorePeriod); service.scheduleChore(slowChore4); Thread.sleep(slowChorePeriod * 10); assertEquals("Chores are missing their start time. Should expand core pool size", 4, service.getCorePoolSize()); SlowChore slowChore5 = new SlowChore("slowChore5", slowChorePeriod); service.scheduleChore(slowChore5); Thread.sleep(slowChorePeriod * 10); assertEquals("Chores are missing their start time. Should expand core pool size", 5, service.getCorePoolSize()); } finally { shutdownService(service); } } @Test(timeout = 30000) public void testCorePoolDecrease() throws InterruptedException { final int initialCorePoolSize = 3; ChoreService service = new ChoreService("testCorePoolDecrease", initialCorePoolSize, false); final int chorePeriod = 100; try { // Slow chores always miss their start time and thus the core pool size should be at least as // large as the number of running slow chores SlowChore slowChore1 = new SlowChore("slowChore1", chorePeriod); SlowChore slowChore2 = new SlowChore("slowChore2", chorePeriod); SlowChore slowChore3 = new SlowChore("slowChore3", chorePeriod); service.scheduleChore(slowChore1); service.scheduleChore(slowChore2); service.scheduleChore(slowChore3); Thread.sleep(chorePeriod * 10); assertEquals("Should not create more pools than scheduled chores", service.getNumberOfScheduledChores(), service.getCorePoolSize()); SlowChore slowChore4 = new SlowChore("slowChore4", chorePeriod); service.scheduleChore(slowChore4); Thread.sleep(chorePeriod * 10); assertEquals("Chores are missing their start time. Should expand core pool size", service.getNumberOfScheduledChores(), service.getCorePoolSize()); SlowChore slowChore5 = new SlowChore("slowChore5", chorePeriod); service.scheduleChore(slowChore5); Thread.sleep(chorePeriod * 10); assertEquals("Chores are missing their start time. Should expand core pool size", service.getNumberOfScheduledChores(), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 5); // Now we begin to cancel the chores that caused an increase in the core thread pool of the // ChoreService. These cancellations should cause a decrease in the core thread pool. slowChore5.cancel(); Thread.sleep(chorePeriod * 10); assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 4); slowChore4.cancel(); Thread.sleep(chorePeriod * 10); assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 3); slowChore3.cancel(); Thread.sleep(chorePeriod * 10); assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 2); slowChore2.cancel(); Thread.sleep(chorePeriod * 10); assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 1); slowChore1.cancel(); Thread.sleep(chorePeriod * 10); assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), service.getCorePoolSize()); assertEquals(service.getNumberOfChoresMissingStartTime(), 0); } finally { shutdownService(service); } } @Test (timeout=20000) public void testNumberOfRunningChores() throws InterruptedException { ChoreService service = new ChoreService("testNumberOfRunningChores"); final int period = 100; final int sleepTime = 5; try { DoNothingChore dn1 = new DoNothingChore("dn1", period); DoNothingChore dn2 = new DoNothingChore("dn2", period); DoNothingChore dn3 = new DoNothingChore("dn3", period); DoNothingChore dn4 = new DoNothingChore("dn4", period); DoNothingChore dn5 = new DoNothingChore("dn5", period); service.scheduleChore(dn1); service.scheduleChore(dn2); service.scheduleChore(dn3); service.scheduleChore(dn4); service.scheduleChore(dn5); Thread.sleep(sleepTime); assertEquals("Scheduled chore mismatch", 5, service.getNumberOfScheduledChores()); dn1.cancel(); Thread.sleep(sleepTime); assertEquals("Scheduled chore mismatch", 4, service.getNumberOfScheduledChores()); dn2.cancel(); dn3.cancel(); dn4.cancel(); Thread.sleep(sleepTime); assertEquals("Scheduled chore mismatch", 1, service.getNumberOfScheduledChores()); dn5.cancel(); Thread.sleep(sleepTime); assertEquals("Scheduled chore mismatch", 0, service.getNumberOfScheduledChores()); } finally { shutdownService(service); } } @Test (timeout=20000) public void testNumberOfChoresMissingStartTime() throws InterruptedException { ChoreService service = new ChoreService("testNumberOfChoresMissingStartTime"); final int period = 100; final int sleepTime = 5 * period; try { // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores // ALWAYS miss their start time since their execution takes longer than their period SlowChore sc1 = new SlowChore("sc1", period); SlowChore sc2 = new SlowChore("sc2", period); SlowChore sc3 = new SlowChore("sc3", period); SlowChore sc4 = new SlowChore("sc4", period); SlowChore sc5 = new SlowChore("sc5", period); service.scheduleChore(sc1); service.scheduleChore(sc2); service.scheduleChore(sc3); service.scheduleChore(sc4); service.scheduleChore(sc5); Thread.sleep(sleepTime); assertEquals(5, service.getNumberOfChoresMissingStartTime()); sc1.cancel(); Thread.sleep(sleepTime); assertEquals(4, service.getNumberOfChoresMissingStartTime()); sc2.cancel(); sc3.cancel(); sc4.cancel(); Thread.sleep(sleepTime); assertEquals(1, service.getNumberOfChoresMissingStartTime()); sc5.cancel(); Thread.sleep(sleepTime); assertEquals(0, service.getNumberOfChoresMissingStartTime()); } finally { shutdownService(service); } } /** * ChoreServices should never have a core pool size that exceeds the number of chores that have * been scheduled with the service. For example, if 4 ScheduledChores are scheduled with a * ChoreService, the number of threads in the ChoreService's core pool should never exceed 4 */ @Test (timeout=20000) public void testMaximumChoreServiceThreads() throws InterruptedException { ChoreService service = new ChoreService("testMaximumChoreServiceThreads"); final int period = 100; final int sleepTime = 5 * period; try { // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores // ALWAYS miss their start time since their execution takes longer than their period. // Chores that miss their start time will trigger the onChoreMissedStartTime callback // in the ChoreService. This callback will try to increase the number of core pool // threads. SlowChore sc1 = new SlowChore("sc1", period); SlowChore sc2 = new SlowChore("sc2", period); SlowChore sc3 = new SlowChore("sc3", period); SlowChore sc4 = new SlowChore("sc4", period); SlowChore sc5 = new SlowChore("sc5", period); service.scheduleChore(sc1); service.scheduleChore(sc2); service.scheduleChore(sc3); service.scheduleChore(sc4); service.scheduleChore(sc5); Thread.sleep(sleepTime); assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); SlowChore sc6 = new SlowChore("sc6", period); SlowChore sc7 = new SlowChore("sc7", period); SlowChore sc8 = new SlowChore("sc8", period); SlowChore sc9 = new SlowChore("sc9", period); SlowChore sc10 = new SlowChore("sc10", period); service.scheduleChore(sc6); service.scheduleChore(sc7); service.scheduleChore(sc8); service.scheduleChore(sc9); service.scheduleChore(sc10); Thread.sleep(sleepTime); assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); } finally { shutdownService(service); } } @Test (timeout=20000) public void testChangingChoreServices() throws InterruptedException { final int period = 100; final int sleepTime = 10; ChoreService service1 = new ChoreService("testChangingChoreServices_1"); ChoreService service2 = new ChoreService("testChangingChoreServices_2"); ScheduledChore chore = new DoNothingChore("sample", period); try { assertFalse(chore.isScheduled()); assertFalse(service1.isChoreScheduled(chore)); assertFalse(service2.isChoreScheduled(chore)); assertTrue(chore.getChoreServicer() == null); service1.scheduleChore(chore); Thread.sleep(sleepTime); assertTrue(chore.isScheduled()); assertTrue(service1.isChoreScheduled(chore)); assertFalse(service2.isChoreScheduled(chore)); assertFalse(chore.getChoreServicer() == null); service2.scheduleChore(chore); Thread.sleep(sleepTime); assertTrue(chore.isScheduled()); assertFalse(service1.isChoreScheduled(chore)); assertTrue(service2.isChoreScheduled(chore)); assertFalse(chore.getChoreServicer() == null); chore.cancel(); assertFalse(chore.isScheduled()); assertFalse(service1.isChoreScheduled(chore)); assertFalse(service2.isChoreScheduled(chore)); assertTrue(chore.getChoreServicer() == null); } finally { shutdownService(service1); shutdownService(service2); } } @Test (timeout=20000) public void testTriggerNowFailsWhenNotScheduled() throws InterruptedException { final int period = 100; // Small sleep time buffer to allow CountingChore to complete final int sleep = 5; ChoreService service = new ChoreService("testTriggerNowFailsWhenNotScheduled"); CountingChore chore = new CountingChore("dn", period); try { assertFalse(chore.triggerNow()); assertTrue(chore.getCountOfChoreCalls() == 0); service.scheduleChore(chore); Thread.sleep(sleep); assertEquals(1, chore.getCountOfChoreCalls()); Thread.sleep(period); assertEquals(2, chore.getCountOfChoreCalls()); assertTrue(chore.triggerNow()); Thread.sleep(sleep); assertTrue(chore.triggerNow()); Thread.sleep(sleep); assertTrue(chore.triggerNow()); Thread.sleep(sleep); assertEquals(5, chore.getCountOfChoreCalls()); } finally { shutdownService(service); } } @Test (timeout=20000) public void testStopperForScheduledChores() throws InterruptedException { ChoreService service = new ChoreService("testStopperForScheduledChores"); Stoppable stopperForGroup1 = new SampleStopper(); Stoppable stopperForGroup2 = new SampleStopper(); final int period = 100; final int delta = 10; try { ScheduledChore chore1_group1 = new DoNothingChore("c1g1", stopperForGroup1, period); ScheduledChore chore2_group1 = new DoNothingChore("c2g1", stopperForGroup1, period); ScheduledChore chore3_group1 = new DoNothingChore("c3g1", stopperForGroup1, period); ScheduledChore chore1_group2 = new DoNothingChore("c1g2", stopperForGroup2, period); ScheduledChore chore2_group2 = new DoNothingChore("c2g2", stopperForGroup2, period); ScheduledChore chore3_group2 = new DoNothingChore("c3g2", stopperForGroup2, period); service.scheduleChore(chore1_group1); service.scheduleChore(chore2_group1); service.scheduleChore(chore3_group1); service.scheduleChore(chore1_group2); service.scheduleChore(chore2_group2); service.scheduleChore(chore3_group2); Thread.sleep(delta); Thread.sleep(10 * period); assertTrue(chore1_group1.isScheduled()); assertTrue(chore2_group1.isScheduled()); assertTrue(chore3_group1.isScheduled()); assertTrue(chore1_group2.isScheduled()); assertTrue(chore2_group2.isScheduled()); assertTrue(chore3_group2.isScheduled()); stopperForGroup1.stop("test stopping group 1"); Thread.sleep(period); assertFalse(chore1_group1.isScheduled()); assertFalse(chore2_group1.isScheduled()); assertFalse(chore3_group1.isScheduled()); assertTrue(chore1_group2.isScheduled()); assertTrue(chore2_group2.isScheduled()); assertTrue(chore3_group2.isScheduled()); stopperForGroup2.stop("test stopping group 2"); Thread.sleep(period); assertFalse(chore1_group1.isScheduled()); assertFalse(chore2_group1.isScheduled()); assertFalse(chore3_group1.isScheduled()); assertFalse(chore1_group2.isScheduled()); assertFalse(chore2_group2.isScheduled()); assertFalse(chore3_group2.isScheduled()); } finally { shutdownService(service); } } @Test (timeout=20000) public void testShutdownCancelsScheduledChores() throws InterruptedException { final int period = 100; ChoreService service = new ChoreService("testShutdownCancelsScheduledChores"); ScheduledChore successChore1 = new DoNothingChore("sc1", period); ScheduledChore successChore2 = new DoNothingChore("sc2", period); ScheduledChore successChore3 = new DoNothingChore("sc3", period); try { assertTrue(service.scheduleChore(successChore1)); assertTrue(successChore1.isScheduled()); assertTrue(service.scheduleChore(successChore2)); assertTrue(successChore2.isScheduled()); assertTrue(service.scheduleChore(successChore3)); assertTrue(successChore3.isScheduled()); } finally { shutdownService(service); } assertFalse(successChore1.isScheduled()); assertFalse(successChore2.isScheduled()); assertFalse(successChore3.isScheduled()); } @Test (timeout=20000) public void testShutdownWorksWhileChoresAreExecuting() throws InterruptedException { final int period = 100; final int sleep = 5 * period; ChoreService service = new ChoreService("testShutdownWorksWhileChoresAreExecuting"); ScheduledChore slowChore1 = new SleepingChore("sc1", period, sleep); ScheduledChore slowChore2 = new SleepingChore("sc2", period, sleep); ScheduledChore slowChore3 = new SleepingChore("sc3", period, sleep); try { assertTrue(service.scheduleChore(slowChore1)); assertTrue(service.scheduleChore(slowChore2)); assertTrue(service.scheduleChore(slowChore3)); Thread.sleep(sleep / 2); shutdownService(service); assertFalse(slowChore1.isScheduled()); assertFalse(slowChore2.isScheduled()); assertFalse(slowChore3.isScheduled()); assertTrue(service.isShutdown()); Thread.sleep(5); assertTrue(service.isTerminated()); } finally { shutdownService(service); } } @Test (timeout=20000) public void testShutdownRejectsNewSchedules() throws InterruptedException { final int period = 100; ChoreService service = new ChoreService("testShutdownRejectsNewSchedules"); ScheduledChore successChore1 = new DoNothingChore("sc1", period); ScheduledChore successChore2 = new DoNothingChore("sc2", period); ScheduledChore successChore3 = new DoNothingChore("sc3", period); ScheduledChore failChore1 = new DoNothingChore("fc1", period); ScheduledChore failChore2 = new DoNothingChore("fc2", period); ScheduledChore failChore3 = new DoNothingChore("fc3", period); try { assertTrue(service.scheduleChore(successChore1)); assertTrue(successChore1.isScheduled()); assertTrue(service.scheduleChore(successChore2)); assertTrue(successChore2.isScheduled()); assertTrue(service.scheduleChore(successChore3)); assertTrue(successChore3.isScheduled()); } finally { shutdownService(service); } assertFalse(service.scheduleChore(failChore1)); assertFalse(failChore1.isScheduled()); assertFalse(service.scheduleChore(failChore2)); assertFalse(failChore2.isScheduled()); assertFalse(service.scheduleChore(failChore3)); assertFalse(failChore3.isScheduled()); } }