aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-12-29 03:36:05 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-01-05 15:30:49 +0300
commite1837dc7da78055ab3e355c3e941a7415146c1b8 (patch)
treef11eab4c14ea9f96a608c4bc1166ce3ba0927dcd
parent30b7cad6fb2fc6a5d01cdc8e68c4f37f5da251b9 (diff)
Print signal/core dump like shell/make
-rw-r--r--build2/test/script/runner.cxx64
-rw-r--r--tests/test/script/runner/driver.cxx96
-rw-r--r--tests/test/script/runner/status.test15
3 files changed, 142 insertions, 33 deletions
diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx
index 6eb2447..55ff57a 100644
--- a/build2/test/script/runner.cxx
+++ b/build2/test/script/runner.cxx
@@ -733,7 +733,7 @@ namespace build2
}
}
- optional<process::status_type> status;
+ optional<process_exit> exit;
builtin* b (builtins.find (c.program.string ()));
if (b != nullptr)
@@ -745,7 +745,7 @@ namespace build2
future<uint8_t> f (
(*b) (sp, c.arguments, move (ifd), move (ofd), move (efd)));
- status = f.get ();
+ exit = process_exit (f.get ());
}
catch (const system_error& e)
{
@@ -784,48 +784,69 @@ namespace build2
efd.reset ();
pr.wait ();
- status = move (pr.status);
+ exit = move (pr.exit);
}
catch (const process_error& e)
{
error (ll) << "unable to execute " << pp << ": " << e.what ();
if (e.child ())
- exit (1);
+ std::exit (1);
throw failed ();
}
}
+ assert (exit);
+
const path& p (c.program);
- // If there is no correct exit status by whatever reason then print the
+ // If there is no correct exit code by whatever reason then print the
// proper diagnostics, dump stderr (if cached and not too large) and
// fail.
//
- // Comparison *status >= 0 causes "always true" warning on Windows
- // where process::status_type is defined as uint32_t.
+ bool valid (exit->normal ());
+
+ // On Windows the exit code can be out of the valid codes range being
+ // defined as uint16_t.
//
- bool valid_status (status && *status < 256 && *status + 1 > 0);
+#ifdef _WIN32
+ if (valid)
+ valid = exit->code () < 256;
+#endif
+
bool eq (c.exit.comparison == exit_comparison::eq);
- bool correct_status (valid_status && eq == (*status == c.exit.status));
+ bool correct (valid && eq == (exit->code () == c.exit.status));
- if (!correct_status)
+ if (!correct)
{
// Fail with a proper diagnostics.
//
diag_record d (fail (ll));
- if (!status)
- d << p << " terminated abnormally";
- else if (!valid_status)
- d << p << " exit status " << *status << " is invalid" <<
- info << "must be an unsigned integer < 256";
- else if (!correct_status)
- d << p << " exit status " << *status << (eq ? " != " : " == ")
- << static_cast<uint16_t> (c.exit.status);
+ if (!exit->normal ())
+ {
+ d << p << " terminated abnormally" <<
+ info << exit->description ();
+
+#ifndef _WIN32
+ if (exit->core ())
+ d << " (core dumped)";
+#endif
+ }
else
- assert (false);
+ {
+ uint16_t ec (exit->code ()); // Make sure is printed as integer.
+
+ if (!valid)
+ d << p << " exit status " << ec << " is invalid" <<
+ info << "must be an unsigned integer < 256";
+ else if (!correct)
+ d << p << " exit status " << ec << (eq ? " != " : " == ")
+ << static_cast<uint16_t> (c.exit.status);
+ else
+ assert (false);
+ }
if (non_empty (esp, ll))
d << info << "stderr: " << esp;
@@ -836,12 +857,13 @@ namespace build2
if (non_empty (isp, ll))
d << info << "stdin: " << isp;
- // Dump cached stderr.
+ // Print cached stderr.
//
print_file (d, esp, ll);
}
- // Check if the standard outputs match expectations.
+ // Exit code is correct. Check if the standard outputs match the
+ // expectations.
//
check_output (p, osp, isp, c.out, ll, sp, "stdout");
check_output (p, esp, isp, c.err, ll, sp, "stderr");
diff --git a/tests/test/script/runner/driver.cxx b/tests/test/script/runner/driver.cxx
index a82b083..6a3698f 100644
--- a/tests/test/script/runner/driver.cxx
+++ b/tests/test/script/runner/driver.cxx
@@ -4,6 +4,7 @@
#include <limits> // numeric_limits
#include <string>
+#include <cstdlib> // abort()
#include <cassert>
#include <ostream> // endl, *bit
#include <istream> // istream::traits_type::eof()
@@ -11,20 +12,65 @@
#include <exception>
#include <butl/path>
+#include <butl/optional>
#include <butl/fdstream>
#include <butl/filesystem>
using namespace std;
using namespace butl;
+// Call itself recursively causing stack overflow. Parameterized to avoid
+// "stack overflow" warning.
+//
+static void
+stack_overflow (bool overflow)
+{
+ if (overflow)
+ stack_overflow (true);
+}
+
int
main (int argc, char* argv[])
{
- // Usage: driver [-i <int>] [-s <int>] (-o <string>)* (-e <string>)*
- // (-f <file>)* (-d <dir>)*
+ // Usage: driver [-i <int>] (-o <string>)* (-e <string>)* (-f <file>)*
+ // (-d <dir>)* [(-t (a|m|s|z)) | (-s <int>)]
+ //
+ // Execute actions specified by -i, -o, -e, -f, -d options in the order as
+ // they appear on the command line. After that terminate abnormally if -t
+ // option is provided, otherwise exit normally with the status specified by
+ // -s option (0 by default).
+ //
+ // -i <fd>
+ // Forward STDIN data to the standard stream denoted by the file
+ // descriptor. Read and discard if 0.
+ //
+ // -o <string>
+ // Print the line to STDOUT.
+ //
+ // -e <string>
+ // Print the line to STDERR.
+ //
+ // -f <path>
+ // Create an empty file with the path specified.
+ //
+ // -d <path>
+ // Create a directory with the path specified. Create parent directories
+ // if required.
+ //
+ // -t <method>
+ // Abnormally terminate itself using one of the following methods:
+ //
+ // a - call abort()
+ // m - dereference null-pointer
+ // s - cause stack overflow using infinite function call recursion
+ // z - divide integer by zero
+ //
+ // -s <int>
+ // Exit normally with the status specified. The default status is 0.
//
- int status (256);
int ifd (3);
+ optional<int> status;
+ char aterm ('\0'); // Abnormal termination method.
cout.exceptions (ostream::failbit | ostream::badbit);
cerr.exceptions (ostream::failbit | ostream::badbit);
@@ -64,7 +110,11 @@ main (int argc, char* argv[])
if (ifd == 0)
cin.ignore (numeric_limits<streamsize>::max ());
else if (cin.peek () != istream::traits_type::eof ())
- (ifd == 1 ? cout : cerr) << cin.rdbuf ();
+ {
+ ostream& o (ifd == 1 ? cout : cerr);
+ o << cin.rdbuf ();
+ o.flush ();
+ }
}
else if (o == "-o")
{
@@ -74,13 +124,6 @@ main (int argc, char* argv[])
{
cerr << v << endl;
}
- else if (o == "-s")
- {
- assert (status == 256); // Make sure is not set yet.
-
- status = toi (v);
- assert (status >= 0 && status < 256);
- }
else if (o == "-f")
{
ofdstream os (v);
@@ -90,9 +133,38 @@ main (int argc, char* argv[])
{
try_mkdir_p (dir_path (v));
}
+ else if (o == "-t")
+ {
+ assert (aterm == '\0' && !status); // Make sure exit method is not set.
+ assert (v.size () == 1 && v.find_first_of ("amsz") != string::npos);
+ aterm = v[0];
+ }
+ else if (o == "-s")
+ {
+ assert (!status && aterm == '\0'); // Make sure exit method is not set.
+ status = toi (v);
+ }
else
assert (false);
}
- return status == 256 ? 0 : status;
+ switch (aterm)
+ {
+ case 'a': abort (); break;
+ case 'm':
+ {
+ int* p (nullptr);
+ *p = 0;
+ break;
+ }
+ case 's': stack_overflow (true); break;
+ case 'z':
+ {
+ int z (0);
+ z /= z;
+ break;
+ }
+ }
+
+ return status ? *status : 0;
}
diff --git a/tests/test/script/runner/status.test b/tests/test/script/runner/status.test
index fc0edff..f12cd58 100644
--- a/tests/test/script/runner/status.test
+++ b/tests/test/script/runner/status.test
@@ -42,3 +42,18 @@ $b 2>>~%EOE% != 0
% info: stderr: test[/\\]1[/\\]stderr%
Error
EOE
+
+#\
+: segmentation-fault
+:
+: Can pop up dialog boxes on Windows or produce coredump on POSIX. Note that
+: under Wine some extra info is printed to STDOUT.
+:
+$c <'$* -t m';
+$b 2>>~%EOE% != 0
+%wine: .+%?
+%testscript:1: error: \.\.[/\\]\.\.[/\\]\.\.[/\\]driver(\.exe)? terminated abnormally%
+% info: .+%
+% info: stdout: test\\1\\stdout%?
+EOE
+#\