xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/src/std/regex/internal/tests2.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 // Split-up due to DMD's enormous memory consumption
2 
3 module std.regex.internal.tests2;
4 
5 package(std.regex):
6 
7 import std.conv, std.exception, std.meta, std.range,
8     std.typecons, std.regex;
9 
10 import std.uni : Escapables; // characters that need escaping
11 
12 @safe unittest
13 {
14     auto cr = ctRegex!("abc");
15     assert(bmatch("abc",cr).hit == "abc");
16     auto cr2 = ctRegex!("ab*c");
17     assert(bmatch("abbbbc",cr2).hit == "abbbbc");
18 }
19 @safe unittest
20 {
21     auto cr3 = ctRegex!("^abc$");
22     assert(bmatch("abc",cr3).hit == "abc");
23     auto cr4 = ctRegex!(`\b(a\B[a-z]b)\b`);
24     assert(array(match("azb",cr4).captures) == ["azb", "azb"]);
25 }
26 
27 @safe unittest
28 {
29     auto cr5 = ctRegex!("(?:a{2,4}b{1,3}){1,2}");
30     assert(bmatch("aaabaaaabbb", cr5).hit == "aaabaaaabbb");
31     auto cr6 = ctRegex!("(?:a{2,4}b{1,3}){1,2}?"w);
32     assert(bmatch("aaabaaaabbb"w,  cr6).hit == "aaab"w);
33 }
34 
35 @safe unittest
36 {
37     auto cr7 = ctRegex!(`\r.*?$`,"sm");
38     assert(bmatch("abc\r\nxy",  cr7).hit == "\r\nxy");
39     auto greed =  ctRegex!("<packet.*?/packet>");
40     assert(bmatch("<packet>text</packet><packet>text</packet>", greed).hit
41             == "<packet>text</packet>");
42 }
43 
44 @safe unittest
45 {
46     import std.algorithm.comparison : equal;
47     auto cr8 = ctRegex!("^(a)(b)?(c*)");
48     auto m8 = bmatch("abcc",cr8);
49     assert(m8);
50     assert(m8.captures[1] == "a");
51     assert(m8.captures[2] == "b");
52     assert(m8.captures[3] == "cc");
53     auto cr9 = ctRegex!("q(a|b)*q");
54     auto m9 = match("xxqababqyy",cr9);
55     assert(m9);
56     assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"]));
57 }
58 
59 @safe unittest
60 {
61     import std.algorithm.comparison : equal;
62     auto rtr = regex("a|b|c");
63     static ctr = regex("a|b|c");
64     assert(equal(rtr.ir,ctr.ir));
65     //CTFE parser BUG is triggered by group
66     //in the middle of alternation (at least not first and not last)
67     static testCT = regex(`abc|(edf)|xyz`);
68     auto testRT = regex(`abc|(edf)|xyz`);
69     assert(equal(testCT.ir,testRT.ir));
70 }
71 
72 @safe unittest
73 {
74     import std.algorithm.comparison : equal;
75     import std.algorithm.iteration : map;
76     enum cx = ctRegex!"(A|B|C)";
77     auto mx = match("B",cx);
78     assert(mx);
79     assert(equal(mx.captures, [ "B", "B"]));
80     enum cx2 = ctRegex!"(A|B)*";
81     assert(match("BAAA",cx2));
82 
83     enum cx3 = ctRegex!("a{3,4}","i");
84     auto mx3 = match("AaA",cx3);
85     assert(mx3);
86     assert(mx3.captures[0] == "AaA");
87     enum cx4 = ctRegex!(`^a{3,4}?[a-zA-Z0-9~]{1,2}`,"i");
88     auto mx4 = match("aaaabc", cx4);
89     assert(mx4);
90     assert(mx4.captures[0] == "aaaab");
91     auto cr8 = ctRegex!("(a)(b)?(c*)");
92     auto m8 = bmatch("abcc",cr8);
93     assert(m8);
94     assert(m8.captures[1] == "a");
95     assert(m8.captures[2] == "b");
96     assert(m8.captures[3] == "cc");
97     auto cr9 = ctRegex!(".*$", "gm");
98     auto m9 = match("First\rSecond", cr9);
99     assert(m9);
100     assert(equal(map!"a.hit"(m9), ["First", "", "Second"]));
101 }
102 
103 @safe unittest
104 {
105     import std.algorithm.comparison : equal;
106     import std.algorithm.iteration : map;
107 //global matching
test_body(alias matchFn)108     void test_body(alias matchFn)()
109     {
110         string s = "a quick brown fox jumps over a lazy dog";
111         auto r1 = regex("\\b[a-z]+\\b","g");
112         string[] test;
113         foreach (m; matchFn(s, r1))
114             test ~= m.hit;
115         assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"]));
116         auto free_reg = regex(`
117 
118             abc
119             \s+
120             "
121             (
122                     [^"]+
123                 |   \\ "
124             )+
125             "
126             z
127         `, "x");
128         auto m = match(`abc  "quoted string with \" inside"z`,free_reg);
129         assert(m);
130         string mails = " hey@you.com no@spam.net ";
131         auto rm = regex(`@(?<=\S+@)\S+`,"g");
132         assert(equal(map!"a[0]"(matchFn(mails, rm)), ["@you.com", "@spam.net"]));
133         auto m2 = matchFn("First line\nSecond line",regex(".*$","gm"));
134         assert(equal(map!"a[0]"(m2), ["First line", "", "Second line"]));
135         auto m2a = matchFn("First line\nSecond line",regex(".+$","gm"));
136         assert(equal(map!"a[0]"(m2a), ["First line", "Second line"]));
137         auto m2b = matchFn("First line\nSecond line",regex(".+?$","gm"));
138         assert(equal(map!"a[0]"(m2b), ["First line", "Second line"]));
139         debug(std_regex_test) writeln("!!! FReD FLAGS test done "~matchFn.stringof~" !!!");
140     }
141     test_body!bmatch();
142     test_body!match();
143 }
144 
145 //tests for accumulated std.regex issues and other regressions
146 @safe unittest
147 {
148     import std.algorithm.comparison : equal;
149     import std.algorithm.iteration : map;
150     void test_body(alias matchFn)()
151     {
152         // https://issues.dlang.org/show_bug.cgi?id=5857
153         //matching goes out of control if ... in (...){x} has .*/.+
154         auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures;
155         assert(c[0] == "axxxzayyyyyzd");
156         assert(c[1] == "ayyyyyz");
157         auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures;
158         assert(c2[0] == "axxxayyyyyd");
159         assert(c2[1] == "ayyyyy");
160         // https://issues.dlang.org/show_bug.cgi?id=2108
161         //greedy vs non-greedy
162         auto nogreed = regex("<packet.*?/packet>");
163         assert(matchFn("<packet>text</packet><packet>text</packet>", nogreed).hit
164                == "<packet>text</packet>");
165         auto greed =  regex("<packet.*/packet>");
166         assert(matchFn("<packet>text</packet><packet>text</packet>", greed).hit
167                == "<packet>text</packet><packet>text</packet>");
168         // https://issues.dlang.org/show_bug.cgi?id=4574
169         //empty successful match still advances the input
170         string[] pres, posts, hits;
171         foreach (m; matchFn("abcabc", regex("","g")))
172         {
173             pres ~= m.pre;
174             posts ~= m.post;
175             assert(m.hit.empty);
176 
177         }
178         auto heads = [
179             "abcabc",
180             "abcab",
181             "abca",
182             "abc",
183             "ab",
184             "a",
185             ""
186         ];
187         auto tails = [
188             "abcabc",
189              "bcabc",
190               "cabc",
191                "abc",
192                 "bc",
193                  "c",
194                   ""
195         ];
196         assert(pres == array(retro(heads)));
197         assert(posts == tails);
198         // https://issues.dlang.org/show_bug.cgi?id=6076
199         //regression on .*
200         auto re = regex("c.*|d");
201         auto m = matchFn("mm", re);
202         assert(!m);
203         debug(std_regex_test) writeln("!!! FReD REGRESSION test done "~matchFn.stringof~" !!!");
204         auto rprealloc = regex(`((.){5}.{1,10}){5}`);
205         auto arr = array(repeat('0',100));
206         auto m2 = matchFn(arr, rprealloc);
207         assert(m2);
208         assert(collectException(
209                 regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$")
210                 ) is null);
211         foreach (ch; [Escapables])
212         {
213             assert(match(to!string(ch),regex(`[\`~ch~`]`)));
214             assert(!match(to!string(ch),regex(`[^\`~ch~`]`)));
215             assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`)));
216         }
217         // https://issues.dlang.org/show_bug.cgi?id=7718
218         string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'";
219         auto reStrCmd = regex (`(".*")|('.*')`, "g");
220         assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)),
221                      [`"/GIT/Ruby Apps/sec"`, `'notimer'`]));
222     }
223     test_body!bmatch();
224     test_body!match();
225 }
226 
227 // tests for replace
228 @safe unittest
229 {
230     void test(alias matchFn)()
231     {
232         import std.uni : toUpper;
233 
234         static foreach (i, v; AliasSeq!(string, wstring, dstring))
235         {{
236             auto baz(Cap)(Cap m)
237             if (is(Cap == Captures!(Cap.String)))
238             {
239                 return toUpper(m.hit);
240             }
241             alias String = v;
242             assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r")), to!String("c"))
243                    == to!String("ack rapacity"));
244             assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r"), "g"), to!String("c"))
245                    == to!String("ack capacity"));
246             assert(std.regex.replace!(matchFn)(to!String("noon"), regex(to!String("^n")), to!String("[$&]"))
247                    == to!String("[n]oon"));
248             assert(std.regex.replace!(matchFn)(
249                 to!String("test1 test2"), regex(to!String(`\w+`),"g"), to!String("$`:$'")
250             ) == to!String(": test2 test1 :"));
251             auto s = std.regex.replace!(baz!(Captures!(String)))(to!String("Strap a rocket engine on a chicken."),
252                     regex(to!String("[ar]"), "g"));
253             assert(s == "StRAp A Rocket engine on A chicken.");
254         }}
255         debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~"  !!!");
256     }
257     test!(bmatch)();
258     test!(match)();
259 }
260 
261 // tests for splitter
262 @safe unittest
263 {
264     import std.algorithm.comparison : equal;
265     auto s1 = ", abc, de,     fg, hi, ";
266     auto sp1 = splitter(s1, regex(", *"));
267     auto w1 = ["", "abc", "de", "fg", "hi", ""];
268     assert(equal(sp1, w1));
269 
270     auto s2 = ", abc, de,  fg, hi";
271     auto sp2 = splitter(s2, regex(", *"));
272     auto w2 = ["", "abc", "de", "fg", "hi"];
273 
274     uint cnt;
275     foreach (e; sp2)
276     {
277         assert(w2[cnt++] == e);
278     }
279     assert(equal(sp2, w2));
280 }
281 
282 @safe unittest
283 {
284     char[] s1 = ", abc, de,  fg, hi, ".dup;
285     auto sp2 = splitter(s1, regex(", *"));
286 }
287 
288 @safe unittest
289 {
290     import std.algorithm.comparison : equal;
291     auto s1 = ", abc, de,  fg, hi, ";
292     auto w1 = ["", "abc", "de", "fg", "hi", ""];
293     assert(equal(split(s1, regex(", *")), w1[]));
294 }
295 
296 // https://issues.dlang.org/show_bug.cgi?id=7141
297 @safe unittest
298 {
299     string pattern = `[a\--b]`;
300     assert(match("-", pattern));
301     assert(match("b", pattern));
302     string pattern2 = `[&-z]`;
303     assert(match("b", pattern2));
304 }
305 
306 // https://issues.dlang.org/show_bug.cgi?id=7111
307 @safe unittest
308 {
309     assert(match("", regex("^")));
310 }
311 
312 // https://issues.dlang.org/show_bug.cgi?id=7300
313 @safe unittest
314 {
315     assert(!match("a"d, "aa"d));
316 }
317 
318 // https://issues.dlang.org/show_bug.cgi?id=7551
319 @safe unittest
320 {
321     auto r = regex("[]abc]*");
322     assert("]ab".matchFirst(r).hit == "]ab");
323     assertThrown(regex("[]"));
324     auto r2 = regex("[]abc--ab]*");
325     assert("]ac".matchFirst(r2).hit == "]");
326 }
327 
328 // https://issues.dlang.org/show_bug.cgi?id=7674
329 @safe unittest
330 {
331     assert("1234".replace(regex("^"), "$$") == "$1234");
332     assert("hello?".replace(regex(r"\?", "g"), r"\?") == r"hello\?");
333     assert("hello?".replace(regex(r"\?", "g"), r"\\?") != r"hello\?");
334 }
335 
336 // https://issues.dlang.org/show_bug.cgi?id=7679
337 @safe unittest
338 {
339     import std.algorithm.comparison : equal;
340     static foreach (S; AliasSeq!(string, wstring, dstring))
341     {{
342         enum re = ctRegex!(to!S(r"\."));
343         auto str = to!S("a.b");
344         assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")]));
345         assert(split(str, re) == [to!S("a"), to!S("b")]);
346     }}
347 }
348 
349 // https://issues.dlang.org/show_bug.cgi?id=8203
350 @safe unittest
351 {
352     string data = "
353     NAME   = XPAW01_STA:STATION
354     NAME   = XPAW01_STA
355     ";
356     auto uniFileOld = data;
357     auto r = regex(
358        r"^NAME   = (?P<comp>[a-zA-Z0-9_]+):*(?P<blk>[a-zA-Z0-9_]*)","gm");
359     auto uniCapturesNew = match(uniFileOld, r);
360     for (int i = 0; i < 20; i++)
361         foreach (matchNew; uniCapturesNew) {}
362     //a second issue with same symptoms
363     auto r2 = regex(`([а-яА-Я\-_]+\s*)+(?<=[\s\.,\^])`);
364     match("аллея Театральная", r2);
365 }
366 
367 // https://issues.dlang.org/show_bug.cgi?id=8637 purity of enforce
368 @safe unittest
369 {
370     auto m = match("hello world", regex("world"));
371     enforce(m);
372 }
373 
374 // https://issues.dlang.org/show_bug.cgi?id=8725
375 @safe unittest
376 {
377   static italic = regex( r"\*
378                 (?!\s+)
379                 (.*?)
380                 (?!\s+)
381                 \*", "gx" );
382   string input = "this * is* interesting, *very* interesting";
383   assert(replace(input, italic, "<i>$1</i>") ==
384       "this * is* interesting, <i>very</i> interesting");
385 }
386 
387 // https://issues.dlang.org/show_bug.cgi?id=8349
388 @safe unittest
389 {
390     enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)</a>";
391     enum peakRegex = ctRegex!(peakRegexStr);
392     //note that the regex pattern itself is probably bogus
393     assert(match(r"\>wgEncode-blah-Tfbs.narrow</a>", peakRegex));
394 }
395 
396 // https://issues.dlang.org/show_bug.cgi?id=9211
397 @safe unittest
398 {
399     import std.algorithm.comparison : equal;
400     auto rx_1 =  regex(r"^(\w)*(\d)");
401     auto m = match("1234", rx_1);
402     assert(equal(m.front, ["1234", "3", "4"]));
403     auto rx_2 = regex(r"^([0-9])*(\d)");
404     auto m2 = match("1234", rx_2);
405     assert(equal(m2.front, ["1234", "3", "4"]));
406 }
407 
408 // https://issues.dlang.org/show_bug.cgi?id=9280
409 @safe unittest
410 {
411     string tomatch = "a!b@c";
412     static r = regex(r"^(?P<nick>.*?)!(?P<ident>.*?)@(?P<host>.*?)$");
413     auto nm = match(tomatch, r);
414     assert(nm);
415     auto c = nm.captures;
416     assert(c[1] == "a");
417     assert(c["nick"] == "a");
418 }
419 
420 
421 // https://issues.dlang.org/show_bug.cgi?id=9579
422 @safe unittest
423 {
424     char[] input = ['a', 'b', 'c'];
425     string format = "($1)";
426     // used to give a compile error:
427     auto re = regex(`(a)`, "g");
428     auto r = replace(input, re, format);
429     assert(r == "(a)bc");
430 }
431 
432 // https://issues.dlang.org/show_bug.cgi?id=9634
433 @safe unittest
434 {
435     auto re = ctRegex!"(?:a+)";
436     assert(match("aaaa", re).hit == "aaaa");
437 }
438 
439 // https://issues.dlang.org/show_bug.cgi?id=10798
440 @safe unittest
441 {
442     auto cr = ctRegex!("[abcd--c]*");
443     auto m  = "abc".match(cr);
444     assert(m);
445     assert(m.hit == "ab");
446 }
447 
448 // https://issues.dlang.org/show_bug.cgi?id=10913
449 @system unittest
450 {
451     @system static string foo(const(char)[] s)
452     {
453         return s.dup;
454     }
455     @safe static string bar(const(char)[] s)
456     {
457         return s.dup;
458     }
459     () @system {
460         replace!((a) => foo(a.hit))("blah", regex(`a`));
461     }();
462     () @safe {
463         replace!((a) => bar(a.hit))("blah", regex(`a`));
464     }();
465 }
466 
467 // https://issues.dlang.org/show_bug.cgi?id=11262
468 @safe unittest
469 {
470     enum reg = ctRegex!(r",", "g");
471     auto str = "This,List";
472     str = str.replace(reg, "-");
473     assert(str == "This-List");
474 }
475 
476 // https://issues.dlang.org/show_bug.cgi?id=11775
477 @safe unittest
478 {
479     assert(collectException(regex("a{1,0}")));
480 }
481 
482 // https://issues.dlang.org/show_bug.cgi?id=11839
483 @safe unittest
484 {
485     import std.algorithm.comparison : equal;
486     assert(regex(`(?P<var1>\w+)`).namedCaptures.equal(["var1"]));
487     assert(collectException(regex(`(?P<1>\w+)`)));
488     assert(regex(`(?P<v1>\w+)`).namedCaptures.equal(["v1"]));
489     assert(regex(`(?P<__>\w+)`).namedCaptures.equal(["__"]));
490     assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"]));
491 }
492 
493 // https://issues.dlang.org/show_bug.cgi?id=12076
494 @safe unittest
495 {
496     auto RE = ctRegex!(r"(?<!x[a-z]+)\s([a-z]+)");
497     string s = "one two";
498     auto m = match(s, RE);
499 }
500 
501 // https://issues.dlang.org/show_bug.cgi?id=12105
502 @safe unittest
503 {
504     auto r = ctRegex!`.*?(?!a)`;
505     assert("aaab".matchFirst(r).hit == "aaa");
506     auto r2 = ctRegex!`.*(?!a)`;
507     assert("aaab".matchFirst(r2).hit == "aaab");
508 }
509 
510 // https://issues.dlang.org/show_bug.cgi?id=11784
511 @safe unittest
512 {
513     assert("abcdefghijklmnopqrstuvwxyz"
514         .matchFirst("[a-z&&[^aeiuo]]").hit == "b");
515 }
516 
517 // https://issues.dlang.org/show_bug.cgi?id=12366
518 @safe unittest
519 {
520      auto re = ctRegex!(`^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+$))\5){2})*x?$`);
521      assert("xxxxxxxx".match(re).empty);
522      assert(!"xxxx".match(re).empty);
523 }
524 
525 // https://issues.dlang.org/show_bug.cgi?id=12582
526 @safe unittest
527 {
528     auto r = regex(`(?P<a>abc)`);
529     assert(collectException("abc".matchFirst(r)["b"]));
530 }
531 
532 // https://issues.dlang.org/show_bug.cgi?id=12691
533 @safe unittest
534 {
535     assert(bmatch("e@", "^([a-z]|)*$").empty);
536     assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty);
537 }
538 
539 // https://issues.dlang.org/show_bug.cgi?id=12713
540 @safe unittest
541 {
542     assertThrown(regex("[[a-z]([a-z]|(([[a-z])))"));
543 }
544 
545 // https://issues.dlang.org/show_bug.cgi?id=12747
546 @safe unittest
547 {
548     assertThrown(regex(`^x(\1)`));
549     assertThrown(regex(`^(x(\1))`));
550     assertThrown(regex(`^((x)(?=\1))`));
551 }
552 
553 // https://issues.dlang.org/show_bug.cgi?id=13532
554 version (none) // TODO: revist once we have proper benchmark framework
555 @safe unittest
556 {
557     import std.datetime.stopwatch : StopWatch, AutoStart;
558     import std.math.algebraic : abs;
559     import std.conv : to;
560     enum re1 = ctRegex!`[0-9][0-9]`;
561     immutable static re2 = ctRegex!`[0-9][0-9]`;
562     immutable iterations = 1_000_000;
563     size_t result1 = 0, result2 = 0;
564     auto sw = StopWatch(AutoStart.yes);
565     foreach (_; 0 .. iterations)
566     {
567         result1 += matchFirst("12345678", re1).length;
568     }
569     const staticTime = sw.peek();
570     sw.reset();
571     foreach (_; 0 .. iterations)
572     {
573         result2 += matchFirst("12345678", re2).length;
574     }
575     const enumTime = sw.peek();
576     assert(result1 == result2);
577     auto ratio = 1.0 * enumTime.total!"usecs" / staticTime.total!"usecs";
578     // enum is faster or the diff is less < 30%
579     assert(ratio < 1.0 || abs(ratio - 1.0) < 0.75,
580         "enum regex to static regex ratio "~to!string(ratio));
581 }
582 
583 // https://issues.dlang.org/show_bug.cgi?id=14504
584 @safe unittest
585 {
586     auto p = ctRegex!("a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?" ~
587             "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
588 }
589 
590 // https://issues.dlang.org/show_bug.cgi?id=14529
591 @safe unittest
592 {
593     auto ctPat2 = regex(r"^[CDF]$", "i");
594     foreach (v; ["C", "c", "D", "d", "F", "f"])
595         assert(matchAll(v, ctPat2).front.hit == v);
596 }
597 
598 // https://issues.dlang.org/show_bug.cgi?id=14615
599 @safe unittest
600 {
601     import std.array : appender;
602     import std.regex : replaceFirst, replaceFirstInto, regex;
603     import std.stdio : writeln;
604 
605     auto example = "Hello, world!";
606     auto pattern = regex("^Hello, (bug)");  // won't find this one
607     auto result = replaceFirst(example, pattern, "$1 Sponge Bob");
608     assert(result == "Hello, world!");  // Ok.
609 
610     auto sink = appender!string;
611     replaceFirstInto(sink, example, pattern, "$1 Sponge Bob");
612     assert(sink.data == "Hello, world!");
613     replaceAllInto(sink, example, pattern, "$1 Sponge Bob");
614     assert(sink.data == "Hello, world!Hello, world!");
615 }
616 
617 // https://issues.dlang.org/show_bug.cgi?id=15573
618 @safe unittest
619 {
620     auto rx = regex("[c d]", "x");
621     assert("a b".matchFirst(rx));
622 }
623 
624 // https://issues.dlang.org/show_bug.cgi?id=15864
625 @safe unittest
626 {
627     regex(`(<a (?:(?:\w+=\"[^"]*\")?\s*)*href="\.\.?)"`);
628 }
629 
630 @safe unittest
631 {
632     auto r = regex("(?# comment)abc(?# comment2)");
633     assert("abc".matchFirst(r));
634     assertThrown(regex("(?#..."));
635 }
636 
637 // https://issues.dlang.org/show_bug.cgi?id=17075
638 @safe unittest
639 {
640     enum titlePattern = `<title>(.+)</title>`;
641     static titleRegex = ctRegex!titlePattern;
642     string input = "<title>" ~ "<".repeat(100_000).join;
643     assert(input.matchFirst(titleRegex).empty);
644 }
645 
646 // https://issues.dlang.org/show_bug.cgi?id=17212
647 @safe unittest
648 {
649     auto r = regex(" [a] ", "x");
650     assert("a".matchFirst(r));
651 }
652 
653 // https://issues.dlang.org/show_bug.cgi?id=17157
654 @safe unittest
655 {
656     import std.algorithm.comparison : equal;
657     auto ctr = ctRegex!"(a)|(b)|(c)|(d)";
658     auto r = regex("(a)|(b)|(c)|(d)", "g");
659     auto s = "--a--b--c--d--";
660     auto outcomes = [
661         ["a", "a", "", "", ""],
662         ["b", "", "b", "", ""],
663         ["c", "", "", "c", ""],
664         ["d", "", "", "", "d"]
665     ];
666     assert(equal!equal(s.matchAll(ctr), outcomes));
667     assert(equal!equal(s.bmatch(r), outcomes));
668 }
669 
670 // https://issues.dlang.org/show_bug.cgi?id=17667
671 @safe unittest
672 {
673     import std.algorithm.searching : canFind;
674     void willThrow(T, size_t line = __LINE__)(T arg, string msg)
675     {
676         auto e = collectException(regex(arg));
677         assert(e.msg.canFind(msg), to!string(line) ~ ": " ~ e.msg);
678     }
679     willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class");
680     willThrow([r"[\", r"123"], "no matching ']' found while parsing character class");
681     willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class");
682     willThrow([r"[a-\", r"123"], "no matching ']' found while parsing character class");
683     willThrow([r"\", r"123"], "invalid escape sequence");
684 }
685 
686 // https://issues.dlang.org/show_bug.cgi?id=17668
687 @safe unittest
688 {
689     import std.algorithm.searching;
690     auto e = collectException!RegexException(regex(q"<[^]>"));
691     assert(e.msg.canFind("no operand for '^'"), e.msg);
692 }
693 
694 // https://issues.dlang.org/show_bug.cgi?id=17673
695 @safe unittest
696 {
697     string str = `<">`;
698     string[] regexps = ["abc", "\"|x"];
699     auto regexp = regex(regexps);
700     auto c = matchFirst(str, regexp);
701     assert(c);
702     assert(c.whichPattern == 2);
703 }
704 
705 // https://issues.dlang.org/show_bug.cgi?id=18692
706 @safe unittest
707 {
708     auto rx = regex("()()()");
709     auto ma = "".matchFirst(rx);
710     auto ma2 = ma;
711     ma = ma2;
712     assert(ma[1] == "");
713 }
714