blob: 68a3e327aadd7299650d3aaa4e91a9177a874cad [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.dev.jjs.test;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.impl.LoadingStrategyBase;
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.Timer;
/**
* Tests runAsync server/network failure handling.
*
* This is skipped in dev mode because runAsync never fails in dev mode.
*/
@DoNotRunWith(Platform.Devel)
public class RunAsyncFailureTest extends GWTTestCase {
abstract static class MyRunAsyncCallback implements RunAsyncCallback {
private static int sToken = 0;
private int attempt;
private int expectedSuccessfulAttempt;
private int token;
public MyRunAsyncCallback(int attempt, int expectedSuccessfulAttempt) {
this.attempt = attempt;
this.expectedSuccessfulAttempt = expectedSuccessfulAttempt;
token = sToken++;
}
public int getToken() {
return token;
}
public boolean onSuccessHelper(String test) {
int token = getToken();
log("onSuccess: attempt = " + attempt + ", token = " + token);
if (attempt == expectedSuccessfulAttempt) {
return true;
} else {
// We don't really care about the test string, but we need to use it
// somewhere so it doesn't get dead stripped out. Each test passes
// in a unique string so it ends up in it's fragment.
fail(test + " - Succeeded on attempt: " + attempt +
" but should have succeeded on attempt: " + expectedSuccessfulAttempt);
}
return false;
}
protected void onFailureHelper(Throwable caught, Timer t) {
// Fail the test if too many attempts have taken place.
if (attempt > 5) {
fail("Too many failures");
}
int token = getToken();
log("onFailure: attempt = " + attempt + ", token = " + token
+ ", caught = " + caught);
t.schedule(100);
}
private native void log(String message) /*-{
// Enable this for testing on Safari/WebKit browsers
// $wnd.console.log(message);
}-*/;
}
private static final int RUNASYNC_TIMEOUT = 30000;
private static int staticWrittenByAsync;
@Override
public String getModuleName() {
return "com.google.gwt.dev.jjs.RunAsyncFailure";
}
/**
* Some subclasses of this test use linkers that do not support retries, in
* which case the expected number of manual retries before success will always
* be 4.
*/
protected boolean supportsRetries() {
return true;
}
// Note that each tests needs a new subclass of MyRunAsyncCallback because
// otherwise the runAsync code will cache the fragment and later tests will
// always succeed on the first try.
private void runAsync1(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
@Override
public void onFailure(Throwable caught) {
onFailureHelper(caught, new Timer() {
@Override
public void run() {
runAsync1(attempt + 1, expectedSuccessfulAttempt);
}
});
}
@Override
public void onSuccess() {
if (onSuccessHelper("DOWNLOAD_FAILURE_TEST_1")) { finishTest(); }
}
});
}
private void runAsync2(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
@Override
public void onFailure(Throwable caught) {
onFailureHelper(caught, new Timer() {
@Override
public void run() {
runAsync2(attempt + 1, expectedSuccessfulAttempt);
}
});
}
@Override
public void onSuccess() {
if (onSuccessHelper("DOWNLOAD_FAILURE_TEST_2")) { finishTest(); }
}
});
}
private void runAsync3(final int attempt, final int expectedSuccessfulAttempt) {
GWT.runAsync(new MyRunAsyncCallback(attempt, expectedSuccessfulAttempt) {
@Override
public void onFailure(Throwable caught) {
onFailureHelper(caught, new Timer() {
@Override
public void run() {
runAsync3(attempt + 1, expectedSuccessfulAttempt);
}
});
}
@Override
public void onSuccess() {
if (onSuccessHelper("DOWNLOAD_FAILURE_TEST_3")) { finishTest(); }
}
});
}
private void runAsync4() {
GWT.runAsync(new RunAsyncCallback() {
@Override
public void onFailure(Throwable caught) {
// This call should fail since no retries are done if the code downloads
// successfully, but fails to install.
finishTest();
}
@Override
public void onSuccess() {
// Use the string "INSTALL_FAILURE_TEST" so we can identify this
// fragment on the server. In the fail message is good enough.
fail("INSTALL_FAILURE_TEST - Code should have failed to install!");
}
});
}
private void runAsync5() {
GWT.runAsync(new RunAsyncCallback() {
@Override
public void onFailure(Throwable caught) {
staticWrittenByAsync++;
}
@Override
public void onSuccess() {
// Use the string "INSTALL_FAILURE_TEST" so we can identify this
// fragment on the server. In the fail message is good enough.
fail("INSTALL_FAILURE_TEST_2 - Code should have failed to install!");
}
});
}
/**
* Test the basic functionality of retrying runAsync until is succeeds.
* A Timer is used to avoid nesting GWT.runAsync calls.
*/
public void testHttpFailureRetries() {
delayTestFinish(RUNASYNC_TIMEOUT);
// Default is 3, but we set it explicitly, since other tests may run first
LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 2;
// In RunAsyncFailureServlet, the 5th time is the charm, but the code
// by default retries 3 times every time we call runAsync, so this
// should succeed on the second runAsync call, which is attempt #1.
runAsync1(0, supportsRetries() ? 1 : 4);
}
public void testHttpFailureRetries2() {
delayTestFinish(RUNASYNC_TIMEOUT);
LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 0;
// In RunAsyncFailureServlet, the 5th time is the charm, so if we do not
// let the code do any retries, we'll need to retry 4 times before succeeding
runAsync2(0, 4);
}
public void testBuiltInRetries() {
delayTestFinish(RUNASYNC_TIMEOUT);
LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 4;
// In RunAsyncFailureServlet, the 5th time is the charm, so retrying 4 times
// should be enough to get it on the first try.
runAsync3(0, supportsRetries() ? 0 : 4);
}
public void testDownloadSuccessButInstallFailure() {
delayTestFinish(RUNASYNC_TIMEOUT);
LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 3;
runAsync4();
}
public void testDownloadSuccessButInstallFailureStillRunsAsync() {
delayTestFinish(RUNASYNC_TIMEOUT);
LoadingStrategyBase.MAX_AUTO_RETRY_COUNT = 3;
staticWrittenByAsync = 0;
assertRunAsyncIsAsync();
// Give it little bit more time to loaded and try runAsync again
Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() {
@Override public boolean execute() {
if (staticWrittenByAsync == 0) {
return true;
}
// Code is loaded, let's assert it still runs async
assertRunAsyncIsAsync();
finishTest();
return false;
}
}, 100);
}
private void assertRunAsyncIsAsync() {
final int lastValue = staticWrittenByAsync;
runAsync5();
assertEquals(lastValue, staticWrittenByAsync);
}
}