@Test public void testSetWorkRate() { executor.setWorkFactor(1.0); assertEquals(1.0, executor.getWorkFactor()); executor.setWorkFactor(0.5); assertEquals(0.5, executor.getWorkFactor()); executor.setWorkFactor(ThrottlingExecutor.MIN_WORK_FACTOR); assertEquals(ThrottlingExecutor.MIN_WORK_FACTOR, executor.getWorkFactor()); }
@Test public void testDecreaseWorkFactor() { executor.setWorkFactor(0.5); executor.setPendingNanos(5000); executor.setWorkFactor(0.3); assertEquals(5000, executor.getPendingNanos()); }
@Test(expected = IllegalArgumentException.class) public void testLessThan0PercentWorkRate() { new ThrottlingExecutor(MAX_NANOS).setWorkFactor(-0.1); }
@Test(expected = IllegalArgumentException.class) public void testLessThan0PercentWorkRate() { new ThrottlingExecutor(MAX_NANOS).setWorkFactor(-0.1); }
@Test(expected = IllegalArgumentException.class) public void testGreaterThan100PercentWorkRate() { new ThrottlingExecutor(MAX_NANOS).setWorkFactor(1.1); }
@Test(expected = IllegalArgumentException.class) public void testGreaterThan100PercentWorkRate() { new ThrottlingExecutor(MAX_NANOS).setWorkFactor(1.1); }
@Test public void testSleepOvershoot() throws InterruptedException { executor.setWorkFactor(0.5); final long workTimeNanos = TimeUnit.MILLISECONDS.toNanos(5); final long expectedDelayNanos = workTimeNanos; final long actualDelayTimeNanos = TimeUnit.MILLISECONDS.toNanos(6); setWorkTime(workTimeNanos); setActualSleepTime(actualDelayTimeNanos); executor.execute(NO_OP); verifySleepTime(expectedDelayNanos); assertEquals(expectedDelayNanos - actualDelayTimeNanos, executor.getPendingNanos()); }
@Test public void testSleepUndershoot() throws InterruptedException { executor.setWorkFactor(0.5); final long workTimeNanos = TimeUnit.MILLISECONDS.toNanos(5); final long expectedDelayNanos = workTimeNanos; final long actualDelayNanos = TimeUnit.MILLISECONDS.toNanos(4); setWorkTime(workTimeNanos); setActualSleepTime(actualDelayNanos); executor.execute(NO_OP); verifySleepTime(expectedDelayNanos); assertEquals(expectedDelayNanos - actualDelayNanos, executor.getPendingNanos()); }
@Test public void testClampDelayMillis() throws InterruptedException { final long maxDelayMillis = 10; final long maxDelayNanos = TimeUnit.MILLISECONDS.toNanos(maxDelayMillis); executor = Mockito.spy(new ThrottlingExecutor(maxDelayMillis, clock)); executor.setWorkFactor(0.5); // Note work time exceeds maxDelayMillis setWorkTime(TimeUnit.MILLISECONDS.toNanos(100)); setActualSleepTime(maxDelayNanos); executor.execute(NO_OP); verifySleepTime(maxDelayNanos); assertEquals(0L, executor.getPendingNanos()); }
@Test public void test50PercentWorkRate() throws InterruptedException { executor.setWorkFactor(0.5); final long workTimeNanos = TimeUnit.MILLISECONDS.toNanos(5); setWorkTime(workTimeNanos); // Sleep time is same as work time at 50% work rate setActualSleepTime(workTimeNanos); executor.execute(NO_OP); verifySleepTime(workTimeNanos); assertEquals(0L, executor.getPendingNanos()); }
@Test public void testNegativePendingNanos() throws InterruptedException { executor.setWorkFactor(0.5); executor.setPendingNanos(-1000); assertEquals(-1000, executor.getPendingNanos()); // Note: we do not expect the delay time to be used because work time + pending delay is // negative. setWorkTime(500); executor.execute(NO_OP); // Sleep should not be called with negative pending nanos Mockito.verify(executor, Mockito.never()).sleep(Mockito.anyLong()); assertEquals(-1000 + 500, executor.getPendingNanos()); }
@Test public void testMinWorkRate() throws InterruptedException { final double workFactor = ThrottlingExecutor.MIN_WORK_FACTOR; executor.setWorkFactor(workFactor); // The math to work out how much to multiply work time to get expected delay time double workToDelayFactor = (1.0 - workFactor) / workFactor; final long workTimeNanos = TimeUnit.MILLISECONDS.toNanos(5); final long delayTimeNanos = (long) (workToDelayFactor * workTimeNanos); setWorkTime(workTimeNanos); setActualSleepTime(delayTimeNanos); executor.execute(NO_OP); verifySleepTime(delayTimeNanos); assertEquals(0, executor.getPendingNanos()); }
@Test public void testNegativePendingNanosGoesPositive() throws InterruptedException { executor.setWorkFactor(0.5); long startPendingNanos = -1000; executor.setPendingNanos(startPendingNanos); assertEquals(-1000, executor.getPendingNanos()); setWorkTime(1250); setActualSleepTime(1250 + startPendingNanos); executor.execute(NO_OP); verifySleepTime(1250 + startPendingNanos); assertEquals(0, executor.getPendingNanos()); }
@Test public void testApplyPendingSleepNanos() throws InterruptedException { // This verifies that the executor tries to re-apply pending sleep time on the next execution. executor.setWorkFactor(0.5); final long workTimeNanos = TimeUnit.MILLISECONDS.toNanos(5); final long actualDelayNanos1 = TimeUnit.MILLISECONDS.toNanos(4); final long actualDelayNanos2 = TimeUnit.MILLISECONDS.toNanos(6); // First execution setWorkTime(workTimeNanos); setActualSleepTime(actualDelayNanos1); executor.execute(NO_OP); verifySleepTime(workTimeNanos); assertEquals(workTimeNanos - actualDelayNanos1, executor.getPendingNanos()); // Second execution setWorkTime(workTimeNanos); setActualSleepTime(actualDelayNanos2); executor.execute(NO_OP); verifySleepTime(workTimeNanos); assertEquals(0L, executor.getPendingNanos()); }
@Test public void testOverflowOfSleepNanos() throws InterruptedException { executor.setWorkFactor(0.5); executor.setPendingNanos(Long.MAX_VALUE); assertEquals(Long.MAX_VALUE, executor.getPendingNanos()); // At a 50% work factor we'd expect work and sleep to match. As they don't, the function will // try to increment the pending sleep nanos, which could (but should not) result in overflow. setWorkTime(5000); setActualSleepTime(Long.MAX_VALUE); executor.execute(NO_OP); // Expect sleep nanos to be clamped to the maximum long value verifySleepTime(Long.MAX_VALUE); }