| /* | |
| * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /** | |
| * @test | |
| * @bug 8016846 8024341 8071479 8145006 | |
| * @summary Unit tests stream and lambda-based methods on Pattern and Matcher | |
| * @library /lib/testlibrary/bootlib | |
| * @build java.base/java.util.stream.OpTestCase | |
| * @run testng/othervm PatternStreamTest | |
| */ | |
| import org.testng.annotations.DataProvider; | |
| import org.testng.annotations.Test; | |
| import java.util.ArrayList; | |
| import java.util.Arrays; | |
| import java.util.ConcurrentModificationException; | |
| import java.util.List; | |
| import java.util.concurrent.atomic.AtomicInteger; | |
| import java.util.function.Supplier; | |
| import java.util.regex.MatchResult; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| import java.util.stream.Collectors; | |
| import java.util.stream.LambdaTestHelpers; | |
| import java.util.stream.OpTestCase; | |
| import java.util.stream.Stream; | |
| import java.util.stream.TestData; | |
| import static org.testng.Assert.*; | |
| @Test | |
| public class PatternStreamTest extends OpTestCase { | |
| @DataProvider(name = "Patterns") | |
| public static Object[][] makeStreamTestData() { | |
| // Each item must match the type signature of the consumer of this data | |
| // String, String, Pattern | |
| List<Object[]> data = new ArrayList<>(); | |
| String description = "All matches"; | |
| String input = "XXXXXX"; | |
| Pattern pattern = Pattern.compile("X"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Bounded every other match"; | |
| input = "XYXYXYYXYX"; | |
| pattern = Pattern.compile("X"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Every other match"; | |
| input = "YXYXYXYYXYXY"; | |
| pattern = Pattern.compile("X"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = ""; | |
| input = "awgqwefg1fefw4vssv1vvv1"; | |
| pattern = Pattern.compile("4"); | |
| data.add(new Object[]{description, input, pattern}); | |
| input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; | |
| pattern = Pattern.compile("\u00a3a"); | |
| data.add(new Object[]{description, input, pattern}); | |
| input = "awgqwefg1fefw4vssv1vvv1"; | |
| pattern = Pattern.compile("1"); | |
| data.add(new Object[]{description, input, pattern}); | |
| input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; | |
| pattern = Pattern.compile("1"); | |
| data.add(new Object[]{description, input, pattern}); | |
| input = "1\u56da23\u56da456\u56da7890"; | |
| pattern = Pattern.compile("\u56da"); | |
| data.add(new Object[]{description, input, pattern}); | |
| input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; | |
| pattern = Pattern.compile("\u56da"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Empty input"; | |
| input = ""; | |
| pattern = Pattern.compile("\u56da"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Empty input with empty pattern"; | |
| input = ""; | |
| pattern = Pattern.compile(""); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Multiple separators"; | |
| input = "This is,testing: with\tdifferent separators."; | |
| pattern = Pattern.compile("[ \t,:.]"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Repeated separators within and at end"; | |
| input = "boo:and:foo"; | |
| pattern = Pattern.compile("o"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Many repeated separators within and at end"; | |
| input = "booooo:and:fooooo"; | |
| pattern = Pattern.compile("o"); | |
| data.add(new Object[]{description, input, pattern}); | |
| description = "Many repeated separators before last match"; | |
| input = "fooooo:"; | |
| pattern = Pattern.compile("o"); | |
| data.add(new Object[] {description, input, pattern}); | |
| return data.toArray(new Object[0][]); | |
| } | |
| @Test(dataProvider = "Patterns") | |
| public void testPatternSplitAsStream(String description, String input, Pattern pattern) { | |
| // Derive expected result from pattern.split | |
| List<String> expected = Arrays.asList(pattern.split(input)); | |
| Supplier<Stream<String>> ss = () -> pattern.splitAsStream(input); | |
| withData(TestData.Factory.ofSupplier(description, ss)) | |
| .stream(LambdaTestHelpers.identity()) | |
| .expectedResult(expected) | |
| .exercise(); | |
| } | |
| @Test(dataProvider = "Patterns") | |
| public void testReplaceFirst(String description, String input, Pattern pattern) { | |
| // Derive expected result from Matcher.replaceFirst(String ) | |
| String expected = pattern.matcher(input).replaceFirst("R"); | |
| String actual = pattern.matcher(input).replaceFirst(r -> "R"); | |
| assertEquals(actual, expected); | |
| } | |
| @Test(dataProvider = "Patterns") | |
| public void testReplaceAll(String description, String input, Pattern pattern) { | |
| // Derive expected result from Matcher.replaceAll(String ) | |
| String expected = pattern.matcher(input).replaceAll("R"); | |
| String actual = pattern.matcher(input).replaceAll(r -> "R"); | |
| assertEquals(actual, expected); | |
| // Derive expected result from Matcher.find | |
| Matcher m = pattern.matcher(input); | |
| int expectedMatches = 0; | |
| while (m.find()) { | |
| expectedMatches++; | |
| } | |
| AtomicInteger actualMatches = new AtomicInteger(); | |
| pattern.matcher(input).replaceAll(r -> "R" + actualMatches.incrementAndGet()); | |
| assertEquals(expectedMatches, actualMatches.get()); | |
| } | |
| @Test(dataProvider = "Patterns") | |
| public void testMatchResults(String description, String input, Pattern pattern) { | |
| // Derive expected result from Matcher.find | |
| Matcher m = pattern.matcher(input); | |
| List<MatchResultHolder> expected = new ArrayList<>(); | |
| while (m.find()) { | |
| expected.add(new MatchResultHolder(m)); | |
| } | |
| Supplier<Stream<MatchResult>> ss = () -> pattern.matcher(input).results(); | |
| withData(TestData.Factory.ofSupplier(description, ss)) | |
| .stream(s -> s.map(MatchResultHolder::new)) | |
| .expectedResult(expected) | |
| .exercise(); | |
| } | |
| @Test | |
| public void testLateBinding() { | |
| Pattern pattern = Pattern.compile(","); | |
| StringBuilder sb = new StringBuilder("a,b,c,d,e"); | |
| Stream<String> stream = pattern.splitAsStream(sb); | |
| sb.setLength(3); | |
| assertEquals(Arrays.asList("a", "b"), stream.collect(Collectors.toList())); | |
| stream = pattern.splitAsStream(sb); | |
| sb.append(",f,g"); | |
| assertEquals(Arrays.asList("a", "b", "f", "g"), stream.collect(Collectors.toList())); | |
| } | |
| public void testFailfastMatchResults() { | |
| Pattern p = Pattern.compile("X"); | |
| Matcher m = p.matcher("XX"); | |
| Stream<MatchResult> s = m.results(); | |
| m.find(); | |
| // Should start on the second match | |
| assertEquals(s.count(), 1); | |
| // Fail fast without short-circuit | |
| // Exercises Iterator.forEachRemaining | |
| m.reset(); | |
| try { | |
| m.results().peek(mr -> m.reset()).count(); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| m.reset(); | |
| try { | |
| m.results().peek(mr -> m.find()).count(); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| // Fail fast with short-circuit | |
| // Exercises Iterator.hasNext/next | |
| m.reset(); | |
| try { | |
| m.results().peek(mr -> m.reset()).limit(2).count(); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| m.reset(); | |
| try { | |
| m.results().peek(mr -> m.find()).limit(2).count(); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| } | |
| public void testFailfastReplace() { | |
| Pattern p = Pattern.compile("X"); | |
| Matcher m = p.matcher("XX"); | |
| // Fail fast without short-circuit | |
| // Exercises Iterator.forEachRemaining | |
| m.reset(); | |
| try { | |
| m.replaceFirst(mr -> { m.reset(); return "Y"; }); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| m.reset(); | |
| try { | |
| m.replaceAll(mr -> { m.reset(); return "Y"; }); | |
| fail(); | |
| } catch (ConcurrentModificationException e) { | |
| // Should reach here | |
| } | |
| } | |
| // A holder of MatchResult that can compare | |
| static class MatchResultHolder implements Comparable<MatchResultHolder> { | |
| final MatchResult mr; | |
| MatchResultHolder(Matcher m) { | |
| this(m.toMatchResult()); | |
| } | |
| MatchResultHolder(MatchResult mr) { | |
| this.mr = mr; | |
| } | |
| @Override | |
| public int compareTo(MatchResultHolder that) { | |
| int c = that.mr.group().compareTo(this.mr.group()); | |
| if (c != 0) | |
| return c; | |
| c = Integer.compare(that.mr.start(), this.mr.start()); | |
| if (c != 0) | |
| return c; | |
| c = Integer.compare(that.mr.end(), this.mr.end()); | |
| if (c != 0) | |
| return c; | |
| c = Integer.compare(that.mr.groupCount(), this.mr.groupCount()); | |
| if (c != 0) | |
| return c; | |
| for (int g = 0; g < this.mr.groupCount(); g++) { | |
| c = that.mr.group(g).compareTo(this.mr.group(g)); | |
| if (c != 0) | |
| return c; | |
| c = Integer.compare(that.mr.start(g), this.mr.start(g)); | |
| if (c != 0) | |
| return c; | |
| c = Integer.compare(that.mr.end(g), this.mr.end(g)); | |
| if (c != 0) | |
| return c; | |
| } | |
| return 0; | |
| } | |
| @Override | |
| public boolean equals(Object that) { | |
| if (this == that) return true; | |
| if (that == null || getClass() != that.getClass()) return false; | |
| return this.compareTo((MatchResultHolder) that) == 0; | |
| } | |
| @Override | |
| public int hashCode() { | |
| return mr.group().hashCode(); | |
| } | |
| } | |
| } |