// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.collide.client.util;
import com.google.collide.shared.util.Reorderer;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.Timer;
/*
* The reason these are in the client is because Reorderer used to be client-only, but was moved
* to shared code, but the tests weren't migrated to be pure java.
*/
/**
* Tests for {@link Reorderer}.
*/
public class ReordererTest extends GWTTestCase {
private static class ItemSink implements Reorderer.ItemSink<Integer> {
int lastVersion;
ItemSink(int firstExpectedVersion) {
this.lastVersion = firstExpectedVersion - 1;
}
@Override
public void onItem(Integer item, int version) {
assertEquals(++lastVersion, version);
assertEquals(item.intValue(), version);
}
}
private class RequiredTimeoutCallback implements Reorderer.TimeoutCallback {
private int requiredLastVersionDispatched;
RequiredTimeoutCallback(int requiredLastVersionDispatched) {
this.requiredLastVersionDispatched = requiredLastVersionDispatched;
}
@Override
public void onTimeout(int lastVersionDispatched) {
assertEquals(requiredLastVersionDispatched, lastVersionDispatched);
finishTest();
}
}
private static final Reorderer.TimeoutCallback FAIL_TIMEOUT_CALLBACK =
new Reorderer.TimeoutCallback() {
@Override
public void onTimeout(int lastVersionDispatched) {
fail("Timeout should not have been called");
}
};
Reorderer<Integer> reorderer;
ItemSink sink;
@Override
public String getModuleName() {
return "com.google.collide.client.util.UtilTestModule";
}
public void testInOrder() {
{
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(4, 4);
reorderer.acceptItem(5, 5);
assertEquals(sink.lastVersion, 5);
}
{
ItemSink sink = new ItemSink(3);
Reorderer<Integer> reorderer =
Reorderer.create(3, sink, 1, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(4, 4);
reorderer.acceptItem(5, 5);
assertEquals(sink.lastVersion, 5);
}
}
public void testOutOfOrderNoTimeout() {
{
// Immediate out-of-order
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, Integer.MAX_VALUE, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(1, 1);
assertEquals(sink.lastVersion, 2);
}
{
// Two out-of-order
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, Integer.MAX_VALUE, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(5, 5);
reorderer.acceptItem(4, 4);
reorderer.acceptItem(3, 3);
assertEquals(sink.lastVersion, 5);
}
{
// Massive out-of-order
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, Integer.MAX_VALUE, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(5, 5);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(4, 4);
reorderer.acceptItem(2, 2);
assertEquals(sink.lastVersion, 5);
}
}
public void testOutOfOrderTimeout() {
ItemSink sink = new ItemSink(1);
RequiredTimeoutCallback timeoutCallback = new RequiredTimeoutCallback(3);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, timeoutCallback, ClientTimer.FACTORY);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(5, 5);
assertEquals(sink.lastVersion, 3);
delayTestFinish(100);
}
public void testOutOfOrderThenFillInGapThenTimeout() {
ItemSink sink = new ItemSink(1);
RequiredTimeoutCallback timeoutCallback = new RequiredTimeoutCallback(4);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, timeoutCallback, ClientTimer.FACTORY);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(4, 4);
reorderer.acceptItem(6, 6);
reorderer.acceptItem(2, 2);
assertEquals(sink.lastVersion, 4);
delayTestFinish(100);
}
public void testOutOfOrderTimeoutDisabled() {
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.setTimeoutEnabled(false);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(5, 5);
assertEquals(sink.lastVersion, 3);
ensureThatFailTimeoutWillNotBeCalled();
}
public void testOutOfOrderTimeoutDisabledAndThenEnabled() {
ItemSink sink = new ItemSink(1);
RequiredTimeoutCallback timeoutCallback = new RequiredTimeoutCallback(3);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, timeoutCallback, ClientTimer.FACTORY);
reorderer.setTimeoutEnabled(false);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(5, 5);
assertEquals(sink.lastVersion, 3);
reorderer.setTimeoutEnabled(true);
delayTestFinish(100);
}
public void testSkipToVersion() {
ItemSink sink = new ItemSink(1);
Reorderer<Integer> reorderer =
Reorderer.create(1, sink, 1, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.acceptItem(1, 1);
reorderer.acceptItem(2, 2);
reorderer.acceptItem(3, 3);
reorderer.acceptItem(5, 5);
reorderer.acceptItem(6, 6);
reorderer.acceptItem(8, 8);
reorderer.acceptItem(7, 7);
assertEquals(sink.lastVersion, 3);
/*
* The timeout callback would normally be called, but our skipToVersion below will cancel it and
* get us back on track. It will also try to process any queued doc ops including the given
* version (5) and after.
*/
// Override what the sink should expect next
sink.lastVersion = 4;
reorderer.skipToVersion(5);
assertEquals(sink.lastVersion, 8);
ensureThatFailTimeoutWillNotBeCalled();
}
public void testQueueUntilSkipToVersionCalled() {
ItemSink sink = new ItemSink(5);
Reorderer<Integer> reorderer =
Reorderer.create(0, sink, 1, FAIL_TIMEOUT_CALLBACK, ClientTimer.FACTORY);
reorderer.queueUntilSkipToVersionIsCalled();
reorderer.acceptItem(5, 5);
reorderer.acceptItem(6, 6);
reorderer.acceptItem(7, 7);
assertNotSame(7, sink.lastVersion);
reorderer.skipToVersion(5);
assertEquals(7, sink.lastVersion);
ensureThatFailTimeoutWillNotBeCalled();
}
private void ensureThatFailTimeoutWillNotBeCalled() {
new Timer() {
@Override
public void run() {
/*
* If the timeout was still enabled, FAIL_TIMEOUT_CALLBACK would have been queued/will run
* before this logic does
*/
finishTest();
}
}.schedule(1);
delayTestFinish(100);
}
}