/* * Copyright 2010 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.regexp.shared; import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; /** * Unit tests for {@link RegExp}. * * <p> * Tests the pure-Java implementation, using a hack that causes a GWTTestCase to * run as a vanilla JUnit TestCase (see {@link #getModuleName()}). The JS * implementation of RegExp is tested by {@link GwtRegExpTest}, which extends * this class. * <p> * The pure Java and GWT implementations are not 100% compatible. Some * discrepancies have been found when writing this test: search for DISCREPANCY * to find them. */ public class RegExpTest extends GWTTestCase { public static final String WORD_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; private static final String NOT_SPACE_CHARACTERS = allAsciiCharsBut( " \f\n\r\t\u000b\u00a0", 255); private static final String SPACE_CHARACTERS = " \f\n\r\t\u000b"; /** * Generates a string containing all characters up to the given limit but the * characters in the given string. * * @param exclude the characters not to return * @param limit the last character to include, typically 127 or 255 * @return an ASCII string */ private static String allAsciiCharsBut(String exclude, int limit) { StringBuilder sb = new StringBuilder(); // DISCREPANCY: character 0 is handled differently by Webkit for (char c = 1; c <= limit; c++) { if (!exclude.contains(String.valueOf(c))) { sb.append(c); } } return sb.toString(); } private RegExp regExp; // This is a hack to force a GWTTestCase to run as a vanilla JUnit TestCase. @Override public String getModuleName() { return null; } public void testCompile_duplicatedFlags() { checkCompileThrows("regexp", "igg", true); } public void testCompile_unknownFlags() { checkCompileThrows("regexp", "z", true); } public void testExec_atLeastNtimes() { regExp = RegExp.compile("a{3,}"); checkExecNoMatch("_a_"); checkExecNoMatch("_aa_"); checkExec("_aaa_", 1, "aaa"); checkExec("_aaaab_", 1, "aaaa"); } /** * Checks that backreferences with two digits are accepted. */ public void testExec_backreferenceMoreThanNine() { regExp = RegExp.compile("(1)(2)(3)(4)(5)(6)(7)(8)(9)(a)(b)\\1\\2\\3\\4\\5\\6\\7\\8\\9\\10\\11"); checkExec("123456789ab123456789ab", 0, "123456789ab123456789ab", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b"); } public void testExec_backreferenceNested() { regExp = RegExp.compile("(([ab])X\\2)([-,])\\1\\3\\2"); checkExec("_aXa-aXa-a_", 1, "aXa-aXa-a", "aXa", "a", "-"); checkExec("_aXa,aXa,a_", 1, "aXa,aXa,a", "aXa", "a", ","); checkExec("_bXb-bXb-b_", 1, "bXb-bXb-b", "bXb", "b", "-"); checkExecNoMatch("_aXa-bXb-a_"); checkExecNoMatch("_aXa,aXa,b_"); checkExecNoMatch("_aXa,aXa-a_"); } public void testExec_backreferenceOne() { regExp = RegExp.compile("([ab])X\\1"); checkExec("_aXa_", 1, "aXa", "a"); checkExec("_bXb_", 1, "bXb", "b"); checkExecNoMatch("_aXb_"); } public void testExec_carriageReturn() { checkAlias("\\r", "\r"); } public void testExec_characterSetList() { regExp = RegExp.compile("[ace]"); checkExec("abcde", 0, "a"); checkExec("XXcYY", 2, "c"); checkExecNoMatch("bdf"); } public void testExec_characterSetRange() { regExp = RegExp.compile("[a-ce-z]"); checkExec("abcde", 0, "a"); checkExec("XXcYY", 2, "c"); checkExecNoMatch("XXdYY"); } // DISCREPANCY: chrome does not support \cX /* public void testExec_controlCharacterInvalid() { // regExp = RegExp.compile("\\c5"); // checkExecNoMatch("c5"); // DISCREPANCY: Java specificity: \c accepts any character; \cX is an alias // for the character of code: asciiCode(X) ^ 64 // checkExecNoMatch(allAsciiCharsBut("")); checkExecNoMatch(allAsciiCharsBut(Character.toString((char)('5' ^ 64)), 255)); } public void testExec_controlCharacterValid() { regExp = RegExp.compile("\\cM"); // DISCREPANCY: does not work on Development Mode // checkExec("\r", 0, "\r"); checkExecNoMatch(allAsciiCharsBut("\r", 255)); } */ public void testExec_digit() { checkAlias("\\d", "0123456789"); } public void testExec_disjunction() { regExp = RegExp.compile("a|abc|def"); checkExec("_a_", 1, "a"); checkExec("_ab_", 1, "a"); checkExec("_abc_", 1, "a"); // First expression has precedence checkExec("_def_", 1, "def"); checkExecNoMatch("_de_"); } public void testExec_dotMultiLine() { testExec_dot("m"); } public void testExec_dotSingleLine() { testExec_dot(""); } public void testExec_emptyFlags() { regExp = RegExp.compile("a", ""); // DISCREPANCY: IE always sets lastIndex, even in non-global mode // regExp.setLastIndex(42); // checkExec("0a_3a_", 1, 42, "a"); checkExec("0a_3a_", 1, "a"); } public void testExec_escaping() { regExp = RegExp.compile("a\\(b"); checkExec("_a(b_", 1, "a(b"); checkExecNoMatch("_a\\(b_"); } public void testExec_formFeed() { checkAlias("\\f", "\f"); } public void testExec_fromNtoMtimes() { regExp = RegExp.compile("a{2,3}"); checkExecNoMatch("_a_"); checkExec("_aa_", 1, "aa"); checkExec("_aaa_", 1, "aaa"); checkExec("_aaaab_", 1, "aaa"); } public void testExec_global() { regExp = RegExp.compile("a", "g"); checkExec("0a_3a_", 1, 2, "a"); checkExec("0a_3a_", 4, 5, "a"); checkExecNoMatch("0a_3a_", 0); } public void testExec_greedinessDisjunction() { regExp = RegExp.compile("a|ab"); checkExec("_ab_", 1, "a"); // First expression has precedence } public void testExec_greedinessOptionalMatch() { regExp = RegExp.compile("ab?"); checkExec("_ab_", 1, "ab"); // Optional match has precedence } public void testExec_greedinessSeveralMatches() { regExp = RegExp.compile("a"); checkExec("_a_a_", 1, "a"); // First match returned } public void testExec_hexa2() { checkAlias("\\x41", "A"); checkAlias("\\x42", "B"); checkAlias("\\x20", " "); checkAlias("\\xff", "\377"); } public void testExec_lineFeed() { checkAlias("\\n", "\n"); } public void testExec_matchBeginningMultiLine() { regExp = RegExp.compile("^ab+", "m"); checkExec("abbc", 0, "abb"); checkExecNoMatch("_abbc"); checkExec("\nabbc\n", 1, "abb"); checkExec("xxx\nabbc", 4, "abb"); } public void testExec_matchBeginningSingleLine() { regExp = RegExp.compile("^ab+"); checkExec("abbc", 0, "abb"); checkExecNoMatch("_abbc"); checkExecNoMatch("\nabbc\n"); checkExecNoMatch("xxx\nabbc"); } public void testExec_matchEndMultiLine() { regExp = RegExp.compile("b+a$", "m"); checkExec("cbba", 1, "bba"); checkExecNoMatch("cbba_"); checkExec("\ncbba\n", 2, "bba"); checkExec("cbba\nxxx", 1, "bba"); } public void testExec_matchEndSingleLine() { regExp = RegExp.compile("b+a$"); checkExec("cbba", 1, "bba"); checkExecNoMatch("cbba_"); // DISCREPANCY: Java specificity: $ matches end of input string even if it // has a trailing \n // checkExecNoMatch("\ncbba\n"); // The discrepancy disappears if the trailing \n is doubled. checkExecNoMatch("\ncbba\n\n"); checkExecNoMatch("cbba\nxxx"); } public void testExec_negativeCharacterSetList() { regExp = RegExp.compile("[^ace]"); checkExec("abcde", 1, "b"); checkExec("aaBcc", 2, "B"); checkExec("bdf", 0, "b"); } public void testExec_negativeCharacterSetRange() { regExp = RegExp.compile("[^a-ce-z]"); checkExec("abcde", 3, "d"); checkExecNoMatch("xxcyy"); checkExec("xxdyy", 2, "d"); } public void testExec_negativeLookahead() { regExp = RegExp.compile("ab(?!cc)"); checkExecNoMatch("_abcc_"); checkExecNoMatch("_abccd_"); checkExec("_abc_", 1, "ab"); } public void testExec_negativeLookaheadComplex() { regExp = RegExp.compile("ab(?!cc).+"); checkExecNoMatch("_ab"); checkExec("_abX", 1, "abX"); checkExecNoMatch("_abcc"); checkExecNoMatch("_abccc"); checkExec("_abXcc", 1, "abXcc"); } public void testExec_negativeLookaheadNested() { regExp = RegExp.compile("ab(?=cc(?=d))"); checkExec("_abccd_", 1, "ab"); checkExecNoMatch("_abcc_"); } public void testExec_nestedCapturingGroups() { regExp = RegExp.compile("a(?:b(?:c+)d)e"); checkExec("_abcccde_", 1, "abcccde"); regExp = RegExp.compile("a(?:b(?:c+)?d)e"); checkExec("_abde_", 1, "abde"); checkExec("_abcccde_", 1, "abcccde"); checkExecNoMatch("_ac_"); } public void testExec_noFlags() { regExp = RegExp.compile("a"); // DISCREPANCY: IE always sets lastIndex, even in non-global mode // regExp.setLastIndex(42); // checkExec("0a_3a_", 1, 42, "a"); checkExec("0a_3a_", 1, "a"); } public void testExec_nonDigit() { checkAlias("\\D", allAsciiCharsBut("0123456789", 255)); } public void testExec_nonSpace() { checkAlias("\\S", NOT_SPACE_CHARACTERS, SPACE_CHARACTERS); } public void testExec_nonWord() { checkAlias("\\W", allAsciiCharsBut(WORD_CHARACTERS, 127), WORD_CHARACTERS); } public void testExec_nonWordBoundary() { regExp = RegExp.compile("\\BX"); checkExecNoMatch("ab X cd"); checkExecNoMatch("ab\fX cd"); checkExecNoMatch("ab\nX cd"); checkExecNoMatch("ab\rX cd"); checkExecNoMatch("ab\tX cd"); checkExecNoMatch("ab\13X cd"); checkExecNoMatch("ab\33X cd"); checkExecNoMatch("ab\34X cd"); for (int i = 0; i <= 32; i++) { checkExecNoMatch("ab" + (char) i + "X cd"); } checkExec("aX", 1, "X"); checkExec("_X", 1, "X"); checkExec(" X aX", 4, "X"); } public void testExec_nTimes() { regExp = RegExp.compile("a{3}"); checkExecNoMatch("_a_"); checkExecNoMatch("_aa_"); checkExec("_aaa_", 1, "aaa"); checkExec("_aaaab_", 1, "aaa"); } public void testExec_nullInput() { regExp = RegExp.compile("a", ""); // DISCREPANCY: IE always sets lastIndex, even in non-global mode // regExp.setLastIndex(42); // checkExecNoMatch(null, 42); checkExecNoMatch(null); regExp = RegExp.compile("a", "g"); checkExecNoMatch(null, 0); } public void testExec_oneCapturingGroup() { regExp = RegExp.compile("a(b+)c"); checkExec("_abbbc_", 1, "abbbc", "bbb"); checkExecNoMatch("_ac_"); } public void testExec_oneNonCapturingGroup() { regExp = RegExp.compile("a(?:b+c)?d"); checkExec("_abbcd_", 1, "abbcd"); checkExec("_ad_", 1, "ad"); } public void testExec_optionalCapturingGroup() { regExp = RegExp.compile("a(b+)?c"); checkExec("_abbbc_", 1, "abbbc", "bbb"); checkExec("_ac_", 1, "ac", null); } public void testExec_plus() { regExp = RegExp.compile("a[bc]+d"); checkExecNoMatch("ad"); checkExec("abd", 0, "abd"); checkExec("acd", 0, "acd"); checkExec("abcbcd", 0, "abcbcd"); checkExecNoMatch("abbbec"); } public void testExec_positiveLookahead() { regExp = RegExp.compile("ab(?=cc)"); checkExec("_abcc_", 1, "ab"); checkExec("_abccd_", 1, "ab"); checkExecNoMatch("_abc_"); } public void testExec_positiveLookaheadDouble() { regExp = RegExp.compile("ab(?=cc)cc(?=d)"); checkExec("_abccd_", 1, "abcc"); checkExecNoMatch("_abcc_"); } public void testExec_positiveLookaheadNested() { regExp = RegExp.compile("ab(?=cc(?=d))"); checkExec("_abccd_", 1, "ab"); checkExecNoMatch("_abcc_"); } public void testExec_questionMark() { regExp = RegExp.compile("a[bc]?d"); checkExec("ad", 0, "ad"); checkExec("abd", 0, "abd"); checkExec("acd", 0, "acd"); checkExecNoMatch("abcd"); checkExecNoMatch("aXd"); checkExecNoMatch("ab"); } public void testExec_space() { checkAlias("\\s", SPACE_CHARACTERS, NOT_SPACE_CHARACTERS); } public void testExec_star() { regExp = RegExp.compile("a[bc]*d"); checkExec("ad", 0, "ad"); checkExec("abd", 0, "abd"); checkExec("acd", 0, "acd"); checkExec("abcbcd", 0, "abcbcd"); checkExecNoMatch("abbbec"); } public void testExec_tab() { checkAlias("\\t", "\t"); } public void testExec_unicode2() { // DISCREPANCY: IE bug and Java specificity: '\'u HH is not recognized. // checkAlias("\\u41", "A"); // checkAlias("\\u42", "B"); // checkAlias("\\u20", " "); // checkAlias("\\uff", "\255"); } public void testExec_unicode4() { checkAlias("\\u0041", "A"); checkAlias("\\u0042", "B"); checkAlias("\\u0020", " "); checkAlias("\\u00ff", "\377"); checkAlias("\\u0100", "\u0100"); checkAlias("\\u1234", "\u1234"); checkAlias("\\uffff", "\uffff"); } public void testExec_verticalTab() { checkAlias("\\v", "\u000b"); } public void testExec_word() { checkAlias("\\w", WORD_CHARACTERS, allAsciiCharsBut(WORD_CHARACTERS, 127)); } public void testExec_wordBoundary() { regExp = RegExp.compile("\\bX"); checkExec("ab X cd", 3, "X"); checkExec("ab\fX cd", 3, "X"); checkExec("ab\nX cd", 3, "X"); checkExec("ab\rX cd", 3, "X"); checkExec("ab\tX cd", 3, "X"); checkExec("ab\13X cd", 3, "X"); checkExec("ab\33X cd", 3, "X"); checkExec("ab\34X cd", 3, "X"); // DISCREPANCY: character 0 is non-space on Webkit for (int i = 1; i <= 32; i++) { checkExec("ab" + (char) i + "X cd", 3, "X"); } checkExecNoMatch("aX"); checkExecNoMatch("_X"); checkExec("aX X", 3, "X"); } public void testExec_zero() { // DISCREPANCY: Java specificity: \0 is not recognized. // checkAlias("\\0", "\0"); } public void testGetGlobal() { assertTrue(RegExp.compile("test", "g").getGlobal()); assertFalse(RegExp.compile("test", "im").getGlobal()); assertFalse(RegExp.compile("test").getGlobal()); } public void testGetIgnoreCase() { assertTrue(RegExp.compile("test", "i").getIgnoreCase()); assertFalse(RegExp.compile("test", "gm").getIgnoreCase()); assertFalse(RegExp.compile("test").getIgnoreCase()); } public void testGetLastIndex_initiallyZero() { assertEquals(0, RegExp.compile("test").getLastIndex()); } public void testGetMultiline() { assertTrue(RegExp.compile("test", "m").getMultiline()); assertFalse(RegExp.compile("test", "ig").getMultiline()); assertFalse(RegExp.compile("test").getMultiline()); } public void testGetSource() { assertEquals("a(b|c)+d$", RegExp.compile("a(b|c)+d$").getSource()); } public void testReplace_backslashReplacement() { regExp = RegExp.compile("A+"); checkReplace("Abc", "\\x", "\\xbc"); } public void testReplace_dollarReplacement() { regExp = RegExp.compile("A+"); checkReplace("the A stops here", "$$", "the $ stops here"); checkReplace("the A stops here", "$$$$", "the $$ stops here"); } public void testReplace_doubleDigitGroupReplacement() { regExp = RegExp.compile("(1)(2)(3)(4)(5)(6)(7)(8)(9)(a)(b)(c)"); checkReplace("it's 123456789abc.", "[$11]", "it's [b]."); } public void testReplace_emptyGlobalRegExp() { regExp = RegExp.compile("", "g"); checkReplace("abc", "x", "xaxbxcx"); } public void testReplace_emptyInput() { regExp = RegExp.compile("A+"); checkReplace("", "x", ""); } public void testReplace_emptyRegExp() { regExp = RegExp.compile(""); checkReplace("abc", "x", "xabc"); } public void testReplace_emptyReplacement() { regExp = RegExp.compile("A+"); checkReplace("AAA", "", ""); } public void testReplace_first() { regExp = RegExp.compile("A+"); checkReplace("AAA AAA", "x", "x AAA"); } public void testReplace_global() { regExp = RegExp.compile("A+", "g"); checkReplace("AAA AAA", "x", "x x"); } public void testReplace_groupAmpersandReplacement() { regExp = RegExp.compile("A(B+)A"); checkReplace("he likes ABBBA", "'$&'", "he likes 'ABBBA'"); checkReplace("he likes ABBBA", "'$$&'", "he likes '$&'"); checkReplace("he likes ABBBA", "'$$$&'", "he likes '$ABBBA'"); checkReplace("he likes ABBBA", "'$$$$&'", "he likes '$$&'"); checkReplace("he likes ABBBA", "'$$$$$&'", "he likes '$$ABBBA'"); checkReplace("he likes ABBBA", "$&!", "he likes ABBBA!"); checkReplace("he likes ABBBA", "$$&!", "he likes $&!"); checkReplace("he likes ABBBA", "$$$&!", "he likes $ABBBA!"); checkReplace("he likes ABBBA", "$$$$&!", "he likes $$&!"); checkReplace("he likes ABBBA", "$$$$$&!", "he likes $$ABBBA!"); checkReplace("he likes ABBBA", "$&$&", "he likes ABBBAABBBA"); checkReplace("he likes ABBBA", "$$&$$&", "he likes $&$&"); checkReplace("he likes ABBBA", "$& $&\n$&\n\n$&", "he likes ABBBA ABBBA\nABBBA\n\nABBBA"); checkReplace("he likes ABBBA", "$$& $$&\n$$&\n\n$$&", "he likes $& $&\n$&\n\n$&"); } public void testReplace_groupReplacement() { regExp = RegExp.compile("A(B+)A"); checkReplace("he likes ABBBA", "$1", "he likes BBB"); } public void testReplace_noMatch() { regExp = RegExp.compile("A+"); checkReplace("none here", "x", "none here"); } public void testReplace_unsupportedReplacement() { regExp = RegExp.compile("foo"); checkReplaceThrows("", "$`", true); checkReplaceThrows("", "($`)", true); checkReplaceThrows("", "$$$`", true); checkReplaceThrows("", "($$$`)", true); checkReplaceThrows("", "$$$$$`", true); checkReplaceThrows("", "($$$$$`)", true); checkReplaceThrows("", "\n$`", true); checkReplaceThrows("", "\n\n$`", true); checkReplaceThrows("", " $`", true); checkReplace("foo", "$$`$$`", "$`$`"); checkReplace("foo", "$$$$`$$$$`", "$$`$$`"); checkReplace("foo", "\n$$`", "\n$`"); checkReplace("foo", "\n\n$$`", "\n\n$`"); checkReplace("foo", " $$`", " $`"); checkReplaceThrows("", "$'", true); checkReplaceThrows("", "($')", true); checkReplaceThrows("", "$$$'", true); checkReplaceThrows("", "($$$')", true); checkReplaceThrows("", "$$$$$'", true); checkReplaceThrows("", "($$$$$')", true); checkReplaceThrows("", "\n$'", true); checkReplaceThrows("", "\n\n$'", true); checkReplaceThrows("", " $'", true); checkReplace("foo", "$$'$$'", "$'$'"); checkReplace("foo", "$$$$'$$$$'", "$$'$$'"); checkReplace("foo", "\n$$'", "\n$'"); checkReplace("foo", "\n\n$$'", "\n\n$'"); checkReplace("foo", " $$'", " $'"); } public void testSetLastIndex() { regExp = RegExp.compile("test"); regExp.setLastIndex(3); assertEquals(3, regExp.getLastIndex()); } public void testSplit_emptyInput() { regExp = RegExp.compile(","); checkSplit("", ""); } public void testSplit_emptyParts() { regExp = RegExp.compile(","); // DISCREPANCY: IE discards empty parts // checkSplit(",a,,b,", "", "a", "", "b", ""); } public void testSplit_emptySeparator() { regExp = RegExp.compile(""); checkSplit("ab", "a", "b"); } public void testSplit_emptySeparatorExactLimit() { regExp = RegExp.compile(""); checkSplit("ab", 2, "a", "b"); } public void testSplit_emptySeparatorHighLimit() { regExp = RegExp.compile(""); checkSplit("ab", 3, "a", "b"); } public void testSplit_emptySeparatorLowLimit() { regExp = RegExp.compile(""); checkSplit("ab", 1, "a"); } public void testSplit_emptySeparatorZeroLimit() { regExp = RegExp.compile(""); checkSplit("ab", 0); } public void testSplit_exactLimit() { regExp = RegExp.compile(","); checkSplit("a,b", 2, "a", "b"); } public void testSplit_highLimit() { regExp = RegExp.compile(","); checkSplit("a,b", 3, "a", "b"); } public void testSplit_lowLimit() { regExp = RegExp.compile(","); checkSplit("a,b", 1, "a"); } public void testSplit_negLimit() { regExp = RegExp.compile(","); checkSplit("a,b", -1, "a", "b"); } public void testSplit_noMatch() { regExp = RegExp.compile(","); checkSplit("ab", "ab"); } public void testSplit_regExpSeparator() { regExp = RegExp.compile(" +|,"); checkSplit("a b,c", "a", "b", "c"); } public void testSplit_veryHighLimit() { regExp = RegExp.compile(","); checkSplit("a,b", 1000, "a", "b"); } public void testSplit_zeroLimit() { regExp = RegExp.compile(","); checkSplit("a,b", 0); } public void testTest() { assertTrue(RegExp.compile("test").test("test")); assertTrue(RegExp.compile("test", "i").test("TeSt")); assertFalse(RegExp.compile("test").test("mismatch")); assertTrue(RegExp.compile("[a-z]+").test("abc")); assertFalse(RegExp.compile("[a-z]+").test("42")); } /** * Checks that a regular expression matches all characters of a string and no * other. * * @param regexp the regular expression * @param matched all ASCII characters the regexp must exactly match, on all * browsers and in Java */ private void checkAlias(String regexp, String matched) { checkAlias(regexp, matched, allAsciiCharsBut(matched, 255)); } /** * Checks that a regular expression matches all characters of a string and * none of another. * * <p> * In theory {@code matched} and {@code notMatched} should be the same. In * practice, discrepancies of regular expressions implementation across * browsers and in Java force to ignore some characters. This leads {@code * matched} to be a subset of {@code notMatched}. * * @param regexp the regular expression * @param matched all characters the regexp must match, on all browsers and in * Java * @param notMatched all characters the regexp must not match, on all browsers * and in Java */ private void checkAlias(String regexp, String matched, String notMatched) { regExp = RegExp.compile(regexp + "+"); checkExec(matched, 0, matched); checkExecNoMatch(notMatched); } private void checkCompileThrows(String regexp, String flags, boolean onlyPureJava) { boolean thrown = false; try { RegExp.compile(regexp, flags); } catch (RuntimeException e) { thrown = true; } if (!onlyPureJava || !GWT.isClient()) { assertTrue(thrown); } } private void checkExec(String input, int expectedIndex, int expectedLastIndex, String... expectedGroups) { MatchResult matchResult = regExp.exec(input); assertNotNull("Match expected", matchResult); assertEquals("Wrong result input", input, matchResult.getInput()); assertEquals("Wrong result index", expectedIndex, matchResult.getIndex()); if (expectedLastIndex >= 0) { assertEquals("Wrong last index", expectedLastIndex, regExp.getLastIndex()); } assertEquals("Wrong group count", expectedGroups.length, matchResult.getGroupCount()); for (int group = 0; group < expectedGroups.length; group++) { String expectedGroup = expectedGroups[group]; String actualGroup = matchResult.getGroup(group); if (expectedGroup == null && "".equals(actualGroup)) { // IE sets non-matching groups to "" instead of null. } else { assertEquals("Wrong group " + group, expectedGroup, actualGroup); } } } private void checkExec(String input, int expectedIndex, String... expectedGroups) { checkExec(input, expectedIndex, -1, expectedGroups); } private void checkExecNoMatch(String input) { checkExecNoMatch(input, 0); } private void checkExecNoMatch(String input, int expectedLastIndex) { MatchResult matchResult = regExp.exec(input); assertNull(matchResult); assertEquals("Wrong last index", expectedLastIndex, regExp.getLastIndex()); } private void checkReplace(String input, String replacement, String expected) { String result = regExp.replace(input, replacement); assertNotNull("Replace result expected", result); assertEquals("Wrong replace result", expected, result); } private void checkReplaceThrows(String input, String replacement, boolean onlyPureJava) { boolean thrown = false; try { regExp.replace(input, replacement); } catch (RuntimeException e) { thrown = true; } if (!onlyPureJava || !GWT.isClient()) { assertTrue(thrown); } } private void checkSplit(SplitResult splitResult, String[] expectedParts) { assertNotNull("Split result expected", splitResult); assertEquals("Wrong result length", expectedParts.length, splitResult.length()); for (int i = 0; i < expectedParts.length; i++) { String expectedPart = expectedParts[i]; String actualPart = splitResult.get(i); assertNotNull("Split part expected", actualPart); assertEquals("Wrong split part " + i, expectedPart, actualPart); } } private void checkSplit(String input, int limit, String... expectedParts) { SplitResult splitResult = regExp.split(input, limit); checkSplit(splitResult, expectedParts); } private void checkSplit(String input, String... expectedParts) { SplitResult splitResult = regExp.split(input); checkSplit(splitResult, expectedParts); } private void testExec_dot(String flags) { regExp = RegExp.compile("a.c", flags); checkExecNoMatch("ac"); checkExec("abc", 0, "abc"); checkExecNoMatch("a\nc"); // DISCREPANCY: Firefox bug: '.' should not match '\r' // checkExec("a\rc", 0, "a\rc"); checkExecNoMatch("a\r\nc"); } }