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