Repository: incubator-reef Updated Branches: refs/heads/master 84becf50c -> d07797442
[REEF-481] Make ClockTest predictable This makes Clock tests more predictable by deleting removable Thread.sleep. JIRA: [REEF-481](https://issues.apache.org/jira/browse/REEF-481) Pull Request: This closes #304 Project: http://git-wip-us.apache.org/repos/asf/incubator-reef/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-reef/commit/d0779744 Tree: http://git-wip-us.apache.org/repos/asf/incubator-reef/tree/d0779744 Diff: http://git-wip-us.apache.org/repos/asf/incubator-reef/diff/d0779744 Branch: refs/heads/master Commit: d0779744261ccb54d8b21e9d2503bed10d9f396d Parents: 84becf5 Author: Byung-Gon Chun <bgc...@apache.org> Authored: Sun Jul 19 00:10:42 2015 +0900 Committer: Brian Cho <chobr...@apache.org> Committed: Wed Jul 22 10:39:51 2015 +0900 ---------------------------------------------------------------------- .../apache/reef/wake/test/time/ClockTest.java | 99 +++++++++++++------- 1 file changed, 65 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/d0779744/lang/java/reef-wake/wake/src/test/java/org/apache/reef/wake/test/time/ClockTest.java ---------------------------------------------------------------------- diff --git a/lang/java/reef-wake/wake/src/test/java/org/apache/reef/wake/test/time/ClockTest.java b/lang/java/reef-wake/wake/src/test/java/org/apache/reef/wake/test/time/ClockTest.java index 2cdab5b..2e93c70 100644 --- a/lang/java/reef-wake/wake/src/test/java/org/apache/reef/wake/test/time/ClockTest.java +++ b/lang/java/reef-wake/wake/src/test/java/org/apache/reef/wake/test/time/ClockTest.java @@ -36,16 +36,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class ClockTest { private static RuntimeClock buildClock() throws Exception { final JavaConfigurationBuilder builder = Tang.Factory.getTang() - .newConfigurationBuilder(); + .newConfigurationBuilder(); final Injector injector = Tang.Factory.getTang() - .newInjector(builder.build()); + .newInjector(builder.build()); return injector.getInstance(RuntimeClock.class); } @@ -65,17 +67,17 @@ public class ClockTest { public void testClock() throws Exception { LoggingUtils.setLoggingLevel(Level.FINE); + final int minEvents = 40; + final CountDownLatch eventCountLatch = new CountDownLatch(minEvents); + final RuntimeClock clock = buildClock(); new Thread(clock).start(); - final RandomAlarmProducer alarmProducer = new RandomAlarmProducer(clock); - ThreadPoolStage<Alarm> stage = new ThreadPoolStage<>(alarmProducer, 10); + final RandomAlarmProducer alarmProducer = new RandomAlarmProducer(clock, eventCountLatch); - try { + try (ThreadPoolStage<Alarm> stage = new ThreadPoolStage<>(alarmProducer, 10)) { stage.onNext(null); - Thread.sleep(5000); - Assert.assertTrue(alarmProducer.getEventCount() > 40); + Assert.assertTrue(eventCountLatch.await(10, TimeUnit.SECONDS)); } finally { - stage.close(); clock.close(); } } @@ -94,20 +96,24 @@ public class ClockTest { // Schedule an Alarm that's far in the future clock.scheduleAlarm(5000, laterAlarmRecorder); Thread.sleep(1000); + // By now, RuntimeClockImpl should be in a timed wait() for 5000 ms. // Scheduler an Alarm that should fire before the existing Alarm: clock.scheduleAlarm(2000, earlierAlarmRecorder); Thread.sleep(1000); + // The earlier Alarm shouldn't have fired yet (we've only slept 1/2 time): - Assert.assertEquals(0, earlierAlarmRecorder.events.size()); + Assert.assertEquals(0, earlierAlarmRecorder.getEventCount()); Thread.sleep(1500); + // The earlier Alarm should have fired, since 3500 > 2000 ms have passed: - Assert.assertEquals(1, earlierAlarmRecorder.events.size()); + Assert.assertEquals(1, earlierAlarmRecorder.getEventCount()); // And the later Alarm shouldn't have fired yet: - Assert.assertEquals(0, laterAlarmRecorder.events.size()); - Thread.sleep(2000); - // The later Alarm should have fired, since 5500 > 5000 ms have passed: - Assert.assertEquals(1, laterAlarmRecorder.events.size()); + Assert.assertEquals(0, laterAlarmRecorder.getEventCount()); + Thread.sleep(2500); + + // The later Alarm should have fired, since 6000 > 5000 ms have passed: + Assert.assertEquals(1, laterAlarmRecorder.getEventCount()); } finally { clock.close(); } @@ -118,19 +124,22 @@ public class ClockTest { LoggingUtils.setLoggingLevel(Level.FINE); final int numThreads = 3; + final CountDownLatch eventCountLatch = new CountDownLatch(numThreads); + final RuntimeClock clock = buildClock(); new Thread(clock).start(); final ThreadPoolStage<Alarm> stage = new ThreadPoolStage<>(new EventHandler<Alarm>() { @Override public void onNext(final Alarm value) { clock.close(); + eventCountLatch.countDown(); } }, numThreads); try { for (int i = 0; i < numThreads; ++i) stage.onNext(null); - Thread.sleep(1000); + Assert.assertTrue(eventCountLatch.await(10, TimeUnit.SECONDS)); } finally { stage.close(); clock.close(); @@ -141,15 +150,18 @@ public class ClockTest { public void testSimultaneousAlarms() throws Exception { LoggingUtils.setLoggingLevel(Level.FINE); + final int expectedEvent = 2; + final CountDownLatch eventCountLatch = new CountDownLatch(expectedEvent); + final RuntimeClock clock = buildLogicalClock(); new Thread(clock).start(); - final EventRecorder alarmRecorder = new EventRecorder(); + final EventRecorder alarmRecorder = new EventRecorder(eventCountLatch); try { - clock.scheduleAlarm(1000, alarmRecorder); - clock.scheduleAlarm(1000, alarmRecorder); - Thread.sleep(2000); - Assert.assertEquals(2, alarmRecorder.events.size()); + clock.scheduleAlarm(500, alarmRecorder); + clock.scheduleAlarm(500, alarmRecorder); + eventCountLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(expectedEvent, alarmRecorder.getEventCount()); } finally { clock.close(); } @@ -159,22 +171,24 @@ public class ClockTest { public void testAlarmOrder() throws Exception { LoggingUtils.setLoggingLevel(Level.FINE); + final int numAlarms = 10; + final CountDownLatch eventCountLatch = new CountDownLatch(numAlarms); + final RuntimeClock clock = buildLogicalClock(); new Thread(clock).start(); - final EventRecorder alarmRecorder = new EventRecorder(); + final EventRecorder alarmRecorder = new EventRecorder(eventCountLatch); try { - int numAlarms = 10; long[] expected = new long[numAlarms]; for (int i = 0; i < numAlarms; ++i) { clock.scheduleAlarm(i * 100, alarmRecorder); expected[i] = i * 100; } - Thread.sleep(2000); + eventCountLatch.await(10, TimeUnit.SECONDS); Long[] actualLong = new Long[numAlarms]; - alarmRecorder.timestamps.toArray(actualLong); + alarmRecorder.getTimestamps().toArray(actualLong); long[] actual = new long[numAlarms]; for (int i = 0; i < numAlarms; ++i) { actual[i] = actualLong[i]; @@ -193,37 +207,54 @@ public class ClockTest { /** * A synchronized List of the events recorded by this EventRecorder. */ - public final List<Time> events = Collections.synchronizedList(new ArrayList<Time>()); - public final List<Long> timestamps = Collections.synchronizedList(new ArrayList<Long>()); + private final List<Time> events = Collections.synchronizedList(new ArrayList<Time>()); + private final List<Long> timestamps = Collections.synchronizedList(new ArrayList<Long>()); + + private final CountDownLatch eventCountLatch; + + public EventRecorder() { + this(null); + } + + public EventRecorder(CountDownLatch latch) { + eventCountLatch = latch; + } + + public int getEventCount() { + return events.size(); + } + + public List<Long> getTimestamps() { + return timestamps; + } @Override public void onNext(final Alarm event) { timestamps.add(event.getTimeStamp()); events.add(event); + if (eventCountLatch != null) { + eventCountLatch.countDown(); + } } } private static class RandomAlarmProducer implements EventHandler<Alarm> { private final RuntimeClock clock; + private final CountDownLatch eventCountLatch; private final Random rand; - private int eventCount = 0; - public RandomAlarmProducer(RuntimeClock clock) { + public RandomAlarmProducer(RuntimeClock clock, CountDownLatch latch) { this.clock = clock; + this.eventCountLatch = latch; this.rand = new Random(); - this.eventCount = 0; - } - - int getEventCount() { - return eventCount; } @Override public void onNext(final Alarm value) { - eventCount += 1; int duration = rand.nextInt(100) + 1; clock.scheduleAlarm(duration, this); + eventCountLatch.countDown(); } } }