1 /* 2 * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.concurrent; 27 28 import javafx.concurrent.mocks.EpicFailTask; 29 import javafx.concurrent.mocks.SimpleTask; 30 import javafx.event.EventHandler; 31 import javafx.util.Callback; 32 import javafx.util.Duration; 33 import java.util.TimerTask; 34 import java.util.concurrent.atomic.AtomicBoolean; 35 import java.util.concurrent.atomic.AtomicInteger; 36 import org.junit.Test; 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertFalse; 39 import static org.junit.Assert.assertNull; 40 import static org.junit.Assert.assertTrue; 41 42 /** 43 * Tests for the ScheduledService. 44 */ 45 public class ScheduledServiceTest extends ServiceTestBase { 46 private static final Callback<Void, AbstractTask> EPIC_FAIL_FACTORY = param -> new EpicFailTask(); 47 48 /** 49 * The service that we're going to test. Because a ScheduledService 50 * in its default configuration will run forever and because for the 51 * sake of testing we've essentially caused ScheduledServiceTest to 52 * run as though it is single threaded, we have to make sure that each 53 * individual iteration is paused and doesn't occur without an explicit 54 * call. So in the test code you can call start(), and then read the wall 55 * clock time, and then call iterate() to cause the scheduled service to 56 * start the next iteration all without affecting the "wall clock" time 57 * inappropriately with the test code. In this way we can test with very 58 * fine tolerances in a consistent manner. 59 */ 60 private ScheduledServiceMock s; 61 62 /** 63 * If specified by the test BEFORE the service is started, then this 64 * task will be used by the service. Defaults to SimpleTask if null. 65 */ 66 private Callback<Void,AbstractTask> taskFactory = null; 67 68 /** 69 * A fake "wall clock" time, to keep track of how much 70 * time was spent executing a task, and how much time was 71 * spent in the delay. We fake out the delay by overriding the 72 * "schedule" method in ScheduledServiceMock, and we fake out 73 * the task execution time by using a custom task which, when 74 * executed, will add to the wall clock time. 75 */ 76 private long wallClock; 77 78 @Override protected TestServiceFactory setupServiceFactory() { 79 return new TestServiceFactory() { 80 @Override protected AbstractTask createTestTask() { 81 return taskFactory == null ? new SimpleTask() : taskFactory.call(null); 82 } 83 84 @Override protected Service<String> createService() { 85 return new ScheduledServiceMock(this); 86 } 87 }; 88 } 89 90 @Override public void setup() { 91 super.setup(); 92 s = (ScheduledServiceMock) service; 93 wallClock = 0; 94 } 95 96 /************************************************************************************************** 97 * Big pile of tests for making sure setting the cumulative period works in a predictable manner * 98 * regardless of what kind of output comes from the back-off algorithm, also taking into * 99 * account the maximum cumulative period value. * 100 *************************************************************************************************/ 101 102 @Test public void setCumulativePeriod_MaxIsInfinity_TwoSeconds() { 103 s.setCumulativePeriod(Duration.seconds(2)); 104 assertEquals(Duration.seconds(2), s.getCumulativePeriod()); 105 } 106 107 @Test public void setCumulativePeriod_MaxIsInfinity_Negative() { 108 s.setCumulativePeriod(Duration.seconds(-2)); 109 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 110 } 111 112 @Test public void setCumulativePeriod_MaxIsInfinity_NegativeInfinity() { 113 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 114 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 115 } 116 117 @Test public void setCumulativePeriod_MaxIsInfinity_NaN() { 118 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 119 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 120 } 121 122 @Test public void setCumulativePeriod_MaxIsInfinity_PositiveInfinity() { 123 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 124 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 125 } 126 127 @Test public void setCumulativePeriod_MaxIsInfinity_MAX_VALUE() { 128 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 129 assertEquals(Duration.millis(Double.MAX_VALUE), s.getCumulativePeriod()); 130 } 131 132 @Test public void setCumulativePeriod_MaxIsNaN_TwoSeconds() { 133 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 134 s.setCumulativePeriod(Duration.seconds(2)); 135 assertEquals(Duration.seconds(2), s.getCumulativePeriod()); 136 } 137 138 @Test public void setCumulativePeriod_MaxIsNaN_Negative() { 139 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 140 s.setCumulativePeriod(Duration.seconds(-2)); 141 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 142 } 143 144 @Test public void setCumulativePeriod_MaxIsNaN_NegativeInfinity() { 145 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 146 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 147 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 148 } 149 150 @Test public void setCumulativePeriod_MaxIsNaN_NaN() { 151 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 152 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 153 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 154 } 155 156 @Test public void setCumulativePeriod_MaxIsNaN_PositiveInfinity() { 157 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 158 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 159 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 160 } 161 162 @Test public void setCumulativePeriod_MaxIsNaN_MAX_VALUE() { 163 s.setMaximumCumulativePeriod(Duration.UNKNOWN); 164 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 165 assertEquals(Duration.millis(Double.MAX_VALUE), s.getCumulativePeriod()); 166 } 167 168 @Test public void setCumulativePeriod_MaxIsNull_TwoSeconds() { 169 s.setMaximumCumulativePeriod(null); 170 s.setCumulativePeriod(Duration.seconds(2)); 171 assertEquals(Duration.seconds(2), s.getCumulativePeriod()); 172 } 173 174 @Test public void setCumulativePeriod_MaxIsNull_Negative() { 175 s.setMaximumCumulativePeriod(null); 176 s.setCumulativePeriod(Duration.seconds(-2)); 177 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 178 } 179 180 @Test public void setCumulativePeriod_MaxIsNull_NegativeInfinity() { 181 s.setMaximumCumulativePeriod(null); 182 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 183 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 184 } 185 186 @Test public void setCumulativePeriod_MaxIsNull_NaN() { 187 s.setMaximumCumulativePeriod(null); 188 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 189 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 190 } 191 192 @Test public void setCumulativePeriod_MaxIsNull_PositiveInfinity() { 193 s.setMaximumCumulativePeriod(null); 194 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 195 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 196 } 197 198 @Test public void setCumulativePeriod_MaxIsNull_MAX_VALUE() { 199 s.setMaximumCumulativePeriod(null); 200 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 201 assertEquals(Duration.millis(Double.MAX_VALUE), s.getCumulativePeriod()); 202 } 203 204 @Test public void setCumulativePeriod_MaxIs10_TwoSeconds() { 205 s.setMaximumCumulativePeriod(Duration.seconds(10)); 206 s.setCumulativePeriod(Duration.seconds(2)); 207 assertEquals(Duration.seconds(2), s.getCumulativePeriod()); 208 } 209 210 @Test public void setCumulativePeriod_MaxIs10_TenSeconds() { 211 s.setMaximumCumulativePeriod(Duration.seconds(10)); 212 s.setCumulativePeriod(Duration.seconds(10)); 213 assertEquals(Duration.seconds(10), s.getCumulativePeriod()); 214 } 215 216 @Test public void setCumulativePeriod_MaxIs10_TwelveSeconds() { 217 s.setMaximumCumulativePeriod(Duration.seconds(10)); 218 s.setCumulativePeriod(Duration.seconds(12)); 219 assertEquals(Duration.seconds(10), s.getCumulativePeriod()); 220 } 221 222 @Test public void setCumulativePeriod_MaxIs10_Negative() { 223 s.setMaximumCumulativePeriod(Duration.seconds(10)); 224 s.setCumulativePeriod(Duration.seconds(-2)); 225 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 226 } 227 228 @Test public void setCumulativePeriod_MaxIs10_NegativeInfinity() { 229 s.setMaximumCumulativePeriod(Duration.seconds(10)); 230 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 231 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 232 } 233 234 @Test public void setCumulativePeriod_MaxIs10_NaN() { 235 s.setMaximumCumulativePeriod(Duration.seconds(10)); 236 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 237 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 238 } 239 240 @Test public void setCumulativePeriod_MaxIs10_PositiveInfinity() { 241 s.setMaximumCumulativePeriod(Duration.seconds(10)); 242 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 243 assertEquals(Duration.seconds(10), s.getCumulativePeriod()); 244 } 245 246 @Test public void setCumulativePeriod_MaxIs10_MAX_VALUE() { 247 s.setMaximumCumulativePeriod(Duration.seconds(10)); 248 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 249 assertEquals(Duration.seconds(10), s.getCumulativePeriod()); 250 } 251 252 @Test public void setCumulativePeriod_MaxIs0_TwoSeconds() { 253 s.setMaximumCumulativePeriod(Duration.ZERO); 254 s.setCumulativePeriod(Duration.seconds(2)); 255 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 256 } 257 258 @Test public void setCumulativePeriod_MaxIs0_TenSeconds() { 259 s.setMaximumCumulativePeriod(Duration.ZERO); 260 s.setCumulativePeriod(Duration.seconds(10)); 261 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 262 } 263 264 @Test public void setCumulativePeriod_MaxIs0_TwelveSeconds() { 265 s.setMaximumCumulativePeriod(Duration.ZERO); 266 s.setCumulativePeriod(Duration.seconds(12)); 267 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 268 } 269 270 @Test public void setCumulativePeriod_MaxIs0_Negative() { 271 s.setMaximumCumulativePeriod(Duration.ZERO); 272 s.setCumulativePeriod(Duration.seconds(-2)); 273 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 274 } 275 276 @Test public void setCumulativePeriod_MaxIs0_NegativeInfinity() { 277 s.setMaximumCumulativePeriod(Duration.ZERO); 278 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 279 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 280 } 281 282 @Test public void setCumulativePeriod_MaxIs0_NaN() { 283 s.setMaximumCumulativePeriod(Duration.ZERO); 284 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 285 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 286 } 287 288 @Test public void setCumulativePeriod_MaxIs0_PositiveInfinity() { 289 s.setMaximumCumulativePeriod(Duration.ZERO); 290 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 291 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 292 } 293 294 @Test public void setCumulativePeriod_MaxIs0_MAX_VALUE() { 295 s.setMaximumCumulativePeriod(Duration.ZERO); 296 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 297 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 298 } 299 300 @Test public void setCumulativePeriod_MaxIsNegative_TwoSeconds() { 301 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 302 s.setCumulativePeriod(Duration.seconds(2)); 303 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 304 } 305 306 @Test public void setCumulativePeriod_MaxIsNegative_TenSeconds() { 307 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 308 s.setCumulativePeriod(Duration.seconds(10)); 309 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 310 } 311 312 @Test public void setCumulativePeriod_MaxIsNegative_TwelveSeconds() { 313 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 314 s.setCumulativePeriod(Duration.seconds(12)); 315 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 316 } 317 318 @Test public void setCumulativePeriod_MaxIsNegative_Negative() { 319 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 320 s.setCumulativePeriod(Duration.seconds(-2)); 321 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 322 } 323 324 @Test public void setCumulativePeriod_MaxIsNegative_NegativeInfinity() { 325 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 326 s.setCumulativePeriod(Duration.seconds(Double.NEGATIVE_INFINITY)); 327 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 328 } 329 330 @Test public void setCumulativePeriod_MaxIsNegative_NaN() { 331 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 332 s.setCumulativePeriod(Duration.seconds(Double.NaN)); 333 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 334 } 335 336 @Test public void setCumulativePeriod_MaxIsNegative_PositiveInfinity() { 337 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 338 s.setCumulativePeriod(Duration.seconds(Double.POSITIVE_INFINITY)); 339 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 340 } 341 342 @Test public void setCumulativePeriod_MaxIsNegative_MAX_VALUE() { 343 s.setMaximumCumulativePeriod(Duration.seconds(-1)); 344 s.setCumulativePeriod(Duration.millis(Double.MAX_VALUE)); 345 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 346 } 347 348 // TODO I think Duration boundary condition checking is wrong. It doesn't use isInfinite, but checks 349 // directly with POSITIVE_INFINITY, neglecting to check NEGATIVE_INFINITY. 350 351 // DELAY 352 // Test that: 353 // delay (positive, unknown, zero) works the first time 354 // delay is not used on the next iteration 355 // delay works on restart 356 // delay works on reset / start 357 358 @Test public void delayIsHonored_Positive() throws InterruptedException { 359 s.setDelay(Duration.seconds(1)); 360 s.start(); 361 assertEquals(1000, wallClock); 362 } 363 364 @Test public void delayIsHonored_Unknown() throws InterruptedException { 365 s.setDelay(Duration.UNKNOWN); 366 s.start(); 367 assertEquals(0, wallClock); 368 } 369 370 @Test public void delayIsHonored_Infinite() throws InterruptedException { 371 s.setDelay(Duration.INDEFINITE); 372 s.start(); 373 assertEquals(Long.MAX_VALUE, wallClock); 374 } 375 376 @Test public void delayIsHonored_ZERO() throws InterruptedException { 377 s.setDelay(Duration.ZERO); 378 s.start(); 379 assertEquals(0, wallClock); 380 } 381 382 @Test public void delayIsNotUsedOnSubsequentIteration() { 383 s.setDelay(Duration.seconds(1)); 384 s.setPeriod(Duration.seconds(3)); 385 s.start(); 386 s.iterate(); 387 assertEquals(4000, wallClock); // 1 sec initial delay + 3 second iteration delay 388 } 389 390 @Test public void delayIsUsedOnRestart() { 391 s.setDelay(Duration.seconds(1)); 392 s.setPeriod(Duration.seconds(3)); 393 s.start(); 394 s.iterate(); 395 s.cancel(); 396 wallClock = 0; 397 s.restart(); 398 assertEquals(1000, wallClock); 399 } 400 401 @Test public void delayIsUsedOnStartFollowingReset() { 402 s.setDelay(Duration.seconds(1)); 403 s.setPeriod(Duration.seconds(3)); 404 s.start(); 405 s.iterate(); 406 s.cancel(); 407 wallClock = 0; 408 s.reset(); 409 s.start(); 410 assertEquals(1000, wallClock); 411 } 412 413 // PERIOD 414 // Test that: 415 // period does not contribute to the delay 416 // amount of time from start of one iteration (run) to another (run) is never < period 417 // run time < period 418 // run time == period 419 // run time > period 420 // start of last period is reset after "reset" call (or restart) 421 422 @Test public void periodDoesNotContributeToDelay() { 423 s.setDelay(Duration.seconds(1)); 424 s.setPeriod(Duration.seconds(3)); 425 s.start(); 426 assertEquals(1000, wallClock); 427 } 428 429 @Test public void executionTimeLessThanPeriod() { 430 s.setDelay(Duration.seconds(1)); 431 s.setPeriod(Duration.seconds(3)); 432 s.start(); 433 s.iterate(); 434 assertEquals(4000, wallClock); // 1 sec initial delay + 3 second iteration delay 435 } 436 437 @Test public void executionTimeEqualsPeriod() { 438 s.setDelay(Duration.seconds(1)); 439 s.setPeriod(Duration.seconds(3)); 440 s.start(); 441 wallClock += 3000; // simulate execution time 442 s.iterate(); 443 assertEquals(4000, wallClock); 444 } 445 446 @Test public void executionTimeExceedsPeriod() { 447 s.setDelay(Duration.seconds(1)); 448 s.setPeriod(Duration.seconds(3)); 449 s.start(); 450 wallClock += 6000; // simulate execution time 451 s.iterate(); 452 assertEquals(7000, wallClock); 453 } 454 455 @Test public void startOfPeriodIsResetAfterReset() { 456 s.setDelay(Duration.seconds(1)); 457 s.setPeriod(Duration.seconds(3)); 458 s.start(); 459 wallClock += 6000; // simulate execution time 460 s.iterate(); 461 s.cancel(); 462 wallClock = 0; 463 s.reset(); 464 s.start(); 465 s.iterate(); 466 assertEquals(4000, wallClock); 467 } 468 469 @Test public void startOfPeriodIsResetAfterRestart() { 470 s.setDelay(Duration.seconds(1)); 471 s.setPeriod(Duration.seconds(3)); 472 s.start(); 473 wallClock += 6000; // simulate execution time 474 s.iterate(); 475 s.cancel(); 476 wallClock = 0; 477 s.reset(); 478 s.start(); 479 s.iterate(); 480 assertEquals(4000, wallClock); 481 } 482 483 // COMPUTE BACKOFF 484 // Test that: 485 // on task failure, cumulative period is increased according to compute backoff 486 // EXPONENTIAL_BACKOFF_STRATEGY, LOGARITHMIC_BACKOFF_STRATEGY, LINEAR_BACKOFF_STRATEGY, custom backoff 487 488 @Test public void onFailureCumulativePeriodIsIncreased_EXPONENTIAL_BACKOFF_zero() { 489 s.setBackoffStrategy(ScheduledService.EXPONENTIAL_BACKOFF_STRATEGY); 490 s.setPeriod(Duration.ZERO); 491 taskFactory = EPIC_FAIL_FACTORY; 492 s.start(); 493 assertEquals(Duration.millis(Math.exp(1)), s.getCumulativePeriod()); 494 } 495 496 @Test public void onFailureCumulativePeriodIsIncreased_EXPONENTIAL_BACKOFF_one() { 497 s.setBackoffStrategy(ScheduledService.EXPONENTIAL_BACKOFF_STRATEGY); 498 s.setPeriod(Duration.seconds(1)); 499 taskFactory = EPIC_FAIL_FACTORY; 500 s.start(); 501 assertEquals(Duration.millis(1000 + (1000 * Math.exp(1))), s.getCumulativePeriod()); 502 } 503 504 @Test public void onFailureCumulativePeriodIsIncreased_EXPONENTIAL_BACKOFF_indefinite() { 505 s.setBackoffStrategy(ScheduledService.EXPONENTIAL_BACKOFF_STRATEGY); 506 s.setPeriod(Duration.INDEFINITE); 507 taskFactory = EPIC_FAIL_FACTORY; 508 s.start(); 509 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 510 } 511 512 @Test public void onFailureCumulativePeriodIsIncreased_EXPONENTIAL_BACKOFF_unknown() { 513 s.setBackoffStrategy(ScheduledService.EXPONENTIAL_BACKOFF_STRATEGY); 514 s.setPeriod(Duration.UNKNOWN); 515 taskFactory = EPIC_FAIL_FACTORY; 516 s.start(); 517 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 518 } 519 520 @Test public void onFailureCumulativePeriodIsIncreased_LOGARITHMIC_BACKOFF_zero() { 521 s.setBackoffStrategy(ScheduledService.LOGARITHMIC_BACKOFF_STRATEGY); 522 s.setPeriod(Duration.ZERO); 523 taskFactory = EPIC_FAIL_FACTORY; 524 s.start(); 525 assertEquals(Duration.millis(Math.log1p(1)), s.getCumulativePeriod()); 526 } 527 528 @Test public void onFailureCumulativePeriodIsIncreased_LOGARITHMIC_BACKOFF_one() { 529 s.setBackoffStrategy(ScheduledService.LOGARITHMIC_BACKOFF_STRATEGY); 530 s.setPeriod(Duration.seconds(1)); 531 taskFactory = EPIC_FAIL_FACTORY; 532 s.start(); 533 assertEquals(Duration.millis(1000 + (1000 * Math.log1p(1))), s.getCumulativePeriod()); 534 } 535 536 @Test public void onFailureCumulativePeriodIsIncreased_LOGARITHMIC_BACKOFF_indefinite() { 537 s.setBackoffStrategy(ScheduledService.LOGARITHMIC_BACKOFF_STRATEGY); 538 s.setPeriod(Duration.INDEFINITE); 539 taskFactory = EPIC_FAIL_FACTORY; 540 s.start(); 541 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 542 } 543 544 @Test public void onFailureCumulativePeriodIsIncreased_LOGARITHMIC_BACKOFF_unknown() { 545 s.setBackoffStrategy(ScheduledService.LOGARITHMIC_BACKOFF_STRATEGY); 546 s.setPeriod(Duration.UNKNOWN); 547 taskFactory = EPIC_FAIL_FACTORY; 548 s.start(); 549 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 550 } 551 552 @Test public void onFailureCumulativePeriodIsIncreased_LINEAR_BACKOFF_zero() { 553 s.setBackoffStrategy(ScheduledService.LINEAR_BACKOFF_STRATEGY); 554 s.setPeriod(Duration.ZERO); 555 taskFactory = EPIC_FAIL_FACTORY; 556 s.start(); 557 assertEquals(Duration.millis(1), s.getCumulativePeriod()); 558 } 559 560 @Test public void onFailureCumulativePeriodIsIncreased_LINEAR_BACKOFF_one() { 561 s.setBackoffStrategy(ScheduledService.LINEAR_BACKOFF_STRATEGY); 562 s.setPeriod(Duration.seconds(1)); 563 taskFactory = EPIC_FAIL_FACTORY; 564 s.start(); 565 assertEquals(Duration.millis(1000 + (1000 * 1)), s.getCumulativePeriod()); 566 } 567 568 @Test public void onFailureCumulativePeriodIsIncreased_LINEAR_BACKOFF_indefinite() { 569 s.setBackoffStrategy(ScheduledService.LINEAR_BACKOFF_STRATEGY); 570 s.setPeriod(Duration.INDEFINITE); 571 taskFactory = EPIC_FAIL_FACTORY; 572 s.start(); 573 assertEquals(Duration.INDEFINITE, s.getCumulativePeriod()); 574 } 575 576 @Test public void onFailureCumulativePeriodIsIncreased_LINEAR_BACKOFF_unknown() { 577 s.setBackoffStrategy(ScheduledService.LINEAR_BACKOFF_STRATEGY); 578 s.setPeriod(Duration.UNKNOWN); 579 taskFactory = EPIC_FAIL_FACTORY; 580 s.start(); 581 assertEquals(Duration.UNKNOWN, s.getCumulativePeriod()); 582 } 583 584 // CUMULATIVE PERIOD 585 // Test that: 586 // cumulative period is initially equivalent to period 587 // Cumulative period is set when the service enters "scheduled" state 588 // cumulative period is unchanged after successful iteration 589 // cumulative period is modified on failure (tested in onFailure*** tests) 590 // cumulative period is not modified on cancel 591 592 @Test public void cumulativePeriodSetWhenScheduled() { 593 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 594 s.setPeriod(Duration.seconds(1)); 595 assertEquals(Duration.ZERO, s.getCumulativePeriod()); 596 s.start(); 597 assertEquals(Duration.seconds(1), s.getCumulativePeriod()); 598 } 599 600 @Test public void cumulativePeriodDoesNotChangeOnSuccessfulRun() { 601 s.setPeriod(Duration.seconds(1)); 602 s.start(); 603 s.iterate(); 604 assertEquals(Duration.seconds(1), s.getCumulativePeriod()); 605 } 606 607 @Test public void cumulativePeriodResetOnSuccessfulRun() { 608 final AtomicInteger counter = new AtomicInteger(); 609 taskFactory = param -> new AbstractTask() { 610 @Override protected String call() throws Exception { 611 int c = counter.incrementAndGet(); 612 if (c < 10) throw new Exception("Kaboom!"); 613 return "Success"; 614 } 615 }; 616 s.setPeriod(Duration.seconds(1)); 617 s.start(); 618 for (int i=0; i<8; i++) s.iterate(); 619 assertTrue(Duration.seconds(1).lessThan(s.getCumulativePeriod())); 620 s.iterate(); 621 assertEquals(Duration.seconds(1), s.getCumulativePeriod()); 622 } 623 624 @Test public void cumulativePeriodDoesNotChangeOnCancelRun() { 625 s.setPeriod(Duration.seconds(1)); 626 s.start(); 627 s.iterate(); 628 s.cancel(); 629 assertEquals(Duration.seconds(1), s.getCumulativePeriod()); 630 } 631 632 // RESTART ON FAILURE 633 // Test that: 634 // value of true causes a new iteration if the task fails 635 // value of false causes the Service to enter Failed state if the task fails. 636 637 @Test public void restartOnFailure_True() { 638 final AtomicInteger counter = new AtomicInteger(); 639 taskFactory = new Callback<Void, AbstractTask>() { 640 @Override public AbstractTask call(Void param) { 641 return new EpicFailTask() { 642 @Override protected String call() throws Exception { 643 counter.incrementAndGet(); 644 return super.call(); 645 } 646 }; 647 } 648 }; 649 s.start(); 650 assertEquals(Worker.State.SCHEDULED, s.getState()); 651 s.iterate(); 652 assertEquals(2, counter.get()); 653 } 654 655 @Test public void restartOnFailure_False() { 656 final AtomicInteger counter = new AtomicInteger(); 657 taskFactory = new Callback<Void, AbstractTask>() { 658 @Override public AbstractTask call(Void param) { 659 return new EpicFailTask() { 660 @Override protected String call() throws Exception { 661 counter.incrementAndGet(); 662 return super.call(); 663 } 664 }; 665 } 666 }; 667 s.setRestartOnFailure(false); 668 s.start(); 669 assertEquals(Worker.State.FAILED, s.getState()); 670 assertEquals(1, counter.get()); 671 } 672 673 // MAXIMUM FAILURE COUNT / CURRENT FAILURE COUNT 674 // Test that: 675 // service iterates while currentFailureCount < maximumFailureCount 676 // service fails after currentFailureCount == maximumFailureCount 677 // service halts when this happens. 678 // currentFailureCount increments on each failure by 1 679 // currentFailureCount is reset on "reset" and "restart" 680 681 @Test public void serviceIteratesWhile_CurrentFailureCount_IsLessThan_MaximumFailureCount() { 682 final AtomicInteger counter = new AtomicInteger(); 683 taskFactory = new Callback<Void, AbstractTask>() { 684 @Override public AbstractTask call(Void param) { 685 return new EpicFailTask() { 686 @Override protected String call() throws Exception { 687 counter.incrementAndGet(); 688 return super.call(); 689 } 690 }; 691 } 692 }; 693 s.setMaximumFailureCount(10); 694 s.start(); 695 while (s.getState() != Worker.State.FAILED) { 696 assertEquals(counter.get(), s.getCurrentFailureCount()); 697 s.iterate(); 698 } 699 assertEquals(10, counter.get()); 700 assertEquals(counter.get(), s.getCurrentFailureCount()); 701 } 702 703 @Test public void currentFailureCountIsResetOnRestart() { 704 taskFactory = EPIC_FAIL_FACTORY; 705 s.start(); 706 for (int i=0; i<10; i++) s.iterate(); 707 taskFactory = null; 708 s.restart(); 709 assertEquals(0, s.getCurrentFailureCount()); 710 } 711 712 @Test public void currentFailureCountIsResetOnReset() { 713 taskFactory = EPIC_FAIL_FACTORY; 714 s.start(); 715 for (int i=0; i<10; i++) s.iterate(); 716 s.cancel(); 717 s.reset(); 718 assertEquals(0, s.getCurrentFailureCount()); 719 } 720 721 @Test public void currentFailureCountIsNotResetOnCancel() { 722 taskFactory = EPIC_FAIL_FACTORY; 723 s.start(); 724 for (int i=0; i<10; i++) s.iterate(); 725 s.cancel(); 726 assertEquals(11, s.getCurrentFailureCount()); 727 } 728 729 // LAST VALUE 730 // Test that: 731 // last value starts as null 732 // last value is still null if first iteration fails 733 // last value equals value from successful iteration (1 & 2) 734 // last value is cleared on "restart" / "reset" 735 736 @Test public void lastValueIsInitiallyNull() { 737 assertNull(s.getLastValue()); 738 } 739 740 @Test public void lastValueIsNullAfterFailedFirstIteration() { 741 taskFactory = EPIC_FAIL_FACTORY; 742 s.start(); 743 assertNull(s.getLastValue()); 744 } 745 746 @Test public void lastValueIsSetAfterSuccessfulFirstIteration() { 747 s.start(); 748 assertEquals("Sentinel", s.getLastValue()); 749 assertNull(s.getValue()); 750 } 751 752 @Test public void lastValueIsSetAfterFailedFirstIterationAndSuccessfulSecondIteration() { 753 final AtomicInteger counter = new AtomicInteger(); 754 taskFactory = param -> new AbstractTask() { 755 @Override protected String call() throws Exception { 756 int c = counter.incrementAndGet(); 757 if (c == 1) throw new Exception("Bombed out!"); 758 return "Success"; 759 } 760 }; 761 s.start(); 762 assertNull(s.getLastValue()); 763 assertNull(s.getValue()); 764 s.iterate(); 765 assertEquals("Success", s.getLastValue()); 766 assertNull(s.getValue()); 767 } 768 769 @Test public void lastValueIsUnchangedAfterSuccessfulFirstIterationAndFailedSecondIteration() { 770 final AtomicInteger counter = new AtomicInteger(); 771 taskFactory = param -> new AbstractTask() { 772 @Override protected String call() throws Exception { 773 int c = counter.incrementAndGet(); 774 if (c == 1) return "Success"; 775 throw new Exception("Bombed out!"); 776 } 777 }; 778 s.start(); 779 assertEquals("Success", s.getLastValue()); 780 assertNull(s.getValue()); 781 s.iterate(); 782 assertEquals("Success", s.getLastValue()); 783 assertNull(s.getValue()); 784 } 785 786 @Test public void lastValueIsClearedOnReset() { 787 s.start(); 788 assertEquals("Sentinel", s.getLastValue()); 789 s.cancel(); 790 assertEquals("Sentinel", s.getLastValue()); 791 s.reset(); 792 assertNull(s.getLastValue()); 793 } 794 795 @Test public void callingCancelFromOnSucceededEventHandlerShouldStopScheduledService() { 796 AtomicBoolean onReadyCalled = new AtomicBoolean(); 797 AtomicBoolean onScheduledCalled = new AtomicBoolean(); 798 AtomicBoolean onCancelledCalled = new AtomicBoolean(); 799 s.setOnSucceeded(event -> { 800 s.cancel(); 801 // Reset these so that they only get set to true if called 802 // after the cancel step 803 onReadyCalled.set(false); 804 onScheduledCalled.set(false); 805 onCancelledCalled.set(false); 806 }); 807 s.setOnReady(event -> onReadyCalled.set(true)); 808 s.setOnScheduled(event -> onScheduledCalled.set(true)); 809 s.setOnCancelled(event -> onCancelledCalled.set(true)); 810 811 s.start(); 812 assertFalse(s.isRunning()); 813 assertEquals(Worker.State.CANCELLED, s.getState()); 814 assertTrue(onReadyCalled.get()); 815 assertTrue(onScheduledCalled.get()); 816 assertTrue(onCancelledCalled.get()); 817 } 818 819 /** 820 * Allows us to monkey with how the threading works for the sake of testing. 821 * Basically, you just call start() in order to go through an entire first 822 * iteration, and a call to iterate() causes it to go through a subsequent 823 * iteration. At the end of each iteration, you are in the SCHEDULED state, 824 * unless failures occurred while running the task that caused the service 825 * to finally enter the FAILED state. 826 */ 827 private final class ScheduledServiceMock extends ScheduledService<String> { 828 private TestServiceFactory factory; 829 private Task<String> nextTask = null; 830 831 ScheduledServiceMock(TestServiceFactory f) { 832 this.factory = f; 833 } 834 835 @Override protected Task<String> createTask() { 836 factory.currentTask = factory.createTestTask(); 837 factory.currentTask.test = factory.test; 838 return factory.currentTask; 839 } 840 841 @Override void checkThread() { } 842 843 @Override void schedule(TimerTask task, long delay) { 844 wallClock += delay; 845 task.run(); 846 } 847 848 @Override protected void executeTask(Task<String> task) { 849 nextTask = task; 850 if (isFreshStart()) iterate(); 851 } 852 853 @Override long clock() { 854 return wallClock; 855 } 856 857 @Override boolean isFxApplicationThread() { 858 return Thread.currentThread() == factory.appThread; 859 } 860 861 void iterate() { 862 assert nextTask != null; 863 Task<String> task = nextTask; 864 nextTask = null; 865 866 super.executeTask(task); 867 handleEvents(); 868 } 869 } 870 }