21 * questions. |
21 * questions. |
22 */ |
22 */ |
23 |
23 |
24 /** |
24 /** |
25 * @test |
25 * @test |
26 * @bug 8016846 8024341 |
26 * @bug 8016846 8024341 8071479 |
27 * @summary Unit tests for wrapping classes should delegate to default methods |
27 * @summary Unit tests stream and lambda-based methods on Pattern and Matcher |
28 * @library ../stream/bootlib |
28 * @library ../stream/bootlib |
29 * @build java.util.stream.OpTestCase |
29 * @build java.util.stream.OpTestCase |
30 * @run testng/othervm PatternStreamTest |
30 * @run testng/othervm PatternStreamTest |
31 */ |
31 */ |
32 |
32 |
33 import org.testng.annotations.DataProvider; |
33 import org.testng.annotations.DataProvider; |
34 import org.testng.annotations.Test; |
34 import org.testng.annotations.Test; |
35 |
35 |
36 import java.util.ArrayList; |
36 import java.util.ArrayList; |
|
37 import java.util.Arrays; |
|
38 import java.util.ConcurrentModificationException; |
37 import java.util.List; |
39 import java.util.List; |
|
40 import java.util.concurrent.atomic.AtomicInteger; |
38 import java.util.function.Supplier; |
41 import java.util.function.Supplier; |
|
42 import java.util.regex.MatchResult; |
|
43 import java.util.regex.Matcher; |
39 import java.util.regex.Pattern; |
44 import java.util.regex.Pattern; |
40 import java.util.stream.LambdaTestHelpers; |
45 import java.util.stream.LambdaTestHelpers; |
41 import java.util.stream.OpTestCase; |
46 import java.util.stream.OpTestCase; |
42 import java.util.stream.Stream; |
47 import java.util.stream.Stream; |
43 import java.util.stream.TestData; |
48 import java.util.stream.TestData; |
44 |
49 |
|
50 import static org.testng.Assert.*; |
|
51 |
45 @Test |
52 @Test |
46 public class PatternStreamTest extends OpTestCase { |
53 public class PatternStreamTest extends OpTestCase { |
47 |
54 |
48 @DataProvider(name = "Stream<String>") |
55 @DataProvider(name = "Patterns") |
49 public static Object[][] makeStreamTestData() { |
56 public static Object[][] makeStreamTestData() { |
|
57 // Each item must match the type signature of the consumer of this data |
|
58 // String, String, Pattern |
50 List<Object[]> data = new ArrayList<>(); |
59 List<Object[]> data = new ArrayList<>(); |
51 |
60 |
52 String description = ""; |
61 String description = "All matches"; |
53 String input = "awgqwefg1fefw4vssv1vvv1"; |
62 String input = "XXXXXX"; |
54 Pattern pattern = Pattern.compile("4"); |
63 Pattern pattern = Pattern.compile("X"); |
55 List<String> expected = new ArrayList<>(); |
64 data.add(new Object[]{description, input, pattern}); |
56 expected.add("awgqwefg1fefw"); |
65 |
57 expected.add("vssv1vvv1"); |
66 description = "Bounded every other match"; |
58 |
67 input = "XYXYXYYXYX"; |
59 // Must match the type signature of the consumer of this data, testStrings |
68 pattern = Pattern.compile("X"); |
60 // String, String, Pattern, List<String> |
69 data.add(new Object[]{description, input, pattern}); |
61 data.add(new Object[]{description, input, pattern, expected}); |
70 |
|
71 description = "Every other match"; |
|
72 input = "YXYXYXYYXYXY"; |
|
73 pattern = Pattern.compile("X"); |
|
74 data.add(new Object[]{description, input, pattern}); |
|
75 |
|
76 description = ""; |
|
77 input = "awgqwefg1fefw4vssv1vvv1"; |
|
78 pattern = Pattern.compile("4"); |
|
79 data.add(new Object[]{description, input, pattern}); |
62 |
80 |
63 input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; |
81 input = "afbfq\u00a3abgwgb\u00a3awngnwggw\u00a3a\u00a3ahjrnhneerh"; |
64 pattern = Pattern.compile("\u00a3a"); |
82 pattern = Pattern.compile("\u00a3a"); |
65 expected = new ArrayList<>(); |
83 data.add(new Object[]{description, input, pattern}); |
66 expected.add("afbfq"); |
|
67 expected.add("bgwgb"); |
|
68 expected.add("wngnwggw"); |
|
69 expected.add(""); |
|
70 expected.add("hjrnhneerh"); |
|
71 |
|
72 data.add(new Object[]{description, input, pattern, expected}); |
|
73 |
|
74 |
84 |
75 input = "awgqwefg1fefw4vssv1vvv1"; |
85 input = "awgqwefg1fefw4vssv1vvv1"; |
76 pattern = Pattern.compile("1"); |
86 pattern = Pattern.compile("1"); |
77 expected = new ArrayList<>(); |
87 data.add(new Object[]{description, input, pattern}); |
78 expected.add("awgqwefg"); |
|
79 expected.add("fefw4vssv"); |
|
80 expected.add("vvv"); |
|
81 |
|
82 data.add(new Object[]{description, input, pattern, expected}); |
|
83 |
|
84 |
88 |
85 input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; |
89 input = "a\u4ebafg1fefw\u4eba4\u9f9cvssv\u9f9c1v\u672c\u672cvv"; |
86 pattern = Pattern.compile("1"); |
90 pattern = Pattern.compile("1"); |
87 expected = new ArrayList<>(); |
91 data.add(new Object[]{description, input, pattern}); |
88 expected.add("a\u4ebafg"); |
|
89 expected.add("fefw\u4eba4\u9f9cvssv\u9f9c"); |
|
90 expected.add("v\u672c\u672cvv"); |
|
91 |
|
92 data.add(new Object[]{description, input, pattern, expected}); |
|
93 |
|
94 |
92 |
95 input = "1\u56da23\u56da456\u56da7890"; |
93 input = "1\u56da23\u56da456\u56da7890"; |
96 pattern = Pattern.compile("\u56da"); |
94 pattern = Pattern.compile("\u56da"); |
97 expected = new ArrayList<>(); |
95 data.add(new Object[]{description, input, pattern}); |
98 expected.add("1"); |
|
99 expected.add("23"); |
|
100 expected.add("456"); |
|
101 expected.add("7890"); |
|
102 |
|
103 data.add(new Object[]{description, input, pattern, expected}); |
|
104 |
|
105 |
96 |
106 input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; |
97 input = "1\u56da23\u9f9c\u672c\u672c\u56da456\u56da\u9f9c\u672c7890"; |
107 pattern = Pattern.compile("\u56da"); |
98 pattern = Pattern.compile("\u56da"); |
108 expected = new ArrayList<>(); |
99 data.add(new Object[]{description, input, pattern}); |
109 expected.add("1"); |
|
110 expected.add("23\u9f9c\u672c\u672c"); |
|
111 expected.add("456"); |
|
112 expected.add("\u9f9c\u672c7890"); |
|
113 |
|
114 data.add(new Object[]{description, input, pattern, expected}); |
|
115 |
|
116 |
100 |
117 description = "Empty input"; |
101 description = "Empty input"; |
118 input = ""; |
102 input = ""; |
119 pattern = Pattern.compile("\u56da"); |
103 pattern = Pattern.compile("\u56da"); |
120 expected = new ArrayList<>(); |
104 data.add(new Object[]{description, input, pattern}); |
121 expected.add(""); |
|
122 |
|
123 data.add(new Object[]{description, input, pattern, expected}); |
|
124 |
|
125 |
105 |
126 description = "Empty input with empty pattern"; |
106 description = "Empty input with empty pattern"; |
127 input = ""; |
107 input = ""; |
128 pattern = Pattern.compile(""); |
108 pattern = Pattern.compile(""); |
129 expected = new ArrayList<>(); |
109 data.add(new Object[]{description, input, pattern}); |
130 expected.add(""); |
|
131 |
|
132 data.add(new Object[]{description, input, pattern, expected}); |
|
133 |
|
134 |
110 |
135 description = "Multiple separators"; |
111 description = "Multiple separators"; |
136 input = "This is,testing: with\tdifferent separators."; |
112 input = "This is,testing: with\tdifferent separators."; |
137 pattern = Pattern.compile("[ \t,:.]"); |
113 pattern = Pattern.compile("[ \t,:.]"); |
138 expected = new ArrayList<>(); |
114 data.add(new Object[]{description, input, pattern}); |
139 expected.add("This"); |
|
140 expected.add("is"); |
|
141 expected.add("testing"); |
|
142 expected.add(""); |
|
143 expected.add("with"); |
|
144 expected.add("different"); |
|
145 expected.add("separators"); |
|
146 |
|
147 |
115 |
148 description = "Repeated separators within and at end"; |
116 description = "Repeated separators within and at end"; |
149 input = "boo:and:foo"; |
117 input = "boo:and:foo"; |
150 pattern = Pattern.compile("o"); |
118 pattern = Pattern.compile("o"); |
151 expected = new ArrayList<>(); |
119 data.add(new Object[]{description, input, pattern}); |
152 expected.add("b"); |
|
153 expected.add(""); |
|
154 expected.add(":and:f"); |
|
155 |
|
156 |
120 |
157 description = "Many repeated separators within and at end"; |
121 description = "Many repeated separators within and at end"; |
158 input = "booooo:and:fooooo"; |
122 input = "booooo:and:fooooo"; |
159 pattern = Pattern.compile("o"); |
123 pattern = Pattern.compile("o"); |
160 expected = new ArrayList<>(); |
124 data.add(new Object[]{description, input, pattern}); |
161 expected.add("b"); |
|
162 expected.add(""); |
|
163 expected.add(""); |
|
164 expected.add(""); |
|
165 expected.add(""); |
|
166 expected.add(":and:f"); |
|
167 |
125 |
168 description = "Many repeated separators before last match"; |
126 description = "Many repeated separators before last match"; |
169 input = "fooooo:"; |
127 input = "fooooo:"; |
170 pattern = Pattern.compile("o"); |
128 pattern = Pattern.compile("o"); |
171 expected = new ArrayList<>(); |
129 data.add(new Object[] {description, input, pattern}); |
172 expected.add("f"); |
130 |
173 expected.add(""); |
|
174 expected.add(""); |
|
175 expected.add(""); |
|
176 expected.add(""); |
|
177 expected.add(":"); |
|
178 |
|
179 data.add(new Object[] {description, input, pattern, expected}); |
|
180 return data.toArray(new Object[0][]); |
131 return data.toArray(new Object[0][]); |
181 } |
132 } |
182 |
133 |
183 @Test(dataProvider = "Stream<String>") |
134 @Test(dataProvider = "Patterns") |
184 public void testStrings(String description, String input, Pattern pattern, List<String> expected) { |
135 public void testPatternSplitAsStream(String description, String input, Pattern pattern) { |
|
136 // Derive expected result from pattern.split |
|
137 List<String> expected = Arrays.asList(pattern.split(input)); |
|
138 |
185 Supplier<Stream<String>> ss = () -> pattern.splitAsStream(input); |
139 Supplier<Stream<String>> ss = () -> pattern.splitAsStream(input); |
186 withData(TestData.Factory.ofSupplier(description, ss)) |
140 withData(TestData.Factory.ofSupplier(description, ss)) |
187 .stream(LambdaTestHelpers.identity()) |
141 .stream(LambdaTestHelpers.identity()) |
188 .expectedResult(expected) |
142 .expectedResult(expected) |
189 .exercise(); |
143 .exercise(); |
190 } |
144 } |
|
145 |
|
146 @Test(dataProvider = "Patterns") |
|
147 public void testReplaceFirst(String description, String input, Pattern pattern) { |
|
148 // Derive expected result from Matcher.replaceFirst(String ) |
|
149 String expected = pattern.matcher(input).replaceFirst("R"); |
|
150 String actual = pattern.matcher(input).replaceFirst(r -> "R"); |
|
151 assertEquals(actual, expected); |
|
152 } |
|
153 |
|
154 @Test(dataProvider = "Patterns") |
|
155 public void testReplaceAll(String description, String input, Pattern pattern) { |
|
156 // Derive expected result from Matcher.replaceAll(String ) |
|
157 String expected = pattern.matcher(input).replaceAll("R"); |
|
158 String actual = pattern.matcher(input).replaceAll(r -> "R"); |
|
159 assertEquals(actual, expected); |
|
160 |
|
161 // Derive expected result from Matcher.find |
|
162 Matcher m = pattern.matcher(input); |
|
163 int expectedMatches = 0; |
|
164 while (m.find()) { |
|
165 expectedMatches++; |
|
166 } |
|
167 AtomicInteger actualMatches = new AtomicInteger(); |
|
168 pattern.matcher(input).replaceAll(r -> "R" + actualMatches.incrementAndGet()); |
|
169 assertEquals(expectedMatches, actualMatches.get()); |
|
170 } |
|
171 |
|
172 @Test(dataProvider = "Patterns") |
|
173 public void testMatchResults(String description, String input, Pattern pattern) { |
|
174 // Derive expected result from Matcher.find |
|
175 Matcher m = pattern.matcher(input); |
|
176 List<MatchResultHolder> expected = new ArrayList<>(); |
|
177 while (m.find()) { |
|
178 expected.add(new MatchResultHolder(m)); |
|
179 } |
|
180 |
|
181 Supplier<Stream<MatchResult>> ss = () -> pattern.matcher(input).results(); |
|
182 withData(TestData.Factory.ofSupplier(description, ss)) |
|
183 .stream(s -> s.map(MatchResultHolder::new)) |
|
184 .expectedResult(expected) |
|
185 .exercise(); |
|
186 } |
|
187 |
|
188 public void testFailfastMatchResults() { |
|
189 Pattern p = Pattern.compile("X"); |
|
190 Matcher m = p.matcher("XX"); |
|
191 |
|
192 Stream<MatchResult> s = m.results(); |
|
193 m.find(); |
|
194 // Should start on the second match |
|
195 assertEquals(s.count(), 1); |
|
196 |
|
197 // Fail fast without short-circuit |
|
198 // Exercises Iterator.forEachRemaining |
|
199 m.reset(); |
|
200 try { |
|
201 m.results().peek(mr -> m.reset()).count(); |
|
202 fail(); |
|
203 } catch (ConcurrentModificationException e) { |
|
204 // Should reach here |
|
205 } |
|
206 |
|
207 m.reset(); |
|
208 try { |
|
209 m.results().peek(mr -> m.find()).count(); |
|
210 fail(); |
|
211 } catch (ConcurrentModificationException e) { |
|
212 // Should reach here |
|
213 } |
|
214 |
|
215 // Fail fast with short-circuit |
|
216 // Exercises Iterator.hasNext/next |
|
217 m.reset(); |
|
218 try { |
|
219 m.results().peek(mr -> m.reset()).limit(2).count(); |
|
220 fail(); |
|
221 } catch (ConcurrentModificationException e) { |
|
222 // Should reach here |
|
223 } |
|
224 |
|
225 m.reset(); |
|
226 try { |
|
227 m.results().peek(mr -> m.find()).limit(2).count(); |
|
228 fail(); |
|
229 } catch (ConcurrentModificationException e) { |
|
230 // Should reach here |
|
231 } |
|
232 } |
|
233 |
|
234 public void testFailfastReplace() { |
|
235 Pattern p = Pattern.compile("X"); |
|
236 Matcher m = p.matcher("XX"); |
|
237 |
|
238 // Fail fast without short-circuit |
|
239 // Exercises Iterator.forEachRemaining |
|
240 m.reset(); |
|
241 try { |
|
242 m.replaceFirst(mr -> { m.reset(); return "Y"; }); |
|
243 fail(); |
|
244 } catch (ConcurrentModificationException e) { |
|
245 // Should reach here |
|
246 } |
|
247 |
|
248 m.reset(); |
|
249 try { |
|
250 m.replaceAll(mr -> { m.reset(); return "Y"; }); |
|
251 fail(); |
|
252 } catch (ConcurrentModificationException e) { |
|
253 // Should reach here |
|
254 } |
|
255 } |
|
256 |
|
257 // A holder of MatchResult that can compare |
|
258 static class MatchResultHolder implements Comparable<MatchResultHolder> { |
|
259 final MatchResult mr; |
|
260 |
|
261 MatchResultHolder(Matcher m) { |
|
262 this(m.toMatchResult()); |
|
263 } |
|
264 |
|
265 MatchResultHolder(MatchResult mr) { |
|
266 this.mr = mr; |
|
267 } |
|
268 |
|
269 @Override |
|
270 public int compareTo(MatchResultHolder that) { |
|
271 int c = that.mr.group().compareTo(this.mr.group()); |
|
272 if (c != 0) |
|
273 return c; |
|
274 |
|
275 c = Integer.compare(that.mr.start(), this.mr.start()); |
|
276 if (c != 0) |
|
277 return c; |
|
278 |
|
279 c = Integer.compare(that.mr.end(), this.mr.end()); |
|
280 if (c != 0) |
|
281 return c; |
|
282 |
|
283 c = Integer.compare(that.mr.groupCount(), this.mr.groupCount()); |
|
284 if (c != 0) |
|
285 return c; |
|
286 |
|
287 for (int g = 0; g < this.mr.groupCount(); g++) { |
|
288 c = that.mr.group(g).compareTo(this.mr.group(g)); |
|
289 if (c != 0) |
|
290 return c; |
|
291 |
|
292 c = Integer.compare(that.mr.start(g), this.mr.start(g)); |
|
293 if (c != 0) |
|
294 return c; |
|
295 |
|
296 c = Integer.compare(that.mr.end(g), this.mr.end(g)); |
|
297 if (c != 0) |
|
298 return c; |
|
299 } |
|
300 return 0; |
|
301 } |
|
302 |
|
303 @Override |
|
304 public boolean equals(Object that) { |
|
305 if (this == that) return true; |
|
306 if (that == null || getClass() != that.getClass()) return false; |
|
307 |
|
308 return this.compareTo((MatchResultHolder) that) == 0; |
|
309 } |
|
310 |
|
311 @Override |
|
312 public int hashCode() { |
|
313 return mr.group().hashCode(); |
|
314 } |
|
315 } |
191 } |
316 } |