a4248c23dfcefad16485b6ab8553da107c8f4d96
[openafs.git] / tests / cmd / command-t.c
1 /*
2  * Copyright (c) 2010 Your File System Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 /*
26  * Test the command line parsing library
27  */
28
29 #include <afsconfig.h>
30 #include <afs/param.h>
31
32 #include <roken.h>
33
34 #include <afs/cmd.h>
35
36 #include <tap/basic.h>
37
38 #define FLAG_OFF    0
39 #define FIRST_OFF   1
40 #define SECOND_OFF  2
41 #define SUGAR_OFF   3
42 #define FOURTH_OFF  4
43 #define FIFTH_OFF   5
44 #define PERHAPS_OFF 6
45 #define SANITY_OFF  7
46
47 static int
48 testproc(struct cmd_syndesc *as, void *rock)
49 {
50     is_string("foo", as->parms[FIRST_OFF].items->data,
51               "first option matches");
52     is_string("bar", as->parms[SECOND_OFF].items->data,
53               "second option matches");
54     ok(as->parms[FLAG_OFF].items != NULL,
55        "flag is set");
56
57     return 0;
58 }
59
60 int
61 main(int argc, char **argv)
62 {
63     char *tv[100];
64     struct cmd_syndesc *opts;
65     struct cmd_syndesc *retopts;
66     int code;
67     int tc;
68     int retval;
69     char *retstring = NULL;
70
71     plan(85);
72
73     initialize_CMD_error_table();
74
75     opts = cmd_CreateSyntax(NULL, testproc, NULL, NULL);
76     cmd_AddParm(opts, "-flag", CMD_FLAG, CMD_OPTIONAL, "a flag");
77     cmd_AddParm(opts, "-first", CMD_SINGLE, CMD_REQUIRED, "first option");
78     cmd_AddParm(opts, "-second", CMD_LIST, CMD_OPTIONAL, "second option");
79
80     /* A simple command line */
81     code = cmd_ParseLine("-first foo -second bar -flag", tv, &tc, 100);
82     is_int(0, code, "cmd_ParseLine succeeds");
83     code = cmd_Dispatch(tc, tv);
84     is_int(0, code, "dispatching simple comamnd line succeeds");
85     code = cmd_Parse(tc, tv, &retopts);
86     is_int(0, code, "parsing simple command line succeeds");
87     is_string("foo", retopts->parms[FIRST_OFF].items->data, " ... 1st option matches");
88     is_string("bar", retopts->parms[SECOND_OFF].items->data, " ... 2nd option matches");
89     ok(retopts->parms[FLAG_OFF].items != NULL, " ... 3rd option matches");
90     cmd_FreeOptions(&retopts);
91     cmd_FreeArgv(tv);
92
93     /* unknown switch */
94     code = cmd_ParseLine("-first foo -second bar -third -flag", tv, &tc, 100);
95     is_int(0, code, "cmd_ParseLine succeeds");
96     code = cmd_Dispatch(tc, tv);
97     is_int(CMD_UNKNOWNSWITCH, code, "invalid options fail as expected");
98     code = cmd_Parse(tc, tv, &retopts);
99     is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse");
100     cmd_FreeArgv(tv);
101
102     /* missing parameter */
103     code = cmd_ParseLine("-first foo -second -flag", tv, &tc, 100);
104     is_int(0, code, "cmd_ParseLine succeeds");
105     code = cmd_Dispatch(tc, tv);
106     is_int(CMD_TOOFEW, code, "missing parameters fail as expected");
107     code = cmd_Parse(tc, tv, &retopts);
108     is_int(CMD_TOOFEW, code, "and still fail with cmd_Parse");
109     cmd_FreeArgv(tv);
110
111     /* missing option */
112     code = cmd_ParseLine("-second bar -third -flag", tv, &tc, 100);
113     is_int(0, code, "cmd_ParseLine succeeds");
114     code = cmd_Dispatch(tc, tv);
115     is_int(CMD_UNKNOWNSWITCH, code, "missing options fail as expected");
116     code = cmd_Parse(tc, tv, &retopts);
117     is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse");
118     cmd_FreeArgv(tv);
119
120     code = cmd_ParseLine("-first foo baz -second bar -third -flag", tv, &tc, 100);
121     is_int(0, code, "cmd_ParseLine succeeds");
122     code = cmd_Dispatch(tc, tv);
123     is_int(CMD_NOTLIST, code, "too many parameters fails as expected");
124     code = cmd_Parse(tc, tv, &retopts);
125     is_int(CMD_NOTLIST, code, "and still fail with cmd_Parse");
126     cmd_FreeArgv(tv);
127
128     /* Positional parameters */
129     code = cmd_ParseLine("foo bar -flag", tv, &tc, 100);
130     is_int(0, code, "cmd_ParseLine succeeds");
131     code = cmd_Dispatch(tc, tv);
132     is_int(0, code, "dispatching positional parameters succeeds");
133     code = cmd_Parse(tc, tv, &retopts);
134     is_int(0, code, "and works with cmd_Parse");
135     cmd_FreeOptions(&retopts);
136     cmd_FreeArgv(tv);
137
138     /* Abbreviations */
139     code = cmd_ParseLine("-fi foo -s bar -flag", tv, &tc, 100);
140     is_int(0, code, "cmd_ParseLine succeeds");
141     code = cmd_Dispatch(tc, tv);
142     is_int(0, code, "dispatching abbreviations succeeds");
143     code = cmd_Parse(tc, tv, &retopts);
144     is_int(0, code, "and works with cmd_Parse");
145     cmd_FreeOptions(&retopts);
146
147     cmd_FreeArgv(tv);
148
149     /* Ambiguous */
150     code = cmd_ParseLine("-f foo -s bar -flag", tv, &tc, 100);
151     is_int(0, code, "cmd_ParseLine succeeds");
152     code = cmd_Dispatch(tc, tv);
153     is_int(CMD_UNKNOWNSWITCH, code, "ambiguous abbreviations correctly fail");
154     code = cmd_Parse(tc, tv, &retopts);
155     is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too");
156     cmd_FreeArgv(tv);
157
158     /* Check that paramaters with abbreviation disabled don't make things
159      * ambiguous */
160     cmd_AddParmAtOffset(opts, SUGAR_OFF, "-sugar", CMD_SINGLE, CMD_OPTIONAL | CMD_NOABBRV,
161                         "sugar with that");
162     code = cmd_ParseLine("-fi foo -s bar -flag", tv, &tc, 100);
163     is_int(0, code, "cmd_ParseLine succeeds");
164     code = cmd_Dispatch(tc, tv);
165     is_int(0, code, "disabling specific abbreviations succeeds");
166     code = cmd_Parse(tc, tv, &retopts);
167     is_int(0, code, "and works with cmd_Parse into the bargain");
168     cmd_FreeOptions(&retopts);
169     cmd_FreeArgv(tv);
170
171     /* Disable positional commands */
172     cmd_DisablePositionalCommands();
173     code = cmd_ParseLine("foo bar -flag", tv, &tc, 100);
174     is_int(0, code, "cmd_ParseLine succeeds");
175     code = cmd_Dispatch(tc, tv);
176     is_int(CMD_NOTLIST, code, "positional parameters can be disabled");
177     code = cmd_Parse(tc, tv, &retopts);
178     is_int(CMD_NOTLIST, code, "and fail with cmd_Parse too");
179     cmd_FreeArgv(tv);
180
181     /* Disable abbreviations */
182     cmd_DisableAbbreviations();
183     code = cmd_ParseLine("-fi foo -s bar -flag", tv, &tc, 100);
184     is_int(0, code, "cmd_ParseLine succeeds");
185     code = cmd_Dispatch(tc, tv);
186     is_int(CMD_UNKNOWNSWITCH, code, "dispatching abbreviations succeeds");
187     code = cmd_Parse(tc, tv, &retopts);
188     is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too");
189
190     cmd_FreeArgv(tv);
191
192     /* Try the new cmd_Parse function with something different*/
193     code = cmd_ParseLine("-first one -second two -flag", tv, &tc, 100);
194     is_int(0, code, "cmd_ParseLine succeeds");
195     code = cmd_Parse(tc, tv, &retopts);
196     is_int(0, code, "Parsing with cmd_Parse works");
197     is_string("one", retopts->parms[FIRST_OFF].items->data, " ... 1st option matches");
198     is_string("two", retopts->parms[SECOND_OFF].items->data, " ... 2nd option matches");
199     ok(retopts->parms[FLAG_OFF].items != NULL, " ... 3rd option matches");
200
201     cmd_FreeOptions(&retopts);
202     cmd_FreeArgv(tv);
203     /* Try adding a couple of parameters at specific positions */
204     cmd_AddParmAtOffset(opts, FIFTH_OFF, "-fifth", CMD_SINGLE, CMD_OPTIONAL,
205                        "fifth option");
206     cmd_AddParmAtOffset(opts, FOURTH_OFF, "-fourth", CMD_SINGLE, CMD_OPTIONAL,
207                        "fourth option" );
208     code = cmd_ParseLine("-first a -fourth b -fifth c", tv, &tc, 100);
209     is_int(0, code, "cmd_ParseLine succeeds");
210     code = cmd_Parse(tc, tv, &retopts);
211     is_int(0, code, "parsing our new options succeeds");
212     is_string("b", retopts->parms[FOURTH_OFF].items->data, " Fourth option in right place");
213     is_string("c", retopts->parms[FIFTH_OFF].items->data, " Fifth option in right place");
214     cmd_FreeOptions(&retopts);
215     cmd_FreeArgv(tv);
216
217     /* Check Accessors */
218     code = cmd_ParseLine("-first 1 -second second -flag", tv, &tc, 100);
219     is_int(0, code, "cmd_ParseLine succeeds");
220     code = cmd_Parse(tc, tv, &retopts);
221
222     code = cmd_OptionAsInt(retopts, FIRST_OFF, &retval);
223     is_int(0, code, "cmd_OptionsAsInt succeeds");
224     is_int(1, retval, " ... and returns correct value");
225
226     code = cmd_OptionAsString(retopts, SECOND_OFF, &retstring);
227     is_int(0, code, "cmd_OptionsAsString succeeds");
228     is_string("second", retstring, " ... and returns correct value");
229     free(retstring);
230     retstring = NULL;
231
232     code = cmd_OptionAsFlag(retopts, FLAG_OFF, &retval);
233     is_int(0, code, "cmd_OptionsAsFlag succeeds");
234     ok(retval, " ... and flag is correct");
235
236     cmd_FreeOptions(&retopts);
237     cmd_FreeArgv(tv);
238
239     /* Add an alias */
240     code = cmd_AddParmAlias(opts, SECOND_OFF, "-twa");
241     is_int(0, code, "cmd_AddParmAlias succeeds");
242
243     code = cmd_ParseLine("-first 1 -twa tup", tv, &tc, 100);
244     is_int(0, code, "cmd_ParseLine succeeds");
245     code = cmd_Parse(tc, tv, &retopts);
246     is_int(0, code, "cmd_Parse succeeds for alias");
247     cmd_OptionAsString(retopts, SECOND_OFF, &retstring);
248     is_string("tup", retstring, " ... and we have the correct value");
249     free(retstring);
250     retstring = NULL;
251
252     cmd_FreeOptions(&retopts);
253     cmd_FreeArgv(tv);
254
255     /* Add something that can be a flag or a value, and put something after
256      * it so we can check for parse problems*/
257     cmd_AddParm(opts, "-perhaps", CMD_SINGLE_OR_FLAG, CMD_OPTIONAL,
258                 "what am I");
259     cmd_AddParm(opts, "-sanity", CMD_SINGLE, CMD_OPTIONAL, "sanity check");
260
261     /* Try using as an option */
262
263     code = cmd_ParseLine("-first 1 -perhaps 2 -sanity 3", tv, &tc, 100);
264     is_int(0, code, "cmd_ParseLine succeeds");
265     code = cmd_Parse(tc, tv, &retopts);
266     is_int(0, code, "cmd_Parse succeeds for option-as-flag as opt");
267     code = cmd_OptionAsInt(retopts, PERHAPS_OFF, &retval);
268     is_int(0, code, "cmd_OptionAsInt succeeds");
269     is_int(2, retval, " ... and we have the correct value");
270     cmd_FreeOptions(&retopts);
271     cmd_FreeArgv(tv);
272
273     /* And now, as a flag */
274
275     code = cmd_ParseLine("-first 1 -perhaps -sanity 3", tv, &tc, 100);
276     is_int(0, code, "cmd_ParseLine succeeds");
277     code = cmd_Parse(tc, tv, &retopts);
278     is_int(0, code, "cmd_Parse succeeds for option-as-flag as flag");
279     code = cmd_OptionAsInt(retopts, PERHAPS_OFF, &retval);
280     is_int(CMD_MISSING, code, " ... pulling out a value fails as expected");
281     cmd_OptionAsFlag(retopts, PERHAPS_OFF, &retval);
282     ok(retval, " ... but parsing as a flag works");
283     cmd_FreeOptions(&retopts);
284     cmd_FreeArgv(tv);
285
286     /* Check that we can produce help output */
287     code = cmd_ParseLine("-help", tv, &tc, 100);
288     is_int(0, code, "cmd_ParseLine succeeds");
289     code = cmd_Parse(tc, tv, &retopts);
290     is_int(CMD_USAGE, code, "cmd_Parse returns usage error with help output");
291     ok(retopts == NULL, " ... and options is empty");
292
293     /* Check splitting with '=' */
294
295     code = cmd_ParseLine("-first 1 -perhaps=6 -sanity=3", tv, &tc, 100);
296     is_int(0, code, "cmd_ParseLine succeeds");
297     code = cmd_Parse(tc, tv, &retopts);
298     is_int(0, code, "cmd_Parse succeeds for items split with '='");
299     code = cmd_OptionAsInt(retopts, PERHAPS_OFF, &retval);
300     is_int(0, code, "cmd_OptionAsInt succeeds");
301     is_int(6, retval, " ... and we have the correct value once");
302     code = cmd_OptionAsInt(retopts, SANITY_OFF, &retval);
303     is_int(0, code, "cmd_OptionAsInt succeeds");
304     is_int(3, retval, " ... and we have the correct value twice");
305     cmd_FreeOptions(&retopts);
306     cmd_FreeArgv(tv);
307
308     return 0;
309 }
310