blob: 86a4abda27519d02322c05057e75c80acccedd4a [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed 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 com.google.gwt.core.client.impl;
import com.google.gwt.core.client.Duration;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.core.client.impl.SchedulerImpl.Task;
import com.google.gwt.junit.client.GWTTestCase;
/**
* This is a white-box test of the Scheduler API implementation.
*/
public class SchedulerImplTest extends GWTTestCase {
static class ArraySetterCommand implements ScheduledCommand {
private final boolean[] values;
public ArraySetterCommand(boolean[] values) {
this.values = values;
}
public void execute() {
values[0] = true;
}
}
static class CountingCommand implements RepeatingCommand {
public final int[] values;
public CountingCommand(int[] values) {
assertEquals(2, values.length);
this.values = values;
}
public boolean execute() {
assertTrue("Called too many times", values[0] < values[1]);
values[0] = values[0] + 1;
return values[0] < values[1];
}
}
/**
* A no-op command used to test internal datastructures.
*/
static class NullCommand implements ScheduledCommand {
public void execute() {
}
}
/**
* The EntryCommand and FinallyCommand queues should have the same behavior,
* so we use this interface to reuse the same test logic.
*/
interface QueueTester {
void flush();
JsArray<Task> queue();
void schedule(RepeatingCommand cmd);
void schedule(ScheduledCommand cmd);
}
private static class RepeatingCommandImpl implements RepeatingCommand {
private boolean firstTime = true;
private boolean commandRanSecondTime = false;
@Override
public boolean execute() {
// Command needs to run for the second time to be executed in runScheduledTasks
if (firstTime) {
firstTime = false;
return true;
}
commandRanSecondTime = true;
return false;
}
}
private static final int TEST_DELAY = 5000;
@Override
public String getModuleName() {
return "com.google.gwt.core.Core";
}
public void testDeferredCommands() {
final SchedulerImpl impl = new SchedulerImpl();
final boolean[] values = {false};
impl.scheduleDeferred(new ArraySetterCommand(values));
assertEquals(1, impl.deferredCommands.length());
ScheduledCommand nullCommand = new NullCommand();
impl.scheduleDeferred(nullCommand);
assertEquals(2, impl.deferredCommands.length());
assertSame(nullCommand, impl.deferredCommands.get(1).getScheduled());
impl.scheduleDeferred(new ScheduledCommand() {
public void execute() {
assertTrue(values[0]);
assertNull(impl.deferredCommands);
finishTest();
}
});
delayTestFinish(TEST_DELAY);
}
/**
* This test could potentially timeout since loop in {@link SchedulerImpl#runRepeatingTasks} would
* run indefinitely since we are mocking Duration to always return zero.
*
* see for details: https://code.google.com/p/google-web-toolkit/issues/detail?id=7307
*/
public void testEarlyBreakIfAllTaskAreFinished() {
final SchedulerImpl impl = new SchedulerImpl() {
@Override
Duration createDuration() {
return new Duration() {
@Override
public int elapsedMillis() {
// never expire
return 0;
}
};
}
};
final RepeatingCommandImpl command = new RepeatingCommandImpl();
impl.scheduleIncremental(command);
impl.scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
if (command.commandRanSecondTime) {
finishTest();
} else {
impl.scheduleDeferred(this);
}
}
});
delayTestFinish(TEST_DELAY);
}
public void testEntryCommands() {
final SchedulerImpl impl = new SchedulerImpl();
testQueue(new QueueTester() {
public void flush() {
impl.flushEntryCommands();
}
public JsArray<Task> queue() {
return impl.entryCommands;
}
public void schedule(RepeatingCommand cmd) {
impl.scheduleEntry(cmd);
}
public void schedule(ScheduledCommand cmd) {
impl.scheduleEntry(cmd);
}
});
}
public void testFinallyCommands() {
final SchedulerImpl impl = new SchedulerImpl();
testQueue(new QueueTester() {
public void flush() {
impl.flushFinallyCommands();
}
public JsArray<Task> queue() {
return impl.finallyCommands;
}
public void schedule(RepeatingCommand cmd) {
impl.scheduleFinally(cmd);
}
public void schedule(ScheduledCommand cmd) {
impl.scheduleFinally(cmd);
}
});
}
public void testFixedDelayCommands() {
final SchedulerImpl impl = new SchedulerImpl();
final int[] values = {0, 4};
impl.scheduleFixedDelay(new CountingCommand(values), 20);
// Scheduler doesn't need to maintain state for this kind of command
assertFalse(impl.isWorkQueued());
// Busy wait for the counter
impl.scheduleDeferred(new ScheduledCommand() {
public void execute() {
if (values[0] == values[1]) {
finishTest();
} else {
impl.scheduleDeferred(this);
}
}
});
delayTestFinish(TEST_DELAY);
}
public void testFixedPeriodCommands() {
final SchedulerImpl impl = new SchedulerImpl();
final int[] values = {0, 4};
impl.scheduleFixedPeriod(new CountingCommand(values), 20);
// Scheduler doesn't need to maintain state for this kind of command
assertFalse(impl.isWorkQueued());
// Busy wait for the counter
impl.scheduleDeferred(new ScheduledCommand() {
public void execute() {
if (values[0] == values[1]) {
finishTest();
} else {
impl.scheduleDeferred(this);
}
}
});
delayTestFinish(TEST_DELAY);
}
public void testIncrementalCommands() {
final SchedulerImpl impl = new SchedulerImpl();
final int[] values = {0, 4};
final CountingCommand counter = new CountingCommand(values);
impl.scheduleIncremental(counter);
// The first pass is scheduled as a deferred command
assertEquals(1, impl.deferredCommands.length());
impl.scheduleDeferred(new ScheduledCommand() {
public void execute() {
// After the incremental command has fired, it's moved to a new queue
assertNull(impl.deferredCommands);
assertTrue(String.valueOf(values[0]), values[0] <= values[1]);
if (values[0] == values[1]) {
// Haven't yet cleared the queue, still in flushPostEventPumpCommands
assertNotNull(impl.incrementalCommands);
assertEquals(0, impl.incrementalCommands.length());
finishTest();
} else {
assertNotNull(impl.incrementalCommands);
assertEquals(1, impl.incrementalCommands.length());
assertSame(counter, impl.incrementalCommands.get(0).getRepeating());
impl.scheduleDeferred(this);
}
}
});
assertEquals(2, impl.deferredCommands.length());
delayTestFinish(TEST_DELAY);
}
private void testQueue(final QueueTester impl) {
boolean[] oneShotValues = {false};
final boolean[] chainedValues = {false};
int[] counterValues = {0, 2};
impl.schedule(new ArraySetterCommand(oneShotValues));
impl.schedule(new CountingCommand(counterValues));
impl.schedule(new ScheduledCommand() {
public void execute() {
// Schedule another entry
impl.schedule(new ArraySetterCommand(chainedValues));
}
});
assertEquals(3, impl.queue().length());
ScheduledCommand nullCommand = new NullCommand();
impl.schedule(nullCommand);
assertEquals(4, impl.queue().length());
assertSame(nullCommand, impl.queue().get(3).getScheduled());
impl.flush();
// Ensure the command-schedules-command case has been executed
assertTrue(chainedValues[0]);
// Test that the RepeatingCommand is still scheduled
assertEquals(1, counterValues[0]);
assertEquals(1, impl.queue().length());
impl.flush();
// Everything should be finished now
assertEquals(2, counterValues[0]);
assertTrue(oneShotValues[0]);
assertNull(impl.queue());
}
}