The Lean Mean C++ Option Parser
src/testparse.cpp
Go to the documentation of this file.
00001 /* Written 2012 by Matthias S. Benkmann
00002  *
00003  * The author hereby waives all copyright and related rights to the contents
00004  * of this example file (testparse.cpp) to the extent possible under the law.
00005  */
00006 
00022 #include <assert.h>
00023 #include <stdio.h>
00024 
00025 #include "optionparser.h"
00026 
00027 using option::Option;
00028 using option::Descriptor;
00029 using option::Parser;
00030 using option::Stats;
00031 using option::ArgStatus;
00032 
00033 struct Arg: public option::Arg
00034 {
00035   static ArgStatus Required(const Option& option, bool)
00036   {
00037     return option.arg == 0 ? option::ARG_ILLEGAL : option::ARG_OK;
00038   }
00039   static ArgStatus Empty(const Option& option, bool)
00040   {
00041     return (option.arg == 0 || option.arg[0] == 0) ? option::ARG_OK : option::ARG_IGNORE;
00042   }
00043 };
00044 
00045 char* gettext(const char * msgid)
00046 {
00047   return (char*) msgid;
00048 }
00049 
00050 const Descriptor empty_usage[] = { { 0, 0, 0, 0, 0, 0 } };
00051 
00052 const Descriptor minimal_usage[] = //
00053     { { 0, 0, "x", "", Arg::None, 0 }, //
00054       { 0, 0, 0, 0, 0, 0 } };
00055 
00056 const Descriptor optional_usage[] = //
00057     { { 0, 0, "f", "", Arg::Required, 0 }, //
00058       { 0, 0, 0, 0, 0, 0 } };
00059 
00060 const Descriptor gettext_usage[] = //
00061     { { 0, 0, "f", "", Arg::Required, gettext("This is a test") }, //
00062       { 0, 0, 0, 0, 0, 0 } };
00063 
00064 enum OptionIndex
00065 {
00066   UNKNOWN, FOO, VERBOSE, X, ABBREVIATE, EMPTY
00067 };
00068 enum OptionType
00069 {
00070   UNUSED = 0, DISABLED = 1, ENABLED = 2
00071 };
00072 
00073 const Descriptor multi_usage[] = //
00074     { { UNKNOWN, 0, "", "", Arg::None, 0 }, // UNKNOWN option catch-all
00075       { FOO, ENABLED, "", "enable-foo", Arg::None, 0 }, // FOO enable
00076       { FOO, DISABLED, "", "disable-foo", Arg::None, 0 }, // FOO disable
00077       { VERBOSE, 0, "v", "verbose", Arg::None, 0 }, // VERBOSE (counted option)
00078       { X, 0, "X", "X", Arg::Required, 0 }, // -X<arg>, -X <arg>, -X=<arg>, --X=<arg>
00079       { ABBREVIATE, 0, "", "abbreviate-me", Arg::None, 0 }, // ABBREVIATE
00080       { EMPTY, 0, "", "emptyarg", Arg::Empty, 0 }, // EMPTY (ignores arguments that are not "")
00081       { 0, 0, 0, 0, 0, 0 } };
00082 
00083 const char* empty_args[] = { 0 };
00084 const char* non_options[] = { "1", "2", "3", (const char*) -1 };
00085 const char* unknown_option[] = { "--unknown", "nonoption", 0 };
00086 const char* lone_minus[] = { "-f", "-", "-", 0 };
00087 const char* lone_doubleminus[] = { "--", 0 };
00088 
00089 // NOTE: (const char*) -1 is used to cause a segfault should this element be dereferenced.
00090 // 0 is not used here, because the parser explicitly checks for 0 which could mask bugs.
00091 // If a number of arguments >= 0 is passed, the parser is supposed to honor that and never
00092 // dereference an element beyond the last.
00093 const char* multi1[] =
00094     { "--enable-foo", "--unknown1", "-u", "-vX", "xyzzy", "--", "--strangefilename", (const char*) -1 };
00095 const char* multi2[] = { "-vvXfoo", "-X", "bar", "-X=foobar", "-X", "", "--disable-foo", "-v", (const char*) -1 };
00096 const char* multi3[] = { "-abbr", "-abb", "--emptyarg", "-verbose", "--emptyarg", "", "--emptyarg=", "nonoption1",
00097                          "nonoption2", (const char*) -1 };
00098 
00099 const char* illegal[] = { "-X", 0 };
00100 const char* reorder[] = { "-X", "--", "-", "-X", "--", "foo", "-v", "--", "bar", "--", 0 };
00101 const char* reorder2[] = { "-X", "--", "-", "-X", "--", "-", 0 };
00102 
00103 int count(const char** args)
00104 {
00105   for (int c = 0;; ++c)
00106     if (args[c] == (const char*) -1)
00107       return c;
00108 }
00109 
00110 bool eq(const char* s1, const char* s2)
00111 {
00112   if (s1 == s2)
00113     return true;
00114 
00115   if (s1 == 0 || s2 == 0)
00116     return false;
00117 
00118   while (*s1 != 0 && *s2 != 0)
00119   {
00120     ++s1;
00121     ++s2;
00122   }
00123 
00124   return *s1 == *s2;
00125 }
00126 
00127 int main()
00128 {
00129   {
00130     Stats stats(empty_usage, -1, empty_args);
00131     stats.add(empty_usage, 0, empty_args);
00132     assert(stats.buffer_max == 1);
00133     assert(stats.options_max == 1);
00134     Option buffer[stats.buffer_max];
00135     Option options[stats.options_max];
00136     Parser parse(empty_usage, 99, empty_args, options, buffer);
00137     parse.parse(empty_usage, -1, empty_args, options, buffer);
00138     assert(parse.optionsCount() == 0);
00139     assert(parse.nonOptionsCount() == 0);
00140     assert(!buffer[0]);
00141     assert(!options[0]);
00142     assert(buffer[0].count()==0);
00143     assert(parse.nonOptions()==0);
00144 
00145     stats.add(empty_usage, 3, non_options);
00146     assert(stats.buffer_max == 1);
00147     assert(stats.options_max == 1);
00148     parse.parse(empty_usage, 3, non_options, options, buffer);
00149     assert(parse.optionsCount() == 0);
00150     assert(parse.nonOptionsCount() == 3);
00151     assert(!buffer[0]);
00152     assert(!options[0]);
00153     assert(parse.nonOptions()==&non_options[0]);
00154 
00155     stats.add(minimal_usage, -1, unknown_option);
00156     assert(stats.buffer_max == 1);
00157     assert(stats.options_max == 2);
00158     parse.parse(minimal_usage, -1, unknown_option, options, buffer);
00159     assert(parse.optionsCount() == 0);
00160     assert(parse.nonOptionsCount() == 1);
00161     assert(!buffer[0]);
00162     assert(!options[0]);
00163     assert(parse.nonOptions()==&unknown_option[1]);
00164   }
00165   {
00166     Stats stats(gettext_usage, -1, lone_minus);
00167     Stats stats2;
00168     stats2.add(gettext_usage, -1, lone_minus);
00169     assert(stats.buffer_max == 2);
00170     assert(stats.options_max == 2);
00171     assert(stats2.buffer_max == 2);
00172     assert(stats2.options_max == 2);
00173     Option buffer[stats.buffer_max];
00174     Option options[stats.options_max];
00175     Parser parse;
00176     parse.parse(gettext_usage, -1, lone_minus, options, buffer);
00177     assert(parse.optionsCount() == 1);
00178     assert(parse.nonOptionsCount() == 1);
00179     assert(parse.nonOptions()==&lone_minus[2]);
00180     assert(options[0]);
00181     assert(buffer[0]);
00182     assert(options[0].count()==1);
00183     assert(options[0].isFirst());
00184     assert(options[0].isLast());
00185     assert(options[0].first() == options[0]);
00186     assert(options[0].last() == options[0]);
00187     assert(options[0].prevwrap() == &options[0]);
00188     assert(options[0].nextwrap() == &options[0]);
00189     assert(options[0].prev() == 0);
00190     assert(options[0].next() == 0);
00191     assert(options[0].desc == &gettext_usage[0]);
00192     assert(eq(options[0].name, "f"));
00193     assert(eq(options[0].arg, "-"));
00194   }
00195   {
00196     Stats stats(optional_usage, -1, lone_minus);
00197     Stats stats2;
00198     stats2.add(optional_usage, -1, lone_minus);
00199     assert(stats.buffer_max == 2);
00200     assert(stats.options_max == 2);
00201     assert(stats2.buffer_max == 2);
00202     assert(stats2.options_max == 2);
00203     Option buffer[stats.buffer_max];
00204     Option options[stats.options_max];
00205     Parser parse;
00206     parse.parse(optional_usage, -1, lone_minus, options, buffer);
00207     assert(parse.optionsCount() == 1);
00208     assert(parse.nonOptionsCount() == 1);
00209     assert(parse.nonOptions()==&lone_minus[2]);
00210     assert(options[0]);
00211     assert(buffer[0]);
00212     assert(options[0].count()==1);
00213     assert(options[0].isFirst());
00214     assert(options[0].isLast());
00215     assert(options[0].first() == options[0]);
00216     assert(options[0].last() == options[0]);
00217     assert(options[0].prevwrap() == &options[0]);
00218     assert(options[0].nextwrap() == &options[0]);
00219     assert(options[0].prev() == 0);
00220     assert(options[0].next() == 0);
00221     assert(options[0].desc == &optional_usage[0]);
00222     assert(eq(options[0].name, "f"));
00223     assert(eq(options[0].arg, "-"));
00224   }
00225   {
00226     Stats stats;
00227     stats.add(minimal_usage, -1, lone_doubleminus);
00228     assert(stats.buffer_max == 1);
00229     assert(stats.options_max == 2);
00230     Option buffer[stats.buffer_max];
00231     Option options[stats.options_max];
00232     Parser parse(minimal_usage, -1, lone_doubleminus, options, buffer);
00233     assert(parse.optionsCount() == 0);
00234     assert(parse.nonOptionsCount() == 0);
00235     assert(!buffer[0]);
00236     assert(!options[0]);
00237     assert(parse.nonOptions()==0);
00238   }
00239   {
00240     Stats stats;
00241     stats.add(multi_usage, count(multi1), multi1, 4, true);
00242     assert(stats.buffer_max == 6);
00243     assert(stats.options_max == 7);
00244     stats.add(multi_usage, count(multi2), multi2, 4, true);
00245     assert(stats.buffer_max == 14);
00246     assert(stats.options_max == 7);
00247     stats.add(multi_usage, count(multi3), multi3, 4, true);
00248     assert(stats.buffer_max == 22);
00249     assert(stats.options_max == 7);
00250     Option buffer[stats.buffer_max];
00251     Option options[stats.options_max];
00252     assert(options[FOO].last()->type() == UNUSED);
00253     assert(options[ABBREVIATE].count()==0);
00254     Parser parse;
00255     assert(!parse.error());
00256 
00257     parse.parse(multi_usage, count(multi1), multi1, options, buffer, 4, true);
00258     assert(!parse.error());
00259     assert(parse.optionsCount() == 5);
00260     assert(parse.nonOptionsCount() == 1);
00261     assert(eq(parse.nonOptions()[0],"--strangefilename"));
00262     assert(options[FOO].last()->type() == ENABLED);
00263     assert(eq(options[FOO].last()->name, "--enable-foo"));
00264     assert(options[FOO].last()->arg == 0);
00265     assert(options[UNKNOWN].count() == 2);
00266     assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
00267     assert(eq(options[UNKNOWN].last()->name,"u"));
00268     assert(options[UNKNOWN].first()->arg == 0);
00269     assert(options[UNKNOWN].last()->arg == 0);
00270     assert(options[VERBOSE].count()==1);
00271     assert(options[VERBOSE].arg==0);
00272     assert(options[VERBOSE].name[0] == 'v' && options[VERBOSE].namelen == 1);
00273     assert(eq(options[X].arg,"xyzzy"));
00274     assert(eq(options[X].name,"X"));
00275 
00276     parse.parse(multi_usage, count(multi2), multi2, options, buffer, 4, true);
00277     assert(!parse.error());
00278     assert(parse.optionsCount() == 13);
00279     assert(parse.nonOptionsCount() == 1);
00280     assert(eq(parse.nonOptions()[0],"--strangefilename"));
00281     assert(options[FOO].last()->type() == DISABLED);
00282     assert(options[FOO].last()->arg == 0);
00283     assert(options[UNKNOWN].count() == 2);
00284     assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
00285     assert(eq(options[UNKNOWN].last()->name,"u"));
00286     assert(options[VERBOSE].count()==4);
00287     assert(options[X].count()==5);
00288     const char* Xargs[] = { "xyzzy", "foo", "bar", "foobar", "", "sentinel" };
00289     const char** Xarg = &Xargs[0];
00290     for (Option* Xiter = options[X]; Xiter != 0; Xiter = Xiter->next())
00291       assert(eq(Xiter->arg, *Xarg++));
00292 
00293     assert(!options[ABBREVIATE]);
00294     parse.parse(multi_usage, count(multi3), multi3, options, buffer, 4, true);
00295     assert(!parse.error());
00296     assert(parse.optionsCount() == 21);
00297     assert(parse.nonOptionsCount() == 2);
00298     assert(eq(parse.nonOptions()[0],"nonoption1"));
00299     assert(eq(parse.nonOptions()[1],"nonoption2"));
00300     assert(options[ABBREVIATE]);
00301     assert(options[EMPTY].count()==3);
00302     assert(options[EMPTY].first()->arg==0);
00303     assert(eq(options[EMPTY].last()->arg,""));
00304     assert(eq(options[EMPTY].last()->prev()->arg,""));
00305     assert(options[FOO].last()->type() == DISABLED);
00306     assert(options[UNKNOWN].count() == 5);
00307     assert(eq(options[UNKNOWN].first()->name,"--unknown1"));
00308     assert(options[UNKNOWN].first()->arg == 0);
00309     assert(eq(options[UNKNOWN].last()->name,"b"));
00310     assert(options[VERBOSE].count()==5);
00311     assert(options[X].count()==5);
00312     Xarg = &Xargs[0];
00313     for (Option* Xiter = options[X]; Xiter != 0; Xiter = Xiter->next())
00314       assert(eq(Xiter->arg, *Xarg++));
00315 
00316     for (Option* opt = buffer[0]; *opt; ++opt)
00317       if (opt->desc->check_arg != Arg::Required && opt->desc->check_arg != Arg::Empty)
00318         assert(opt->arg == 0);
00319   }
00320   {
00321     Option buffer[2];
00322     Option options[20];
00323     Parser parse;
00324     assert(!parse.error());
00325     parse.parse(multi_usage, -1, illegal, options, buffer, 0, false, 2);
00326     assert(parse.error());
00327   }
00328   {
00329     Stats stats(multi_usage, count(multi3), multi3, 0, true);
00330     const int bufmax = 3;
00331     Option buffer[bufmax];
00332     Option options[stats.options_max];
00333     assert(!options[ABBREVIATE]);
00334     Parser parse(multi_usage, count(multi3), multi3, options, buffer, 4, true, bufmax);
00335     assert(!parse.error());
00336     assert(parse.optionsCount() == bufmax);
00337     assert(parse.nonOptionsCount() == 2);
00338     assert(eq(parse.nonOptions()[0],"nonoption1"));
00339     assert(eq(parse.nonOptions()[1],"nonoption2"));
00340     assert(options[ABBREVIATE]);
00341     assert(options[UNKNOWN].count() == 2); // because of buxmax the 2nd 'b' cannot be stored
00342     assert(options[UNKNOWN].first()->name[0] == 'a' && options[UNKNOWN].first()->namelen == 1);
00343     assert(options[UNKNOWN].first()->arg == 0);
00344     assert(eq(options[UNKNOWN].last()->name,"bb"));
00345   }
00346   {
00347     Stats stats(true, multi_usage, -1, reorder);
00348     Option buffer[stats.buffer_max];
00349     Option options[stats.options_max];
00350     Parser parse(true, multi_usage, -1, reorder, options, buffer);
00351     assert(!parse.error());
00352     assert(parse.optionsCount() == 3);
00353     assert(parse.nonOptionsCount() == 4);
00354     assert(parse.nonOptions() == &reorder[6]);
00355   }
00356   {
00357     Option buffer[10];
00358     Option options[10];
00359     Parser parse(true, multi_usage, 666, reorder2, options, buffer, 0, false, 10);
00360     assert(!parse.error());
00361     assert(parse.optionsCount() == 2);
00362     assert(parse.nonOptionsCount() == 2);
00363   }
00364 
00365   fprintf(stdout, "All tests passed.\n");
00366   return 0;
00367 }
00368 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator