1 /* 2 * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @bug 8016846 8024341 8071479 8145006 27 * @summary Unit tests stream and lambda-based methods on Pattern and Matcher 28 * @library ../stream/bootlib 29 * @build java.base/java.util.stream.OpTestCase 30 * @run testng/othervm PatternStreamTest 31 */ 32 33 import org.testng.annotations.DataProvider; 34 import org.testng.annotations.Test; 35 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.ConcurrentModificationException; 39 import java.util.List; 40 import java.util.concurrent.atomic.AtomicInteger; 41 import java.util.function.Supplier; 42 import java.util.regex.MatchResult; 43 import java.util.regex.Matcher; 44 import java.util.regex.Pattern; 45 import java.util.stream.Collectors; 46 import java.util.stream.LambdaTestHelpers; 47 import java.util.stream.OpTestCase; 48 import java.util.stream.Stream; 49 import java.util.stream.TestData; 50 51 import static org.testng.Assert.*; 52 53 @Test 54 public class PatternStreamTest extends OpTestCase { 55 56 @DataProvider(name = "Patterns") 57 public static Object[][] makeStreamTestData() { 58 // Each item must match the type signature of the consumer of this data 59 // String, String, Pattern 60 List<Object[]> data = new ArrayList<>(); 61 62 String description = "All matches"; 63 String input = "XXXXXX"; 64 Pattern pattern = Pattern.compile("X"); 65 data.add(new Object[]{description, input, pattern}); 66 67 description = "Bounded every other match"; 68 input = "XYXYXYYXYX"; 69 pattern = Pattern.compile("X"); 70 data.add(new Object[]{description, input, pattern}); 71 72 description = "Every other match"; 73 input = "YXYXYXYYXYXY"; 74 pattern = Pattern.compile("X"); 75 data.add(new Object[]{description, input, pattern}); 76 77 description = ""; 78 input = "awgqwefg1fefw4vssv1vvv1"; 79 pattern = Pattern.compile("4"); 80 data.add(new Object[]{description, input, pattern}); 81 82 input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; 83 pattern = Pattern.compile("\u00a3a"); 84 data.add(new Object[]{description, input, pattern}); 85 86 input = "awgqwefg1fefw4vssv1vvv1"; 87 pattern = Pattern.compile("1"); 88 data.add(new Object[]{description, input, pattern}); 89 90 input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; 91 pattern = Pattern.compile("1"); 92 data.add(new Object[]{description, input, pattern}); 93 94 input = "1\u56da23\u56da456\u56da7890"; 95 pattern = Pattern.compile("\u56da"); 96 data.add(new Object[]{description, input, pattern}); 97 98 input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; 99 pattern = Pattern.compile("\u56da"); 100 data.add(new Object[]{description, input, pattern}); 101 102 description = "Empty input"; 103 input = ""; 104 pattern = Pattern.compile("\u56da"); 105 data.add(new Object[]{description, input, pattern}); 106 107 description = "Empty input with empty pattern"; 108 input = ""; 109 pattern = Pattern.compile(""); 110 data.add(new Object[]{description, input, pattern}); 111 112 description = "Multiple separators"; 113 input = "This is,testing: with\tdifferent separators."; 114 pattern = Pattern.compile("[ \t,:.]"); 115 data.add(new Object[]{description, input, pattern}); 116 117 description = "Repeated separators within and at end"; 118 input = "boo:and:foo"; 119 pattern = Pattern.compile("o"); 120 data.add(new Object[]{description, input, pattern}); 121 122 description = "Many repeated separators within and at end"; 123 input = "booooo:and:fooooo"; 124 pattern = Pattern.compile("o"); 125 data.add(new Object[]{description, input, pattern}); 126 127 description = "Many repeated separators before last match"; 128 input = "fooooo:"; 129 pattern = Pattern.compile("o"); 130 data.add(new Object[] {description, input, pattern}); 131 132 return data.toArray(new Object[0][]); 133 } 134 135 @Test(dataProvider = "Patterns") 136 public void testPatternSplitAsStream(String description, String input, Pattern pattern) { 137 // Derive expected result from pattern.split 138 List<String> expected = Arrays.asList(pattern.split(input)); 139 140 Supplier<Stream<String>> ss = () -> pattern.splitAsStream(input); 141 withData(TestData.Factory.ofSupplier(description, ss)) 142 .stream(LambdaTestHelpers.identity()) 143 .expectedResult(expected) 144 .exercise(); 145 } 146 147 @Test(dataProvider = "Patterns") 148 public void testReplaceFirst(String description, String input, Pattern pattern) { 149 // Derive expected result from Matcher.replaceFirst(String ) 150 String expected = pattern.matcher(input).replaceFirst("R"); 151 String actual = pattern.matcher(input).replaceFirst(r -> "R"); 152 assertEquals(actual, expected); 153 } 154 155 @Test(dataProvider = "Patterns") 156 public void testReplaceAll(String description, String input, Pattern pattern) { 157 // Derive expected result from Matcher.replaceAll(String ) 158 String expected = pattern.matcher(input).replaceAll("R"); 159 String actual = pattern.matcher(input).replaceAll(r -> "R"); 160 assertEquals(actual, expected); 161 162 // Derive expected result from Matcher.find 163 Matcher m = pattern.matcher(input); 164 int expectedMatches = 0; 165 while (m.find()) { 166 expectedMatches++; 167 } 168 AtomicInteger actualMatches = new AtomicInteger(); 169 pattern.matcher(input).replaceAll(r -> "R" + actualMatches.incrementAndGet()); 170 assertEquals(expectedMatches, actualMatches.get()); 171 } 172 173 @Test(dataProvider = "Patterns") 174 public void testMatchResults(String description, String input, Pattern pattern) { 175 // Derive expected result from Matcher.find 176 Matcher m = pattern.matcher(input); 177 List<MatchResultHolder> expected = new ArrayList<>(); 178 while (m.find()) { 179 expected.add(new MatchResultHolder(m)); 180 } 181 182 Supplier<Stream<MatchResult>> ss = () -> pattern.matcher(input).results(); 183 withData(TestData.Factory.ofSupplier(description, ss)) 184 .stream(s -> s.map(MatchResultHolder::new)) 185 .expectedResult(expected) 186 .exercise(); 187 } 188 189 @Test 190 public void testLateBinding() { 191 Pattern pattern = Pattern.compile(","); 192 193 StringBuilder sb = new StringBuilder("a,b,c,d,e"); 194 Stream<String> stream = pattern.splitAsStream(sb); 195 sb.setLength(3); 196 assertEquals(Arrays.asList("a", "b"), stream.collect(Collectors.toList())); 197 198 stream = pattern.splitAsStream(sb); 199 sb.append(",f,g"); 200 assertEquals(Arrays.asList("a", "b", "f", "g"), stream.collect(Collectors.toList())); 201 } 202 203 public void testFailfastMatchResults() { 204 Pattern p = Pattern.compile("X"); 205 Matcher m = p.matcher("XX"); 206 207 Stream<MatchResult> s = m.results(); 208 m.find(); 209 // Should start on the second match 210 assertEquals(s.count(), 1); 211 212 // Fail fast without short-circuit 213 // Exercises Iterator.forEachRemaining 214 m.reset(); 215 try { 216 m.results().peek(mr -> m.reset()).count(); 217 fail(); 218 } catch (ConcurrentModificationException e) { 219 // Should reach here 220 } 221 222 m.reset(); 223 try { 224 m.results().peek(mr -> m.find()).count(); 225 fail(); 226 } catch (ConcurrentModificationException e) { 227 // Should reach here 228 } 229 230 // Fail fast with short-circuit 231 // Exercises Iterator.hasNext/next 232 m.reset(); 233 try { 234 m.results().peek(mr -> m.reset()).limit(2).count(); 235 fail(); 236 } catch (ConcurrentModificationException e) { 237 // Should reach here 238 } 239 240 m.reset(); 241 try { 242 m.results().peek(mr -> m.find()).limit(2).count(); 243 fail(); 244 } catch (ConcurrentModificationException e) { 245 // Should reach here 246 } 247 } 248 249 public void testFailfastReplace() { 250 Pattern p = Pattern.compile("X"); 251 Matcher m = p.matcher("XX"); 252 253 // Fail fast without short-circuit 254 // Exercises Iterator.forEachRemaining 255 m.reset(); 256 try { 257 m.replaceFirst(mr -> { m.reset(); return "Y"; }); 258 fail(); 259 } catch (ConcurrentModificationException e) { 260 // Should reach here 261 } 262 263 m.reset(); 264 try { 265 m.replaceAll(mr -> { m.reset(); return "Y"; }); 266 fail(); 267 } catch (ConcurrentModificationException e) { 268 // Should reach here 269 } 270 } 271 272 // A holder of MatchResult that can compare 273 static class MatchResultHolder implements Comparable<MatchResultHolder> { 274 final MatchResult mr; 275 276 MatchResultHolder(Matcher m) { 277 this(m.toMatchResult()); 278 } 279 280 MatchResultHolder(MatchResult mr) { 281 this.mr = mr; 282 } 283 284 @Override 285 public int compareTo(MatchResultHolder that) { 286 int c = that.mr.group().compareTo(this.mr.group()); 287 if (c != 0) 288 return c; 289 290 c = Integer.compare(that.mr.start(), this.mr.start()); 291 if (c != 0) 292 return c; 293 294 c = Integer.compare(that.mr.end(), this.mr.end()); 295 if (c != 0) 296 return c; 297 298 c = Integer.compare(that.mr.groupCount(), this.mr.groupCount()); 299 if (c != 0) 300 return c; 301 302 for (int g = 0; g < this.mr.groupCount(); g++) { 303 c = that.mr.group(g).compareTo(this.mr.group(g)); 304 if (c != 0) 305 return c; 306 307 c = Integer.compare(that.mr.start(g), this.mr.start(g)); 308 if (c != 0) 309 return c; 310 311 c = Integer.compare(that.mr.end(g), this.mr.end(g)); 312 if (c != 0) 313 return c; 314 } 315 return 0; 316 } 317 318 @Override 319 public boolean equals(Object that) { 320 if (this == that) return true; 321 if (that == null || getClass() != that.getClass()) return false; 322 323 return this.compareTo((MatchResultHolder) that) == 0; 324 } 325 326 @Override 327 public int hashCode() { 328 return mr.group().hashCode(); 329 } 330 } 331 }