* Mono.Options/Options.cs: "Code sharing": Use StringCodea.WrappedLines()
[mono.git] / mcs / class / Mono.Options / Test / Mono.Options / OptionSetTest.cs
1 //
2 // OptionSetTest.cs
3 //
4 // Authors:
5 //  Jonathan Pryor <jpryor@novell.com>
6 //
7 // Copyright (C) 2008 Novell (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System;
30 using System.Collections.Generic;
31 using System.ComponentModel;
32 using System.Globalization;
33 using System.IO;
34
35 #if NDESK_OPTIONS
36 using NDesk.Options;
37 #else
38 using Mono.Options;
39 #endif
40
41 using NUnit.Framework;
42
43 #if NDESK_OPTIONS
44 namespace Tests.NDesk.Options
45 #else
46 namespace Tests.Mono.Options
47 #endif
48 {
49         class FooConverter : TypeConverter {
50                 public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType)
51                 {
52                         if (sourceType == typeof (string))
53                                 return true;
54                         return base.CanConvertFrom (context, sourceType);
55                 }
56
57                 public override object ConvertFrom (ITypeDescriptorContext context,
58                                 CultureInfo culture, object value)
59                 {
60                         string v = value as string;
61                         if (v != null) {
62                                 switch (v) {
63                                         case "A": return Foo.A;
64                                         case "B": return Foo.B;
65                                 }
66                         }
67
68                         return base.ConvertFrom (context, culture, value);
69                 }
70         }
71
72         [TypeConverter (typeof(FooConverter))]
73         class Foo {
74                 public static readonly Foo A = new Foo ("A");
75                 public static readonly Foo B = new Foo ("B");
76                 string s;
77                 Foo (string s) { this.s = s; }
78                 public override string ToString () {return s;}
79         }
80
81         [TestFixture]
82         public class OptionSetTest {
83                 static IEnumerable<string> _ (params string[] a)
84                 {
85                         return a;
86                 }
87
88                 [Test]
89                 public void BundledValues ()
90                 {
91                         var defines = new List<string> ();
92                         var libs    = new List<string> ();
93                         bool debug  = false;
94                         var p = new OptionSet () {
95                                 { "D|define=",  v => defines.Add (v) },
96                                 { "L|library:", v => libs.Add (v) },
97                                 { "Debug",      v => debug = v != null },
98                                 { "E",          v => { /* ignore */ } },
99                         };
100                         p.Parse (_("-DNAME", "-D", "NAME2", "-Debug", "-L/foo", "-L", "/bar", "-EDNAME3"));
101                         Assert.AreEqual (defines.Count, 3);
102                         Assert.AreEqual (defines [0], "NAME");
103                         Assert.AreEqual (defines [1], "NAME2");
104                         Assert.AreEqual (defines [2], "NAME3");
105                         Assert.AreEqual (debug, true);
106                         Assert.AreEqual (libs.Count, 2);
107                         Assert.AreEqual (libs [0], "/foo");
108                         Assert.AreEqual (libs [1], null);
109
110                         Utils.AssertException (typeof(OptionException), 
111                                         "Cannot bundle unregistered option '-V'.",
112                                         p, v => { v.Parse (_("-EVALUENOTSUP")); });
113                 }
114
115                 [Test]
116                 public void RequiredValues ()
117                 {
118                         string a = null;
119                         int n = 0;
120                         OptionSet p = new OptionSet () {
121                                 { "a=", v => a = v },
122                                 { "n=", (int v) => n = v },
123                         };
124                         List<string> extra = p.Parse (_("a", "-a", "s", "-n=42", "n"));
125                         Assert.AreEqual (extra.Count, 2);
126                         Assert.AreEqual (extra [0], "a");
127                         Assert.AreEqual (extra [1], "n");
128                         Assert.AreEqual (a, "s");
129                         Assert.AreEqual (n, 42);
130
131                         extra = p.Parse (_("-a="));
132                         Assert.AreEqual (extra.Count, 0);
133                         Assert.AreEqual (a, "");
134                 }
135
136                 [Test]
137                 public void OptionalValues ()
138                 {
139                         string a = null;
140                         int? n = -1;
141                         Foo f = null;
142                         OptionSet p = new OptionSet () {
143                                 { "a:", v => a = v },
144                                 { "n:", (int? v) => n = v },
145                                 { "f:", (Foo v) => f = v },
146                         };
147                         p.Parse (_("-a=s"));
148                         Assert.AreEqual (a, "s");
149                         p.Parse (_("-a"));
150                         Assert.AreEqual (a, null);
151                         p.Parse (_("-a="));
152                         Assert.AreEqual (a, "");
153
154                         p.Parse (_("-f", "A"));
155                         Assert.AreEqual (f, null);
156                         p.Parse (_("-f"));
157                         Assert.AreEqual (f, null);
158                         p.Parse (_("-f=A"));
159                         Assert.AreEqual (f, Foo.A);
160                         f = null;
161                         p.Parse (_("-fA"));
162                         Assert.AreEqual (f, Foo.A);
163
164                         p.Parse (_("-n42"));
165                         Assert.AreEqual (n.Value, 42);
166                         p.Parse (_("-n", "42"));
167                         Assert.AreEqual (n.HasValue, false);
168                         p.Parse (_("-n=42"));
169                         Assert.AreEqual (n.Value, 42);
170                         p.Parse (_("-n"));
171                         Assert.AreEqual (n.HasValue, false);
172                         Utils.AssertException (typeof(OptionException),
173                                         "Could not convert string `' to type Int32 for option `-n'.",
174                                         p, v => { v.Parse (_("-n=")); });
175                 }
176
177                 [Test]
178                 public void BooleanValues ()
179                 {
180                         bool a = false;
181                         OptionSet p = new OptionSet () {
182                                 { "a", v => a = v != null },
183                         };
184                         p.Parse (_("-a"));
185                         Assert.AreEqual (a, true);
186                         p.Parse (_("-a+"));
187                         Assert.AreEqual (a, true);
188                         p.Parse (_("-a-"));
189                         Assert.AreEqual (a, false);
190                 }
191
192                 [Test]
193                 public void CombinationPlatter ()
194                 {
195                         int a = -1, b = -1;
196                         string av = null, bv = null;
197                         Foo f = null;
198                         int help = 0;
199                         int verbose = 0;
200                         OptionSet p = new OptionSet () {
201                                 { "a=", v => { a = 1; av = v; } },
202                                 { "b", "desc", v => {b = 2; bv = v;} },
203                                 { "f=", (Foo v) => f = v },
204                                 { "v", v => { ++verbose; } },
205                                 { "h|?|help", (v) => { switch (v) {
206                                         case "h": help |= 0x1; break; 
207                                         case "?": help |= 0x2; break;
208                                         case "help": help |= 0x4; break;
209                                 } } },
210                         };
211                         List<string> e = p.Parse (new string[]{"foo", "-v", "-a=42", "/b-",
212                                 "-a", "64", "bar", "--f", "B", "/h", "-?", "--help", "-v"});
213
214                         Assert.AreEqual (e.Count, 2);
215                         Assert.AreEqual (e[0], "foo");
216                         Assert.AreEqual (e[1], "bar");
217                         Assert.AreEqual (a, 1);
218                         Assert.AreEqual (av, "64");
219                         Assert.AreEqual (b, 2);
220                         Assert.AreEqual (bv, null);
221                         Assert.AreEqual (verbose, 2);
222                         Assert.AreEqual (help, 0x7);
223                         Assert.AreEqual (f, Foo.B);
224                 }
225
226                 [Test]
227                 public void Exceptions ()
228                 {
229                         string a = null;
230                         var p = new OptionSet () {
231                                 { "a=", v => a = v },
232                                 { "b",  v => { } },
233                                 { "c",  v => { } },
234                                 { "n=", (int v) => { } },
235                                 { "f=", (Foo v) => { } },
236                         };
237                         // missing argument
238                         Utils.AssertException (typeof(OptionException), 
239                                         "Missing required value for option '-a'.", 
240                                         p, v => { v.Parse (_("-a")); });
241                         // another named option while expecting one -- follow Getopt::Long
242                         Utils.AssertException (null, null,
243                                         p, v => { v.Parse (_("-a", "-a")); });
244                         Assert.AreEqual (a, "-a");
245                         // no exception when an unregistered named option follows.
246                         Utils.AssertException (null, null, 
247                                         p, v => { v.Parse (_("-a", "-b")); });
248                         Assert.AreEqual (a, "-b");
249                         Utils.AssertException (typeof(ArgumentNullException),
250                                         "Argument cannot be null.\nParameter name: option",
251                                         p, v => { v.Add (null); });
252
253                         // bad type
254                         Utils.AssertException (typeof(OptionException),
255                                         "Could not convert string `value' to type Int32 for option `-n'.",
256                                         p, v => { v.Parse (_("-n", "value")); });
257                         Utils.AssertException (typeof(OptionException),
258                                         "Could not convert string `invalid' to type Foo for option `--f'.",
259                                         p, v => { v.Parse (_("--f", "invalid")); });
260
261                         // try to bundle with an option requiring a value
262                         Utils.AssertException (typeof(OptionException), 
263                                         "Cannot bundle unregistered option '-z'.", 
264                                         p, v => { v.Parse (_("-cz", "extra")); });
265
266                         Utils.AssertException (typeof(ArgumentNullException), 
267                                         "Argument cannot be null.\nParameter name: action",
268                                         p, v => { v.Add ("foo", (Action<string>) null); });
269                         Utils.AssertException (typeof(ArgumentException), 
270                                         "Cannot provide maxValueCount of 2 for OptionValueType.None.\nParameter name: maxValueCount",
271                                         p, v => { v.Add ("foo", (k, val) => {/* ignore */}); });
272                 }
273
274                 [Test]
275                 public void WriteOptionDescriptions ()
276                 {
277                         var p = new OptionSet () {
278                                 { "p|indicator-style=", "append / indicator to directories",    v => {} },
279                                 { "color:",             "controls color info",                  v => {} },
280                                 { "color2:",            "set {color}",                          v => {} },
281                                 { "rk=",                "required key/value option",            (k, v) => {} },
282                                 { "rk2=",               "required {{foo}} {0:key}/{1:value} option",    (k, v) => {} },
283                                 { "ok:",                "optional key/value option",            (k, v) => {} },
284                                 { "long-desc",
285                                         "This has a really\nlong, multi-line description that also\ntests\n" +
286                                                 "the-builtin-supercalifragilisticexpialidicious-break-on-hyphen.  " + 
287                                                 "Also, a list:\n" +
288                                                 "  item 1\n" +
289                                                 "  item 2",
290                                         v => {} },
291                                 { "long-desc2",
292                                         "IWantThisDescriptionToBreakInsideAWordGeneratingAutoWordHyphenation.",
293                                         v => {} },
294                                 { "long-desc3",
295                                         "OnlyOnePeriod.AndNoWhitespaceShouldBeSupportedEvenWithLongDescriptions",
296                                         v => {} },
297                                 { "long-desc4",
298                                         "Lots of spaces in the middle 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 and more until the end.",
299                                         v => {} },
300                                 { "long-desc5",
301                                         "Lots of spaces in the middle - . - . - . - . - . - . - . - and more until the end.",
302                                         v => {} },
303                                 { "o|out=",
304                                         "The {DIRECTORY} to place the generated files and directories.\n\n" +
305                                         "If not specified, defaults to\n`dirname FILE`/cache/`basename FILE .tree`.",
306                                         v => {} },
307                                 { "h|?|help",           "show help text",                       v => {} },
308                                 { "version",            "output version information and exit",  v => {} },
309                                 { "<>", v => {} },
310                         };
311
312                         StringWriter expected = new StringWriter ();
313                         expected.WriteLine ("  -p, --indicator-style=VALUE");
314                         expected.WriteLine ("                             append / indicator to directories");
315                         expected.WriteLine ("      --color[=VALUE]        controls color info");
316                         expected.WriteLine ("      --color2[=color]       set color");
317                         expected.WriteLine ("      --rk=VALUE1:VALUE2     required key/value option");
318                         expected.WriteLine ("      --rk2=key:value        required {foo} key/value option");
319                         expected.WriteLine ("      --ok[=VALUE1:VALUE2]   optional key/value option");
320                         expected.WriteLine ("      --long-desc            This has a really");
321                         expected.WriteLine ("                               long, multi-line description that also");
322                         expected.WriteLine ("                               tests");
323                         expected.WriteLine ("                               the-builtin-supercalifragilisticexpialidicious-");
324                         expected.WriteLine ("                               break-on-hyphen.  Also, a list:");
325                         expected.WriteLine ("                                 item 1");
326                         expected.WriteLine ("                                 item 2");
327                         expected.WriteLine ("      --long-desc2           IWantThisDescriptionToBreakInsideAWordGeneratingAu-");
328                         expected.WriteLine ("                               toWordHyphenation.");
329                         expected.WriteLine ("      --long-desc3           OnlyOnePeriod.");
330                         expected.WriteLine ("                               AndNoWhitespaceShouldBeSupportedEvenWithLongDesc-");
331                         expected.WriteLine ("                               riptions");
332                         expected.WriteLine ("      --long-desc4           Lots of spaces in the middle 1 2 3 4 5 6 7 8 9 0 1");
333                         expected.WriteLine ("                               2 3 4 5 and more until the end.");
334                         expected.WriteLine ("      --long-desc5           Lots of spaces in the middle - . - . - . - . - . -");
335                         expected.WriteLine ("                               . - . - and more until the end.");
336                         expected.WriteLine ("  -o, --out=DIRECTORY        The DIRECTORY to place the generated files and");
337                         expected.WriteLine ("                               directories.");
338                         expected.WriteLine ("                               ");
339                         expected.WriteLine ("                               If not specified, defaults to");
340                         expected.WriteLine ("                               `dirname FILE`/cache/`basename FILE .tree`.");
341                         expected.WriteLine ("  -h, -?, --help             show help text");
342                         expected.WriteLine ("      --version              output version information and exit");
343
344                         StringWriter actual = new StringWriter ();
345                         p.WriteOptionDescriptions (actual);
346
347                         Assert.AreEqual (expected.ToString (), actual.ToString ());
348                 }
349
350                 [Test]
351                 public void OptionBundling ()
352                 {
353                         string a, b, c, f;
354                         a = b = c = f = null;
355                         var p = new OptionSet () {
356                                 { "a", v => a = "a" },
357                                 { "b", v => b = "b" },
358                                 { "c", v => c = "c" },
359                                 { "f=", v => f = v },
360                         };
361                         List<string> extra = p.Parse (_ ("-abcf", "foo", "bar"));
362                         Assert.AreEqual (extra.Count, 1);
363                         Assert.AreEqual (extra [0], "bar");
364                         Assert.AreEqual (a, "a");
365                         Assert.AreEqual (b, "b");
366                         Assert.AreEqual (c, "c");
367                         Assert.AreEqual (f, "foo");
368                 }
369
370                 [Test]
371                 public void HaltProcessing ()
372                 {
373                         var p = new OptionSet () {
374                                 { "a", v => {} },
375                                 { "b", v => {} },
376                         };
377                         List<string> e = p.Parse (_ ("-a", "-b", "--", "-a", "-b"));
378                         Assert.AreEqual (e.Count, 2);
379                         Assert.AreEqual (e [0], "-a");
380                         Assert.AreEqual (e [1], "-b");
381                 }
382
383                 [Test]
384                 public void KeyValueOptions ()
385                 {
386                         var a = new Dictionary<string, string> ();
387                         var b = new Dictionary<int, char> ();
388                         var p = new OptionSet () {
389                                 { "a=", (k,v) => a.Add (k, v) },
390                                 { "b=", (int k, char v) => b.Add (k, v) },
391                                 { "c:", (k, v) => {if (k != null) a.Add (k, v);} },
392                                 { "d={=>}{-->}", (k, v) => a.Add (k, v) },
393                                 { "e={}", (k, v) => a.Add (k, v) },
394                                 { "f=+/", (k, v) => a.Add (k, v) },
395                         };
396                         p.Parse (_("-a", "A", "B", "-a", "C", "D", "-a=E=F", "-a:G:H", "-aI=J", "-b", "1", "a", "-b", "2", "b"));
397                         AssertDictionary (a, 
398                                         "A", "B", 
399                                         "C", "D", 
400                                         "E", "F", 
401                                         "G", "H", 
402                                         "I", "J");
403                         AssertDictionary (b,
404                                         "1", "a",
405                                         "2", "b");
406
407                         a.Clear ();
408                         p.Parse (_("-c"));
409                         Assert.AreEqual (a.Count, 0);
410                         p.Parse (_("-c", "a"));
411                         Assert.AreEqual (a.Count, 0);
412                         p.Parse (_("-ca"));
413                         AssertDictionary (a, "a", null);
414                         a.Clear ();
415                         p.Parse (_("-ca=b"));
416                         AssertDictionary (a, "a", "b");
417
418                         a.Clear ();
419                         p.Parse (_("-dA=>B", "-d", "C-->D", "-d:E", "F", "-d", "G", "H", "-dJ-->K"));
420                         AssertDictionary (a,
421                                         "A", "B",
422                                         "C", "D", 
423                                         "E", "F",
424                                         "G", "H",
425                                         "J", "K");
426
427                         a.Clear ();
428                         p.Parse (_("-eA=B", "-eC=D", "-eE", "F", "-e:G", "H"));
429                         AssertDictionary (a,
430                                         "A=B", "-eC=D",
431                                         "E", "F", 
432                                         "G", "H");
433
434                         a.Clear ();
435                         p.Parse (_("-f1/2", "-f=3/4", "-f:5+6", "-f7", "8", "-f9=10", "-f11=12"));
436                         AssertDictionary (a,
437                                         "1", "2",
438                                         "3", "4",
439                                         "5", "6", 
440                                         "7", "8", 
441                                         "9=10", "-f11=12");
442                 }
443
444                 static void AssertDictionary<TKey, TValue> (Dictionary<TKey, TValue> dict, params string[] set)
445                 {
446                         TypeConverter k = TypeDescriptor.GetConverter (typeof (TKey));
447                         TypeConverter v = TypeDescriptor.GetConverter (typeof (TValue));
448
449                         Assert.AreEqual (dict.Count, set.Length / 2);
450                         for (int i = 0; i < set.Length; i += 2) {
451                                 TKey key = (TKey) k.ConvertFromString (set [i]);
452                                 Assert.AreEqual (dict.ContainsKey (key), true);
453                                 if (set [i+1] == null)
454                                         Assert.AreEqual (dict [key], default (TValue));
455                                 else
456                                         Assert.AreEqual (dict [key], (TValue) v.ConvertFromString (set [i+1]));
457                         }
458                 }
459
460                 class CustomOption : Option {
461                         Action<OptionValueCollection> action;
462
463                         public CustomOption (string p, string d, int c, Action<OptionValueCollection> a)
464                                 : base (p, d, c)
465                         {
466                                 this.action = a;
467                         }
468
469                         protected override void OnParseComplete (OptionContext c)
470                         {
471                                 action (c.OptionValues);
472                         }
473                 }
474
475                 [Test]
476                 public void CustomKeyValue ()
477                 {
478                         var a = new Dictionary<string, string> ();
479                         var b = new Dictionary<string, string[]> ();
480                         var p = new OptionSet () {
481                                 new CustomOption ("a==:", null, 2, v => a.Add (v [0], v [1])),
482                                 new CustomOption ("b==:", null, 3, v => b.Add (v [0], new string[]{v [1], v [2]})),
483                         };
484                         p.Parse (_("-a=b=c", "-a=d", "e", "-a:f=g", "-a:h:i", "-a", "j=k", "-a", "l:m"));
485                         Assert.AreEqual (a.Count, 6);
486                         Assert.AreEqual (a ["b"], "c");
487                         Assert.AreEqual (a ["d"], "e");
488                         Assert.AreEqual (a ["f"], "g");
489                         Assert.AreEqual (a ["h"], "i");
490                         Assert.AreEqual (a ["j"], "k");
491                         Assert.AreEqual (a ["l"], "m");
492
493                         Utils.AssertException (typeof(OptionException),
494                                         "Missing required value for option '-a'.",
495                                         p, v => {v.Parse (_("-a=b"));});
496
497                         p.Parse (_("-b", "a", "b", "c", "-b:d:e:f", "-b=g=h:i", "-b:j=k:l"));
498                         Assert.AreEqual (b.Count, 4);
499                         Assert.AreEqual (b ["a"][0], "b");
500                         Assert.AreEqual (b ["a"][1], "c");
501                         Assert.AreEqual (b ["d"][0], "e");
502                         Assert.AreEqual (b ["d"][1], "f");
503                         Assert.AreEqual (b ["g"][0], "h");
504                         Assert.AreEqual (b ["g"][1], "i");
505                         Assert.AreEqual (b ["j"][0], "k");
506                         Assert.AreEqual (b ["j"][1], "l");
507                 }
508
509                 [Test]
510                 public void Localization ()
511                 {
512                         var p = new OptionSet (f => "hello!") {
513                                 { "n=", (int v) => { } },
514                         };
515                         Utils.AssertException (typeof(OptionException), "hello!",
516                                         p, v => { v.Parse (_("-n=value")); });
517
518                         StringWriter expected = new StringWriter ();
519                         expected.WriteLine ("  -nhello!                   hello!");
520
521                         StringWriter actual = new StringWriter ();
522                         p.WriteOptionDescriptions (actual);
523
524                         Assert.AreEqual (actual.ToString (), expected.ToString ());
525                 }
526
527                 class CiOptionSet : OptionSet {
528                         protected override void InsertItem (int index, Option item)
529                         {
530                                 if (item.Prototype.ToLower () != item.Prototype)
531                                         throw new ArgumentException ("prototypes must be null!");
532                                 base.InsertItem (index, item);
533                         }
534
535                         protected override bool Parse (string option, OptionContext c)
536                         {
537                                 if (c.Option != null)
538                                         return base.Parse (option, c);
539                                 string f, n, s, v;
540                                 if (!GetOptionParts (option, out f, out n, out s, out v)) {
541                                         return base.Parse (option, c);
542                                 }
543                                 return base.Parse (f + n.ToLower () + (v != null && s != null ? s + v : ""), c);
544                         }
545
546                         public new Option GetOptionForName (string n)
547                         {
548                                 return base.GetOptionForName (n);
549                         }
550
551                         public void CheckOptionParts (string option, bool er, string ef, string en, string es, string ev)
552                         {
553                                 string f, n, s, v;
554                                 bool r = GetOptionParts (option, out f, out n, out s, out v);
555                                 Assert.AreEqual (r, er);
556                                 Assert.AreEqual (f, ef);
557                                 Assert.AreEqual (n, en);
558                                 Assert.AreEqual (s, es);
559                                 Assert.AreEqual (v, ev);
560                         }
561                 }
562
563                 [Test]
564                 public void DerivedType ()
565                 {
566                         bool help = false;
567                         var p = new CiOptionSet () {
568                                 { "h|help", v => help = v != null },
569                         };
570                         p.Parse (_("-H"));
571                         Assert.AreEqual (help, true);
572                         help = false;
573                         p.Parse (_("-HELP"));
574                         Assert.AreEqual (help, true);
575
576                         Assert.AreEqual (p.GetOptionForName ("h"), p [0]);
577                         Assert.AreEqual (p.GetOptionForName ("help"), p [0]);
578                         Assert.AreEqual (p.GetOptionForName ("invalid"), null);
579
580                         Utils.AssertException (typeof(ArgumentException), "prototypes must be null!",
581                                         p, v => { v.Add ("N|NUM=", (int n) => {}); });
582                         Utils.AssertException (typeof(ArgumentNullException),
583                                         "Argument cannot be null.\nParameter name: option",
584                                         p, v => { v.GetOptionForName (null); });
585                 }
586
587                 [Test]
588                 public void OptionParts ()
589                 {
590                         var p = new CiOptionSet ();
591                         p.CheckOptionParts ("A",        false,  null, null, null, null);
592                         p.CheckOptionParts ("A=B",      false,  null, null, null, null);
593                         p.CheckOptionParts ("-A=B",     true,   "-",  "A",  "=",  "B");
594                         p.CheckOptionParts ("-A:B",     true,   "-",  "A",  ":",  "B");
595                         p.CheckOptionParts ("--A=B",    true,   "--", "A",  "=",  "B");
596                         p.CheckOptionParts ("--A:B",    true,   "--", "A",  ":",  "B");
597                         p.CheckOptionParts ("/A=B",     true,   "/",  "A",  "=",  "B");
598                         p.CheckOptionParts ("/A:B",     true,   "/",  "A",  ":",  "B");
599                         p.CheckOptionParts ("-A=B=C",   true,   "-",  "A",  "=",  "B=C");
600                         p.CheckOptionParts ("-A:B=C",   true,   "-",  "A",  ":",  "B=C");
601                         p.CheckOptionParts ("-A:B:C",   true,   "-",  "A",  ":",  "B:C");
602                         p.CheckOptionParts ("--A=B=C",  true,   "--", "A",  "=",  "B=C");
603                         p.CheckOptionParts ("--A:B=C",  true,   "--", "A",  ":",  "B=C");
604                         p.CheckOptionParts ("--A:B:C",  true,   "--", "A",  ":",  "B:C");
605                         p.CheckOptionParts ("/A=B=C",   true,   "/",  "A",  "=",  "B=C");
606                         p.CheckOptionParts ("/A:B=C",   true,   "/",  "A",  ":",  "B=C");
607                         p.CheckOptionParts ("/A:B:C",   true,   "/",  "A",  ":",  "B:C");
608                         p.CheckOptionParts ("-AB=C",    true,   "-",  "AB", "=",  "C");
609                         p.CheckOptionParts ("-AB:C",    true,   "-",  "AB", ":",  "C");
610                 }
611
612                 class ContextCheckerOption : Option {
613                         string eName, eValue;
614                         int index;
615
616                         public ContextCheckerOption (string p, string d, string eName, string eValue, int index)
617                                 : base (p, d)
618                         {
619                                 this.eName  = eName;
620                                 this.eValue = eValue;
621                                 this.index  = index;
622                         }
623
624                         protected override void OnParseComplete (OptionContext c)
625                         {
626                                 Assert.AreEqual (c.OptionValues.Count, 1);
627                                 Assert.AreEqual (c.OptionValues [0], eValue);
628                                 Assert.AreEqual (c.OptionName, eName);
629                                 Assert.AreEqual (c.OptionIndex, index);
630                                 Assert.AreEqual (c.Option, this);
631                                 Assert.AreEqual (c.Option.Description, base.Description);
632                         }
633                 }
634
635                 [Test]
636                 public void OptionContext ()
637                 {
638                         var p = new OptionSet () {
639                                 new ContextCheckerOption ("a=", "a desc", "/a",   "a-val", 1),
640                                 new ContextCheckerOption ("b",  "b desc", "--b+", "--b+",  2),
641                                 new ContextCheckerOption ("c=", "c desc", "--c",  "C",     3),
642                                 new ContextCheckerOption ("d",  "d desc", "/d-",  null,    4),
643                         };
644                         Assert.AreEqual (p.Count, 4);
645                         p.Parse (_("/a", "a-val", "--b+", "--c=C", "/d-"));
646                 }
647
648                 [Test]
649                 public void DefaultHandler ()
650                 {
651                         var extra = new List<string> ();
652                         var p = new OptionSet () {
653                                 { "<>", v => extra.Add (v) },
654                         };
655                         var e = p.Parse (_("-a", "b", "--c=D", "E"));
656                         Assert.AreEqual (e.Count, 0);
657                         Assert.AreEqual (extra.Count, 4);
658                         Assert.AreEqual (extra [0], "-a");
659                         Assert.AreEqual (extra [1], "b");
660                         Assert.AreEqual (extra [2], "--c=D");
661                         Assert.AreEqual (extra [3], "E");
662                 }
663
664                 [Test]
665                 public void MixedDefaultHandler ()
666                 {
667                         var tests = new List<string> ();
668                         var p = new OptionSet () {
669                                 { "t|<>=", v => tests.Add (v) },
670                         };
671                         var e = p.Parse (_("-tA", "-t:B", "-t=C", "D", "--E=F"));
672                         Assert.AreEqual (e.Count, 0);
673                         Assert.AreEqual (tests.Count, 5);
674                         Assert.AreEqual (tests [0], "A");
675                         Assert.AreEqual (tests [1], "B");
676                         Assert.AreEqual (tests [2], "C");
677                         Assert.AreEqual (tests [3], "D");
678                         Assert.AreEqual (tests [4], "--E=F");
679                 }
680
681                 [Test]
682                 public void DefaultHandlerRuns ()
683                 {
684                         var formats = new Dictionary<string, List<string>> ();
685                         string format = "foo";
686                         var p = new OptionSet () {
687                                 { "f|format=", v => format = v },
688                                 { "<>", 
689                                         v => {
690                                                 List<string> f;
691                                                 if (!formats.TryGetValue (format, out f)) {
692                                                         f = new List<string> ();
693                                                         formats.Add (format, f);
694                                                 }
695                                                 f.Add (v);
696                                 } },
697                         };
698                         var e = p.Parse (_("a", "b", "-fbar", "c", "d", "--format=baz", "e", "f"));
699                         Assert.AreEqual (e.Count, 0);
700                         Assert.AreEqual (formats.Count, 3);
701                         Assert.AreEqual (formats ["foo"].Count, 2);
702                         Assert.AreEqual (formats ["foo"][0], "a");
703                         Assert.AreEqual (formats ["foo"][1], "b");
704                         Assert.AreEqual (formats ["bar"].Count, 2);
705                         Assert.AreEqual (formats ["bar"][0], "c");
706                         Assert.AreEqual (formats ["bar"][1], "d");
707                         Assert.AreEqual (formats ["baz"].Count, 2);
708                         Assert.AreEqual (formats ["baz"][0], "e");
709                         Assert.AreEqual (formats ["baz"][1], "f");
710                 }
711         }
712 }
713