1 // Copyright 2012 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #if defined(HAVE_CONFIG_H)
49 # define GID_MAX INT_MAX
52 # define UID_MAX INT_MAX
55 #if defined(HAVE_GETOPT_GNU)
56 # define GETOPT_PLUS "+"
62 /// Terminates execution if the given error is set.
64 /// \param error The error to validate.
66 /// \post The program terminates if the given error is set.
68 check_error(kyua_error_t error
)
70 if (kyua_error_is_set(error
)) {
71 const bool usage_error
= kyua_error_is_type(error
,
72 kyua_usage_error_type
);
75 kyua_error_format(error
, buffer
, sizeof(buffer
));
76 kyua_error_free(error
);
78 errx(usage_error
? EXIT_USAGE_ERROR
: EXIT_INTERNAL_ERROR
,
84 /// Converts a string to an unsigned long.
86 /// \param str The string to convert.
87 /// \param message Part of the error message to print if the string does not
88 /// represent a valid unsigned long number.
89 /// \param max Maximum accepted value.
91 /// \return The converted numerical value.
93 /// \post The program terminates if the value is invalid.
95 parse_ulong(const char* str
, const char* message
, const unsigned long max
)
100 const unsigned long value
= strtoul(str
, &endptr
, 10);
101 if (str
[0] == '\0' || *endptr
!= '\0')
102 errx(EXIT_USAGE_ERROR
, "%s '%s' (not a number)", message
, str
);
103 else if (errno
== ERANGE
|| value
== LONG_MAX
|| value
> max
)
104 errx(EXIT_USAGE_ERROR
, "%s '%s' (out of range)", message
, str
);
109 /// Clears getopt(3) state to allow calling the function again.
114 optind
= GETOPT_OPTIND_RESET_VALUE
;
115 #if defined(HAVE_GETOPT_WITH_OPTRESET)
121 /// Prints the list of test cases and their metadata in a test program.
123 /// \param argc Number of arguments to the command, including the command name.
124 /// \param argv Arguments to the command, including the command name.
125 /// \param tester Description of the tester implemented by this binary.
126 /// \param run_params Execution parameters to configure the test process.
128 /// \return An exit status to indicate the success or failure of the listing.
130 /// \post Usage errors terminate the execution of the program right away.
132 list_command(const int argc
, char* const* const argv
,
133 const kyua_cli_tester_t
* tester
,
134 const kyua_run_params_t
* run_params
)
137 errx(EXIT_USAGE_ERROR
, "No test program provided");
139 errx(EXIT_USAGE_ERROR
, "Only one test program allowed");
140 const char* test_program
= argv
[1];
142 check_error(tester
->list_test_cases(test_program
, run_params
));
147 /// Runs and cleans up a single test case.
149 /// \param argc Number of arguments to the command, including the command name.
150 /// \param argv Arguments to the command, including the command name.
151 /// \param tester Description of the tester implemented by this binary.
152 /// \param run_params Execution parameters to configure the test process.
154 /// \return An exit status to indicate the success or failure of the test case
157 /// \post Usage errors terminate the execution of the program right away.
159 test_command(int argc
, char* const* argv
, const kyua_cli_tester_t
* tester
,
160 const kyua_run_params_t
* run_params
)
162 # define MAX_USER_VARIABLES 256
163 const char* user_variables
[MAX_USER_VARIABLES
];
165 const char** last_variable
= user_variables
;
167 while ((ch
= getopt(argc
, argv
, GETOPT_PLUS
":v:")) != -1) {
170 *last_variable
++ = optarg
;
174 errx(EXIT_USAGE_ERROR
, "%s's -%c requires an argument", argv
[0],
178 errx(EXIT_USAGE_ERROR
, "Unknown %s option -%c", argv
[0], optopt
);
186 *last_variable
= NULL
;
189 errx(EXIT_USAGE_ERROR
, "Must provide a test program, a test case name "
190 "and a result file");
191 const char* test_program
= argv
[0];
192 const char* test_case
= argv
[1];
193 const char* result_file
= argv
[2];
196 check_error(tester
->run_test_case(test_program
, test_case
, result_file
,
197 user_variables
, run_params
, &success
));
198 return success
? EXIT_SUCCESS
: EXIT_FAILURE
;
202 /// Generic entry point to the tester's command-line interface.
204 /// \param argc Verbatim argc passed to main().
205 /// \param argv Verbatim argv passed to main().
206 /// \param tester Description of the tester implemented by this binary.
208 /// \return An exit status.
210 /// \post Usage errors terminate the execution of the program right away.
212 kyua_cli_main(int argc
, char* const* argv
, const kyua_cli_tester_t
* tester
)
214 kyua_run_params_t run_params
;
215 kyua_run_params_init(&run_params
);
218 while ((ch
= getopt(argc
, argv
, GETOPT_PLUS
":g:t:u:")) != -1) {
221 run_params
.unprivileged_group
= (uid_t
)parse_ulong(
222 optarg
, "Invalid GID", GID_MAX
);
226 run_params
.timeout_seconds
= parse_ulong(
227 optarg
, "Invalid timeout value", LONG_MAX
);
231 run_params
.unprivileged_user
= (uid_t
)parse_ulong(
232 optarg
, "Invalid UID", UID_MAX
);
236 errx(EXIT_USAGE_ERROR
, "-%c requires an argument", optopt
);
239 errx(EXIT_USAGE_ERROR
, "Unknown option -%c", optopt
);
250 errx(EXIT_USAGE_ERROR
, "Must provide a command");
251 const char* command
= argv
[0];
253 // Keep sorted by order of likelyhood (yeah, micro-optimization).
254 if (strcmp(command
, "test") == 0)
255 return test_command(argc
, argv
, tester
, &run_params
);
256 else if (strcmp(command
, "list") == 0)
257 return list_command(argc
, argv
, tester
, &run_params
);
259 errx(EXIT_USAGE_ERROR
, "Unknown command '%s'", command
);