@Test public void test100PercentWorkRate() throws InterruptedException { setWorkTime(TimeUnit.MILLISECONDS.toNanos(5)); executor.execute(NO_OP); assertEquals(0L, executor.getPendingNanos()); // At 100% work rate sleep should not be called Mockito.verify(executor, Mockito.never()).sleep(Mockito.anyLong()); }
@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 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 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 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 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 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); }