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