| /* |
| * 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) { |
| public void onFailure(Throwable caught) { |
| onFailureHelper(caught, new Timer() { |
| @Override |
| public void run() { |
| runAsync1(attempt + 1, expectedSuccessfulAttempt); |
| } |
| }); |
| } |
| |
| 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) { |
| public void onFailure(Throwable caught) { |
| onFailureHelper(caught, new Timer() { |
| @Override |
| public void run() { |
| runAsync2(attempt + 1, expectedSuccessfulAttempt); |
| } |
| }); |
| } |
| |
| 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) { |
| public void onFailure(Throwable caught) { |
| onFailureHelper(caught, new Timer() { |
| @Override |
| public void run() { |
| runAsync3(attempt + 1, expectedSuccessfulAttempt); |
| } |
| }); |
| } |
| |
| public void onSuccess() { |
| if (onSuccessHelper("DOWNLOAD_FAILURE_TEST_3")) { finishTest(); } |
| } |
| }); |
| } |
| |
| private void runAsync4() { |
| GWT.runAsync(new RunAsyncCallback() { |
| public void onFailure(Throwable caught) { |
| // This call should fail since no retries are done if the code downloads |
| // successfully, but fails to install. |
| finishTest(); |
| } |
| 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() { |
| public void onFailure(Throwable caught) { |
| staticWrittenByAsync++; |
| } |
| 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); |
| } |
| } |