xref: /minix3/external/bsd/kyua-testers/dist/plain_main.c (revision 6e5a113837816c382dd4f2a25924f16fbbb1c493)
1 // Copyright 2012 Google Inc.
2 // 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 are
6 // met:
7 //
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.
16 //
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.
28 
29 #include <sys/wait.h>
30 
31 #include <assert.h>
32 #include <err.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "cli.h"
38 #include "defs.h"
39 #include "error.h"
40 #include "result.h"
41 #include "run.h"
42 #include "stacktrace.h"
43 
44 #if !defined(WCOREDUMP) && defined(__minix)
45 #define WCOREDUMP(x) ((x) & 0x80)
46 #endif /*!defined(WCOREDUMP) && defined(__minix) */
47 
48 
49 /// Template for the creation of the temporary work directories.
50 #define WORKDIR_TEMPLATE "kyua.plain-tester.XXXXXX"
51 
52 
53 /// Name of the fake test case exposed by the program.
54 const char* const fake_test_case_name = "main";
55 
56 
57 /// Converts the exit status of a program to a result.
58 ///
59 /// \param status Exit status of the test program, as returned by waitpid().
60 /// \param timed_out Whether the test program timed out or not.
61 /// \param result_file Path to the result file to create.
62 /// \param [out] success Set to true if the test program returned with a
63 ///     successful condition.
64 ///
65 /// \return An error if something went wrong.
66 static kyua_error_t
67 status_to_result(int status, const bool timed_out, const char* result_file,
68                  bool* success)
69 {
70     if (timed_out) {
71         *success = false;
72         return kyua_result_write(result_file, KYUA_RESULT_BROKEN,
73                                  "Test case timed out");
74     }
75 
76     if (WIFEXITED(status)) {
77         if (WEXITSTATUS(status) == EXIT_SUCCESS) {
78             *success = true;
79             return kyua_result_write(result_file, KYUA_RESULT_PASSED, NULL);
80         } else {
81             *success = false;
82             return kyua_result_write(result_file, KYUA_RESULT_FAILED,
83                                      "Returned non-success exit status %d",
84                                      WEXITSTATUS(status));
85         }
86     } else {
87         assert(WIFSIGNALED(status));
88         *success = false;
89         return kyua_result_write(result_file, KYUA_RESULT_BROKEN,
90                                  "Received signal %d", WTERMSIG(status));
91     }
92 }
93 
94 
95 /// Lists the test cases in a test program.
96 ///
97 /// \param unused_test_program Path to the test program for which to list the
98 ///     test cases.  Should be absolute.
99 /// \param unused_run_params Execution parameters to configure the test process.
100 ///
101 /// \return An error if the listing fails; OK otherwise.
102 static kyua_error_t
103 list_test_cases(const char* KYUA_DEFS_UNUSED_PARAM(test_program),
104                 const kyua_run_params_t* KYUA_DEFS_UNUSED_PARAM(run_params))
105 {
106     printf("test_case{name='%s'}\n", fake_test_case_name);
107     return kyua_error_ok();
108 }
109 
110 
111 /// Runs a single test cases of a test program.
112 ///
113 /// \param test_program Path to the test program for which to list the test
114 ///     cases.  Should be absolute.
115 /// \param test_case Name of the test case to run.
116 /// \param result_file Path to the file to which to write the result of the
117 ///     test.  Should be absolute.
118 /// \param user_variables Array of name=value pairs that describe the user
119 ///     configuration variables for the test case.
120 /// \param run_params Execution parameters to configure the test process.
121 /// \param [out] success Set to true if the test case reported a valid exit
122 ///     condition (like "passed" or "skipped"); false otherwise.  This is
123 ///     only updated if the method returns OK.
124 ///
125 /// \return An error if the listing fails; OK otherwise.
126 static kyua_error_t
127 run_test_case(const char* test_program, const char* test_case,
128               const char* result_file, const char* const user_variables[],
129               const kyua_run_params_t* run_params,
130               bool* success)
131 {
132     kyua_error_t error;
133 
134     if (strcmp(test_case, fake_test_case_name) != 0) {
135         error = kyua_generic_error_new("Unknown test case '%s'", test_case);
136         goto out;
137     }
138 
139     const char* const* iter;
140     for (iter = user_variables; *iter != NULL; ++iter) {
141         warnx("Configuration variables not supported; ignoring '%s'", *iter);
142     }
143 
144     char *work_directory;
145     error = kyua_run_work_directory_enter(WORKDIR_TEMPLATE,
146                                           run_params->unprivileged_user,
147                                           run_params->unprivileged_group,
148                                           &work_directory);
149     if (kyua_error_is_set(error))
150         goto out;
151     kyua_run_params_t real_run_params = *run_params;
152     real_run_params.work_directory = work_directory;
153 
154     pid_t pid;
155     error = kyua_run_fork(&real_run_params, &pid);
156     if (!kyua_error_is_set(error) && pid == 0) {
157         const char* const program_args[] = { test_program, NULL };
158         kyua_run_exec(test_program, program_args);
159     }
160     assert(pid != -1 && pid != 0);
161     if (kyua_error_is_set(error))
162         goto out_work_directory;
163 
164     int status; bool timed_out;
165     error = kyua_run_wait(pid, &status, &timed_out);
166     if (kyua_error_is_set(error))
167         goto out_work_directory;
168 
169     if (WIFSIGNALED(status) && WCOREDUMP(status)) {
170         kyua_stacktrace_dump(test_program, pid, run_params, stderr);
171     }
172 
173     error = status_to_result(status, timed_out, result_file, success);
174 
175 out_work_directory:
176     error = kyua_error_subsume(error,
177         kyua_run_work_directory_leave(&work_directory));
178 out:
179     return error;
180 }
181 
182 
183 /// Definition of the tester.
184 static kyua_cli_tester_t plain_tester = {
185     .list_test_cases = list_test_cases,
186     .run_test_case = run_test_case,
187 };
188 
189 
190 /// Tester entry point.
191 ///
192 /// \param argc Number of command line arguments.
193 /// \param argv NULL-terminated array of command line arguments.
194 ///
195 /// \return An exit code.
196 int
197 main(const int argc, char* const* const argv)
198 {
199     return kyua_cli_main(argc, argv, &plain_tester);
200 }
201