blob: de9d2a8769c16156cd5825c6cae0961ab21e0f9f [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.impl.AsyncFragmentLoader.LoadTerminatedHandler;
import com.google.gwt.core.client.impl.XhrLoadingStrategy.MockableXMLHttpRequest;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
/**
* Tests the default loading strategy and its retry behavior.
*/
public class XhrLoadingStrategyTest extends TestCase {
static class MockXhr implements MockableXMLHttpRequest {
public static final String SUCCESSFUL_RESPONSE_TEXT = "successful response text";
public static final String INSTALL_FAILED_RESPONSE_TEXT = "install failed response text";
private ReadyStateChangeHandler handler;
private int httpStatus;
private int state;
private String statusText;
private String text;
private HashMap<String, String> headers;
public MockXhr(int status, String statusText, boolean loads,
boolean installs, String... headers) {
this.httpStatus = status;
this.statusText = statusText;
if (installs) {
text = SUCCESSFUL_RESPONSE_TEXT;
} else if (loads) {
text = INSTALL_FAILED_RESPONSE_TEXT;
} else {
text = null;
}
handler = null;
state = 0;
assert headers.length % 2 == 0;
this.headers = new HashMap<String, String>();
for (int i = 0; i < headers.length; i += 2) {
this.headers.put(headers[i], headers[i + 1]);
}
}
public void clearOnReadyStateChange() {
handler = null;
}
public int getReadyState() {
return state;
}
public String getResponseText() {
return state > 3 ? text : null;
}
public int getStatus() {
return state > 1 ? httpStatus : 0;
}
public String getStatusText() {
return state > 1 ? statusText : null;
}
public void open(String method, String url) {
state = 1;
}
public void send() {
state = 4;
if (headers.size() != 0) {
throw new IllegalStateException("not all expected headers set");
}
if (handler != null) {
/*
* This is brittle, but I don't have a better idea. The problem is that
* onReadyStateChange takes a REAL XMLHttpRequest, which I can't mock
* because it's all final. I don't want to open
* ReadyStateChangeHandler's long-standing API to let it take a non-real
* XMLHttpRequest, just for my wee test here, so instead I admit that
* null works 'cause the handler won't *use* its argument.
*/
handler.onReadyStateChange(null);
}
}
public void setOnReadyStateChange(ReadyStateChangeHandler handler) {
this.handler = handler;
}
public void setRequestHeader(String header, String value) {
String val = headers.get(header);
if (val == null) {
throw new IllegalArgumentException("set of unexpected header " + header);
}
if (!val.equals(value)) {
throw new IllegalArgumentException("set of header " + header
+ " to unexpected value " + value + ", not " + val);
}
headers.remove(header);
}
}
/**
* {@link XhrLoadingStrategy}, but without actual live XHRs.
*/
static class MockXhrLoadingStrategy extends XhrLoadingStrategy {
private static final String FRAGMENT_URL = "http://nowhere.net/fragment";
private ArrayList<MockXhr> xhrs;
public MockXhrLoadingStrategy(MockXhr... input) {
xhrs = new ArrayList<MockXhr>(Arrays.asList(input));
}
public void assertDone() {
if (xhrs.size() != 0) {
throw new IllegalStateException("leftover createXhr() data"
+ " (too few load retries?)");
}
}
/**
* Test stub; install succeeds unless text says otherwise.
*/
@Override
protected void gwtInstallCode(String text) {
if (MockXhr.INSTALL_FAILED_RESPONSE_TEXT.equals(text)) {
throw new RuntimeException(text);
}
}
/**
* Test stub; bypass the JSNI, but we're returning a (mock) URL.
*/
@Override
protected String gwtStartLoadingFragment(int fragment,
LoadTerminatedHandler LoadFinishedHandler) {
return FRAGMENT_URL;
}
@Override
protected MockableXMLHttpRequest createXhr() {
if (xhrs.size() == 0) {
throw new IllegalStateException("createXhr() underflow"
+ " (too many load retries?)");
}
return xhrs.remove(0);
}
}
/**
* Basic succeeds-on-first-try case.
*/
public void testNoRetrySucceeds() {
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
"200 Ok", true, true));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
fail();
}
});
xls.assertDone();
}
/**
* Fails irrevocably on first try; doesn't retry.
*/
public void testNoRetryFails() {
final boolean loadFailedCalled[] = new boolean[1];
loadFailedCalled[0] = false;
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
"Ok", true, false));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
loadFailedCalled[0] = true;
}
});
xls.assertDone();
if (!loadFailedCalled[0]) {
fail("should have failed to install, but didn't");
}
}
/**
* Needs some retries, but succeeds.
*/
public void testRetrySucceeds() {
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(0,
"Could not connect", false, false), new MockXhr(200, "Ok", true, true,
"Cache-Control", "no-cache"));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
fail();
}
});
xls.assertDone();
}
/**
* Needs retries, and never succeeds.
*/
public void testRetryFails() {
final boolean loadFailedCalled[] = new boolean[1];
loadFailedCalled[0] = false;
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(0,
"Could not connect", false, false), new MockXhr(0, "Could not connect",
false, false, "Cache-Control", "no-cache"), new MockXhr(0,
"Could not connect", false, false, "Cache-Control", "no-cache"));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
loadFailedCalled[0] = true;
}
});
xls.assertDone();
if (!loadFailedCalled[0]) {
fail("should have failed to install, but didn't");
}
}
/**
* A bizarre case we've seen in the wild...
*/
public void testNull200Case() {
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(200,
"Ok", false, false), new MockXhr(200, "Ok", false, false,
"Cache-Control", "no-cache"), new MockXhr(200, "Ok", true, true,
"Cache-Control", "no-cache"));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
fail();
}
});
xls.assertDone();
}
/**
* Check some HTTP status codes....
*/
public void testRetryCodes() {
MockXhrLoadingStrategy xls = new MockXhrLoadingStrategy(new MockXhr(500,
"Server Error", false, false), new MockXhr(404, "Not Found", false,
false, "Cache-Control", "no-cache"), new MockXhr(200, "Ok", true, true,
"Cache-Control", "no-cache"));
xls.startLoadingFragment(1, new LoadTerminatedHandler() {
public void loadTerminated(Throwable reason) {
fail();
}
});
xls.assertDone();
}
}