From e1837dc7da78055ab3e355c3e941a7415146c1b8 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 29 Dec 2016 03:36:05 +0300 Subject: Print signal/core dump like shell/make --- build2/test/script/runner.cxx | 64 ++++++++++++++++-------- tests/test/script/runner/driver.cxx | 96 +++++++++++++++++++++++++++++++----- tests/test/script/runner/status.test | 15 ++++++ 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 status; + optional exit; builtin* b (builtins.find (c.program.string ())); if (b != nullptr) @@ -745,7 +745,7 @@ namespace build2 future 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 (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 (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 // numeric_limits #include +#include // abort() #include #include // endl, *bit #include // istream::traits_type::eof() @@ -11,20 +12,65 @@ #include #include +#include #include #include 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 ] [-s ] (-o )* (-e )* - // (-f )* (-d )* + // Usage: driver [-i ] (-o )* (-e )* (-f )* + // (-d )* [(-t (a|m|s|z)) | (-s )] + // + // 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 + // Forward STDIN data to the standard stream denoted by the file + // descriptor. Read and discard if 0. + // + // -o + // Print the line to STDOUT. + // + // -e + // Print the line to STDERR. + // + // -f + // Create an empty file with the path specified. + // + // -d + // Create a directory with the path specified. Create parent directories + // if required. + // + // -t + // 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 + // Exit normally with the status specified. The default status is 0. // - int status (256); int ifd (3); + optional 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::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 +#\ -- cgit v1.1