/* * Copyright (C) 2008 The Guava Authors * * 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.common.base; import static com.google.common.base.CharMatcher.anyOf; import static com.google.common.base.CharMatcher.forPredicate; import static com.google.common.base.CharMatcher.inRange; import static com.google.common.base.CharMatcher.is; import static com.google.common.base.CharMatcher.isNot; import static com.google.common.base.CharMatcher.noneOf; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.testing.NullPointerTester; import junit.framework.AssertionFailedError; import junit.framework.TestCase; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; /** * Unit test for {@link CharMatcher}. * * @author Kevin Bourrillion */ @GwtCompatible(emulated = true) public class CharMatcherTest extends TestCase { @GwtIncompatible("NullPointerTester") public void testStaticNullPointers() throws Exception { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(CharMatcher.class); tester.testAllPublicInstanceMethods(CharMatcher.ANY); tester.testAllPublicInstanceMethods(CharMatcher.anyOf("abc")); } private static final CharMatcher WHATEVER = new CharMatcher() { @Override public boolean matches(char c) { throw new AssertionFailedError( "You weren't supposed to actually invoke me!"); } }; public void testAnyAndNone_logicalOps() throws Exception { // These are testing behavior that's never promised by the API, but since // we're lucky enough that these do pass, it saves us from having to write // more excruciating tests! Hooray! assertSame(CharMatcher.ANY, CharMatcher.NONE.negate()); assertSame(CharMatcher.NONE, CharMatcher.ANY.negate()); assertSame(WHATEVER, CharMatcher.ANY.and(WHATEVER)); assertSame(CharMatcher.ANY, CharMatcher.ANY.or(WHATEVER)); assertSame(CharMatcher.NONE, CharMatcher.NONE.and(WHATEVER)); assertSame(WHATEVER, CharMatcher.NONE.or(WHATEVER)); } // The rest of the behavior of ANY and NONE will be covered in the tests for // the text processing methods below. // The next tests require ICU4J and have, at least for now, been sliced out // of the open-source view of the tests. @GwtIncompatible("Character.isISOControl") public void testJavaIsoControl() { for (int c = 0; c <= Character.MAX_VALUE; c++) { assertEquals("" + c, Character.isISOControl(c), CharMatcher.JAVA_ISO_CONTROL.matches((char) c)); } } // Omitting tests for the rest of the JAVA_* constants as these are defined // as extremely straightforward pass-throughs to the JDK methods. // We're testing the is(), isNot(), anyOf(), noneOf() and inRange() methods // below by testing their text-processing methods. // The organization of this test class is unusual, as it's not done by // method, but by overall "scenario". Also, the variety of actual tests we // do borders on absurd overkill. Better safe than sorry, though? @GwtIncompatible("java.util.BitSet") public void testSetBits() { doTestSetBits(CharMatcher.ANY); doTestSetBits(CharMatcher.NONE); doTestSetBits(is('a')); doTestSetBits(isNot('a')); doTestSetBits(anyOf("")); doTestSetBits(anyOf("x")); doTestSetBits(anyOf("xy")); doTestSetBits(anyOf("CharMatcher")); doTestSetBits(noneOf("CharMatcher")); doTestSetBits(inRange('n', 'q')); doTestSetBits(forPredicate(Predicates.equalTo('c'))); doTestSetBits(CharMatcher.ASCII); doTestSetBits(CharMatcher.DIGIT); doTestSetBits(CharMatcher.INVISIBLE); doTestSetBits(inRange('A', 'Z').and(inRange('F', 'K').negate())); } @GwtIncompatible("java.util.BitSet") private void doTestSetBits(CharMatcher matcher) { BitSet bitset = new BitSet(); matcher.setBits(bitset); for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; i++) { assertEquals(matcher.matches((char) i), bitset.get(i)); } } public void testEmpty() throws Exception { doTestEmpty(CharMatcher.ANY); doTestEmpty(CharMatcher.NONE); doTestEmpty(is('a')); doTestEmpty(isNot('a')); doTestEmpty(anyOf("")); doTestEmpty(anyOf("x")); doTestEmpty(anyOf("xy")); doTestEmpty(anyOf("CharMatcher")); doTestEmpty(noneOf("CharMatcher")); doTestEmpty(inRange('n', 'q')); doTestEmpty(forPredicate(Predicates.equalTo('c'))); } @GwtIncompatible("NullPointerTester") public void testNull() throws Exception { doTestNull(CharMatcher.ANY); doTestNull(CharMatcher.NONE); doTestNull(is('a')); doTestNull(isNot('a')); doTestNull(anyOf("")); doTestNull(anyOf("x")); doTestNull(anyOf("xy")); doTestNull(anyOf("CharMatcher")); doTestNull(noneOf("CharMatcher")); doTestNull(inRange('n', 'q')); doTestNull(forPredicate(Predicates.equalTo('c'))); } private void doTestEmpty(CharMatcher matcher) throws Exception { reallyTestEmpty(matcher); reallyTestEmpty(matcher.negate()); reallyTestEmpty(matcher.precomputed()); } private void reallyTestEmpty(CharMatcher matcher) throws Exception { assertEquals(-1, matcher.indexIn("")); assertEquals(-1, matcher.indexIn("", 0)); try { matcher.indexIn("", 1); fail(); } catch (IndexOutOfBoundsException expected) { } try { matcher.indexIn("", -1); fail(); } catch (IndexOutOfBoundsException expected) { } assertEquals(-1, matcher.lastIndexIn("")); assertFalse(matcher.matchesAnyOf("")); assertTrue(matcher.matchesAllOf("")); assertTrue(matcher.matchesNoneOf("")); assertEquals("", matcher.removeFrom("")); assertEquals("", matcher.replaceFrom("", 'z')); assertEquals("", matcher.replaceFrom("", "ZZ")); assertEquals("", matcher.trimFrom("")); assertEquals(0, matcher.countIn("")); } @GwtIncompatible("NullPointerTester") private void doTestNull(CharMatcher matcher) throws Exception { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicInstanceMethods(matcher); } public void testNoMatches() { doTestNoMatches(CharMatcher.NONE, "blah"); doTestNoMatches(is('a'), "bcde"); doTestNoMatches(isNot('a'), "aaaa"); doTestNoMatches(anyOf(""), "abcd"); doTestNoMatches(anyOf("x"), "abcd"); doTestNoMatches(anyOf("xy"), "abcd"); doTestNoMatches(anyOf("CharMatcher"), "zxqy"); doTestNoMatches(noneOf("CharMatcher"), "ChMa"); doTestNoMatches(inRange('p', 'x'), "mom"); doTestNoMatches(forPredicate(Predicates.equalTo('c')), "abe"); doTestNoMatches(inRange('A', 'Z').and(inRange('F', 'K').negate()), "F1a"); doTestNoMatches(CharMatcher.DIGIT, "\tAz()"); doTestNoMatches(CharMatcher.JAVA_DIGIT, "\tAz()"); doTestNoMatches(CharMatcher.DIGIT.and(CharMatcher.ASCII), "\tAz()"); doTestNoMatches(CharMatcher.SINGLE_WIDTH, "\u05bf\u3000"); } private void doTestNoMatches(CharMatcher matcher, String s) { reallyTestNoMatches(matcher, s); reallyTestAllMatches(matcher.negate(), s); reallyTestNoMatches(matcher.precomputed(), s); reallyTestAllMatches(matcher.negate().precomputed(), s); reallyTestAllMatches(matcher.precomputed().negate(), s); reallyTestNoMatches(forPredicate(matcher), s); reallyTestNoMatches(matcher, new StringBuilder(s)); } public void testAllMatches() { doTestAllMatches(CharMatcher.ANY, "blah"); doTestAllMatches(isNot('a'), "bcde"); doTestAllMatches(is('a'), "aaaa"); doTestAllMatches(noneOf("CharMatcher"), "zxqy"); doTestAllMatches(anyOf("x"), "xxxx"); doTestAllMatches(anyOf("xy"), "xyyx"); doTestAllMatches(anyOf("CharMatcher"), "ChMa"); doTestAllMatches(inRange('m', 'p'), "mom"); doTestAllMatches(forPredicate(Predicates.equalTo('c')), "ccc"); doTestAllMatches(CharMatcher.DIGIT, "0123456789\u0ED0\u1B59"); doTestAllMatches(CharMatcher.JAVA_DIGIT, "0123456789"); doTestAllMatches(CharMatcher.DIGIT.and(CharMatcher.ASCII), "0123456789"); doTestAllMatches(CharMatcher.SINGLE_WIDTH, "\t0123ABCdef~\u00A0\u2111"); } private void doTestAllMatches(CharMatcher matcher, String s) { reallyTestAllMatches(matcher, s); reallyTestNoMatches(matcher.negate(), s); reallyTestAllMatches(matcher.precomputed(), s); reallyTestNoMatches(matcher.negate().precomputed(), s); reallyTestNoMatches(matcher.precomputed().negate(), s); reallyTestAllMatches(forPredicate(matcher), s); reallyTestAllMatches(matcher, new StringBuilder(s)); } private void reallyTestNoMatches(CharMatcher matcher, CharSequence s) { assertFalse(matcher.matches(s.charAt(0))); assertEquals(-1, matcher.indexIn(s)); assertEquals(-1, matcher.indexIn(s, 0)); assertEquals(-1, matcher.indexIn(s, 1)); assertEquals(-1, matcher.indexIn(s, s.length())); try { matcher.indexIn(s, s.length() + 1); fail(); } catch (IndexOutOfBoundsException expected) { } try { matcher.indexIn(s, -1); fail(); } catch (IndexOutOfBoundsException expected) { } assertEquals(-1, matcher.lastIndexIn(s)); assertFalse(matcher.matchesAnyOf(s)); assertFalse(matcher.matchesAllOf(s)); assertTrue(matcher.matchesNoneOf(s)); assertEquals(s.toString(), matcher.removeFrom(s)); assertEquals(s.toString(), matcher.replaceFrom(s, 'z')); assertEquals(s.toString(), matcher.replaceFrom(s, "ZZ")); assertEquals(s.toString(), matcher.trimFrom(s)); assertEquals(0, matcher.countIn(s)); } private void reallyTestAllMatches(CharMatcher matcher, CharSequence s) { assertTrue(matcher.matches(s.charAt(0))); assertEquals(0, matcher.indexIn(s)); assertEquals(0, matcher.indexIn(s, 0)); assertEquals(1, matcher.indexIn(s, 1)); assertEquals(-1, matcher.indexIn(s, s.length())); assertEquals(s.length() - 1, matcher.lastIndexIn(s)); assertTrue(matcher.matchesAnyOf(s)); assertTrue(matcher.matchesAllOf(s)); assertFalse(matcher.matchesNoneOf(s)); assertEquals("", matcher.removeFrom(s)); assertEquals(Strings.repeat("z", s.length()), matcher.replaceFrom(s, 'z')); assertEquals(Strings.repeat("ZZ", s.length()), matcher.replaceFrom(s, "ZZ")); assertEquals("", matcher.trimFrom(s)); assertEquals(s.length(), matcher.countIn(s)); } public void testGeneral() { doTestGeneral(is('a'), 'a', 'b'); doTestGeneral(isNot('a'), 'b', 'a'); doTestGeneral(anyOf("x"), 'x', 'z'); doTestGeneral(anyOf("xy"), 'y', 'z'); doTestGeneral(anyOf("CharMatcher"), 'C', 'z'); doTestGeneral(noneOf("CharMatcher"), 'z', 'C'); doTestGeneral(inRange('p', 'x'), 'q', 'z'); } private void doTestGeneral(CharMatcher matcher, char match, char noMatch) { doTestOneCharMatch(matcher, "" + match); doTestOneCharNoMatch(matcher, "" + noMatch); doTestMatchThenNoMatch(matcher, "" + match + noMatch); doTestNoMatchThenMatch(matcher, "" + noMatch + match); } private void doTestOneCharMatch(CharMatcher matcher, String s) { reallyTestOneCharMatch(matcher, s); reallyTestOneCharNoMatch(matcher.negate(), s); reallyTestOneCharMatch(matcher.precomputed(), s); reallyTestOneCharNoMatch(matcher.negate().precomputed(), s); reallyTestOneCharNoMatch(matcher.precomputed().negate(), s); } private void doTestOneCharNoMatch(CharMatcher matcher, String s) { reallyTestOneCharNoMatch(matcher, s); reallyTestOneCharMatch(matcher.negate(), s); reallyTestOneCharNoMatch(matcher.precomputed(), s); reallyTestOneCharMatch(matcher.negate().precomputed(), s); reallyTestOneCharMatch(matcher.precomputed().negate(), s); } private void doTestMatchThenNoMatch(CharMatcher matcher, String s) { reallyTestMatchThenNoMatch(matcher, s); reallyTestNoMatchThenMatch(matcher.negate(), s); reallyTestMatchThenNoMatch(matcher.precomputed(), s); reallyTestNoMatchThenMatch(matcher.negate().precomputed(), s); reallyTestNoMatchThenMatch(matcher.precomputed().negate(), s); } private void doTestNoMatchThenMatch(CharMatcher matcher, String s) { reallyTestNoMatchThenMatch(matcher, s); reallyTestMatchThenNoMatch(matcher.negate(), s); reallyTestNoMatchThenMatch(matcher.precomputed(), s); reallyTestMatchThenNoMatch(matcher.negate().precomputed(), s); reallyTestMatchThenNoMatch(matcher.precomputed().negate(), s); } private void reallyTestOneCharMatch(CharMatcher matcher, String s) { assertTrue(matcher.matches(s.charAt(0))); assertTrue(matcher.apply(s.charAt(0))); assertEquals(0, matcher.indexIn(s)); assertEquals(0, matcher.indexIn(s, 0)); assertEquals(-1, matcher.indexIn(s, 1)); assertEquals(0, matcher.lastIndexIn(s)); assertTrue(matcher.matchesAnyOf(s)); assertTrue(matcher.matchesAllOf(s)); assertFalse(matcher.matchesNoneOf(s)); assertEquals("", matcher.removeFrom(s)); assertEquals("z", matcher.replaceFrom(s, 'z')); assertEquals("ZZ", matcher.replaceFrom(s, "ZZ")); assertEquals("", matcher.trimFrom(s)); assertEquals(1, matcher.countIn(s)); } private void reallyTestOneCharNoMatch(CharMatcher matcher, String s) { assertFalse(matcher.matches(s.charAt(0))); assertFalse(matcher.apply(s.charAt(0))); assertEquals(-1, matcher.indexIn(s)); assertEquals(-1, matcher.indexIn(s, 0)); assertEquals(-1, matcher.indexIn(s, 1)); assertEquals(-1, matcher.lastIndexIn(s)); assertFalse(matcher.matchesAnyOf(s)); assertFalse(matcher.matchesAllOf(s)); assertTrue(matcher.matchesNoneOf(s)); // Note: only 'assertEquals' is promised by the API. Although they could // have been assertSame() on the server side, they have to be assertEquals // in GWT, because of GWT issue 4491. assertEquals(s, matcher.removeFrom(s)); assertEquals(s, matcher.replaceFrom(s, 'z')); assertEquals(s, matcher.replaceFrom(s, "ZZ")); assertEquals(s, matcher.trimFrom(s)); assertEquals(0, matcher.countIn(s)); } private void reallyTestMatchThenNoMatch(CharMatcher matcher, String s) { assertEquals(0, matcher.indexIn(s)); assertEquals(0, matcher.indexIn(s, 0)); assertEquals(-1, matcher.indexIn(s, 1)); assertEquals(-1, matcher.indexIn(s, 2)); assertEquals(0, matcher.lastIndexIn(s)); assertTrue(matcher.matchesAnyOf(s)); assertFalse(matcher.matchesAllOf(s)); assertFalse(matcher.matchesNoneOf(s)); assertEquals(s.substring(1), matcher.removeFrom(s)); assertEquals("z" + s.substring(1), matcher.replaceFrom(s, 'z')); assertEquals("ZZ" + s.substring(1), matcher.replaceFrom(s, "ZZ")); assertEquals(s.substring(1), matcher.trimFrom(s)); assertEquals(1, matcher.countIn(s)); } private void reallyTestNoMatchThenMatch(CharMatcher matcher, String s) { assertEquals(1, matcher.indexIn(s)); assertEquals(1, matcher.indexIn(s, 0)); assertEquals(1, matcher.indexIn(s, 1)); assertEquals(-1, matcher.indexIn(s, 2)); assertEquals(1, matcher.lastIndexIn(s)); assertTrue(matcher.matchesAnyOf(s)); assertFalse(matcher.matchesAllOf(s)); assertFalse(matcher.matchesNoneOf(s)); assertEquals(s.substring(0, 1), matcher.removeFrom(s)); assertEquals(s.substring(0, 1) + "z", matcher.replaceFrom(s, 'z')); assertEquals(s.substring(0, 1) + "ZZ", matcher.replaceFrom(s, "ZZ")); assertEquals(s.substring(0, 1), matcher.trimFrom(s)); assertEquals(1, matcher.countIn(s)); } // Test collapse() a little differently than the rest, as we really want to // cover lots of different configurations of input text public void testCollapse() { // collapsing groups of - into _ doTestCollapse("-", "_"); doTestCollapse("x-", "x_"); doTestCollapse("-x", "_x"); doTestCollapse("--", "_"); doTestCollapse("x--", "x_"); doTestCollapse("--x", "_x"); doTestCollapse("-x-", "_x_"); doTestCollapse("x-x", "x_x"); doTestCollapse("---", "_"); doTestCollapse("--x-", "_x_"); doTestCollapse("--xx", "_xx"); doTestCollapse("-x--", "_x_"); doTestCollapse("-x-x", "_x_x"); doTestCollapse("-xx-", "_xx_"); doTestCollapse("x--x", "x_x"); doTestCollapse("x-x-", "x_x_"); doTestCollapse("x-xx", "x_xx"); doTestCollapse("x-x--xx---x----x", "x_x_xx_x_x"); doTestCollapseWithNoChange(""); doTestCollapseWithNoChange("x"); doTestCollapseWithNoChange("xx"); } private void doTestCollapse(String in, String out) { // Try a few different matchers which all match '-' and not 'x' assertEquals(out, is('-').collapseFrom(in, '_')); assertEquals(out, is('-').or(is('#')).collapseFrom(in, '_')); assertEquals(out, isNot('x').collapseFrom(in, '_')); assertEquals(out, is('x').negate().collapseFrom(in, '_')); assertEquals(out, anyOf("-").collapseFrom(in, '_')); assertEquals(out, anyOf("-#").collapseFrom(in, '_')); assertEquals(out, anyOf("-#123").collapseFrom(in, '_')); } private void doTestCollapseWithNoChange(String inout) { /* * Note: assertSame(), not promised by the spec, happens to work with the * current implementation because String.toString() promises to return * |this|. However, GWT bug 4491 keeps it from working there, so we stick * with assertEquals(). */ assertEquals(inout, is('-').collapseFrom(inout, '_')); assertEquals(inout, is('-').or(is('#')).collapseFrom(inout, '_')); assertEquals(inout, isNot('x').collapseFrom(inout, '_')); assertEquals(inout, is('x').negate().collapseFrom(inout, '_')); assertEquals(inout, anyOf("-").collapseFrom(inout, '_')); assertEquals(inout, anyOf("-#").collapseFrom(inout, '_')); assertEquals(inout, anyOf("-#123").collapseFrom(inout, '_')); assertEquals(inout, CharMatcher.NONE.collapseFrom(inout, '_')); } public void testCollapse_any() { assertEquals("", CharMatcher.ANY.collapseFrom("", '_')); assertEquals("_", CharMatcher.ANY.collapseFrom("a", '_')); assertEquals("_", CharMatcher.ANY.collapseFrom("ab", '_')); assertEquals("_", CharMatcher.ANY.collapseFrom("abcd", '_')); } public void testTrimFrom() { // trimming - doTestTrimFrom("-", ""); doTestTrimFrom("x-", "x"); doTestTrimFrom("-x", "x"); doTestTrimFrom("--", ""); doTestTrimFrom("x--", "x"); doTestTrimFrom("--x", "x"); doTestTrimFrom("-x-", "x"); doTestTrimFrom("x-x", "x-x"); doTestTrimFrom("---", ""); doTestTrimFrom("--x-", "x"); doTestTrimFrom("--xx", "xx"); doTestTrimFrom("-x--", "x"); doTestTrimFrom("-x-x", "x-x"); doTestTrimFrom("-xx-", "xx"); doTestTrimFrom("x--x", "x--x"); doTestTrimFrom("x-x-", "x-x"); doTestTrimFrom("x-xx", "x-xx"); doTestTrimFrom("x-x--xx---x----x", "x-x--xx---x----x"); // additional testing using the doc example assertEquals("cat", anyOf("ab").trimFrom("abacatbab")); } private void doTestTrimFrom(String in, String out) { // Try a few different matchers which all match '-' and not 'x' assertEquals(out, is('-').trimFrom(in)); assertEquals(out, is('-').or(is('#')).trimFrom(in)); assertEquals(out, isNot('x').trimFrom(in)); assertEquals(out, is('x').negate().trimFrom(in)); assertEquals(out, anyOf("-").trimFrom(in)); assertEquals(out, anyOf("-#").trimFrom(in)); assertEquals(out, anyOf("-#123").trimFrom(in)); } public void testTrimLeadingFrom() { // trimming - doTestTrimLeadingFrom("-", ""); doTestTrimLeadingFrom("x-", "x-"); doTestTrimLeadingFrom("-x", "x"); doTestTrimLeadingFrom("--", ""); doTestTrimLeadingFrom("x--", "x--"); doTestTrimLeadingFrom("--x", "x"); doTestTrimLeadingFrom("-x-", "x-"); doTestTrimLeadingFrom("x-x", "x-x"); doTestTrimLeadingFrom("---", ""); doTestTrimLeadingFrom("--x-", "x-"); doTestTrimLeadingFrom("--xx", "xx"); doTestTrimLeadingFrom("-x--", "x--"); doTestTrimLeadingFrom("-x-x", "x-x"); doTestTrimLeadingFrom("-xx-", "xx-"); doTestTrimLeadingFrom("x--x", "x--x"); doTestTrimLeadingFrom("x-x-", "x-x-"); doTestTrimLeadingFrom("x-xx", "x-xx"); doTestTrimLeadingFrom("x-x--xx---x----x", "x-x--xx---x----x"); // additional testing using the doc example assertEquals("catbab", anyOf("ab").trimLeadingFrom("abacatbab")); } private void doTestTrimLeadingFrom(String in, String out) { // Try a few different matchers which all match '-' and not 'x' assertEquals(out, is('-').trimLeadingFrom(in)); assertEquals(out, is('-').or(is('#')).trimLeadingFrom(in)); assertEquals(out, isNot('x').trimLeadingFrom(in)); assertEquals(out, is('x').negate().trimLeadingFrom(in)); assertEquals(out, anyOf("-#").trimLeadingFrom(in)); assertEquals(out, anyOf("-#123").trimLeadingFrom(in)); } public void testTrimTrailingFrom() { // trimming - doTestTrimTrailingFrom("-", ""); doTestTrimTrailingFrom("x-", "x"); doTestTrimTrailingFrom("-x", "-x"); doTestTrimTrailingFrom("--", ""); doTestTrimTrailingFrom("x--", "x"); doTestTrimTrailingFrom("--x", "--x"); doTestTrimTrailingFrom("-x-", "-x"); doTestTrimTrailingFrom("x-x", "x-x"); doTestTrimTrailingFrom("---", ""); doTestTrimTrailingFrom("--x-", "--x"); doTestTrimTrailingFrom("--xx", "--xx"); doTestTrimTrailingFrom("-x--", "-x"); doTestTrimTrailingFrom("-x-x", "-x-x"); doTestTrimTrailingFrom("-xx-", "-xx"); doTestTrimTrailingFrom("x--x", "x--x"); doTestTrimTrailingFrom("x-x-", "x-x"); doTestTrimTrailingFrom("x-xx", "x-xx"); doTestTrimTrailingFrom("x-x--xx---x----x", "x-x--xx---x----x"); // additional testing using the doc example assertEquals("abacat", anyOf("ab").trimTrailingFrom("abacatbab")); } private void doTestTrimTrailingFrom(String in, String out) { // Try a few different matchers which all match '-' and not 'x' assertEquals(out, is('-').trimTrailingFrom(in)); assertEquals(out, is('-').or(is('#')).trimTrailingFrom(in)); assertEquals(out, isNot('x').trimTrailingFrom(in)); assertEquals(out, is('x').negate().trimTrailingFrom(in)); assertEquals(out, anyOf("-#").trimTrailingFrom(in)); assertEquals(out, anyOf("-#123").trimTrailingFrom(in)); } public void testTrimAndCollapse() { // collapsing groups of - into _ doTestTrimAndCollapse("-", ""); doTestTrimAndCollapse("x-", "x"); doTestTrimAndCollapse("-x", "x"); doTestTrimAndCollapse("--", ""); doTestTrimAndCollapse("x--", "x"); doTestTrimAndCollapse("--x", "x"); doTestTrimAndCollapse("-x-", "x"); doTestTrimAndCollapse("x-x", "x_x"); doTestTrimAndCollapse("---", ""); doTestTrimAndCollapse("--x-", "x"); doTestTrimAndCollapse("--xx", "xx"); doTestTrimAndCollapse("-x--", "x"); doTestTrimAndCollapse("-x-x", "x_x"); doTestTrimAndCollapse("-xx-", "xx"); doTestTrimAndCollapse("x--x", "x_x"); doTestTrimAndCollapse("x-x-", "x_x"); doTestTrimAndCollapse("x-xx", "x_xx"); doTestTrimAndCollapse("x-x--xx---x----x", "x_x_xx_x_x"); } private void doTestTrimAndCollapse(String in, String out) { // Try a few different matchers which all match '-' and not 'x' assertEquals(out, is('-').trimAndCollapseFrom(in, '_')); assertEquals(out, is('-').or(is('#')).trimAndCollapseFrom(in, '_')); assertEquals(out, isNot('x').trimAndCollapseFrom(in, '_')); assertEquals(out, is('x').negate().trimAndCollapseFrom(in, '_')); assertEquals(out, anyOf("-").trimAndCollapseFrom(in, '_')); assertEquals(out, anyOf("-#").trimAndCollapseFrom(in, '_')); assertEquals(out, anyOf("-#123").trimAndCollapseFrom(in, '_')); } public void testReplaceFrom() { assertEquals("yoho", is('a').replaceFrom("yaha", 'o')); assertEquals("yh", is('a').replaceFrom("yaha", "")); assertEquals("yoho", is('a').replaceFrom("yaha", "o")); assertEquals("yoohoo", is('a').replaceFrom("yaha", "oo")); assertEquals("12 > 5", is('>').replaceFrom("12 > 5", ">")); } public void testPrecomputedOptimizations() { // These are testing behavior that's never promised by the API. // Some matchers are so efficient that it is a waste of effort to // build a precomputed version. CharMatcher m1 = is('x'); assertSame(m1, m1.precomputed()); assertSame(m1.toString(), m1.precomputed().toString()); CharMatcher m2 = anyOf("Az"); assertSame(m2, m2.precomputed()); assertSame(m2.toString(), m2.precomputed().toString()); CharMatcher m3 = inRange('A', 'Z'); assertSame(m3, m3.precomputed()); assertSame(m3.toString(), m3.precomputed().toString()); assertSame(CharMatcher.NONE, CharMatcher.NONE.precomputed()); assertSame(CharMatcher.ANY, CharMatcher.ANY.precomputed()); } @GwtIncompatible("java.util.BitSet") private static BitSet bitSet(String chars) { return bitSet(chars.toCharArray()); } @GwtIncompatible("java.util.BitSet") private static BitSet bitSet(char[] chars) { BitSet tmp = new BitSet(); for (int i = 0; i < chars.length; i++) { tmp.set(chars[i]); } return tmp; } @GwtIncompatible("java.util.Random, java.util.BitSet") public void testSmallCharMatcher() { CharMatcher len1 = SmallCharMatcher.from(bitSet("#"), "#"); CharMatcher len2 = SmallCharMatcher.from(bitSet("ab"), "ab"); CharMatcher len3 = SmallCharMatcher.from(bitSet("abc"), "abc"); CharMatcher len4 = SmallCharMatcher.from(bitSet("abcd"), "abcd"); assertTrue(len1.matches('#')); assertFalse(len1.matches('!')); assertTrue(len2.matches('a')); assertTrue(len2.matches('b')); for (char c = 'c'; c < 'z'; c++) { assertFalse(len2.matches(c)); } assertTrue(len3.matches('a')); assertTrue(len3.matches('b')); assertTrue(len3.matches('c')); for (char c = 'd'; c < 'z'; c++) { assertFalse(len3.matches(c)); } assertTrue(len4.matches('a')); assertTrue(len4.matches('b')); assertTrue(len4.matches('c')); assertTrue(len4.matches('d')); for (char c = 'e'; c < 'z'; c++) { assertFalse(len4.matches(c)); } Random rand = new Random(1234); for (int testCase = 0; testCase < 100; testCase++) { char[] chars = randomChars(rand, rand.nextInt(63) + 1); CharMatcher m = SmallCharMatcher.from(bitSet(chars), new String(chars)); checkExactMatches(m, chars); } } static void checkExactMatches(CharMatcher m, char[] chars) { Set<Character> positive = Sets.newHashSetWithExpectedSize(chars.length); for (int i = 0; i < chars.length; i++) { positive.add(chars[i]); } for (int c = 0; c <= Character.MAX_VALUE; c++) { assertFalse(positive.contains(new Character((char) c)) ^ m.matches((char) c)); } } static char[] randomChars(Random rand, int size) { Set<Character> chars = new HashSet<Character>(size); for (int i = 0; i < size; i++) { char c; while (true) { c = (char) rand.nextInt(Character.MAX_VALUE - Character.MIN_VALUE + 1); if (!chars.contains(c)) { break; } } chars.add(c); } char[] retValue = new char[chars.size()]; int i = 0; for (char c : chars) { retValue[i++] = c; } Arrays.sort(retValue); return retValue; } @GwtIncompatible("java.util.Random, java.util.BitSet") public void testMediumCharMatcher() { CharMatcher len1 = MediumCharMatcher.from(bitSet("#"), "#"); CharMatcher len2 = MediumCharMatcher.from(bitSet("ab"), "ab"); CharMatcher len3 = MediumCharMatcher.from(bitSet("abc"), "abc"); CharMatcher len4 = MediumCharMatcher.from(bitSet("abcd"), "abcd"); assertTrue(len1.matches('#')); assertFalse(len1.matches('!')); assertTrue(len2.matches('a')); assertTrue(len2.matches('b')); for (char c = 'c'; c < 'z'; c++) { assertFalse(len2.matches(c)); } assertTrue(len3.matches('a')); assertTrue(len3.matches('b')); assertTrue(len3.matches('c')); for (char c = 'd'; c < 'z'; c++) { assertFalse(len3.matches(c)); } assertTrue(len4.matches('a')); assertTrue(len4.matches('b')); assertTrue(len4.matches('c')); assertTrue(len4.matches('d')); for (char c = 'e'; c < 'z'; c++) { assertFalse(len4.matches(c)); } Random rand = new Random(1234); for (int testCase = 0; testCase < 100; testCase++) { char[] chars = randomChars(rand, rand.nextInt(1023) + 1); CharMatcher m = MediumCharMatcher.from(bitSet(chars), new String(chars)); checkExactMatches(m, chars); } } /** * Create an iterable of ints based on a string description, like, * "1-2,3,4,rand(5, 20, 6)". The last part says to generate 6 random values * between 5 and 20 (inclusive). */ Iterable<Integer> intValues(String desc) { Random random = new Random(1234); ArrayList<Integer> values = new ArrayList<Integer>(); for (String part : Splitter.on(",").split(desc)) { if (part.contains("-")) { int i = part.indexOf('-'); int begin = Integer.parseInt(part.substring(0, i)); int end = Integer.parseInt(part.substring(i + 1)); for (int j = begin; j <= end; j++) { values.add(j); } } else if (part.startsWith("rand")) { List<String> args = ImmutableList.copyOf(Splitter.on(";").split(part.substring(5, part.length() - 1))); assertEquals(args.size(), 3); int minValue = Integer.parseInt(args.get(0)); int maxValue = Integer.parseInt(args.get(1)); int count = Integer.parseInt(args.get(2)); int diff = maxValue - minValue + 1; for (int i = 0; i < count; i++) { int value = random.nextInt(diff) + minValue; values.add(value); } } else { values.add(Integer.parseInt(part)); } } return values; } public void testToString() { assertEquals("CharMatcher.NONE", CharMatcher.anyOf("").toString()); assertEquals("CharMatcher.is(31)", CharMatcher.anyOf("1").toString()); assertEquals("CharMatcher.anyOf(\"12\")", CharMatcher.anyOf("12").toString()); assertEquals("CharMatcher.anyOf(\"123\")", CharMatcher.anyOf("123").toString()); } }