From 30b7cad6fb2fc6a5d01cdc8e68c4f37f5da251b9 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 26 Dec 2016 00:41:37 +0300 Subject: Save diff output for {stdout,stderr}.diff --- build2/test/script/runner.cxx | 133 ++++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 44 deletions(-) (limited to 'build2') diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx index 08d358c..6eb2447 100644 --- a/build2/test/script/runner.cxx +++ b/build2/test/script/runner.cxx @@ -5,7 +5,7 @@ #include #include -#include // cerr +#include // streamsize #include // fdopen_mode, fdnull(), fddup() @@ -45,6 +45,57 @@ namespace build2 } } + // If the file exists, not empty and not larger than 4KB print it to the + // diag record. The file content goes from the new line and is not + // indented. + // + static void + print_file (diag_record& d, const path& p, const location& ll) + { + if (exists (p)) + { + try + { + ifdstream is (p, ifdstream::in, ifdstream::badbit); + + if (is.peek () != ifdstream::traits_type::eof ()) + { + char buf[4096 + 1]; // Extra byte is for terminating '\0'. + + // Note that the string is always '\0'-terminated with a maximum + // sizeof (buf) - 1 bytes read. + // + is.getline (buf, sizeof (buf), '\0'); + + // Print if the file fits 4KB-size buffer. Note that if it + // doesn't the failbit is set. + // + if (is.eof ()) + { + // Suppress the trailing newline character as the diag record + // adds it's own one when flush. + // + streamsize n (is.gcount ()); + assert (n > 0); + + // Note that if the file contains '\0' it will also be counted + // by gcount(). But even in the worst case we will stay in the + // buffer boundaries (and so not crash). + // + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + + d << "\n" << buf; + } + } + } + catch (const io_error& e) + { + fail (ll) << "unable to read " << p << ": " << e.what (); + } + } + } + // Check if the test command output matches the expected result (redirect // value). Noop for redirect types other than none, here_*. // @@ -198,39 +249,44 @@ namespace build2 try { - // Diff utility prints the differences to stdout. But for the - // user it is a part of the test failure diagnostics so let's - // redirect stdout to stderr. + // Save diff's stdout to a file for troubleshooting and for the + // optional (if not too large) printing (at the end of + // diagnostics). // - process p (pp, args.data (), 0, 2); + path ep (op + ".diff"); + auto_fd efd; try { - if (p.wait ()) - return; + efd = fdopen (ep, fdopen_mode::out | fdopen_mode::create); + sp.clean ({cleanup_type::always, ep}, true); + } + catch (const io_error& e) + { + fail (ll) << "unable to write " << ep << ": " << e.what (); + } - // Output doesn't match the expected result. - // - diag_record d (error (ll)); - d << pr << " " << what << " doesn't match the expected output"; + // Diff utility prints the differences to stdout. But for the + // user it is a part of the test failure diagnostics so let's + // redirect stdout to stderr. + // + process p (pp, args.data (), 0, 2, efd.get ()); + efd.reset (); - output_info (d, op); - output_info (d, opp, "expected "); - input_info (d); + if (p.wait ()) + return; - // Fall through. - // - } - catch (const io_error&) - { - // Child exit status doesn't matter. Assume the child process - // issued diagnostics. Just wait for the process completion. - // - p.wait (); // Check throw. + // Output doesn't match the expected result. + // + diag_record d (error (ll)); + d << pr << " " << what << " doesn't match the expected output"; - error (ll) << "failed to compare " << what - << " with the expected output"; - } + output_info (d, op); + output_info (d, opp, "expected "); + output_info (d, ep, "", " diff"); + input_info (d); + + print_file (d, ep, ll); // Fall through. // @@ -743,8 +799,9 @@ namespace build2 const path& p (c.program); - // If there is no correct exit status by whatever reason then dump - // stderr (if cached), print the proper diagnostics and fail. + // If there is no correct exit status 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. @@ -755,22 +812,6 @@ namespace build2 if (!correct_status) { - // Dump cached stderr. - // - if (exists (esp)) - { - try - { - ifdstream is (esp); - if (is.peek () != ifdstream::traits_type::eof ()) - cerr << is.rdbuf (); - } - catch (const io_error& e) - { - fail (ll) << "unable to read " << esp << ": " << e.what (); - } - } - // Fail with a proper diagnostics. // diag_record d (fail (ll)); @@ -794,6 +835,10 @@ namespace build2 if (non_empty (isp, ll)) d << info << "stdin: " << isp; + + // Dump cached stderr. + // + print_file (d, esp, ll); } // Check if the standard outputs match expectations. -- cgit v1.1