cmd: Add support for params with optional values
[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 static int
39 testproc(struct cmd_syndesc *as, void *rock)
40 {
41     is_string("foo", as->parms[0].items->data,
42               "first option matches");
43     is_string("bar", as->parms[1].items->data,
44               "second option matches");
45     ok(as->parms[2].items != NULL,
46        "flag is set");
47
48     return 0;
49 }
50
51 int
52 main(int argc, char **argv)
53 {
54     char *tv[100];
55     struct cmd_syndesc *opts;
56     struct cmd_syndesc *retopts;
57     int code;
58     int tc;
59     int retval;
60     char *retstring;
61
62     plan(70);
63
64     initialize_CMD_error_table();
65
66     opts = cmd_CreateSyntax(NULL, testproc, NULL, NULL);
67     cmd_AddParm(opts, "-first", CMD_SINGLE, CMD_REQUIRED, "first option");
68     cmd_AddParm(opts, "-second", CMD_LIST, CMD_OPTIONAL, "second option");
69     cmd_AddParm(opts, "-flag", CMD_FLAG, CMD_OPTIONAL, "a flag");
70
71     /* A simple command line */
72     code = cmd_ParseLine("-first foo -second bar -flag", tv, &tc, 100);
73     is_int(0, code, "cmd_ParseLine succeeds");
74     code = cmd_Dispatch(tc, tv);
75     is_int(0, code, "dispatching simple comamnd line succeeds");
76     code = cmd_Parse(tc, tv, &retopts);
77     is_int(0, code, "parsing simple command line succeeds");
78     is_string("foo", retopts->parms[0].items->data, " ... 1st option matches");
79     is_string("bar", retopts->parms[1].items->data, " ... 2nd option matches");
80     ok(retopts->parms[2].items != NULL, " ... 3rd option matches");
81     cmd_FreeOptions(&retopts);
82     cmd_FreeArgv(tv);
83
84     /* unknown switch */
85     code = cmd_ParseLine("-first foo -second bar -third -flag", tv, &tc, 100);
86     is_int(0, code, "cmd_ParseLine succeeds");
87     code = cmd_Dispatch(tc, tv);
88     is_int(CMD_UNKNOWNSWITCH, code, "invalid options fail as expected");
89     code = cmd_Parse(tc, tv, &retopts);
90     is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse");
91     cmd_FreeArgv(tv);
92
93     /* missing parameter */
94     code = cmd_ParseLine("-first foo -second -flag", tv, &tc, 100);
95     is_int(0, code, "cmd_ParseLine succeeds");
96     code = cmd_Dispatch(tc, tv);
97     is_int(CMD_TOOFEW, code, "missing parameters fail as expected");
98     code = cmd_Parse(tc, tv, &retopts);
99     is_int(CMD_TOOFEW, code, "and still fail with cmd_Parse");
100     cmd_FreeArgv(tv);
101
102     /* missing option */
103     code = cmd_ParseLine("-second bar -third -flag", tv, &tc, 100);
104     is_int(0, code, "cmd_ParseLine succeeds");
105     code = cmd_Dispatch(tc, tv);
106     is_int(CMD_UNKNOWNSWITCH, code, "missing options fail as expected");
107     code = cmd_Parse(tc, tv, &retopts);
108     is_int(CMD_UNKNOWNSWITCH, code, "and still fail with cmd_Parse");
109     cmd_FreeArgv(tv);
110
111     code = cmd_ParseLine("-first foo baz -second bar -third -flag", tv, &tc, 100);
112     is_int(0, code, "cmd_ParseLine succeeds");
113     code = cmd_Dispatch(tc, tv);
114     is_int(CMD_NOTLIST, code, "too many parameters fails as expected");
115     code = cmd_Parse(tc, tv, &retopts);
116     is_int(CMD_NOTLIST, code, "and still fail with cmd_Parse");
117     cmd_FreeArgv(tv);
118
119     /* Positional parameters */
120     code = cmd_ParseLine("foo bar -flag", tv, &tc, 100);
121     is_int(0, code, "cmd_ParseLine succeeds");
122     code = cmd_Dispatch(tc, tv);
123     is_int(0, code, "dispatching positional parameters succeeds");
124     code = cmd_Parse(tc, tv, &retopts);
125     is_int(0, code, "and works with cmd_Parse");
126     cmd_FreeOptions(&retopts);
127     cmd_FreeArgv(tv);
128
129     /* Abbreviations */
130     code = cmd_ParseLine("-fi foo -s bar -flag", tv, &tc, 100);
131     is_int(0, code, "cmd_ParseLine succeeds");
132     code = cmd_Dispatch(tc, tv);
133     is_int(0, code, "dispatching abbreviations succeeds");
134     code = cmd_Parse(tc, tv, &retopts);
135     is_int(0, code, "and works with cmd_Parse");
136     cmd_FreeOptions(&retopts);
137
138     cmd_FreeArgv(tv);
139
140     /* Ambiguous */
141     code = cmd_ParseLine("-f foo -s bar -flag", tv, &tc, 100);
142     is_int(0, code, "cmd_ParseLine succeeds");
143     code = cmd_Dispatch(tc, tv);
144     is_int(CMD_UNKNOWNSWITCH, code, "ambiguous abbreviations correctly fail");
145     code = cmd_Parse(tc, tv, &retopts);
146     is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too");
147     cmd_FreeArgv(tv);
148
149     /* Disable positional commands */
150     cmd_DisablePositionalCommands();
151     code = cmd_ParseLine("foo bar -flag", tv, &tc, 100);
152     is_int(0, code, "cmd_ParseLine succeeds");
153     code = cmd_Dispatch(tc, tv);
154     is_int(CMD_NOTLIST, code, "positional parameters can be disabled");
155     code = cmd_Parse(tc, tv, &retopts);
156     is_int(CMD_NOTLIST, code, "and fail with cmd_Parse too");
157     cmd_FreeArgv(tv);
158
159     /* Disable abbreviations */
160     cmd_DisableAbbreviations();
161     code = cmd_ParseLine("-fi foo -s bar -flag", tv, &tc, 100);
162     is_int(0, code, "cmd_ParseLine succeeds");
163     code = cmd_Dispatch(tc, tv);
164     is_int(CMD_UNKNOWNSWITCH, code, "dispatching abbreviations succeeds");
165     code = cmd_Parse(tc, tv, &retopts);
166     is_int(CMD_UNKNOWNSWITCH, code, "and fail with cmd_Parse too");
167
168     cmd_FreeArgv(tv);
169
170     /* Try the new cmd_Parse function with something different*/
171     code = cmd_ParseLine("-first one -second two -flag", tv, &tc, 100);
172     is_int(0, code, "cmd_ParseLine succeeds");
173     code = cmd_Parse(tc, tv, &retopts);
174     is_int(0, code, "Parsing with cmd_Parse works");
175     is_string("one", retopts->parms[0].items->data, " ... 1st option matches");
176     is_string("two", retopts->parms[1].items->data, " ... 2nd option matches");
177     ok(retopts->parms[2].items != NULL, " ... 3rd option matches");
178
179     cmd_FreeOptions(&retopts);
180     cmd_FreeArgv(tv);
181     /* Try adding a couple of parameters at specific positions */
182     cmd_AddParmAtOffset(opts, "-fifth", CMD_SINGLE, CMD_OPTIONAL,
183                        "fifth option", 5);
184     cmd_AddParmAtOffset(opts, "-fourth", CMD_SINGLE, CMD_OPTIONAL,
185                        "fourth option", 4);
186     code = cmd_ParseLine("-first a -fourth b -fifth c", tv, &tc, 100);
187     is_int(0, code, "cmd_ParseLine succeeds");
188     code = cmd_Parse(tc, tv, &retopts);
189     is_int(0, code, "parsing our new options succeeds");
190     is_string("b", retopts->parms[4].items->data, " Fourth option in right place");
191     is_string("c", retopts->parms[5].items->data, " Fifth option in right place");
192     cmd_FreeOptions(&retopts);
193     cmd_FreeArgv(tv);
194
195     /* Check Accessors */
196     code = cmd_ParseLine("-first 1 -second second -flag", tv, &tc, 100);
197     is_int(0, code, "cmd_ParseLine succeeds");
198     code = cmd_Parse(tc, tv, &retopts);
199
200     code = cmd_OptionAsInt(retopts, 0, &retval);
201     is_int(0, code, "cmd_OptionsAsInt succeeds");
202     is_int(1, retval, " ... and returns correct value");
203
204     code = cmd_OptionAsString(retopts, 1, &retstring);
205     is_int(0, code, "cmd_OptionsAsString succeeds");
206     is_string("second", retstring, " ... and returns correct value");
207     free(retstring);
208     retstring = NULL;
209
210     code = cmd_OptionAsFlag(retopts, 2, &retval);
211     is_int(0, code, "cmd_OptionsAsFlag succeeds");
212     ok(retval, " ... and flag is correct");
213
214     cmd_FreeOptions(&retopts);
215     cmd_FreeArgv(tv);
216
217     /* Add an alias */
218     code = cmd_AddParmAlias(opts, 1, "-twa");
219     is_int(0, code, "cmd_AddParmAlias succeeds");
220
221     code = cmd_ParseLine("-first 1 -twa tup", tv, &tc, 100);
222     is_int(0, code, "cmd_ParseLine succeeds");
223     code = cmd_Parse(tc, tv, &retopts);
224     is_int(0, code, "cmd_Parse succeeds for alias");
225     cmd_OptionAsString(retopts, 1, &retstring);
226     is_string("tup", retstring, " ... and we have the correct value");
227     free(retstring);
228     retstring = NULL;
229
230     cmd_FreeOptions(&retopts);
231     cmd_FreeArgv(tv);
232
233     /* Add something that can be a flag or a value, and put something after
234      * it so we can check for parse problems*/
235     cmd_AddParm(opts, "-perhaps", CMD_SINGLE_OR_FLAG, CMD_OPTIONAL,
236                 "what am I");
237     cmd_AddParm(opts, "-sanity", CMD_SINGLE, CMD_OPTIONAL, "sanity check");
238
239     /* Try using as an option */
240
241     code = cmd_ParseLine("-first 1 -perhaps 2 -sanity 3", tv, &tc, 100);
242     is_int(0, code, "cmd_ParseLine succeeds");
243     code = cmd_Parse(tc, tv, &retopts);
244     is_int(0, code, "cmd_Parse succeeds for option-as-flag as opt");
245     code = cmd_OptionAsInt(retopts, 6, &retval);
246     is_int(0, code, "cmd_OptionAsInt succeeds");
247     is_int(2, retval, " ... and we have the correct value");
248     cmd_FreeOptions(&retopts);
249     cmd_FreeArgv(tv);
250
251     /* And now, as a flag */
252
253     code = cmd_ParseLine("-first 1 -perhaps -sanity 3", tv, &tc, 100);
254     is_int(0, code, "cmd_ParseLine succeeds");
255     code = cmd_Parse(tc, tv, &retopts);
256     is_int(0, code, "cmd_Parse succeeds for option-as-flag as flag");
257     code = cmd_OptionAsInt(retopts, 6, &retval);
258     is_int(CMD_MISSING, code, " ... pulling out a value fails as expected");
259     cmd_OptionAsFlag(retopts, 6, &retval);
260     ok(retval, " ... but parsing as a flag works");
261     cmd_FreeOptions(&retopts);
262     cmd_FreeArgv(tv);
263
264     return 0;
265 }
266