2 // Automated Testing Framework (atf)
4 // Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
5 // All rights reserved.
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
44 #include "atf-c++/application.hpp"
45 #include "atf-c++/exceptions.hpp"
46 #include "atf-c++/check.hpp"
47 #include "atf-c++/config.hpp"
48 #include "atf-c++/fs.hpp"
49 #include "atf-c++/io.hpp"
50 #include "atf-c++/process.hpp"
51 #include "atf-c++/sanity.hpp"
52 #include "atf-c++/text.hpp"
53 #include "atf-c++/utils.hpp"
55 // ------------------------------------------------------------------------
56 // Auxiliary functions.
57 // ------------------------------------------------------------------------
61 flatten_argv(char* const* argv
)
65 char* const* arg
= &argv
[0];
66 while (*arg
!= NULL
) {
79 atf::check::check_result
80 execute(const char* const* argv
)
82 std::cout
<< "Executing command [ ";
83 for (int i
= 0; argv
[i
] != NULL
; ++i
)
84 std::cout
<< argv
[i
] << " ";
85 std::cout
<< "]" << std::endl
;
87 atf::process::argv_array
argva(argv
);
88 return atf::check::exec(argva
);
92 atf::check::check_result
93 execute_with_shell(char* const* argv
)
95 const std::string cmd
= flatten_argv(argv
);
97 const char* sh_argv
[4];
98 sh_argv
[0] = atf::config::get("atf_shell").c_str();
100 sh_argv
[2] = cmd
.c_str();
102 return execute(sh_argv
);
105 // ------------------------------------------------------------------------
106 // The "atf_check" application.
107 // ------------------------------------------------------------------------
109 class atf_check
: public atf::application::app
{
110 enum output_check_t
{
118 enum status_check_t
{
126 output_check_t m_stdout_check
;
127 std::string m_stdout_arg
;
129 output_check_t m_stderr_check
;
130 std::string m_stderr_arg
;
132 status_check_t m_status_check
;
135 static const char* m_description
;
137 bool file_empty(const atf::fs::path
&) const;
138 void print_diff(const atf::fs::path
&, const atf::fs::path
&) const;
139 void print_file(const atf::fs::path
&) const;
140 std::string
decode(const std::string
&) const;
142 bool run_status_check(const atf::check::check_result
&) const;
143 bool run_output_check(const atf::check::check_result
&,
144 const std::string
&) const;
146 std::string
specific_args(void) const;
147 options_set
specific_options(void) const;
148 void process_option(int, const char*);
149 void process_option_s(const std::string
&);
150 void process_option_o(const std::string
&);
151 void process_option_e(const std::string
&);
158 const char* atf_check::m_description
=
159 "atf-check executes given command and analyzes its results.";
161 atf_check::atf_check(void) :
162 app(m_description
, "atf-check(1)", "atf(7)"),
164 m_stdout_check(oc_empty
),
165 m_stderr_check(oc_empty
),
166 m_status_check(sc_equal
),
172 atf_check::file_empty(const atf::fs::path
& p
)
175 atf::fs::file_info
f(p
);
177 return (f
.get_size() == 0);
181 atf_check::print_diff(const atf::fs::path
& p1
, const atf::fs::path
& p2
)
184 const atf::process::status s
=
185 atf::process::exec(atf::fs::path("diff"),
186 atf::process::argv_array("diff", "-u", p1
.c_str(),
188 atf::process::stream_connect(STDOUT_FILENO
,
190 atf::process::stream_inherit());
193 std::cerr
<< "Failed to run diff(3)" << std::endl
;
195 if (s
.exitstatus() != 1)
196 std::cerr
<< "Error while running diff(3)" << std::endl
;
200 atf_check::print_file(const atf::fs::path
& p
)
203 std::ifstream
f(p
.c_str());
205 std::istream_iterator
< char > begin(f
), end
;
206 std::ostream_iterator
< char > out(std::cerr
);
207 std::copy(begin
, end
, out
);
211 atf_check::decode(const std::string
& s
)
217 res
.reserve(s
.length());
220 while (i
< s
.length()) {
225 case 'a': c
= '\a'; break;
226 case 'b': c
= '\b'; break;
228 case 'e': c
= 033; break;
229 case 'f': c
= '\f'; break;
230 case 'n': c
= '\n'; break;
231 case 'r': c
= '\r'; break;
232 case 't': c
= '\t'; break;
233 case 'v': c
= '\v'; break;
239 while (--count
>= 0 && (unsigned)(s
[i
] - '0') < 8)
240 c
= (c
<< 3) + (s
[i
++] - '0');
256 atf_check::run_status_check(const atf::check::check_result
& r
)
262 std::cerr
<< "Fail: program did not exit cleanly" << std::endl
;
265 const int& status
= r
.exitcode();
267 if (m_status_check
== sc_equal
) {
268 if (m_status_arg
!= status
) {
269 std::cerr
<< "Fail: incorrect exit status: "
270 << status
<< ", expected: "
271 << m_status_arg
<< std::endl
;
274 } else if (m_status_check
== sc_not_equal
) {
275 if (m_status_arg
== status
) {
276 std::cerr
<< "Fail: incorrect exit status: "
277 << status
<< ", expected: "
278 << "anything other" << std::endl
;
284 if (retval
== false) {
285 std::cerr
<< "stdout:" << std::endl
;
286 print_file(r
.stdout_path());
287 std::cerr
<< std::endl
;
289 std::cerr
<< "stderr:" << std::endl
;
290 print_file(r
.stderr_path());
291 std::cerr
<< std::endl
;
298 atf_check::run_output_check(const atf::check::check_result
& r
,
299 const std::string
& stdxxx
)
302 atf::fs::path
path("/");
304 output_check_t check
= m_stdout_check
;
306 if (stdxxx
== "stdout") {
307 path
= r
.stdout_path();
308 arg
= m_stdout_arg
.c_str();
309 check
= m_stdout_check
;
310 } else if (stdxxx
== "stderr") {
311 path
= r
.stderr_path();
312 arg
= m_stderr_arg
.c_str();
313 check
= m_stderr_check
;
317 if (check
== oc_empty
) {
318 if (!file_empty(path
)) {
319 std::cerr
<< "Fail: incorrect " << stdxxx
<< std::endl
;
320 print_diff(atf::fs::path("/dev/null"), path
);
324 } else if (check
== oc_file
) {
325 if (atf::io::cmp(path
, atf::fs::path(arg
)) != 0) {
326 std::cerr
<< "Fail: incorrect " << stdxxx
<< std::endl
;
327 print_diff(atf::fs::path(arg
), path
);
331 } else if (check
== oc_inline
) {
332 atf::fs::path path2
= atf::fs::path(atf::config::get("atf_workdir"))
334 atf::fs::temp_file
temp(path2
);
338 if (atf::io::cmp(path
, temp
.get_path()) != 0) {
339 std::cerr
<< "Fail: incorrect " << stdxxx
<< std::endl
;
340 print_diff(temp
.get_path(), path
);
344 } else if (check
== oc_save
) {
345 std::ifstream
ifs(path
.c_str(), std::fstream::binary
);
346 ifs
>> std::noskipws
;
347 std::istream_iterator
< char > begin(ifs
), end
;
349 std::ofstream
ofs(arg
.c_str(), std::fstream::binary
350 | std::fstream::trunc
);
351 std::ostream_iterator
<char> obegin(ofs
);
353 std::copy(begin
, end
, obegin
);
360 atf_check::specific_args(void)
366 atf_check::options_set
367 atf_check::specific_options(void)
370 using atf::application::option
;
373 opts
.insert(option('s', "qual:value", "Handle status. Qualifier "
374 "must be one of: ignore eq:<num> ne:<num>"));
375 opts
.insert(option('o', "action:arg", "Handle stdout. Action must be "
376 "one of: empty ignore file:<path> inline:<val> save:<path>"));
377 opts
.insert(option('e', "action:arg", "Handle stderr. Action must be "
378 "one of: empty ignore file:<path> inline:<val> save:<path>"));
379 opts
.insert(option('x', "", "Execute command as a shell command"));
385 atf_check::process_option_s(const std::string
& arg
)
387 using atf::application::usage_error
;
389 if (arg
== "ignore") {
390 m_status_check
= sc_ignore
;
394 std::string::size_type pos
= arg
.find(':');
395 std::string action
= arg
.substr(0, pos
);
398 m_status_check
= sc_equal
;
399 else if (action
== "ne")
400 m_status_check
= sc_not_equal
;
402 throw usage_error("Invalid value for -s option");
404 std::string value
= arg
.substr(pos
+ 1);
407 m_status_arg
= atf::text::to_type
< unsigned int >(value
);
408 } catch (std::runtime_error
) {
409 throw usage_error("Invalid value for -s option; must be an "
410 "integer in range 0-255");
413 if ((m_status_arg
< 0) || (m_status_arg
> 255))
414 throw usage_error("Invalid value for -s option; must be an "
415 "integer in range 0-255");
420 atf_check::process_option_o(const std::string
& arg
)
422 using atf::application::usage_error
;
424 std::string::size_type pos
= arg
.find(':');
425 std::string action
= arg
.substr(0, pos
);
427 if (action
== "empty")
428 m_stdout_check
= oc_empty
;
429 else if (action
== "ignore")
430 m_stdout_check
= oc_ignore
;
431 else if (action
== "save")
432 m_stdout_check
= oc_save
;
433 else if (action
== "inline")
434 m_stdout_check
= oc_inline
;
435 else if (action
== "file")
436 m_stdout_check
= oc_file
;
438 throw usage_error("Invalid value for -o option");
440 m_stdout_arg
= arg
.substr(pos
+ 1);
444 atf_check::process_option_e(const std::string
& arg
)
446 using atf::application::usage_error
;
448 std::string::size_type pos
= arg
.find(':');
449 std::string action
= arg
.substr(0, pos
);
451 if (action
== "empty")
452 m_stderr_check
= oc_empty
;
453 else if (action
== "ignore")
454 m_stderr_check
= oc_ignore
;
455 else if (action
== "save")
456 m_stderr_check
= oc_save
;
457 else if (action
== "inline")
458 m_stderr_check
= oc_inline
;
459 else if (action
== "file")
460 m_stderr_check
= oc_file
;
462 throw usage_error("Invalid value for -e option");
464 m_stderr_arg
= arg
.substr(pos
+ 1);
468 atf_check::process_option(int ch
, const char* arg
)
472 process_option_s(arg
);
476 process_option_o(arg
);
480 process_option_e(arg
);
493 atf_check::main(void)
496 throw atf::application::usage_error("No command specified");
498 int status
= EXIT_FAILURE
;
500 atf::check::check_result r
=
501 m_xflag
? execute_with_shell(m_argv
) : execute(m_argv
);
503 if ((run_status_check(r
) == false) ||
504 (run_output_check(r
, "stderr") == false) ||
505 (run_output_check(r
, "stdout") == false))
506 status
= EXIT_FAILURE
;
508 status
= EXIT_SUCCESS
;
514 main(int argc
, char* const* argv
)
516 return atf_check().run(argc
, argv
);