001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertTrue; 023 024import java.util.concurrent.TimeUnit; 025import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.CountingChore; 026import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.DoNothingChore; 027import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.FailInitialChore; 028import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SampleStopper; 029import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SleepingChore; 030import org.apache.hadoop.hbase.TestChoreService.ScheduledChoreSamples.SlowChore; 031import org.apache.hadoop.hbase.testclassification.SmallTests; 032import org.junit.ClassRule; 033import org.junit.Test; 034import org.junit.experimental.categories.Category; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038@Category(SmallTests.class) 039public class TestChoreService { 040 041 @ClassRule 042 public static final HBaseClassTestRule CLASS_RULE = 043 HBaseClassTestRule.forClass(TestChoreService.class); 044 045 public static final Logger log = LoggerFactory.getLogger(TestChoreService.class); 046 047 /** 048 * A few ScheduledChore samples that are useful for testing with ChoreService 049 */ 050 public static class ScheduledChoreSamples { 051 /** 052 * Straight forward stopper implementation that is used by default when one is not provided 053 */ 054 public static class SampleStopper implements Stoppable { 055 private boolean stopped = false; 056 057 @Override 058 public void stop(String why) { 059 stopped = true; 060 } 061 062 @Override 063 public boolean isStopped() { 064 return stopped; 065 } 066 } 067 068 /** 069 * Sleeps for longer than the scheduled period. This chore always misses its scheduled periodic 070 * executions 071 */ 072 public static class SlowChore extends ScheduledChore { 073 public SlowChore(String name, int period) { 074 this(name, new SampleStopper(), period); 075 } 076 077 public SlowChore(String name, Stoppable stopper, int period) { 078 super(name, stopper, period); 079 } 080 081 @Override 082 protected boolean initialChore() { 083 try { 084 Thread.sleep(getPeriod() * 2); 085 } catch (InterruptedException e) { 086 log.warn("", e); 087 } 088 return true; 089 } 090 091 @Override 092 protected void chore() { 093 try { 094 Thread.sleep(getPeriod() * 2); 095 } catch (InterruptedException e) { 096 log.warn("", e); 097 } 098 } 099 } 100 101 /** 102 * Lightweight ScheduledChore used primarily to fill the scheduling queue in tests 103 */ 104 public static class DoNothingChore extends ScheduledChore { 105 public DoNothingChore(String name, int period) { 106 super(name, new SampleStopper(), period); 107 } 108 109 public DoNothingChore(String name, Stoppable stopper, int period) { 110 super(name, stopper, period); 111 } 112 113 @Override 114 protected void chore() { 115 // DO NOTHING 116 } 117 118 } 119 120 public static class SleepingChore extends ScheduledChore { 121 private int sleepTime; 122 123 public SleepingChore(String name, int chorePeriod, int sleepTime) { 124 this(name, new SampleStopper(), chorePeriod, sleepTime); 125 } 126 127 public SleepingChore(String name, Stoppable stopper, int period, int sleepTime) { 128 super(name, stopper, period); 129 this.sleepTime = sleepTime; 130 } 131 132 @Override 133 protected boolean initialChore() { 134 try { 135 Thread.sleep(sleepTime); 136 } catch (InterruptedException e) { 137 log.warn("", e); 138 } 139 return true; 140 } 141 142 @Override 143 protected void chore() { 144 try { 145 Thread.sleep(sleepTime); 146 } catch (Exception e) { 147 log.warn("", e); 148 } 149 } 150 } 151 152 public static class CountingChore extends ScheduledChore { 153 private int countOfChoreCalls; 154 private boolean outputOnTicks = false; 155 156 public CountingChore(String name, int period) { 157 this(name, new SampleStopper(), period); 158 } 159 160 public CountingChore(String name, Stoppable stopper, int period) { 161 this(name, stopper, period, false); 162 } 163 164 public CountingChore(String name, Stoppable stopper, int period, 165 final boolean outputOnTicks) { 166 super(name, stopper, period); 167 this.countOfChoreCalls = 0; 168 this.outputOnTicks = outputOnTicks; 169 } 170 171 @Override 172 protected boolean initialChore() { 173 countOfChoreCalls++; 174 if (outputOnTicks) { 175 outputTickCount(); 176 } 177 return true; 178 } 179 180 @Override 181 protected void chore() { 182 countOfChoreCalls++; 183 if (outputOnTicks) { 184 outputTickCount(); 185 } 186 } 187 188 private void outputTickCount() { 189 log.info("Chore: " + getName() + ". Count of chore calls: " + countOfChoreCalls); 190 } 191 192 public int getCountOfChoreCalls() { 193 return countOfChoreCalls; 194 } 195 196 public boolean isOutputtingOnTicks() { 197 return outputOnTicks; 198 } 199 200 public void setOutputOnTicks(boolean o) { 201 outputOnTicks = o; 202 } 203 } 204 205 /** 206 * A Chore that will try to execute the initial chore a few times before succeeding. Once the 207 * initial chore is complete the chore cancels itself 208 */ 209 public static class FailInitialChore extends ScheduledChore { 210 private int numberOfFailures; 211 private int failureThreshold; 212 213 /** 214 * @param failThreshold Number of times the Chore fails when trying to execute initialChore 215 * before succeeding. 216 */ 217 public FailInitialChore(String name, int period, int failThreshold) { 218 this(name, new SampleStopper(), period, failThreshold); 219 } 220 221 public FailInitialChore(String name, Stoppable stopper, int period, int failThreshold) { 222 super(name, stopper, period); 223 numberOfFailures = 0; 224 failureThreshold = failThreshold; 225 } 226 227 @Override 228 protected boolean initialChore() { 229 if (numberOfFailures < failureThreshold) { 230 numberOfFailures++; 231 return false; 232 } else { 233 return true; 234 } 235 } 236 237 @Override 238 protected void chore() { 239 assertTrue(numberOfFailures == failureThreshold); 240 cancel(false); 241 } 242 243 } 244 } 245 246 @Test 247 public void testInitialChorePrecedence() throws InterruptedException { 248 ChoreService service = new ChoreService("testInitialChorePrecedence"); 249 250 final int period = 100; 251 final int failureThreshold = 5; 252 253 try { 254 ScheduledChore chore = new FailInitialChore("chore", period, failureThreshold); 255 service.scheduleChore(chore); 256 257 int loopCount = 0; 258 boolean brokeOutOfLoop = false; 259 260 while (!chore.isInitialChoreComplete() && chore.isScheduled()) { 261 Thread.sleep(failureThreshold * period); 262 loopCount++; 263 if (loopCount > 3) { 264 brokeOutOfLoop = true; 265 break; 266 } 267 } 268 269 assertFalse(brokeOutOfLoop); 270 } finally { 271 shutdownService(service); 272 } 273 } 274 275 @Test 276 public void testCancelChore() throws InterruptedException { 277 final int period = 100; 278 ScheduledChore chore1 = new DoNothingChore("chore1", period); 279 ChoreService service = new ChoreService("testCancelChore"); 280 try { 281 service.scheduleChore(chore1); 282 assertTrue(chore1.isScheduled()); 283 284 chore1.cancel(true); 285 assertFalse(chore1.isScheduled()); 286 assertTrue(service.getNumberOfScheduledChores() == 0); 287 } finally { 288 shutdownService(service); 289 } 290 } 291 292 @Test 293 public void testScheduledChoreConstruction() { 294 final String NAME = "chore"; 295 final int PERIOD = 100; 296 final long VALID_DELAY = 0; 297 final long INVALID_DELAY = -100; 298 final TimeUnit UNIT = TimeUnit.NANOSECONDS; 299 300 ScheduledChore chore1 = 301 new ScheduledChore(NAME, new SampleStopper(), PERIOD, VALID_DELAY, UNIT) { 302 @Override 303 protected void chore() { 304 // DO NOTHING 305 } 306 }; 307 308 assertEquals("Name construction failed", NAME, chore1.getName()); 309 assertEquals("Period construction failed", PERIOD, chore1.getPeriod()); 310 assertEquals("Initial Delay construction failed", VALID_DELAY, chore1.getInitialDelay()); 311 assertEquals("TimeUnit construction failed", UNIT, chore1.getTimeUnit()); 312 313 ScheduledChore invalidDelayChore = 314 new ScheduledChore(NAME, new SampleStopper(), PERIOD, INVALID_DELAY, UNIT) { 315 @Override 316 protected void chore() { 317 // DO NOTHING 318 } 319 }; 320 321 assertEquals("Initial Delay should be set to 0 when invalid", 0, 322 invalidDelayChore.getInitialDelay()); 323 } 324 325 @Test 326 public void testChoreServiceConstruction() throws InterruptedException { 327 final int corePoolSize = 10; 328 final int defaultCorePoolSize = ChoreService.MIN_CORE_POOL_SIZE; 329 330 ChoreService customInit = 331 new ChoreService("testChoreServiceConstruction_custom", corePoolSize, false); 332 try { 333 assertEquals(corePoolSize, customInit.getCorePoolSize()); 334 } finally { 335 shutdownService(customInit); 336 } 337 338 ChoreService defaultInit = new ChoreService("testChoreServiceConstruction_default"); 339 try { 340 assertEquals(defaultCorePoolSize, defaultInit.getCorePoolSize()); 341 } finally { 342 shutdownService(defaultInit); 343 } 344 345 ChoreService invalidInit = new ChoreService("testChoreServiceConstruction_invalid", -10, false); 346 try { 347 assertEquals(defaultCorePoolSize, invalidInit.getCorePoolSize()); 348 } finally { 349 shutdownService(invalidInit); 350 } 351 } 352 353 @Test 354 public void testFrequencyOfChores() throws InterruptedException { 355 final int period = 100; 356 // Small delta that acts as time buffer (allowing chores to complete if running slowly) 357 final int delta = period/5; 358 ChoreService service = new ChoreService("testFrequencyOfChores"); 359 CountingChore chore = new CountingChore("countingChore", period); 360 try { 361 service.scheduleChore(chore); 362 363 Thread.sleep(10 * period + delta); 364 assertEquals("10 periods have elapsed.", 11, chore.getCountOfChoreCalls()); 365 366 Thread.sleep(10 * period + delta); 367 assertEquals("20 periods have elapsed.", 21, chore.getCountOfChoreCalls()); 368 } finally { 369 shutdownService(service); 370 } 371 } 372 373 public void shutdownService(ChoreService service) throws InterruptedException { 374 service.shutdown(); 375 while (!service.isTerminated()) { 376 Thread.sleep(100); 377 } 378 } 379 380 @Test 381 public void testForceTrigger() throws InterruptedException { 382 final int period = 100; 383 final int delta = period/10; 384 ChoreService service = new ChoreService("testForceTrigger"); 385 final CountingChore chore = new CountingChore("countingChore", period); 386 try { 387 service.scheduleChore(chore); 388 Thread.sleep(10 * period + delta); 389 390 assertEquals("10 periods have elapsed.", 11, chore.getCountOfChoreCalls()); 391 392 // Force five runs of the chore to occur, sleeping between triggers to ensure the 393 // chore has time to run 394 chore.triggerNow(); 395 Thread.sleep(delta); 396 chore.triggerNow(); 397 Thread.sleep(delta); 398 chore.triggerNow(); 399 Thread.sleep(delta); 400 chore.triggerNow(); 401 Thread.sleep(delta); 402 chore.triggerNow(); 403 Thread.sleep(delta); 404 405 assertEquals("Trigger was called 5 times after 10 periods.", 16, 406 chore.getCountOfChoreCalls()); 407 408 Thread.sleep(10 * period + delta); 409 410 // Be loosey-goosey. It used to be '26' but it was a big flakey relying on timing. 411 assertTrue("Expected at least 16 invocations, instead got " + chore.getCountOfChoreCalls(), 412 chore.getCountOfChoreCalls() > 16); 413 } finally { 414 shutdownService(service); 415 } 416 } 417 418 @Test 419 public void testCorePoolIncrease() throws InterruptedException { 420 final int initialCorePoolSize = 3; 421 ChoreService service = new ChoreService("testCorePoolIncrease", initialCorePoolSize, false); 422 423 try { 424 assertEquals("Setting core pool size gave unexpected results.", initialCorePoolSize, 425 service.getCorePoolSize()); 426 427 final int slowChorePeriod = 100; 428 SlowChore slowChore1 = new SlowChore("slowChore1", slowChorePeriod); 429 SlowChore slowChore2 = new SlowChore("slowChore2", slowChorePeriod); 430 SlowChore slowChore3 = new SlowChore("slowChore3", slowChorePeriod); 431 432 service.scheduleChore(slowChore1); 433 service.scheduleChore(slowChore2); 434 service.scheduleChore(slowChore3); 435 436 Thread.sleep(slowChorePeriod * 10); 437 assertEquals("Should not create more pools than scheduled chores", 3, 438 service.getCorePoolSize()); 439 440 SlowChore slowChore4 = new SlowChore("slowChore4", slowChorePeriod); 441 service.scheduleChore(slowChore4); 442 443 Thread.sleep(slowChorePeriod * 10); 444 assertEquals("Chores are missing their start time. Should expand core pool size", 4, 445 service.getCorePoolSize()); 446 447 SlowChore slowChore5 = new SlowChore("slowChore5", slowChorePeriod); 448 service.scheduleChore(slowChore5); 449 450 Thread.sleep(slowChorePeriod * 10); 451 assertEquals("Chores are missing their start time. Should expand core pool size", 5, 452 service.getCorePoolSize()); 453 } finally { 454 shutdownService(service); 455 } 456 } 457 458 @Test 459 public void testCorePoolDecrease() throws InterruptedException { 460 final int initialCorePoolSize = 3; 461 ChoreService service = new ChoreService("testCorePoolDecrease", initialCorePoolSize, false); 462 final int chorePeriod = 100; 463 try { 464 // Slow chores always miss their start time and thus the core pool size should be at least as 465 // large as the number of running slow chores 466 SlowChore slowChore1 = new SlowChore("slowChore1", chorePeriod); 467 SlowChore slowChore2 = new SlowChore("slowChore2", chorePeriod); 468 SlowChore slowChore3 = new SlowChore("slowChore3", chorePeriod); 469 470 service.scheduleChore(slowChore1); 471 service.scheduleChore(slowChore2); 472 service.scheduleChore(slowChore3); 473 474 Thread.sleep(chorePeriod * 10); 475 assertEquals("Should not create more pools than scheduled chores", 476 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 477 478 SlowChore slowChore4 = new SlowChore("slowChore4", chorePeriod); 479 service.scheduleChore(slowChore4); 480 Thread.sleep(chorePeriod * 10); 481 assertEquals("Chores are missing their start time. Should expand core pool size", 482 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 483 484 SlowChore slowChore5 = new SlowChore("slowChore5", chorePeriod); 485 service.scheduleChore(slowChore5); 486 Thread.sleep(chorePeriod * 10); 487 assertEquals("Chores are missing their start time. Should expand core pool size", 488 service.getNumberOfScheduledChores(), service.getCorePoolSize()); 489 assertEquals(5, service.getNumberOfChoresMissingStartTime()); 490 491 // Now we begin to cancel the chores that caused an increase in the core thread pool of the 492 // ChoreService. These cancellations should cause a decrease in the core thread pool. 493 slowChore5.cancel(); 494 Thread.sleep(chorePeriod * 10); 495 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 496 service.getCorePoolSize()); 497 assertEquals(4, service.getNumberOfChoresMissingStartTime()); 498 499 slowChore4.cancel(); 500 Thread.sleep(chorePeriod * 10); 501 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 502 service.getCorePoolSize()); 503 assertEquals(3, service.getNumberOfChoresMissingStartTime()); 504 505 slowChore3.cancel(); 506 Thread.sleep(chorePeriod * 10); 507 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 508 service.getCorePoolSize()); 509 assertEquals(2, service.getNumberOfChoresMissingStartTime()); 510 511 slowChore2.cancel(); 512 Thread.sleep(chorePeriod * 10); 513 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 514 service.getCorePoolSize()); 515 assertEquals(1, service.getNumberOfChoresMissingStartTime()); 516 517 slowChore1.cancel(); 518 Thread.sleep(chorePeriod * 10); 519 assertEquals(Math.max(ChoreService.MIN_CORE_POOL_SIZE, service.getNumberOfScheduledChores()), 520 service.getCorePoolSize()); 521 assertEquals(0, service.getNumberOfChoresMissingStartTime()); 522 } finally { 523 shutdownService(service); 524 } 525 } 526 527 @Test 528 public void testNumberOfRunningChores() throws InterruptedException { 529 ChoreService service = new ChoreService("testNumberOfRunningChores"); 530 531 final int period = 100; 532 final int sleepTime = 5; 533 534 try { 535 DoNothingChore dn1 = new DoNothingChore("dn1", period); 536 DoNothingChore dn2 = new DoNothingChore("dn2", period); 537 DoNothingChore dn3 = new DoNothingChore("dn3", period); 538 DoNothingChore dn4 = new DoNothingChore("dn4", period); 539 DoNothingChore dn5 = new DoNothingChore("dn5", period); 540 541 service.scheduleChore(dn1); 542 service.scheduleChore(dn2); 543 service.scheduleChore(dn3); 544 service.scheduleChore(dn4); 545 service.scheduleChore(dn5); 546 547 Thread.sleep(sleepTime); 548 assertEquals("Scheduled chore mismatch", 5, service.getNumberOfScheduledChores()); 549 550 dn1.cancel(); 551 Thread.sleep(sleepTime); 552 assertEquals("Scheduled chore mismatch", 4, service.getNumberOfScheduledChores()); 553 554 dn2.cancel(); 555 dn3.cancel(); 556 dn4.cancel(); 557 Thread.sleep(sleepTime); 558 assertEquals("Scheduled chore mismatch", 1, service.getNumberOfScheduledChores()); 559 560 dn5.cancel(); 561 Thread.sleep(sleepTime); 562 assertEquals("Scheduled chore mismatch", 0, service.getNumberOfScheduledChores()); 563 } finally { 564 shutdownService(service); 565 } 566 } 567 568 @Test 569 public void testNumberOfChoresMissingStartTime() throws InterruptedException { 570 ChoreService service = new ChoreService("testNumberOfChoresMissingStartTime"); 571 572 final int period = 100; 573 final int sleepTime = 5 * period; 574 575 try { 576 // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores 577 // ALWAYS miss their start time since their execution takes longer than their period 578 SlowChore sc1 = new SlowChore("sc1", period); 579 SlowChore sc2 = new SlowChore("sc2", period); 580 SlowChore sc3 = new SlowChore("sc3", period); 581 SlowChore sc4 = new SlowChore("sc4", period); 582 SlowChore sc5 = new SlowChore("sc5", period); 583 584 service.scheduleChore(sc1); 585 service.scheduleChore(sc2); 586 service.scheduleChore(sc3); 587 service.scheduleChore(sc4); 588 service.scheduleChore(sc5); 589 590 Thread.sleep(sleepTime); 591 assertEquals(5, service.getNumberOfChoresMissingStartTime()); 592 593 sc1.cancel(); 594 Thread.sleep(sleepTime); 595 assertEquals(4, service.getNumberOfChoresMissingStartTime()); 596 597 sc2.cancel(); 598 sc3.cancel(); 599 sc4.cancel(); 600 Thread.sleep(sleepTime); 601 assertEquals(1, service.getNumberOfChoresMissingStartTime()); 602 603 sc5.cancel(); 604 Thread.sleep(sleepTime); 605 assertEquals(0, service.getNumberOfChoresMissingStartTime()); 606 } finally { 607 shutdownService(service); 608 } 609 } 610 611 /** 612 * ChoreServices should never have a core pool size that exceeds the number of chores that have 613 * been scheduled with the service. For example, if 4 ScheduledChores are scheduled with a 614 * ChoreService, the number of threads in the ChoreService's core pool should never exceed 4 615 */ 616 @Test 617 public void testMaximumChoreServiceThreads() throws InterruptedException { 618 ChoreService service = new ChoreService("testMaximumChoreServiceThreads"); 619 620 final int period = 100; 621 final int sleepTime = 5 * period; 622 623 try { 624 // Slow chores sleep for a length of time LONGER than their period. Thus, SlowChores 625 // ALWAYS miss their start time since their execution takes longer than their period. 626 // Chores that miss their start time will trigger the onChoreMissedStartTime callback 627 // in the ChoreService. This callback will try to increase the number of core pool 628 // threads. 629 SlowChore sc1 = new SlowChore("sc1", period); 630 SlowChore sc2 = new SlowChore("sc2", period); 631 SlowChore sc3 = new SlowChore("sc3", period); 632 SlowChore sc4 = new SlowChore("sc4", period); 633 SlowChore sc5 = new SlowChore("sc5", period); 634 635 service.scheduleChore(sc1); 636 service.scheduleChore(sc2); 637 service.scheduleChore(sc3); 638 service.scheduleChore(sc4); 639 service.scheduleChore(sc5); 640 641 Thread.sleep(sleepTime); 642 assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); 643 644 SlowChore sc6 = new SlowChore("sc6", period); 645 SlowChore sc7 = new SlowChore("sc7", period); 646 SlowChore sc8 = new SlowChore("sc8", period); 647 SlowChore sc9 = new SlowChore("sc9", period); 648 SlowChore sc10 = new SlowChore("sc10", period); 649 650 service.scheduleChore(sc6); 651 service.scheduleChore(sc7); 652 service.scheduleChore(sc8); 653 service.scheduleChore(sc9); 654 service.scheduleChore(sc10); 655 656 Thread.sleep(sleepTime); 657 assertTrue(service.getCorePoolSize() <= service.getNumberOfScheduledChores()); 658 } finally { 659 shutdownService(service); 660 } 661 } 662 663 @Test 664 public void testChangingChoreServices() throws InterruptedException { 665 final int period = 100; 666 final int sleepTime = 10; 667 ChoreService service1 = new ChoreService("testChangingChoreServices_1"); 668 ChoreService service2 = new ChoreService("testChangingChoreServices_2"); 669 ScheduledChore chore = new DoNothingChore("sample", period); 670 671 try { 672 assertFalse(chore.isScheduled()); 673 assertFalse(service1.isChoreScheduled(chore)); 674 assertFalse(service2.isChoreScheduled(chore)); 675 assertTrue(chore.getChoreServicer() == null); 676 677 service1.scheduleChore(chore); 678 Thread.sleep(sleepTime); 679 assertTrue(chore.isScheduled()); 680 assertTrue(service1.isChoreScheduled(chore)); 681 assertFalse(service2.isChoreScheduled(chore)); 682 assertFalse(chore.getChoreServicer() == null); 683 684 service2.scheduleChore(chore); 685 Thread.sleep(sleepTime); 686 assertTrue(chore.isScheduled()); 687 assertFalse(service1.isChoreScheduled(chore)); 688 assertTrue(service2.isChoreScheduled(chore)); 689 assertFalse(chore.getChoreServicer() == null); 690 691 chore.cancel(); 692 assertFalse(chore.isScheduled()); 693 assertFalse(service1.isChoreScheduled(chore)); 694 assertFalse(service2.isChoreScheduled(chore)); 695 assertTrue(chore.getChoreServicer() == null); 696 } finally { 697 shutdownService(service1); 698 shutdownService(service2); 699 } 700 } 701 702 @Test 703 public void testStopperForScheduledChores() throws InterruptedException { 704 ChoreService service = new ChoreService("testStopperForScheduledChores"); 705 Stoppable stopperForGroup1 = new SampleStopper(); 706 Stoppable stopperForGroup2 = new SampleStopper(); 707 final int period = 100; 708 final int delta = period/10; 709 710 try { 711 ScheduledChore chore1_group1 = new DoNothingChore("c1g1", stopperForGroup1, period); 712 ScheduledChore chore2_group1 = new DoNothingChore("c2g1", stopperForGroup1, period); 713 ScheduledChore chore3_group1 = new DoNothingChore("c3g1", stopperForGroup1, period); 714 715 ScheduledChore chore1_group2 = new DoNothingChore("c1g2", stopperForGroup2, period); 716 ScheduledChore chore2_group2 = new DoNothingChore("c2g2", stopperForGroup2, period); 717 ScheduledChore chore3_group2 = new DoNothingChore("c3g2", stopperForGroup2, period); 718 719 service.scheduleChore(chore1_group1); 720 service.scheduleChore(chore2_group1); 721 service.scheduleChore(chore3_group1); 722 service.scheduleChore(chore1_group2); 723 service.scheduleChore(chore2_group2); 724 service.scheduleChore(chore3_group2); 725 726 Thread.sleep(delta); 727 Thread.sleep(10 * period); 728 assertTrue(chore1_group1.isScheduled()); 729 assertTrue(chore2_group1.isScheduled()); 730 assertTrue(chore3_group1.isScheduled()); 731 assertTrue(chore1_group2.isScheduled()); 732 assertTrue(chore2_group2.isScheduled()); 733 assertTrue(chore3_group2.isScheduled()); 734 735 stopperForGroup1.stop("test stopping group 1"); 736 Thread.sleep(period); 737 assertFalse(chore1_group1.isScheduled()); 738 assertFalse(chore2_group1.isScheduled()); 739 assertFalse(chore3_group1.isScheduled()); 740 assertTrue(chore1_group2.isScheduled()); 741 assertTrue(chore2_group2.isScheduled()); 742 assertTrue(chore3_group2.isScheduled()); 743 744 stopperForGroup2.stop("test stopping group 2"); 745 Thread.sleep(period); 746 assertFalse(chore1_group1.isScheduled()); 747 assertFalse(chore2_group1.isScheduled()); 748 assertFalse(chore3_group1.isScheduled()); 749 assertFalse(chore1_group2.isScheduled()); 750 assertFalse(chore2_group2.isScheduled()); 751 assertFalse(chore3_group2.isScheduled()); 752 } finally { 753 shutdownService(service); 754 } 755 } 756 757 @Test 758 public void testShutdownCancelsScheduledChores() throws InterruptedException { 759 final int period = 100; 760 ChoreService service = new ChoreService("testShutdownCancelsScheduledChores"); 761 ScheduledChore successChore1 = new DoNothingChore("sc1", period); 762 ScheduledChore successChore2 = new DoNothingChore("sc2", period); 763 ScheduledChore successChore3 = new DoNothingChore("sc3", period); 764 765 try { 766 assertTrue(service.scheduleChore(successChore1)); 767 assertTrue(successChore1.isScheduled()); 768 assertTrue(service.scheduleChore(successChore2)); 769 assertTrue(successChore2.isScheduled()); 770 assertTrue(service.scheduleChore(successChore3)); 771 assertTrue(successChore3.isScheduled()); 772 } finally { 773 shutdownService(service); 774 } 775 776 assertFalse(successChore1.isScheduled()); 777 assertFalse(successChore2.isScheduled()); 778 assertFalse(successChore3.isScheduled()); 779 } 780 781 @Test 782 public void testShutdownWorksWhileChoresAreExecuting() throws InterruptedException { 783 final int period = 100; 784 final int sleep = 5 * period; 785 ChoreService service = new ChoreService("testShutdownWorksWhileChoresAreExecuting"); 786 ScheduledChore slowChore1 = new SleepingChore("sc1", period, sleep); 787 ScheduledChore slowChore2 = new SleepingChore("sc2", period, sleep); 788 ScheduledChore slowChore3 = new SleepingChore("sc3", period, sleep); 789 try { 790 assertTrue(service.scheduleChore(slowChore1)); 791 assertTrue(service.scheduleChore(slowChore2)); 792 assertTrue(service.scheduleChore(slowChore3)); 793 794 Thread.sleep(sleep / 2); 795 shutdownService(service); 796 797 assertFalse(slowChore1.isScheduled()); 798 assertFalse(slowChore2.isScheduled()); 799 assertFalse(slowChore3.isScheduled()); 800 assertTrue(service.isShutdown()); 801 802 Thread.sleep(5); 803 assertTrue(service.isTerminated()); 804 } finally { 805 shutdownService(service); 806 } 807 } 808 809 @Test 810 public void testShutdownRejectsNewSchedules() throws InterruptedException { 811 final int period = 100; 812 ChoreService service = new ChoreService("testShutdownRejectsNewSchedules"); 813 ScheduledChore successChore1 = new DoNothingChore("sc1", period); 814 ScheduledChore successChore2 = new DoNothingChore("sc2", period); 815 ScheduledChore successChore3 = new DoNothingChore("sc3", period); 816 ScheduledChore failChore1 = new DoNothingChore("fc1", period); 817 ScheduledChore failChore2 = new DoNothingChore("fc2", period); 818 ScheduledChore failChore3 = new DoNothingChore("fc3", period); 819 820 try { 821 assertTrue(service.scheduleChore(successChore1)); 822 assertTrue(successChore1.isScheduled()); 823 assertTrue(service.scheduleChore(successChore2)); 824 assertTrue(successChore2.isScheduled()); 825 assertTrue(service.scheduleChore(successChore3)); 826 assertTrue(successChore3.isScheduled()); 827 } finally { 828 shutdownService(service); 829 } 830 831 assertFalse(service.scheduleChore(failChore1)); 832 assertFalse(failChore1.isScheduled()); 833 assertFalse(service.scheduleChore(failChore2)); 834 assertFalse(failChore2.isScheduled()); 835 assertFalse(service.scheduleChore(failChore3)); 836 assertFalse(failChore3.isScheduled()); 837 } 838}