diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-23 09:59:52 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2015-07-23 09:59:52 +0200 |
commit | 3c57a25a4d6a80301ece82ab33f1394e34f8b873 (patch) | |
tree | 2ff64ebcfab74f90e81fdd2963fb653630d6d17b /build/test/rule.cxx | |
parent | 34e5a2da18f76c7d7de79a5c12b0e85ee89c4095 (diff) |
Basic test support
Diffstat (limited to 'build/test/rule.cxx')
-rw-r--r-- | build/test/rule.cxx | 111 |
1 files changed, 109 insertions, 2 deletions
diff --git a/build/test/rule.cxx b/build/test/rule.cxx index 3fd3a16..91ddb4f 100644 --- a/build/test/rule.cxx +++ b/build/test/rule.cxx @@ -4,12 +4,16 @@ #include <build/test/rule> +#include <butl/process> +#include <butl/fdstream> + #include <build/scope> #include <build/target> #include <build/algorithm> #include <build/diagnostics> using namespace std; +using namespace butl; namespace build { @@ -38,12 +42,115 @@ namespace build } recipe rule:: - apply (action, target&, const match_result& mr) const + apply (action a, target&, const match_result& mr) const { if (!mr.value) // Not a test. return noop_recipe; - return noop_recipe; //@@ TMP + return a == action (perform_id, test_id) + ? &perform_test + : noop_recipe; // Don't do anything for other meta-operations. + } + + // The format of args shall be: + // + // name1 arg arg ... nullptr + // name2 arg arg ... nullptr + // ... + // nameN arg arg ... nullptr nullptr + // + static bool + pipe_process (char const** args, process* prev = nullptr) + { + // Find the next process, if any. + // + char const** next (args); + for (next++; *next != nullptr; next++) ; + next++; + + // Redirect stdout to a pipe unless we are last, in which + // case redirect it to stderr. + // + int out (*next == nullptr ? 2 : -1); + bool pr, wr; + + if (prev == nullptr) + { + // First process. + // + process p (args, 0, out); + pr = *next == nullptr || pipe_process (next, &p); + wr = p.wait (); + } + else + { + // Next process. + // + process p (args, *prev, out); + pr = *next == nullptr || pipe_process (next, &p); + wr = p.wait (); + } + + if (!wr) + { + // @@ Needs to go into the same diag record. + // + error << "non-zero exit status from:"; + print_process (args); + } + + return pr && wr; + } + + + target_state rule:: + perform_test (action, target& t) + { + // @@ Would be nice to print what signal/core was dumped. + // + + // @@ Doesn't have to be a file target if we have test.cmd. + // + file& ft (static_cast<file&> (t)); + assert (!ft.path ().empty ()); // Should have been assigned by update. + + cstrings args {ft.path ().string ().c_str (), nullptr}; + + args.push_back ("diff"); + args.push_back ("-u"); + args.push_back ("test.std"); + args.push_back ("-"); + args.push_back (nullptr); + + args.push_back (nullptr); // Second. + + if (verb) + print_process (args); + else + text << "test " << t; + + try + { + if (!pipe_process (args.data ())) + { + //@@ Need to use the same diag record. + // + error << "failed test:"; + print_process (args); + throw failed (); + } + + return target_state::changed; + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } } } } |