Improve correctness of UnicodeEscapingTest.
Splits testClientToServerBMP() into two methods to correctly report failures.
Removes support for Safari2.
Extends WebKit regex to support Safari4.
Patch by: bobv
Review by: jat, jgw
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5666 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
index fe14d16..fe069cf 100644
--- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
+++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -56,26 +56,18 @@
private static native JavaScriptObject getQuotingRegex() /*-{
// "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR
var ua = navigator.userAgent.toLowerCase();
- var webkitregex = /webkit\/([\d]+)/;
- var webkit = 0;
- var result = webkitregex.exec(ua);
- if (result) {
- webkit = parseInt(result[1]);
- }
if (ua.indexOf("android") != -1) {
// initial version of Android WebKit has a double-encoding bug for UTF8,
// so we have to encode every non-ASCII character.
// TODO(jat): revisit when this bug is fixed in Android
return /[\u0000\|\\\u0080-\uFFFF]/g;
- } else if (webkit < 522) {
- // Safari 2 doesn't handle \\uXXXX in regexes
- // TODO(jat): should iPhone be treated specially?
- return /[\x00\|\\]/g;
- } else if (webkit > 0) {
- // other WebKit-based browsers need some additional quoting
- return /[\u0000\|\\\u0300-\u036F\u0590-\u05FF\uD800-\uFFFF]/g;
+ } else if (ua.indexOf("webkit") != -1) {
+ // other WebKit-based browsers need some additional quoting due to combining
+ // forms and normalization (one codepoint being replaced with another).
+ // Verified with Safari 4.0.1 (5530.18)
+ return /[\u0000\|\\\u0300-\u03ff\u0590-\u05FF\u0600-\u06ff\u0730-\u074A\u07eb-\u07f3\u0940-\u0963\u0980-\u09ff\u0a00-\u0a7f\u0b00-\u0b7f\u0e00-\u0e7f\u0f00-\u0fff\u1900-\u194f\u1a00-\u1a1f\u1b00-\u1b7f\u1dc0-\u1dff\u1f00-\u1fff\u2000-\u206f\u20d0-\u20ff\u2100-\u214f\u2300-\u23ff\u2a00-\u2aff\u3000-\u303f\uD800-\uFFFF]/g;
} else {
- return /[\u0000\|\\\uD800-\uFFFF]/g;
+ return /[\u0000\|\\\uD800-\uFFFF]/g;
}
}-*/;
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
index f45777e..dcb8fbb 100644
--- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
+++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
@@ -19,11 +19,14 @@
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Test that any valid string can be sent via RPC in both directions.
*
- * TODO(jat): make unpaired surrogates work properly if it is possible to do
- * so on all browsers, then add them to this test.
+ * TODO(jat): make unpaired surrogates work properly if it is possible to do so
+ * on all browsers, then add them to this test.
*/
public class UnicodeEscapingTest extends GWTTestCase {
@@ -32,8 +35,8 @@
/**
* When doing the non-BMP test, we don't test every block of characters
- * because it takes too long - this is the increment to use. It is not a
- * power of two so we alter the alignment of the block of characters we skip.
+ * because it takes too long - this is the increment to use. It is not a power
+ * of two so we alter the alignment of the block of characters we skip.
*/
private static final int NON_BMP_TEST_INCREMENT = 8192 + 64;
@@ -60,28 +63,6 @@
return buf.toString();
}
- /*
- * Copied from HistoryTest.
- */
- private static native boolean isSafari2() /*-{
- var exp = / AppleWebKit\/([\d]+)/;
- var result = exp.exec(navigator.userAgent);
- if (result) {
- // The standard history implementation works fine on WebKit >= 522
- // (Safari 3 beta).
- if (parseInt(result[1]) >= 522) {
- return false;
- }
- }
-
- // The standard history implementation works just fine on the iPhone, which
- // unfortunately reports itself as WebKit/420+.
- if (navigator.userAgent.indexOf('iPhone') != -1) {
- return false;
- }
-
- return true;
- }-*/;
/**
* Verifies that the supplied string includes the requested code points.
*
@@ -98,8 +79,8 @@
}
int expectedLen = end - start;
int strLen = str.codePointCount(0, str.length());
- for (int i = 0, codePoint = start; i < strLen;
- i = Character.offsetByCodePoints(str, i, 1)) {
+ for (int i = 0, codePoint = start; i < strLen; i = Character.offsetByCodePoints(
+ str, i, 1)) {
int strCodePoint = str.codePointAt(i);
if (strCodePoint != codePoint) {
throw new InvalidCharacterException(i, codePoint, strCodePoint);
@@ -109,14 +90,16 @@
if (strLen < expectedLen) {
throw new InvalidCharacterException(strLen, start + strLen, -1);
} else if (expectedLen != strLen) {
- throw new RuntimeException("Too many characters returned on block from U+"
- + Integer.toHexString(start) + " to U+" + Integer.toHexString(end)
- + ": expected=" + expectedLen + ", actual=" + strLen);
+ throw new RuntimeException(
+ "Too many characters returned on block from U+"
+ + Integer.toHexString(start) + " to U+"
+ + Integer.toHexString(end) + ": expected=" + expectedLen
+ + ", actual=" + strLen);
}
}
+
private static UnicodeEscapingServiceAsync getService() {
- UnicodeEscapingServiceAsync service = GWT.create(
- UnicodeEscapingService.class);
+ UnicodeEscapingServiceAsync service = GWT.create(UnicodeEscapingService.class);
ServiceDefTarget target = (ServiceDefTarget) service;
target.setServiceEntryPoint(GWT.getModuleBaseURL() + "unicodeEscape");
return service;
@@ -136,30 +119,41 @@
* properly handles all BMP characters.
*
* Unpaired or improperly paired surrogates are not tested here, as some
- * browsers refuse to accept them. Properly paired surrogates are tested
- * in the non-BMP test.
- *
+ * browsers refuse to accept them. Properly paired surrogates are tested in
+ * the non-BMP test.
+ *
* Note that this does not test all possible combinations, which might be an
* issue, particularly with combining marks, though they should be logically
* equivalent in that case.
*
* @throws InvalidCharacterException
*/
- public void testClientToServerBMP() throws InvalidCharacterException {
+ public void testClientToServerBMPHigh() throws InvalidCharacterException {
delayTestFinish(TEST_FINISH_DELAY_MS);
- if (isSafari2()) {
- // Safari2 can't be fixed for many characters, including null
- // We only guarantee that basic ISO-Latin characters are unmolested.
- clientToServerVerifyRange(0x0001, 0x0300, CHARACTER_BLOCK_SIZE,
- CHARACTER_BLOCK_SIZE);
- } else {
- clientToServerVerifyRange(Character.MIN_CODE_POINT,
- Character.MIN_SURROGATE, CHARACTER_BLOCK_SIZE,
- CHARACTER_BLOCK_SIZE);
- clientToServerVerifyRange(Character.MAX_SURROGATE + 1,
- Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE,
- CHARACTER_BLOCK_SIZE);
- }
+ clientToServerVerifyRange(Character.MAX_SURROGATE + 1,
+ Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE,
+ CHARACTER_BLOCK_SIZE);
+ }
+
+ /**
+ * Generate strings containing ranges of characters and sends them to the
+ * server for verification. This ensures that client->server string escaping
+ * properly handles all BMP characters.
+ *
+ * Unpaired or improperly paired surrogates are not tested here, as some
+ * browsers refuse to accept them. Properly paired surrogates are tested in
+ * the non-BMP test.
+ *
+ * Note that this does not test all possible combinations, which might be an
+ * issue, particularly with combining marks, though they should be logically
+ * equivalent in that case.
+ *
+ * @throws InvalidCharacterException
+ */
+ public void testClientToServerBMPLow() throws InvalidCharacterException {
+ delayTestFinish(TEST_FINISH_DELAY_MS);
+ clientToServerVerifyRange(Character.MIN_CODE_POINT,
+ Character.MIN_SURROGATE, CHARACTER_BLOCK_SIZE, CHARACTER_BLOCK_SIZE);
}
/**
@@ -181,9 +175,9 @@
}
/**
- * Requests strings of CHARACTER_RANGE_SIZE from the server and validates
- * that the returned string length matches CHARACTER_RANGE_SIZE and that all
- * of the characters remain intact.
+ * Requests strings of CHARACTER_RANGE_SIZE from the server and validates that
+ * the returned string length matches CHARACTER_RANGE_SIZE and that all of the
+ * characters remain intact.
*
* Note that this does not test all possible combinations, which might be an
* issue, particularly with combining marks, though they should be logically
@@ -199,7 +193,7 @@
/**
* Requests strings of CHARACTER_RANGE_SIZE from the server and validates that
* the returned string length matches CHARACTER_RANGE_SIZE and that all of the
- * characters remain intact. Note that this test verifies non-BMP characters
+ * characters remain intact. Note that this test verifies non-BMP characters
* (ie, those which are represented as pairs of surrogates).
*
* Note that this does not test all possible combinations, which might be an
@@ -220,53 +214,78 @@
getService().verifyStringContainingCharacterRange(current, blockEnd,
getStringContainingCharacterRange(start, blockEnd),
new AsyncCallback<Boolean>() {
- public void onFailure(Throwable caught) {
- TestSetValidator.rethrowException(caught);
- }
+ List<Throwable> fails = new ArrayList<Throwable>();
- public void onSuccess(Boolean ignored) {
- current += step;
- if (current < end) {
- delayTestFinish(TEST_FINISH_DELAY_MS);
- int blockEnd = Math.min(end, current + size);
- try {
- getService().verifyStringContainingCharacterRange(current, blockEnd,
- getStringContainingCharacterRange(current, blockEnd), this);
- } catch (InvalidCharacterException e) {
- TestSetValidator.rethrowException(e);
+ public void onFailure(Throwable caught) {
+ fails.add(caught);
+ onSuccess(false);
}
- } else {
- finishTest();
- }
- }
- });
+
+ public void onSuccess(Boolean ignored) {
+ current += step;
+ if (current < end) {
+ delayTestFinish(TEST_FINISH_DELAY_MS);
+ int blockEnd = Math.min(end, current + size);
+ try {
+ getService().verifyStringContainingCharacterRange(current,
+ blockEnd,
+ getStringContainingCharacterRange(current, blockEnd), this);
+ } catch (InvalidCharacterException e) {
+ fails.add(e);
+ }
+ } else if (!fails.isEmpty()) {
+ StringBuilder msg = new StringBuilder();
+ for (Throwable t : fails) {
+ msg.append(t.getMessage()).append("\n");
+ }
+ TestSetValidator.rethrowException(new RuntimeException(
+ msg.toString()));
+ } else {
+ finishTest();
+ }
+ }
+ });
}
private void serverToClientVerify(final int start, final int end,
final int size, final int step) {
current = start;
- getService().getStringContainingCharacterRange(start, Math.min(end,
- current + size), new AsyncCallback<String>() {
- public void onFailure(Throwable caught) {
- TestSetValidator.rethrowException(caught);
- }
+ getService().getStringContainingCharacterRange(start,
+ Math.min(end, current + size), new AsyncCallback<String>() {
+ List<Throwable> fails = new ArrayList<Throwable>();
- public void onSuccess(String str) {
- try {
- verifyStringContainingCharacterRange(current, Math.min(end,
- current + size), str);
- } catch (InvalidCharacterException e) {
- TestSetValidator.rethrowException(e);
- }
- current += step;
- if (current < end) {
- delayTestFinish(TEST_FINISH_DELAY_MS);
- getService().getStringContainingCharacterRange(current,
- Math.min(end, current + size), this);
- } else {
- finishTest();
- }
- }
- });
+ public void onFailure(Throwable caught) {
+ fails.add(caught);
+ nextBatch();
+ }
+
+ public void onSuccess(String str) {
+ try {
+ verifyStringContainingCharacterRange(current, Math.min(end,
+ current + size), str);
+ } catch (InvalidCharacterException e) {
+ fails.add(e);
+ }
+ nextBatch();
+ }
+
+ private void nextBatch() {
+ current += step;
+ if (current < end) {
+ delayTestFinish(TEST_FINISH_DELAY_MS);
+ getService().getStringContainingCharacterRange(current,
+ Math.min(end, current + size), this);
+ } else if (!fails.isEmpty()) {
+ StringBuilder msg = new StringBuilder();
+ for (Throwable t : fails) {
+ msg.append(t.getMessage()).append("\n");
+ }
+ TestSetValidator.rethrowException(new RuntimeException(
+ msg.toString()));
+ } else {
+ finishTest();
+ }
+ }
+ });
}
}