diff options
Diffstat (limited to 'build/test')
-rw-r--r-- | build/test/operation.cxx | 2 | ||||
-rw-r--r-- | build/test/rule | 3 | ||||
-rw-r--r-- | build/test/rule.cxx | 111 |
3 files changed, 113 insertions, 3 deletions
diff --git a/build/test/operation.cxx b/build/test/operation.cxx index 75e3e80..da98e7d 100644 --- a/build/test/operation.cxx +++ b/build/test/operation.cxx @@ -25,7 +25,7 @@ namespace build "test", "test", "testing", - "tested", + "has nothing to test", // We cannot "be tested". execution_mode::first, &test_pre, nullptr diff --git a/build/test/rule b/build/test/rule index db10606..259b72b 100644 --- a/build/test/rule +++ b/build/test/rule @@ -23,6 +23,9 @@ namespace build virtual recipe apply (action, target&, const match_result&) const; + static target_state + perform_test (action, target&); + private: operation_id test_id; }; 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 (); + } } } } |