Merge pull request #980 from StephenMcConnel/bug-18638
[mono.git] / mcs / class / System / Test / System.Text.RegularExpressions / RegexTest.cs
1 //
2 // assembly:    System_test
3 // namespace:   MonoTests.System.Text.RegularExpressions
4 // file:        RegexTest.cs
5 //
6 // Authors:     
7 //   Juraj Skripsky (juraj@hotfeet.ch)
8 //
9 // (c) 2003 Juraj Skripsky
10
11 using System;
12 using System.Text.RegularExpressions;
13
14 #if NET_2_0
15 using System.Collections.Generic;
16 #endif
17
18 using NUnit.Framework;
19
20 namespace MonoTests.System.Text.RegularExpressions
21 {
22
23         [TestFixture]
24         public class CompiledRegexTest :  RegexTest
25         {
26                 [SetUp]
27         public void SetUp ()
28                 {
29                         Compiled = true;
30                 }
31         }
32
33         [TestFixture]
34         public class InterpretedRegexTest :  RegexTest
35         {
36         [SetUp]
37         public void SetUp ()
38         {
39                 Compiled = false;
40         }
41         }
42
43
44         public class RegexTest
45         {
46
47        RegexOptions AddOptions ( RegexOptions options ){
48                if( Compiled ){
49                        options |= RegexOptions.Compiled;
50                }
51
52                return options;
53        }
54
55        protected bool Compiled { get; set; }
56
57 #if NET_2_0
58                 private int cache_initial_value;
59
60                 [TestFixtureSetUp]
61                 public void FixtureSetUp ()
62                 {
63                         cache_initial_value = Regex.CacheSize;
64                 }
65
66                 [TearDown]
67                 public void TearDown ()
68                 {
69                         Regex.CacheSize = cache_initial_value;
70                 }
71 #endif
72
73                 [Test]
74                 public void Simple ()
75                 {
76                         char[] c = { (char)32, (char)8212, (char)32 };
77                         string s = new String(c);
78                         Assert.IsTrue (Regex.IsMatch(s, s), "char");
79                 }
80
81                 [Test, ExpectedException (typeof (ArgumentNullException))]
82                 public void NullPattern1 ()
83                 {
84                         new Regex (null);
85                 }
86
87                 [Test, ExpectedException (typeof (ArgumentNullException))]
88                 public void NullPattern2 ()
89                 {
90                         new Regex (null, AddOptions( RegexOptions.None ));
91                 }
92
93                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
94                 public void InvalidOptions1 ()
95                 {
96                         new Regex ("foo", (RegexOptions) Int32.MaxValue);
97                 }
98
99                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
100                 public void InvalidOptions2 ()
101                 {
102                         new Regex ("foo", AddOptions( RegexOptions.ECMAScript | RegexOptions.RightToLeft ));
103                 }
104                 
105                 [Test]
106                 public void Unescape ()
107                 {
108                         string inString = @"\a\b\t\r\v\f\n\e\02400\x231\cC\ufffff\*";
109                         char [] c = { (char)7, (char)8, (char)9, (char)13, 
110                                       (char)11, (char)12, (char)10, (char)27, (char) 20,
111                                       (char)48, (char)48, (char)35, (char)49, 
112                                       (char)3, (char)65535, (char)102, (char)42
113                         };
114                         string expectedString = new String(c);
115                         string outString = Regex.Unescape(inString);
116
117                         Assert.AreEqual (outString, expectedString, "unescape");
118                 }
119
120                 [Test]
121                 public void Match1 ()
122                 {
123                         Regex email = new Regex ("(?<user>[^@]+)@(?<domain>.+)",
124                                                  AddOptions( RegexOptions.None ));
125                         Match m;
126
127                         m = email.Match ("mono@go-mono.com");
128
129                         Assert.IsTrue (m.Success, "#m01");
130                         Assert.AreEqual ("mono", m.Groups ["user"].Value, "#m02");
131                         Assert.AreEqual ("go-mono.com", m.Groups ["domain"].Value, "#m03");
132
133                         m = email.Match ("mono.bugs@go-mono.com");
134                         Assert.IsTrue (m.Success, "m04");
135                         Assert.AreEqual ("mono.bugs", m.Groups ["user"].Value, "#m05");
136                         Assert.AreEqual ("go-mono.com", m.Groups ["domain"].Value, "#m06");
137                 }
138
139                 [Test]
140                 public void Match2 ()
141                 {
142                         Regex regex = new Regex(@"(?<tab>\t)|(?<text>[^\t]*)",
143                                                 AddOptions( RegexOptions.None ));
144                         MatchCollection col = regex.Matches("\tjust a text");
145                         Assert.AreEqual(3, col.Count);
146                         Assert.AreEqual (col [0].Value, "\t");
147                         Assert.AreEqual (col [1].Value, "just a text");
148                         Assert.AreEqual(col[2].Value, string.Empty);
149                 }
150
151                 [Test, ExpectedException (typeof (ArgumentNullException))]
152                 public void Match_Null1 ()
153                 {
154                         new Regex (@"foo",AddOptions( RegexOptions.None )).Match (null);
155                 }
156
157                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
158                 public void Match_BadStart1 ()
159                 {
160                         new Regex (@"foo",
161                                    AddOptions( RegexOptions.None )).Match ("foobar", -1);
162                 }
163
164                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
165                 public void Match_BadStart2 ()
166                 {
167                         new Regex (@"foo",
168                                    AddOptions( RegexOptions.None )).Match ("foobar", -1, 0);
169                 }
170
171                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
172                 public void Match_BadStart3 ()
173                 {
174                         new Regex (@"foo",
175                                    AddOptions( RegexOptions.None )).Match ("foobar", 7);
176                 }
177
178                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
179                 public void Match_BadStart4 ()
180                 {
181                         new Regex (@"foo",
182                                    AddOptions( RegexOptions.None )).Match ("foobar", 7, 0);
183                 }
184
185                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
186                 public void Match_BadLength1 ()
187                 {
188                         new Regex (@"foo",
189                                    AddOptions( RegexOptions.None )).Match ("foobar", 5, -1);
190                 }
191
192                 [Test, ExpectedException (typeof (IndexOutOfRangeException))]
193                 public void Match_BadLength2 ()
194                 {
195                         new Regex (@"foo",
196                                    AddOptions( RegexOptions.None )).Match ("foobar", 5, 3);
197                 }
198
199                 [Test, ExpectedException (typeof (ArgumentNullException))]
200                 public void Matches_Null1 ()
201                 {
202                         new Regex (@"foo",
203                                    AddOptions( RegexOptions.None )).Matches (null);
204                 }
205
206                 [Test, ExpectedException (typeof (ArgumentNullException))]
207                 public void Matches_Null2 ()
208                 {
209                         new Regex (@"foo",
210                                    AddOptions( RegexOptions.None )).Matches (null, 0);
211                 }
212
213                 [Test, ExpectedException (typeof (ArgumentNullException))]
214                 public void Matches_Null3 ()
215                 {
216                         new Regex (@"foo",
217                                    AddOptions(RegexOptions.RightToLeft)).Matches (null);
218                 }
219
220                 [Test]
221                 public void Match_SubstringAnchors ()
222                 {
223                         Regex r = new Regex ("^ooba$",
224                                              AddOptions( RegexOptions.None ));
225                         Match m = r.Match ("foobar", 1, 4);
226
227                         Assert.IsTrue (m.Success);
228                         Assert.AreEqual ("ooba", m.Value);
229                 }
230
231                 [Test]
232                 public void Match_SubstringRtl ()
233                 {
234                         Regex r = new Regex(@".*", RegexOptions.RightToLeft);
235                         Match m = r.Match("ABCDEFGHI", 2, 6);
236
237                         Assert.IsTrue (m.Success);
238                         Assert.AreEqual ("CDEFGH", m.Value);
239                 }
240
241                 [Test, ExpectedException (typeof (ArgumentNullException))]
242                 public void Replace_InputNull ()
243                 {
244                         Regex r = new Regex ("^.*$",
245                                              AddOptions( RegexOptions.None ));
246                         MatchEvaluator m = delegate (Match match) {return null;};
247                         r.Replace (null, m, 0, 0);
248                 }
249
250                 [Test, ExpectedException (typeof (ArgumentNullException))]
251                 public void Replace_InputNull2 ()
252                 {
253                         Regex r = new Regex ("^.*$",
254                                              AddOptions( RegexOptions.None ));
255                         r.Replace (null, "abc", 0, 0);
256                 }
257
258                 [Test, ExpectedException (typeof (ArgumentNullException))]
259                 public void Replace_InputNull3 ()
260                 {
261                         Regex r = new Regex ("^.*$",
262                                              AddOptions(RegexOptions.RightToLeft));
263                         MatchEvaluator m = delegate (Match match) {return null;};
264                         r.Replace (null, m);
265                 }
266
267                 [Test, ExpectedException (typeof (ArgumentNullException))]
268                 public void Replace_InputNull4 ()
269                 {
270                         Regex r = new Regex ("^.*$",
271                                              AddOptions(RegexOptions.RightToLeft));
272                         r.Replace (null, "abc");
273                 }
274
275                 [Test, ExpectedException (typeof (ArgumentNullException))]
276                 public void Replace_ReplacementNull ()
277                 {
278                         Regex r = new Regex ("^.*$",
279                                              AddOptions( RegexOptions.None ));
280                         r.Replace ("string", (string) null, 0, 0);
281                 }
282
283                 [Test, ExpectedException (typeof (ArgumentNullException))]
284                 public void Replace_EvaluatorNull ()
285                 {
286                         Regex r = new Regex ("^.*$",
287                                              AddOptions( RegexOptions.None ));
288                         MatchEvaluator m = null;
289                         r.Replace ("string", m, 0, 0);
290                 }
291
292                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
293                 public void Replace_InvalidCount ()
294                 {
295                         Regex r = new Regex ("foo|bar",
296                                              AddOptions( RegexOptions.None ));
297                         r.Replace ("foo",  "baz", -4);
298                 }
299
300                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
301                 public void Replace_InvalidStart ()
302                 {
303                         Regex r = new Regex ("foo|bar",
304                                              AddOptions( RegexOptions.None ));
305                         r.Replace ("foo", "baz", 1, -4);
306                 }
307
308                 [Test, ExpectedException (typeof (ArgumentNullException))]
309                 public void Split_InputNull1 ()
310                 {
311                         Regex.Split (null, "^.*$");
312                 }
313
314                 [Test, ExpectedException (typeof (ArgumentNullException))]
315                 public void Split_InputNull2 ()
316                 {
317                         Regex.Split (null, "^.*$", RegexOptions.RightToLeft);
318                 }
319
320                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
321                 public void Split_InvalidCount ()
322                 {
323                         Regex r = new Regex ("^.*$",
324                                              AddOptions( RegexOptions.None ));
325                         r.Split ("foo", -4);
326                 }
327
328                 [Test, ExpectedException (typeof (ArgumentOutOfRangeException))]
329                 public void Split_InvalidCount2 ()
330                 {
331                         Regex r = new Regex ("^.*$",
332                                              AddOptions( RegexOptions.None ));
333                         r.Split ("foo", 1, -4);
334                 }
335
336                 [Test, ExpectedException (typeof (ArgumentNullException))]
337                 public void Escape_Null ()
338                 {
339                         Regex.Escape (null);
340                 }
341
342                 [Test, ExpectedException (typeof (ArgumentNullException))]
343                 public void Unescape_Null ()
344                 {
345                         Regex.Unescape (null);
346                 }
347
348                 static string story =
349                         "Two little dragons lived in the forest\n" +
350                         "They spent their days collecting honey suckle,\n" +
351                         "And eating curds and whey\n" +
352                         "Until an evil sorcer came along\n" +
353                         "And chased my dragon friends away";
354
355                 struct MatchCollectionTrial {
356                         public readonly string name;
357                         public readonly string text;
358                         public readonly string regex;
359                         public readonly string [] matches;
360                         public MatchCollectionTrial (string name, string text, string regex, string [] matches)
361                         {
362                                 this.name = name;
363                                 this.text = text;
364                                 this.regex = regex;
365                                 this.matches = matches;
366                         }
367                 }
368
369                 static readonly MatchCollectionTrial [] trials = {
370                         new MatchCollectionTrial ("word", "the fat cat ate the rat", "(?<word>\\w+)", 
371                                 new string [] { "the", "fat", "cat", "ate", "the", "rat" }),
372                         new MatchCollectionTrial ("digit", "0 1 2 3 4 5 6a7b8c9d10", "(?<digit>\\d+)", 
373                                 new string [] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" }),
374                         new MatchCollectionTrial ("line", story, "(?<line>.+)", 
375                                 new string [] { "Two little dragons lived in the forest",
376                                                 "They spent their days collecting honey suckle,",
377                                                 "And eating curds and whey",
378                                                 "Until an evil sorcer came along",
379                                                 "And chased my dragon friends away" }),
380                         new MatchCollectionTrial ("nonwhite", "ab 12 cde 456 fghi .,\niou", "(?<nonwhite>\\S+)",
381                                 new string [] { "ab", "12", "cde", "456", "fghi", ".,", "iou" }),
382                         new MatchCollectionTrial ("nondigit", "ab0cd1ef2", "(?<nondigit>\\D+)",
383                                 new string [] { "ab", "cd", "ef" })
384                 };
385
386                 static void runTrial (MatchCollectionTrial t, bool compiled)
387                 {
388                         runTrial (t, false, compiled);
389                         runTrial (t, true, compiled);
390                 }
391
392                 static void runTrial (MatchCollectionTrial t, bool rtl, bool compiled)
393                 {
394                         int i;
395                         MatchCollection mc;
396
397                         string name = t.name;
398                         if (rtl)
399                                 name += "-rtl";
400
401                         int len = t.matches.Length;
402                         RegexOptions options = rtl ? RegexOptions.RightToLeft : RegexOptions.None;
403                         if( compiled )
404                                 options |= RegexOptions.Compiled;
405
406                         Regex r = new Regex (t.regex,options);
407
408                         // Incremental mode -- this access
409                         mc = r.Matches (t.text);
410                         for (i = 0; i < len; ++i)
411                                 Assert.AreEqual (mc [i].Value, t.matches [rtl ? len - i - 1 : i], "{0}:this:{1}", name, i);
412                         Assert.AreEqual (i, mc.Count, "{0}:this:count", name);
413
414                         // Incremental mode -- enumerator
415                         mc = r.Matches (t.text);
416                         i = 0;
417                         foreach (Match m in mc) {
418                                 Assert.AreEqual (m.Value, t.matches [rtl ? len - i - 1 : i], "{0}:enum:{1}", name, i);
419                                 ++i;
420                         }
421                         Assert.AreEqual (i, len, "{0}:enum:count", name);
422
423                         // random mode
424                         Random rng = new Random ();
425                         for (int j = 0; j < len * 5; ++j) {
426                                 i = rng.Next (len);
427                                 Assert.AreEqual (mc [i].Value, t.matches [rtl ? len - i - 1 : i], "{0}:random{1}:{2}", name, j, i);
428                         }
429
430                         // Non-incremental mode
431                         mc = r.Matches (t.text);
432                         Assert.AreEqual (mc.Count, len);
433                         i = 0;
434                         foreach (Match m in mc) {
435                                 Assert.AreEqual (m.Value, t.matches [rtl ? len - i - 1 : i], "{0}:nienum:{1}", name, i);
436                                 ++i;
437                         }
438                         for (i = 0; i < len; ++i)
439                                 Assert.AreEqual (mc [i].Value, t.matches [rtl ? len - i - 1 : i], "{0}:nithis:{1}", name, i);
440                 }
441
442                 [Test]
443                 public void Matches ()
444                 {
445                         foreach (MatchCollectionTrial t in trials)
446                                 runTrial (t,Compiled);
447                 }
448 #if NET_2_0
449                 [Test]
450                 public void CacheSize ()
451                 {
452                         Assert.AreEqual (15, Regex.CacheSize, "CacheSize");
453                         Regex.CacheSize = 0;
454                         Regex.CacheSize = Int32.MaxValue;
455                 }
456
457                 [Test]
458                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
459                 public void CacheSize_Negative ()
460                 {
461                         Regex.CacheSize = -1;
462                 }
463
464                 [Test]
465                 [ExpectedException (typeof (ArgumentOutOfRangeException))]
466                 public void CacheSize_Min ()
467                 {
468                         Regex.CacheSize = Int32.MinValue;
469                 }
470
471                 static IEnumerable<uint> Primes (uint m)
472                 {
473                         if (m < 2)
474                                 yield break;
475
476                         yield return 2;
477
478                         Dictionary<uint, uint> w = new Dictionary<uint, uint> ();
479                         uint p2, n1;
480
481                         for (uint n = 3; n < m; n += 2) {
482                                 if (w.TryGetValue (n, out p2)) {
483                                         w.Remove (n);
484                                         n1 = n + p2;
485                                 } else {
486                                         yield return n;
487                                         n1 = n * n;
488                                         p2 = n + n;
489
490                                         // if there's an overflow, don't bother
491                                         if (n1 / n != n || n1 >= m)
492                                                 continue;
493                                 }
494
495                                 while (w.ContainsKey (n1))
496                                         n1 += p2;
497                                 w [n1] = p2;
498                         }
499                 }
500
501                 [Test]
502                 public void PrimeRegex ()
503                 {
504                         // Perl regex oneliner by: abigail@fnx.com (Abigail)
505                         // from: http://www.mit.edu:8008/bloom-picayune.mit.edu/perl/10138
506                         // perl -wle 'print "Prime" if (1 x shift) !~ /^1?$|^(11+?)\1+$/'
507
508                         // This is a backtracking torture test
509
510                         Regex composite = new Regex (@"^1?$|^(11+?)\1+$",
511                                                      AddOptions( RegexOptions.None ));
512
513                         uint i = 0;
514                         string x = "";
515
516                         foreach (uint p in Primes (3333)) {
517                                 while (i < p) {
518                                         Assert.IsTrue (composite.IsMatch (x));
519                                         ++i;
520                                         x += "1";
521                                 }
522                                 // i == p
523                                 Assert.IsFalse (composite.IsMatch (x));
524                                 ++i;
525                                 x += "1";
526                         }
527                 }
528 #endif
529         }
530 }