| @@ -1,847 +1,847 @@ | | | @@ -1,847 +1,847 @@ |
1 | // | | 1 | // |
2 | // Automated Testing Framework (atf) | | 2 | // Automated Testing Framework (atf) |
3 | // | | 3 | // |
4 | // Copyright (c) 2008 The NetBSD Foundation, Inc. | | 4 | // Copyright (c) 2008 The NetBSD Foundation, Inc. |
5 | // All rights reserved. | | 5 | // All rights reserved. |
6 | // | | 6 | // |
7 | // Redistribution and use in source and binary forms, with or without | | 7 | // Redistribution and use in source and binary forms, with or without |
8 | // modification, are permitted provided that the following conditions | | 8 | // modification, are permitted provided that the following conditions |
9 | // are met: | | 9 | // are met: |
10 | // 1. Redistributions of source code must retain the above copyright | | 10 | // 1. Redistributions of source code must retain the above copyright |
11 | // notice, this list of conditions and the following disclaimer. | | 11 | // notice, this list of conditions and the following disclaimer. |
12 | // 2. Redistributions in binary form must reproduce the above copyright | | 12 | // 2. Redistributions in binary form must reproduce the above copyright |
13 | // notice, this list of conditions and the following disclaimer in the | | 13 | // notice, this list of conditions and the following disclaimer in the |
14 | // documentation and/or other materials provided with the distribution. | | 14 | // documentation and/or other materials provided with the distribution. |
15 | // | | 15 | // |
16 | // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND | | 16 | // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND |
17 | // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | | 17 | // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
18 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | | 18 | // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
19 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 19 | // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
20 | // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY | | 20 | // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY |
21 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 21 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | | 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
23 | // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 23 | // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | | 24 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
25 | // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | | 25 | // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
26 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | | 26 | // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
27 | // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | 27 | // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | // | | 28 | // |
29 | | | 29 | |
30 | extern "C" { | | 30 | extern "C" { |
31 | #include <sys/types.h> | | 31 | #include <sys/types.h> |
32 | #include <sys/wait.h> | | 32 | #include <sys/wait.h> |
33 | | | 33 | |
34 | #include <limits.h> | | 34 | #include <limits.h> |
35 | #include <signal.h> | | 35 | #include <signal.h> |
36 | #include <unistd.h> | | 36 | #include <unistd.h> |
37 | } | | 37 | } |
38 | | | 38 | |
39 | #include <cerrno> | | 39 | #include <cerrno> |
40 | #include <cstdlib> | | 40 | #include <cstdlib> |
41 | #include <cstring> | | 41 | #include <cstring> |
42 | #include <fstream> | | 42 | #include <fstream> |
43 | #include <ios> | | 43 | #include <ios> |
44 | #include <iostream> | | 44 | #include <iostream> |
45 | #include <iterator> | | 45 | #include <iterator> |
46 | #include <list> | | 46 | #include <list> |
47 | #include <memory> | | 47 | #include <memory> |
48 | #include <utility> | | 48 | #include <utility> |
49 | | | 49 | |
50 | #include "atf-c++/check.hpp" | | 50 | #include "atf-c++/check.hpp" |
51 | #include "atf-c++/config.hpp" | | 51 | #include "atf-c++/config.hpp" |
52 | | | 52 | |
53 | #include "atf-c++/detail/application.hpp" | | 53 | #include "atf-c++/detail/application.hpp" |
54 | #include "atf-c++/detail/auto_array.hpp" | | 54 | #include "atf-c++/detail/auto_array.hpp" |
55 | #include "atf-c++/detail/exceptions.hpp" | | 55 | #include "atf-c++/detail/exceptions.hpp" |
56 | #include "atf-c++/detail/fs.hpp" | | 56 | #include "atf-c++/detail/fs.hpp" |
57 | #include "atf-c++/detail/process.hpp" | | 57 | #include "atf-c++/detail/process.hpp" |
58 | #include "atf-c++/detail/sanity.hpp" | | 58 | #include "atf-c++/detail/sanity.hpp" |
59 | #include "atf-c++/detail/text.hpp" | | 59 | #include "atf-c++/detail/text.hpp" |
60 | | | 60 | |
61 | // ------------------------------------------------------------------------ | | 61 | // ------------------------------------------------------------------------ |
62 | // Auxiliary functions. | | 62 | // Auxiliary functions. |
63 | // ------------------------------------------------------------------------ | | 63 | // ------------------------------------------------------------------------ |
64 | | | 64 | |
65 | namespace { | | 65 | namespace { |
66 | | | 66 | |
67 | enum status_check_t { | | 67 | enum status_check_t { |
68 | sc_exit, | | 68 | sc_exit, |
69 | sc_ignore, | | 69 | sc_ignore, |
70 | sc_signal, | | 70 | sc_signal, |
71 | }; | | 71 | }; |
72 | | | 72 | |
73 | struct status_check { | | 73 | struct status_check { |
74 | status_check_t type; | | 74 | status_check_t type; |
75 | bool negated; | | 75 | bool negated; |
76 | int value; | | 76 | int value; |
77 | | | 77 | |
78 | status_check(const status_check_t& p_type, const bool p_negated, | | 78 | status_check(const status_check_t& p_type, const bool p_negated, |
79 | const int p_value) : | | 79 | const int p_value) : |
80 | type(p_type), | | 80 | type(p_type), |
81 | negated(p_negated), | | 81 | negated(p_negated), |
82 | value(p_value) | | 82 | value(p_value) |
83 | { | | 83 | { |
84 | } | | 84 | } |
85 | }; | | 85 | }; |
86 | | | 86 | |
87 | enum output_check_t { | | 87 | enum output_check_t { |
88 | oc_ignore, | | 88 | oc_ignore, |
89 | oc_inline, | | 89 | oc_inline, |
90 | oc_file, | | 90 | oc_file, |
91 | oc_empty, | | 91 | oc_empty, |
92 | oc_match, | | 92 | oc_match, |
93 | oc_save | | 93 | oc_save |
94 | }; | | 94 | }; |
95 | | | 95 | |
96 | struct output_check { | | 96 | struct output_check { |
97 | output_check_t type; | | 97 | output_check_t type; |
98 | bool negated; | | 98 | bool negated; |
99 | std::string value; | | 99 | std::string value; |
100 | | | 100 | |
101 | output_check(const output_check_t& p_type, const bool p_negated, | | 101 | output_check(const output_check_t& p_type, const bool p_negated, |
102 | const std::string& p_value) : | | 102 | const std::string& p_value) : |
103 | type(p_type), | | 103 | type(p_type), |
104 | negated(p_negated), | | 104 | negated(p_negated), |
105 | value(p_value) | | 105 | value(p_value) |
106 | { | | 106 | { |
107 | } | | 107 | } |
108 | }; | | 108 | }; |
109 | | | 109 | |
110 | class temp_file : public std::ostream { | | 110 | class temp_file : public std::ostream { |
111 | std::auto_ptr< atf::fs::path > m_path; | | 111 | std::auto_ptr< atf::fs::path > m_path; |
112 | int m_fd; | | 112 | int m_fd; |
113 | | | 113 | |
114 | public: | | 114 | public: |
115 | temp_file(const atf::fs::path& p) : | | 115 | temp_file(const atf::fs::path& p) : |
116 | std::ostream(NULL), | | 116 | std::ostream(NULL), |
117 | m_fd(-1) | | 117 | m_fd(-1) |
118 | { | | 118 | { |
119 | atf::auto_array< char > buf(new char[p.str().length() + 1]); | | 119 | atf::auto_array< char > buf(new char[p.str().length() + 1]); |
120 | std::strcpy(buf.get(), p.c_str()); | | 120 | std::strcpy(buf.get(), p.c_str()); |
121 | | | 121 | |
122 | m_fd = ::mkstemp(buf.get()); | | 122 | m_fd = ::mkstemp(buf.get()); |
123 | if (m_fd == -1) | | 123 | if (m_fd == -1) |
124 | throw atf::system_error("atf_check::temp_file::temp_file(" + | | 124 | throw atf::system_error("atf_check::temp_file::temp_file(" + |
125 | p.str() + ")", "mkstemp(3) failed", | | 125 | p.str() + ")", "mkstemp(3) failed", |
126 | errno); | | 126 | errno); |
127 | | | 127 | |
128 | m_path.reset(new atf::fs::path(buf.get())); | | 128 | m_path.reset(new atf::fs::path(buf.get())); |
129 | } | | 129 | } |
130 | | | 130 | |
131 | ~temp_file(void) | | 131 | ~temp_file(void) |
132 | { | | 132 | { |
133 | close(); | | 133 | close(); |
134 | try { | | 134 | try { |
135 | remove(*m_path); | | 135 | remove(*m_path); |
136 | } catch (const atf::system_error&) { | | 136 | } catch (const atf::system_error&) { |
137 | // Ignore deletion errors. | | 137 | // Ignore deletion errors. |
138 | } | | 138 | } |
139 | } | | 139 | } |
140 | | | 140 | |
141 | const atf::fs::path& | | 141 | const atf::fs::path& |
142 | get_path(void) const | | 142 | get_path(void) const |
143 | { | | 143 | { |
144 | return *m_path; | | 144 | return *m_path; |
145 | } | | 145 | } |
146 | | | 146 | |
147 | void | | 147 | void |
148 | write(const std::string& text) | | 148 | write(const std::string& text) |
149 | { | | 149 | { |
150 | if (::write(m_fd, text.c_str(), text.size()) == -1) | | 150 | if (::write(m_fd, text.c_str(), text.size()) == -1) |
151 | throw atf::system_error("atf_check", "write(2) failed", errno); | | 151 | throw atf::system_error("atf_check", "write(2) failed", errno); |
152 | } | | 152 | } |
153 | | | 153 | |
154 | void | | 154 | void |
155 | close(void) | | 155 | close(void) |
156 | { | | 156 | { |
157 | if (m_fd != -1) { | | 157 | if (m_fd != -1) { |
158 | flush(); | | 158 | flush(); |
159 | ::close(m_fd); | | 159 | ::close(m_fd); |
160 | m_fd = -1; | | 160 | m_fd = -1; |
161 | } | | 161 | } |
162 | } | | 162 | } |
163 | }; | | 163 | }; |
164 | | | 164 | |
165 | } // anonymous namespace | | 165 | } // anonymous namespace |
166 | | | 166 | |
167 | static int | | 167 | static int |
168 | parse_exit_code(const std::string& str) | | 168 | parse_exit_code(const std::string& str) |
169 | { | | 169 | { |
170 | try { | | 170 | try { |
171 | const int value = atf::text::to_type< int >(str); | | 171 | const int value = atf::text::to_type< int >(str); |
172 | if (value < 0 || value > 255) | | 172 | if (value < 0 || value > 255) |
173 | throw std::runtime_error("Unused reason"); | | 173 | throw std::runtime_error("Unused reason"); |
174 | return value; | | 174 | return value; |
175 | } catch (const std::runtime_error&) { | | 175 | } catch (const std::runtime_error&) { |
176 | throw atf::application::usage_error("Invalid exit code for -s option; " | | 176 | throw atf::application::usage_error("Invalid exit code for -s option; " |
177 | "must be an integer in range 0-255"); | | 177 | "must be an integer in range 0-255"); |
178 | } | | 178 | } |
179 | } | | 179 | } |
180 | | | 180 | |
181 | static struct name_number { | | 181 | static struct name_number { |
182 | const char *name; | | 182 | const char *name; |
183 | int signo; | | 183 | int signo; |
184 | } signal_names_to_numbers[] = { | | 184 | } signal_names_to_numbers[] = { |
185 | { "hup", SIGHUP }, | | 185 | { "hup", SIGHUP }, |
186 | { "int", SIGINT }, | | 186 | { "int", SIGINT }, |
187 | { "quit", SIGQUIT }, | | 187 | { "quit", SIGQUIT }, |
188 | { "trap", SIGTRAP }, | | 188 | { "trap", SIGTRAP }, |
189 | { "abrt", SIGABRT }, | | 189 | { "abrt", SIGABRT }, |
190 | { "kill", SIGKILL }, | | 190 | { "kill", SIGKILL }, |
191 | { "segv", SIGSEGV }, | | 191 | { "segv", SIGSEGV }, |
192 | { "pipe", SIGPIPE }, | | 192 | { "pipe", SIGPIPE }, |
193 | { "alrm", SIGALRM }, | | 193 | { "alrm", SIGALRM }, |
194 | { "term", SIGTERM }, | | 194 | { "term", SIGTERM }, |
195 | { "usr1", SIGUSR1 }, | | 195 | { "usr1", SIGUSR1 }, |
196 | { "usr2", SIGUSR2 }, | | 196 | { "usr2", SIGUSR2 }, |
197 | { NULL, INT_MIN }, | | 197 | { NULL, INT_MIN }, |
198 | }; | | 198 | }; |
199 | | | 199 | |
200 | static int | | 200 | static int |
201 | signal_name_to_number(const std::string& str) | | 201 | signal_name_to_number(const std::string& str) |
202 | { | | 202 | { |
203 | struct name_number* iter = signal_names_to_numbers; | | 203 | struct name_number* iter = signal_names_to_numbers; |
204 | int signo = INT_MIN; | | 204 | int signo = INT_MIN; |
205 | while (signo == INT_MIN && iter->name != NULL) { | | 205 | while (signo == INT_MIN && iter->name != NULL) { |
206 | if (str == iter->name || str == std::string("sig") + iter->name) | | 206 | if (str == iter->name || str == std::string("sig") + iter->name) |
207 | signo = iter->signo; | | 207 | signo = iter->signo; |
208 | else | | 208 | else |
209 | iter++; | | 209 | iter++; |
210 | } | | 210 | } |
211 | return signo; | | 211 | return signo; |
212 | } | | 212 | } |
213 | | | 213 | |
214 | static int | | 214 | static int |
215 | parse_signal(const std::string& str) | | 215 | parse_signal(const std::string& str) |
216 | { | | 216 | { |
217 | const int signo = signal_name_to_number(str); | | 217 | const int signo = signal_name_to_number(str); |
218 | if (signo == INT_MIN) { | | 218 | if (signo == INT_MIN) { |
219 | try { | | 219 | try { |
220 | return atf::text::to_type< int >(str); | | 220 | return atf::text::to_type< int >(str); |
221 | } catch (std::runtime_error &e) { | | 221 | } catch (std::runtime_error &e) { |
222 | throw atf::application::usage_error("Invalid signal name or number " | | 222 | throw atf::application::usage_error("Invalid signal name or number " |
223 | "in -s option"); | | 223 | "in -s option"); |
224 | } | | 224 | } |
225 | } | | 225 | } |
226 | INV(signo != INT_MIN); | | 226 | INV(signo != INT_MIN); |
227 | return signo; | | 227 | return signo; |
228 | } | | 228 | } |
229 | | | 229 | |
230 | static status_check | | 230 | static status_check |
231 | parse_status_check_arg(const std::string& arg) | | 231 | parse_status_check_arg(const std::string& arg) |
232 | { | | 232 | { |
233 | const std::string::size_type delimiter = arg.find(':'); | | 233 | const std::string::size_type delimiter = arg.find(':'); |
234 | bool negated = (arg.compare(0, 4, "not-") == 0); | | 234 | bool negated = (arg.compare(0, 4, "not-") == 0); |
235 | const std::string action_str = arg.substr(0, delimiter); | | 235 | const std::string action_str = arg.substr(0, delimiter); |
236 | const std::string action = negated ? action_str.substr(4) : action_str; | | 236 | const std::string action = negated ? action_str.substr(4) : action_str; |
237 | const std::string value_str = ( | | 237 | const std::string value_str = ( |
238 | delimiter == std::string::npos ? "" : arg.substr(delimiter + 1)); | | 238 | delimiter == std::string::npos ? "" : arg.substr(delimiter + 1)); |
239 | int value; | | 239 | int value; |
240 | | | 240 | |
241 | status_check_t type; | | 241 | status_check_t type; |
242 | if (action == "eq") { | | 242 | if (action == "eq") { |
243 | // Deprecated; use exit instead. TODO: Remove after 0.10. | | 243 | // Deprecated; use exit instead. TODO: Remove after 0.10. |
244 | type = sc_exit; | | 244 | type = sc_exit; |
245 | if (negated) | | 245 | if (negated) |
246 | throw atf::application::usage_error("Cannot negate eq checker"); | | 246 | throw atf::application::usage_error("Cannot negate eq checker"); |
247 | negated = false; | | 247 | negated = false; |
248 | value = parse_exit_code(value_str); | | 248 | value = parse_exit_code(value_str); |
249 | } else if (action == "exit") { | | 249 | } else if (action == "exit") { |
250 | type = sc_exit; | | 250 | type = sc_exit; |
251 | if (value_str.empty()) | | 251 | if (value_str.empty()) |
252 | value = INT_MIN; | | 252 | value = INT_MIN; |
253 | else | | 253 | else |
254 | value = parse_exit_code(value_str); | | 254 | value = parse_exit_code(value_str); |
255 | } else if (action == "ignore") { | | 255 | } else if (action == "ignore") { |
256 | if (negated) | | 256 | if (negated) |
257 | throw atf::application::usage_error("Cannot negate ignore checker"); | | 257 | throw atf::application::usage_error("Cannot negate ignore checker"); |
258 | type = sc_ignore; | | 258 | type = sc_ignore; |
259 | value = INT_MIN; | | 259 | value = INT_MIN; |
260 | } else if (action == "ne") { | | 260 | } else if (action == "ne") { |
261 | // Deprecated; use not-exit instead. TODO: Remove after 0.10. | | 261 | // Deprecated; use not-exit instead. TODO: Remove after 0.10. |
262 | type = sc_exit; | | 262 | type = sc_exit; |
263 | if (negated) | | 263 | if (negated) |
264 | throw atf::application::usage_error("Cannot negate ne checker"); | | 264 | throw atf::application::usage_error("Cannot negate ne checker"); |
265 | negated = true; | | 265 | negated = true; |
266 | value = parse_exit_code(value_str); | | 266 | value = parse_exit_code(value_str); |
267 | } else if (action == "signal") { | | 267 | } else if (action == "signal") { |
268 | type = sc_signal; | | 268 | type = sc_signal; |
269 | if (value_str.empty()) | | 269 | if (value_str.empty()) |
270 | value = INT_MIN; | | 270 | value = INT_MIN; |
271 | else | | 271 | else |
272 | value = parse_signal(value_str); | | 272 | value = parse_signal(value_str); |
273 | } else | | 273 | } else |
274 | throw atf::application::usage_error("Invalid status checker"); | | 274 | throw atf::application::usage_error("Invalid status checker"); |
275 | | | 275 | |
276 | return status_check(type, negated, value); | | 276 | return status_check(type, negated, value); |
277 | } | | 277 | } |
278 | | | 278 | |
279 | static | | 279 | static |
280 | output_check | | 280 | output_check |
281 | parse_output_check_arg(const std::string& arg) | | 281 | parse_output_check_arg(const std::string& arg) |
282 | { | | 282 | { |
283 | const std::string::size_type delimiter = arg.find(':'); | | 283 | const std::string::size_type delimiter = arg.find(':'); |
284 | const bool negated = (arg.compare(0, 4, "not-") == 0); | | 284 | const bool negated = (arg.compare(0, 4, "not-") == 0); |
285 | const std::string action_str = arg.substr(0, delimiter); | | 285 | const std::string action_str = arg.substr(0, delimiter); |
286 | const std::string action = negated ? action_str.substr(4) : action_str; | | 286 | const std::string action = negated ? action_str.substr(4) : action_str; |
287 | | | 287 | |
288 | output_check_t type; | | 288 | output_check_t type; |
289 | if (action == "empty") | | 289 | if (action == "empty") |
290 | type = oc_empty; | | 290 | type = oc_empty; |
291 | else if (action == "file") | | 291 | else if (action == "file") |
292 | type = oc_file; | | 292 | type = oc_file; |
293 | else if (action == "ignore") { | | 293 | else if (action == "ignore") { |
294 | if (negated) | | 294 | if (negated) |
295 | throw atf::application::usage_error("Cannot negate ignore checker"); | | 295 | throw atf::application::usage_error("Cannot negate ignore checker"); |
296 | type = oc_ignore; | | 296 | type = oc_ignore; |
297 | } else if (action == "inline") | | 297 | } else if (action == "inline") |
298 | type = oc_inline; | | 298 | type = oc_inline; |
299 | else if (action == "match") | | 299 | else if (action == "match") |
300 | type = oc_match; | | 300 | type = oc_match; |
301 | else if (action == "save") { | | 301 | else if (action == "save") { |
302 | if (negated) | | 302 | if (negated) |
303 | throw atf::application::usage_error("Cannot negate save checker"); | | 303 | throw atf::application::usage_error("Cannot negate save checker"); |
304 | type = oc_save; | | 304 | type = oc_save; |
305 | } else | | 305 | } else |
306 | throw atf::application::usage_error("Invalid output checker"); | | 306 | throw atf::application::usage_error("Invalid output checker"); |
307 | | | 307 | |
308 | return output_check(type, negated, arg.substr(delimiter + 1)); | | 308 | return output_check(type, negated, arg.substr(delimiter + 1)); |
309 | } | | 309 | } |
310 | | | 310 | |
311 | static | | 311 | static |
312 | std::string | | 312 | std::string |
313 | flatten_argv(char* const* argv) | | 313 | flatten_argv(char* const* argv) |
314 | { | | 314 | { |
315 | std::string cmdline; | | 315 | std::string cmdline; |
316 | | | 316 | |
317 | char* const* arg = &argv[0]; | | 317 | char* const* arg = &argv[0]; |
318 | while (*arg != NULL) { | | 318 | while (*arg != NULL) { |
319 | if (arg != &argv[0]) | | 319 | if (arg != &argv[0]) |
320 | cmdline += ' '; | | 320 | cmdline += ' '; |
321 | | | 321 | |
322 | cmdline += *arg; | | 322 | cmdline += *arg; |
323 | | | 323 | |
324 | arg++; | | 324 | arg++; |
325 | } | | 325 | } |
326 | | | 326 | |
327 | return cmdline; | | 327 | return cmdline; |
328 | } | | 328 | } |
329 | | | 329 | |
330 | static | | 330 | static |
331 | std::auto_ptr< atf::check::check_result > | | 331 | std::auto_ptr< atf::check::check_result > |
332 | execute(const char* const* argv) | | 332 | execute(const char* const* argv) |
333 | { | | 333 | { |
334 | // TODO: This should go to stderr... but fixing it now may be hard as test | | 334 | // TODO: This should go to stderr... but fixing it now may be hard as test |
335 | // cases out there might be relying on stderr being silent. | | 335 | // cases out there might be relying on stderr being silent. |
336 | std::cout << "Executing command [ "; | | 336 | std::cout << "Executing command [ "; |
337 | for (int i = 0; argv[i] != NULL; ++i) | | 337 | for (int i = 0; argv[i] != NULL; ++i) |
338 | std::cout << argv[i] << " "; | | 338 | std::cout << argv[i] << " "; |
339 | std::cout << "]\n"; | | 339 | std::cout << "]\n"; |
340 | std::cout.flush(); | | 340 | std::cout.flush(); |
341 | | | 341 | |
342 | atf::process::argv_array argva(argv); | | 342 | atf::process::argv_array argva(argv); |
343 | return atf::check::exec(argva); | | 343 | return atf::check::exec(argva); |
344 | } | | 344 | } |
345 | | | 345 | |
346 | static | | 346 | static |
347 | std::auto_ptr< atf::check::check_result > | | 347 | std::auto_ptr< atf::check::check_result > |
348 | execute_with_shell(char* const* argv) | | 348 | execute_with_shell(char* const* argv) |
349 | { | | 349 | { |
350 | const std::string cmd = flatten_argv(argv); | | 350 | const std::string cmd = flatten_argv(argv); |
351 | | | 351 | |
352 | const char* sh_argv[4]; | | 352 | const char* sh_argv[4]; |
353 | sh_argv[0] = atf::config::get("atf_shell").c_str(); | | 353 | sh_argv[0] = atf::config::get("atf_shell").c_str(); |
354 | sh_argv[1] = "-c"; | | 354 | sh_argv[1] = "-c"; |
355 | sh_argv[2] = cmd.c_str(); | | 355 | sh_argv[2] = cmd.c_str(); |
356 | sh_argv[3] = NULL; | | 356 | sh_argv[3] = NULL; |
357 | return execute(sh_argv); | | 357 | return execute(sh_argv); |
358 | } | | 358 | } |
359 | | | 359 | |
360 | static | | 360 | static |
361 | void | | 361 | void |
362 | open_error(const atf::fs::path& path) | | 362 | open_error(const atf::fs::path& path) |
363 | { | | 363 | { |
364 | throw std::runtime_error("Failed to open " + path.str() + " " | | 364 | throw std::runtime_error("Failed to open " + path.str() + " " |
365 | + ::strerror(errno)); | | 365 | + ::strerror(errno)); |
366 | } | | 366 | } |
367 | | | 367 | |
368 | | | 368 | |
369 | static | | 369 | static |
370 | void | | 370 | void |
371 | cat_file(const atf::fs::path& path) | | 371 | cat_file(const atf::fs::path& path) |
372 | { | | 372 | { |
373 | std::ifstream stream(path.c_str()); | | 373 | std::ifstream stream(path.c_str()); |
374 | if (!stream) | | 374 | if (!stream) |
375 | open_error(path); | | 375 | open_error(path); |
376 | | | 376 | |
377 | stream >> std::noskipws; | | 377 | stream >> std::noskipws; |
378 | std::istream_iterator< char > begin(stream), end; | | 378 | std::istream_iterator< char > begin(stream), end; |
379 | std::ostream_iterator< char > out(std::cerr); | | 379 | std::ostream_iterator< char > out(std::cerr); |
380 | std::copy(begin, end, out); | | 380 | std::copy(begin, end, out); |
381 | | | 381 | |
382 | stream.close(); | | 382 | stream.close(); |
383 | } | | 383 | } |
384 | | | 384 | |
385 | static | | 385 | static |
386 | bool | | 386 | bool |
387 | grep_file(const atf::fs::path& path, const std::string& regexp) | | 387 | grep_file(const atf::fs::path& path, const std::string& regexp) |
388 | { | | 388 | { |
389 | std::ifstream stream(path.c_str()); | | 389 | std::ifstream stream(path.c_str()); |
390 | if (!stream) | | 390 | if (!stream) |
391 | open_error(path); | | 391 | open_error(path); |
392 | | | 392 | |
393 | bool found = false; | | 393 | bool found = false; |
394 | | | 394 | |
395 | std::string line; | | 395 | std::string line; |
396 | while (!found && !std::getline(stream, line).fail()) { | | 396 | while (!found && !std::getline(stream, line).fail()) { |
397 | if (atf::text::match(line, regexp)) | | 397 | if (atf::text::match(line, regexp)) |
398 | found = true; | | 398 | found = true; |
399 | } | | 399 | } |
400 | | | 400 | |
401 | stream.close(); | | 401 | stream.close(); |
402 | | | 402 | |
403 | return found; | | 403 | return found; |
404 | } | | 404 | } |
405 | | | 405 | |
406 | static | | 406 | static |
407 | bool | | 407 | bool |
408 | file_empty(const atf::fs::path& p) | | 408 | file_empty(const atf::fs::path& p) |
409 | { | | 409 | { |
410 | atf::fs::file_info f(p); | | 410 | atf::fs::file_info f(p); |
411 | | | 411 | |
412 | return (f.get_size() == 0); | | 412 | return (f.get_size() == 0); |
413 | } | | 413 | } |
414 | | | 414 | |
415 | static bool | | 415 | static bool |
416 | compare_files(const atf::fs::path& p1, const atf::fs::path& p2) | | 416 | compare_files(const atf::fs::path& p1, const atf::fs::path& p2) |
417 | { | | 417 | { |
418 | bool equal = false; | | 418 | bool equal = false; |
419 | | | 419 | |
420 | std::ifstream f1(p1.c_str()); | | 420 | std::ifstream f1(p1.c_str()); |
421 | if (!f1) | | 421 | if (!f1) |
422 | open_error(p1); | | 422 | open_error(p1); |
423 | | | 423 | |
424 | std::ifstream f2(p2.c_str()); | | 424 | std::ifstream f2(p2.c_str()); |
425 | if (!f2) | | 425 | if (!f2) |
426 | open_error(p2); | | 426 | open_error(p2); |
427 | | | 427 | |
428 | for (;;) { | | 428 | for (;;) { |
429 | char buf1[512], buf2[512]; | | 429 | char buf1[512], buf2[512]; |
430 | | | 430 | |
431 | f1.read(buf1, sizeof(buf1)); | | 431 | f1.read(buf1, sizeof(buf1)); |
432 | if (f1.bad()) | | 432 | if (f1.bad()) |
433 | throw std::runtime_error("Failed to read from " + p1.str()); | | 433 | throw std::runtime_error("Failed to read from " + p1.str()); |
434 | | | 434 | |
435 | f2.read(buf2, sizeof(buf2)); | | 435 | f2.read(buf2, sizeof(buf2)); |
436 | if (f2.bad()) | | 436 | if (f2.bad()) |
437 | throw std::runtime_error("Failed to read from " + p1.str()); | | 437 | throw std::runtime_error("Failed to read from " + p1.str()); |
438 | | | 438 | |
439 | if ((f1.gcount() == 0) && (f2.gcount() == 0)) { | | 439 | if ((f1.gcount() == 0) && (f2.gcount() == 0)) { |
440 | equal = true; | | 440 | equal = true; |
441 | break; | | 441 | break; |
442 | } | | 442 | } |
443 | | | 443 | |
444 | if ((f1.gcount() != f2.gcount()) || | | 444 | if ((f1.gcount() != f2.gcount()) || |
445 | (std::memcmp(buf1, buf2, f1.gcount()) != 0)) { | | 445 | (std::memcmp(buf1, buf2, f1.gcount()) != 0)) { |
446 | break; | | 446 | break; |
447 | } | | 447 | } |
448 | } | | 448 | } |
449 | | | 449 | |
450 | return equal; | | 450 | return equal; |
451 | } | | 451 | } |
452 | | | 452 | |
453 | static | | 453 | static |
454 | void | | 454 | void |
455 | print_diff(const atf::fs::path& p1, const atf::fs::path& p2) | | 455 | print_diff(const atf::fs::path& p1, const atf::fs::path& p2) |
456 | { | | 456 | { |
457 | const atf::process::status s = | | 457 | const atf::process::status s = |
458 | atf::process::exec(atf::fs::path("diff"), | | 458 | atf::process::exec(atf::fs::path("diff"), |
459 | atf::process::argv_array("diff", "-u", p1.c_str(), | | 459 | atf::process::argv_array("diff", "-u", p1.c_str(), |
460 | p2.c_str(), NULL), | | 460 | p2.c_str(), NULL), |
461 | atf::process::stream_connect(STDOUT_FILENO, | | 461 | atf::process::stream_connect(STDOUT_FILENO, |
462 | STDERR_FILENO), | | 462 | STDERR_FILENO), |
463 | atf::process::stream_inherit()); | | 463 | atf::process::stream_inherit()); |
464 | | | 464 | |
465 | if (!s.exited()) | | 465 | if (!s.exited()) |
466 | std::cerr << "Failed to run diff(3)\n"; | | 466 | std::cerr << "Failed to run diff(3)\n"; |
467 | | | 467 | |
468 | if (s.exitstatus() != 1) | | 468 | if (s.exitstatus() != 1) |
469 | std::cerr << "Error while running diff(3)\n"; | | 469 | std::cerr << "Error while running diff(3)\n"; |
470 | } | | 470 | } |
471 | | | 471 | |
472 | static | | 472 | static |
473 | std::string | | 473 | std::string |
474 | decode(const std::string& s) | | 474 | decode(const std::string& s) |
475 | { | | 475 | { |
476 | size_t i; | | 476 | size_t i; |
477 | std::string res; | | 477 | std::string res; |
478 | | | 478 | |
479 | res.reserve(s.length()); | | 479 | res.reserve(s.length()); |
480 | | | 480 | |
481 | i = 0; | | 481 | i = 0; |
482 | while (i < s.length()) { | | 482 | while (i < s.length()) { |
483 | char c = s[i++]; | | 483 | char c = s[i++]; |
484 | | | 484 | |
485 | if (c == '\\') { | | 485 | if (c == '\\') { |
486 | switch (s[i++]) { | | 486 | switch (s[i++]) { |
487 | case 'a': c = '\a'; break; | | 487 | case 'a': c = '\a'; break; |
488 | case 'b': c = '\b'; break; | | 488 | case 'b': c = '\b'; break; |
489 | case 'c': break; | | 489 | case 'c': break; |
490 | case 'e': c = 033; break; | | 490 | case 'e': c = 033; break; |
491 | case 'f': c = '\f'; break; | | 491 | case 'f': c = '\f'; break; |
492 | case 'n': c = '\n'; break; | | 492 | case 'n': c = '\n'; break; |
493 | case 'r': c = '\r'; break; | | 493 | case 'r': c = '\r'; break; |
494 | case 't': c = '\t'; break; | | 494 | case 't': c = '\t'; break; |
495 | case 'v': c = '\v'; break; | | 495 | case 'v': c = '\v'; break; |
496 | case '\\': break; | | 496 | case '\\': break; |
497 | case '0': | | 497 | case '0': |
498 | { | | 498 | { |
499 | int count = 3; | | 499 | int count = 3; |
500 | c = 0; | | 500 | c = 0; |
501 | while (--count >= 0 && (unsigned)(s[i] - '0') < 8) | | 501 | while (--count >= 0 && static_cast<unsigned>(s[i] - '0') < 8) |
502 | c = (c << 3) + (s[i++] - '0'); | | 502 | c = (c << 3) + (s[i++] - '0'); |
503 | break; | | 503 | break; |
504 | } | | 504 | } |
505 | default: | | 505 | default: |
506 | --i; | | 506 | --i; |
507 | break; | | 507 | break; |
508 | } | | 508 | } |
509 | } | | 509 | } |
510 | | | 510 | |
511 | res.push_back(c); | | 511 | res.push_back(c); |
512 | } | | 512 | } |
513 | | | 513 | |
514 | return res; | | 514 | return res; |
515 | } | | 515 | } |
516 | | | 516 | |
517 | static | | 517 | static |
518 | bool | | 518 | bool |
519 | run_status_check(const status_check& sc, const atf::check::check_result& cr) | | 519 | run_status_check(const status_check& sc, const atf::check::check_result& cr) |
520 | { | | 520 | { |
521 | bool result; | | 521 | bool result; |
522 | | | 522 | |
523 | if (sc.type == sc_exit) { | | 523 | if (sc.type == sc_exit) { |
524 | if (cr.exited() && sc.value != INT_MIN) { | | 524 | if (cr.exited() && sc.value != INT_MIN) { |
525 | const int status = cr.exitcode(); | | 525 | const int status = cr.exitcode(); |
526 | | | 526 | |
527 | if (!sc.negated && sc.value != status) { | | 527 | if (!sc.negated && sc.value != status) { |
528 | std::cerr << "Fail: incorrect exit status: " | | 528 | std::cerr << "Fail: incorrect exit status: " |
529 | << status << ", expected: " | | 529 | << status << ", expected: " |
530 | << sc.value << "\n"; | | 530 | << sc.value << "\n"; |
531 | result = false; | | 531 | result = false; |
532 | } else if (sc.negated && sc.value == status) { | | 532 | } else if (sc.negated && sc.value == status) { |
533 | std::cerr << "Fail: incorrect exit status: " | | 533 | std::cerr << "Fail: incorrect exit status: " |
534 | << status << ", expected: " | | 534 | << status << ", expected: " |
535 | << "anything else\n"; | | 535 | << "anything else\n"; |
536 | result = false; | | 536 | result = false; |
537 | } else | | 537 | } else |
538 | result = true; | | 538 | result = true; |
539 | } else if (cr.exited() && sc.value == INT_MIN) { | | 539 | } else if (cr.exited() && sc.value == INT_MIN) { |
540 | result = true; | | 540 | result = true; |
541 | } else { | | 541 | } else { |
542 | std::cerr << "Fail: program did not exit cleanly\n"; | | 542 | std::cerr << "Fail: program did not exit cleanly\n"; |
543 | result = false; | | 543 | result = false; |
544 | } | | 544 | } |
545 | } else if (sc.type == sc_ignore) { | | 545 | } else if (sc.type == sc_ignore) { |
546 | result = true; | | 546 | result = true; |
547 | } else if (sc.type == sc_signal) { | | 547 | } else if (sc.type == sc_signal) { |
548 | if (cr.signaled() && sc.value != INT_MIN) { | | 548 | if (cr.signaled() && sc.value != INT_MIN) { |
549 | const int status = cr.termsig(); | | 549 | const int status = cr.termsig(); |
550 | | | 550 | |
551 | if (!sc.negated && sc.value != status) { | | 551 | if (!sc.negated && sc.value != status) { |
552 | std::cerr << "Fail: incorrect signal received: " | | 552 | std::cerr << "Fail: incorrect signal received: " |
553 | << status << ", expected: " << sc.value << "\n"; | | 553 | << status << ", expected: " << sc.value << "\n"; |
554 | result = false; | | 554 | result = false; |
555 | } else if (sc.negated && sc.value == status) { | | 555 | } else if (sc.negated && sc.value == status) { |
556 | std::cerr << "Fail: incorrect signal received: " | | 556 | std::cerr << "Fail: incorrect signal received: " |
557 | << status << ", expected: " | | 557 | << status << ", expected: " |
558 | << "anything else\n"; | | 558 | << "anything else\n"; |
559 | result = false; | | 559 | result = false; |
560 | } else | | 560 | } else |
561 | result = true; | | 561 | result = true; |
562 | } else if (cr.signaled() && sc.value == INT_MIN) { | | 562 | } else if (cr.signaled() && sc.value == INT_MIN) { |
563 | result = true; | | 563 | result = true; |
564 | } else { | | 564 | } else { |
565 | std::cerr << "Fail: program did not receive a signal\n"; | | 565 | std::cerr << "Fail: program did not receive a signal\n"; |
566 | result = false; | | 566 | result = false; |
567 | } | | 567 | } |
568 | } else { | | 568 | } else { |
569 | UNREACHABLE; | | 569 | UNREACHABLE; |
570 | result = false; | | 570 | result = false; |
571 | } | | 571 | } |
572 | | | 572 | |
573 | if (result == false) { | | 573 | if (result == false) { |
574 | std::cerr << "stdout:\n"; | | 574 | std::cerr << "stdout:\n"; |
575 | cat_file(atf::fs::path(cr.stdout_path())); | | 575 | cat_file(atf::fs::path(cr.stdout_path())); |
576 | std::cerr << "\n"; | | 576 | std::cerr << "\n"; |
577 | | | 577 | |
578 | std::cerr << "stderr:\n"; | | 578 | std::cerr << "stderr:\n"; |
579 | cat_file(atf::fs::path(cr.stderr_path())); | | 579 | cat_file(atf::fs::path(cr.stderr_path())); |
580 | std::cerr << "\n"; | | 580 | std::cerr << "\n"; |
581 | } | | 581 | } |
582 | | | 582 | |
583 | return result; | | 583 | return result; |
584 | } | | 584 | } |
585 | | | 585 | |
586 | static | | 586 | static |
587 | bool | | 587 | bool |
588 | run_status_checks(const std::vector< status_check >& checks, | | 588 | run_status_checks(const std::vector< status_check >& checks, |
589 | const atf::check::check_result& result) | | 589 | const atf::check::check_result& result) |
590 | { | | 590 | { |
591 | bool ok = false; | | 591 | bool ok = false; |
592 | | | 592 | |
593 | for (std::vector< status_check >::const_iterator iter = checks.begin(); | | 593 | for (std::vector< status_check >::const_iterator iter = checks.begin(); |
594 | !ok && iter != checks.end(); iter++) { | | 594 | !ok && iter != checks.end(); iter++) { |
595 | ok |= run_status_check(*iter, result); | | 595 | ok |= run_status_check(*iter, result); |
596 | } | | 596 | } |
597 | | | 597 | |
598 | return ok; | | 598 | return ok; |
599 | } | | 599 | } |
600 | | | 600 | |
601 | static | | 601 | static |
602 | bool | | 602 | bool |
603 | run_output_check(const output_check oc, const atf::fs::path& path, | | 603 | run_output_check(const output_check oc, const atf::fs::path& path, |
604 | const std::string& stdxxx) | | 604 | const std::string& stdxxx) |
605 | { | | 605 | { |
606 | bool result; | | 606 | bool result; |
607 | | | 607 | |
608 | if (oc.type == oc_empty) { | | 608 | if (oc.type == oc_empty) { |
609 | const bool is_empty = file_empty(path); | | 609 | const bool is_empty = file_empty(path); |
610 | if (!oc.negated && !is_empty) { | | 610 | if (!oc.negated && !is_empty) { |
611 | std::cerr << "Fail: " << stdxxx << " not empty\n"; | | 611 | std::cerr << "Fail: " << stdxxx << " not empty\n"; |
612 | print_diff(atf::fs::path("/dev/null"), path); | | 612 | print_diff(atf::fs::path("/dev/null"), path); |
613 | result = false; | | 613 | result = false; |
614 | } else if (oc.negated && is_empty) { | | 614 | } else if (oc.negated && is_empty) { |
615 | std::cerr << "Fail: " << stdxxx << " is empty\n"; | | 615 | std::cerr << "Fail: " << stdxxx << " is empty\n"; |
616 | result = false; | | 616 | result = false; |
617 | } else | | 617 | } else |
618 | result = true; | | 618 | result = true; |
619 | } else if (oc.type == oc_file) { | | 619 | } else if (oc.type == oc_file) { |
620 | const bool equals = compare_files(path, atf::fs::path(oc.value)); | | 620 | const bool equals = compare_files(path, atf::fs::path(oc.value)); |
621 | if (!oc.negated && !equals) { | | 621 | if (!oc.negated && !equals) { |
622 | std::cerr << "Fail: " << stdxxx << " does not match golden " | | 622 | std::cerr << "Fail: " << stdxxx << " does not match golden " |
623 | "output\n"; | | 623 | "output\n"; |
624 | print_diff(atf::fs::path(oc.value), path); | | 624 | print_diff(atf::fs::path(oc.value), path); |
625 | result = false; | | 625 | result = false; |
626 | } else if (oc.negated && equals) { | | 626 | } else if (oc.negated && equals) { |
627 | std::cerr << "Fail: " << stdxxx << " matches golden output\n"; | | 627 | std::cerr << "Fail: " << stdxxx << " matches golden output\n"; |
628 | cat_file(atf::fs::path(oc.value)); | | 628 | cat_file(atf::fs::path(oc.value)); |
629 | result = false; | | 629 | result = false; |
630 | } else | | 630 | } else |
631 | result = true; | | 631 | result = true; |
632 | } else if (oc.type == oc_ignore) { | | 632 | } else if (oc.type == oc_ignore) { |
633 | result = true; | | 633 | result = true; |
634 | } else if (oc.type == oc_inline) { | | 634 | } else if (oc.type == oc_inline) { |
635 | atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir")) | | 635 | atf::fs::path path2 = atf::fs::path(atf::config::get("atf_workdir")) |
636 | / "inline.XXXXXX"; | | 636 | / "inline.XXXXXX"; |
637 | temp_file temp(path2); | | 637 | temp_file temp(path2); |
638 | temp.write(decode(oc.value)); | | 638 | temp.write(decode(oc.value)); |
639 | temp.close(); | | 639 | temp.close(); |
640 | | | 640 | |
641 | const bool equals = compare_files(path, temp.get_path()); | | 641 | const bool equals = compare_files(path, temp.get_path()); |
642 | if (!oc.negated && !equals) { | | 642 | if (!oc.negated && !equals) { |
643 | std::cerr << "Fail: " << stdxxx << " does not match expected " | | 643 | std::cerr << "Fail: " << stdxxx << " does not match expected " |
644 | "value\n"; | | 644 | "value\n"; |
645 | print_diff(temp.get_path(), path); | | 645 | print_diff(temp.get_path(), path); |
646 | result = false; | | 646 | result = false; |
647 | } else if (oc.negated && equals) { | | 647 | } else if (oc.negated && equals) { |
648 | std::cerr << "Fail: " << stdxxx << " matches expected value\n"; | | 648 | std::cerr << "Fail: " << stdxxx << " matches expected value\n"; |
649 | cat_file(temp.get_path()); | | 649 | cat_file(temp.get_path()); |
650 | result = false; | | 650 | result = false; |
651 | } else | | 651 | } else |
652 | result = true; | | 652 | result = true; |
653 | } else if (oc.type == oc_match) { | | 653 | } else if (oc.type == oc_match) { |
654 | const bool matches = grep_file(path, oc.value); | | 654 | const bool matches = grep_file(path, oc.value); |
655 | if (!oc.negated && !matches) { | | 655 | if (!oc.negated && !matches) { |
656 | std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx | | 656 | std::cerr << "Fail: regexp " + oc.value + " not in " << stdxxx |
657 | << "\n"; | | 657 | << "\n"; |
658 | cat_file(path); | | 658 | cat_file(path); |
659 | result = false; | | 659 | result = false; |
660 | } else if (oc.negated && matches) { | | 660 | } else if (oc.negated && matches) { |
661 | std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx | | 661 | std::cerr << "Fail: regexp " + oc.value + " is in " << stdxxx |
662 | << "\n"; | | 662 | << "\n"; |
663 | cat_file(path); | | 663 | cat_file(path); |
664 | result = false; | | 664 | result = false; |
665 | } else | | 665 | } else |
666 | result = true; | | 666 | result = true; |
667 | } else if (oc.type == oc_save) { | | 667 | } else if (oc.type == oc_save) { |
668 | INV(!oc.negated); | | 668 | INV(!oc.negated); |
669 | std::ifstream ifs(path.c_str(), std::fstream::binary); | | 669 | std::ifstream ifs(path.c_str(), std::fstream::binary); |
670 | ifs >> std::noskipws; | | 670 | ifs >> std::noskipws; |
671 | std::istream_iterator< char > begin(ifs), end; | | 671 | std::istream_iterator< char > begin(ifs), end; |
672 | | | 672 | |
673 | std::ofstream ofs(oc.value.c_str(), std::fstream::binary | | 673 | std::ofstream ofs(oc.value.c_str(), std::fstream::binary |
674 | | std::fstream::trunc); | | 674 | | std::fstream::trunc); |
675 | std::ostream_iterator <char> obegin(ofs); | | 675 | std::ostream_iterator <char> obegin(ofs); |
676 | | | 676 | |
677 | std::copy(begin, end, obegin); | | 677 | std::copy(begin, end, obegin); |
678 | result = true; | | 678 | result = true; |
679 | } else { | | 679 | } else { |
680 | UNREACHABLE; | | 680 | UNREACHABLE; |
681 | result = false; | | 681 | result = false; |
682 | } | | 682 | } |
683 | | | 683 | |
684 | return result; | | 684 | return result; |
685 | } | | 685 | } |
686 | | | 686 | |
687 | static | | 687 | static |
688 | bool | | 688 | bool |
689 | run_output_checks(const std::vector< output_check >& checks, | | 689 | run_output_checks(const std::vector< output_check >& checks, |
690 | const atf::fs::path& path, const std::string& stdxxx) | | 690 | const atf::fs::path& path, const std::string& stdxxx) |
691 | { | | 691 | { |
692 | bool ok = true; | | 692 | bool ok = true; |
693 | | | 693 | |
694 | for (std::vector< output_check >::const_iterator iter = checks.begin(); | | 694 | for (std::vector< output_check >::const_iterator iter = checks.begin(); |
695 | iter != checks.end(); iter++) { | | 695 | iter != checks.end(); iter++) { |
696 | ok &= run_output_check(*iter, path, stdxxx); | | 696 | ok &= run_output_check(*iter, path, stdxxx); |
697 | } | | 697 | } |
698 | | | 698 | |
699 | return ok; | | 699 | return ok; |
700 | } | | 700 | } |
701 | | | 701 | |
702 | // ------------------------------------------------------------------------ | | 702 | // ------------------------------------------------------------------------ |
703 | // The "atf_check" application. | | 703 | // The "atf_check" application. |
704 | // ------------------------------------------------------------------------ | | 704 | // ------------------------------------------------------------------------ |
705 | | | 705 | |
706 | namespace { | | 706 | namespace { |
707 | | | 707 | |
708 | class atf_check : public atf::application::app { | | 708 | class atf_check : public atf::application::app { |
709 | bool m_xflag; | | 709 | bool m_xflag; |
710 | | | 710 | |
711 | std::vector< status_check > m_status_checks; | | 711 | std::vector< status_check > m_status_checks; |
712 | std::vector< output_check > m_stdout_checks; | | 712 | std::vector< output_check > m_stdout_checks; |
713 | std::vector< output_check > m_stderr_checks; | | 713 | std::vector< output_check > m_stderr_checks; |
714 | | | 714 | |
715 | static const char* m_description; | | 715 | static const char* m_description; |
716 | | | 716 | |
717 | bool run_output_checks(const atf::check::check_result&, | | 717 | bool run_output_checks(const atf::check::check_result&, |
718 | const std::string&) const; | | 718 | const std::string&) const; |
719 | | | 719 | |
720 | std::string specific_args(void) const; | | 720 | std::string specific_args(void) const; |
721 | options_set specific_options(void) const; | | 721 | options_set specific_options(void) const; |
722 | void process_option(int, const char*); | | 722 | void process_option(int, const char*); |
723 | void process_option_s(const std::string&); | | 723 | void process_option_s(const std::string&); |
724 | | | 724 | |
725 | public: | | 725 | public: |
726 | atf_check(void); | | 726 | atf_check(void); |
727 | int main(void); | | 727 | int main(void); |
728 | }; | | 728 | }; |
729 | | | 729 | |
730 | } // anonymous namespace | | 730 | } // anonymous namespace |
731 | | | 731 | |
732 | const char* atf_check::m_description = | | 732 | const char* atf_check::m_description = |
733 | "atf-check executes given command and analyzes its results."; | | 733 | "atf-check executes given command and analyzes its results."; |
734 | | | 734 | |
735 | atf_check::atf_check(void) : | | 735 | atf_check::atf_check(void) : |
736 | app(m_description, "atf-check(1)"), | | 736 | app(m_description, "atf-check(1)"), |
737 | m_xflag(false) | | 737 | m_xflag(false) |
738 | { | | 738 | { |
739 | } | | 739 | } |
740 | | | 740 | |
741 | bool | | 741 | bool |
742 | atf_check::run_output_checks(const atf::check::check_result& r, | | 742 | atf_check::run_output_checks(const atf::check::check_result& r, |
743 | const std::string& stdxxx) | | 743 | const std::string& stdxxx) |
744 | const | | 744 | const |
745 | { | | 745 | { |
746 | if (stdxxx == "stdout") { | | 746 | if (stdxxx == "stdout") { |
747 | return ::run_output_checks(m_stdout_checks, | | 747 | return ::run_output_checks(m_stdout_checks, |
748 | atf::fs::path(r.stdout_path()), "stdout"); | | 748 | atf::fs::path(r.stdout_path()), "stdout"); |
749 | } else if (stdxxx == "stderr") { | | 749 | } else if (stdxxx == "stderr") { |
750 | return ::run_output_checks(m_stderr_checks, | | 750 | return ::run_output_checks(m_stderr_checks, |
751 | atf::fs::path(r.stderr_path()), "stderr"); | | 751 | atf::fs::path(r.stderr_path()), "stderr"); |
752 | } else { | | 752 | } else { |
753 | UNREACHABLE; | | 753 | UNREACHABLE; |
754 | return false; | | 754 | return false; |
755 | } | | 755 | } |
756 | } | | 756 | } |
757 | | | 757 | |
758 | std::string | | 758 | std::string |
759 | atf_check::specific_args(void) | | 759 | atf_check::specific_args(void) |
760 | const | | 760 | const |
761 | { | | 761 | { |
762 | return "<command>"; | | 762 | return "<command>"; |
763 | } | | 763 | } |
764 | | | 764 | |
765 | atf_check::options_set | | 765 | atf_check::options_set |
766 | atf_check::specific_options(void) | | 766 | atf_check::specific_options(void) |
767 | const | | 767 | const |
768 | { | | 768 | { |
769 | using atf::application::option; | | 769 | using atf::application::option; |
770 | options_set opts; | | 770 | options_set opts; |
771 | | | 771 | |
772 | opts.insert(option('s', "qual:value", "Handle status. Qualifier " | | 772 | opts.insert(option('s', "qual:value", "Handle status. Qualifier " |
773 | "must be one of: ignore exit:<num> signal:<name|num>")); | | 773 | "must be one of: ignore exit:<num> signal:<name|num>")); |
774 | opts.insert(option('o', "action:arg", "Handle stdout. Action must be " | | 774 | opts.insert(option('o', "action:arg", "Handle stdout. Action must be " |
775 | "one of: empty ignore file:<path> inline:<val> match:regexp " | | 775 | "one of: empty ignore file:<path> inline:<val> match:regexp " |
776 | "save:<path>")); | | 776 | "save:<path>")); |
777 | opts.insert(option('e', "action:arg", "Handle stderr. Action must be " | | 777 | opts.insert(option('e', "action:arg", "Handle stderr. Action must be " |
778 | "one of: empty ignore file:<path> inline:<val> match:regexp " | | 778 | "one of: empty ignore file:<path> inline:<val> match:regexp " |
779 | "save:<path>")); | | 779 | "save:<path>")); |
780 | opts.insert(option('x', "", "Execute command as a shell command")); | | 780 | opts.insert(option('x', "", "Execute command as a shell command")); |
781 | | | 781 | |
782 | return opts; | | 782 | return opts; |
783 | } | | 783 | } |
784 | | | 784 | |
785 | void | | 785 | void |
786 | atf_check::process_option(int ch, const char* arg) | | 786 | atf_check::process_option(int ch, const char* arg) |
787 | { | | 787 | { |
788 | switch (ch) { | | 788 | switch (ch) { |
789 | case 's': | | 789 | case 's': |
790 | m_status_checks.push_back(parse_status_check_arg(arg)); | | 790 | m_status_checks.push_back(parse_status_check_arg(arg)); |
791 | break; | | 791 | break; |
792 | | | 792 | |
793 | case 'o': | | 793 | case 'o': |
794 | m_stdout_checks.push_back(parse_output_check_arg(arg)); | | 794 | m_stdout_checks.push_back(parse_output_check_arg(arg)); |
795 | break; | | 795 | break; |
796 | | | 796 | |
797 | case 'e': | | 797 | case 'e': |
798 | m_stderr_checks.push_back(parse_output_check_arg(arg)); | | 798 | m_stderr_checks.push_back(parse_output_check_arg(arg)); |
799 | break; | | 799 | break; |
800 | | | 800 | |
801 | case 'x': | | 801 | case 'x': |
802 | m_xflag = true; | | 802 | m_xflag = true; |
803 | break; | | 803 | break; |
804 | | | 804 | |
805 | default: | | 805 | default: |
806 | UNREACHABLE; | | 806 | UNREACHABLE; |
807 | } | | 807 | } |
808 | } | | 808 | } |
809 | | | 809 | |
810 | int | | 810 | int |
811 | atf_check::main(void) | | 811 | atf_check::main(void) |
812 | { | | 812 | { |
813 | if (m_argc < 1) | | 813 | if (m_argc < 1) |
814 | throw atf::application::usage_error("No command specified"); | | 814 | throw atf::application::usage_error("No command specified"); |
815 | | | 815 | |
816 | int status = EXIT_FAILURE; | | 816 | int status = EXIT_FAILURE; |
817 | | | 817 | |
818 | std::auto_ptr< atf::check::check_result > r = | | 818 | std::auto_ptr< atf::check::check_result > r = |
819 | m_xflag ? execute_with_shell(m_argv) : execute(m_argv); | | 819 | m_xflag ? execute_with_shell(m_argv) : execute(m_argv); |
820 | | | 820 | |
821 | if (m_status_checks.empty()) | | 821 | if (m_status_checks.empty()) |
822 | m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS)); | | 822 | m_status_checks.push_back(status_check(sc_exit, false, EXIT_SUCCESS)); |
823 | else if (m_status_checks.size() > 1) { | | 823 | else if (m_status_checks.size() > 1) { |
824 | // TODO: Remove this restriction. | | 824 | // TODO: Remove this restriction. |
825 | throw atf::application::usage_error("Cannot specify -s more than once"); | | 825 | throw atf::application::usage_error("Cannot specify -s more than once"); |
826 | } | | 826 | } |
827 | | | 827 | |
828 | if (m_stdout_checks.empty()) | | 828 | if (m_stdout_checks.empty()) |
829 | m_stdout_checks.push_back(output_check(oc_empty, false, "")); | | 829 | m_stdout_checks.push_back(output_check(oc_empty, false, "")); |
830 | if (m_stderr_checks.empty()) | | 830 | if (m_stderr_checks.empty()) |
831 | m_stderr_checks.push_back(output_check(oc_empty, false, "")); | | 831 | m_stderr_checks.push_back(output_check(oc_empty, false, "")); |
832 | | | 832 | |
833 | if ((run_status_checks(m_status_checks, *r) == false) || | | 833 | if ((run_status_checks(m_status_checks, *r) == false) || |
834 | (run_output_checks(*r, "stderr") == false) || | | 834 | (run_output_checks(*r, "stderr") == false) || |
835 | (run_output_checks(*r, "stdout") == false)) | | 835 | (run_output_checks(*r, "stdout") == false)) |
836 | status = EXIT_FAILURE; | | 836 | status = EXIT_FAILURE; |
837 | else | | 837 | else |
838 | status = EXIT_SUCCESS; | | 838 | status = EXIT_SUCCESS; |
839 | | | 839 | |
840 | return status; | | 840 | return status; |
841 | } | | 841 | } |
842 | | | 842 | |
843 | int | | 843 | int |
844 | main(int argc, char* const* argv) | | 844 | main(int argc, char* const* argv) |
845 | { | | 845 | { |
846 | return atf_check().run(argc, argv); | | 846 | return atf_check().run(argc, argv); |
847 | } | | 847 | } |