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