/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.waveprotocol.wave.client.scheduler.testing; import org.waveprotocol.wave.client.scheduler.Scheduler.IncrementalTask; import org.waveprotocol.wave.client.scheduler.Scheduler.Task; import org.jmock.integration.junit3.MockObjectTestCase; import org.jmock.Expectations; import org.jmock.Sequence; /** * The FakeTimerService is a complicated enough fake that it deserves its own * tests. (Normally I wouldn't test fakes.) * */ public class FakeTimerServiceTest extends MockObjectTestCase { private Task oneoff; private IncrementalTask repeating; private FakeTimerService timer; @Override protected void setUp() throws Exception { super.setUp(); oneoff = mock(Task.class, "oneoff"); repeating = mock(IncrementalTask.class, "repeating"); timer = new FakeTimerService(); } public void testTicks() { timer.scheduleDelayed(oneoff, 500); timer.scheduleRepeating(repeating, 0, 1000); checking(new Expectations() {{ one(oneoff).execute(); exactly(2).of(repeating).execute(); will(returnValue(true)); }}); timer.tick(1000); timer.tick(500); timer.tick(499); checking(new Expectations() {{ one(repeating).execute(); will(returnValue(true)); }}); timer.tick(1); timer.tick(1); timer.tick(1); checking(new Expectations() {{ one(repeating).execute(); will(returnValue(true)); }}); timer.tick(999); checking(new Expectations() {{ exactly(3).of(repeating).execute(); will(returnValue(true)); }}); timer.tick(3000); } public void testCancel() { timer.scheduleDelayed(oneoff, 500); timer.cancel(oneoff); timer.scheduleRepeating(repeating, 0, 1000); timer.cancel(repeating); checking(new Expectations() {{ never(repeating).execute(); never(oneoff).execute(); }}); timer.tick(10 * 1000); } public void testScheduleWillExecuteImmediatelyOnAnyTick() { timer.schedule(oneoff); checking(new Expectations() {{ one(oneoff).execute(); }}); timer.tick(0); } /** Tests that scheduling a task with negative start time throws an exception. */ public void testNegativeStartTime() { try { timer.scheduleDelayed(oneoff, -1); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException e) { // pass } try { timer.scheduleRepeating(repeating, -1, 10); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException e) { // pass } } /** Tests that scheduling a task with negative interval throws an exception. */ public void testNegativeInterval() { try { timer.scheduleRepeating(repeating, 500, -1); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException e) { // pass } } /** * Tests that a repeated task with interval 0 is repeatedly executed * at the same time until it returns false. */ public void testInterval0() { timer.scheduleRepeating(repeating, 500, 0); timer.tick(499); checking(new Expectations() {{ exactly(5).of(repeating).execute(); will(returnValue(true)); one(repeating).execute(); will(returnValue(false)); never(repeating).execute(); }}); timer.tick(1); } /** * Tests that when there are several tasks with interval 0 running at the same time, * all of them are executed once before the first one is executed again. */ public void testTasksRepeatedlyRunningAtSameTimeAreScheduledFairly() { int taskCount = 10; final IncrementalTask[] tasks = new IncrementalTask[taskCount]; for (int i = 0; i < taskCount; i++) { tasks[i] = mock(IncrementalTask.class, "repeating_" + i); timer.scheduleRepeating(tasks[i], 1, 0); } final Sequence seq = sequence("callOrder"); checking(new Expectations() {{ for (IncrementalTask task : tasks) { one(task).execute(); will(returnValue(true)); inSequence(seq); } for (IncrementalTask task : tasks) { one(task).execute(); will(returnValue(false)); inSequence(seq); never(task).execute(); } }}); timer.tick(1); } public void testReschedulingCancelsFirst() { timer.scheduleDelayed(oneoff, 500); checking(new Expectations() {{ never(oneoff).execute(); }}); timer.tick(499); timer.scheduleDelayed(oneoff, 1000); timer.tick(2); checking(new Expectations() {{ one(oneoff).execute(); }}); timer.tick(1000); } /** * Tests that if multiple tasks are scheduled for the same time, they are all * executed. */ public void testMultipleTasksAtSameTime() { final int time = 100; timer.scheduleDelayed(oneoff, time); final Task anotherTask = mock(Task.class, "another_task"); timer.scheduleDelayed(anotherTask, time); checking(new Expectations() {{ one(oneoff).execute(); one(anotherTask).execute(); }}); timer.tick(time); } /** Tests that multiple processes can be run at the same time. */ public void testMultipleProcessesAtSameTime() { final int time = 100; final IncrementalTask anotherTask = mock(IncrementalTask.class, "anotherTask"); timer.scheduleRepeating(repeating, 0, time); timer.scheduleRepeating(anotherTask, time, time); checking(new Expectations() {{ one(repeating).execute(); will(returnValue(true)); }}); timer.tick(0); checking(new Expectations() {{ one(repeating).execute(); will(returnValue(true)); one(anotherTask).execute(); will(returnValue(true)); }}); timer.tick(time); } /** * Tests that when the timer advances across more than one execution time * point of the same repeated task, the task is run several times. */ public void testRepeatedTaskMakesUpForMissedExecutions() { timer.scheduleRepeating(repeating, 5, 10); timer.tick(4); checking(new Expectations() {{ exactly(10).of(repeating).execute(); will(returnValue(true)); }}); timer.tick(99); } /** * Tests that when {@link IncrementalTask#execute()} returns false, the task * is no longer rescheduled for execution. */ public void testIncrementalTaskIsCanceledIfItReturnsFalse() { timer.scheduleRepeating(repeating, 0, 1); checking(new Expectations() {{ exactly(10).of(repeating).execute(); will(returnValue(true)); one(repeating).execute(); will(returnValue(false)); never(repeating).execute(); }}); timer.tick(1000); } /** * Tests that a non-repeating task is scheduled only once, even though * internally in the {@link FakeTimerService} it is modeled as an * {@link IncrementalTask} that repeats every 1 msec but then opts out of the * rescheduling by returning false from {@link IncrementalTask#execute()}. */ public void testNormalTaskIsRunOnlyOnce() { timer.schedule(oneoff); checking(new Expectations() {{ one(oneoff).execute(); }}); timer.tick(1000); } }