From 3c57a25a4d6a80301ece82ab33f1394e34f8b873 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 23 Jul 2015 09:59:52 +0200 Subject: Basic test support --- build/cli/module.cxx | 2 +- build/config/operation.cxx | 6 +-- build/cxx/compile.cxx | 2 +- build/cxx/link.cxx | 2 +- build/cxx/module.cxx | 2 +- build/diagnostics | 19 ++++++-- build/diagnostics.cxx | 41 +++++++++++----- build/operation | 15 +++--- build/operation.cxx | 8 ++-- build/test/operation.cxx | 2 +- build/test/rule | 3 ++ build/test/rule.cxx | 111 ++++++++++++++++++++++++++++++++++++++++++- tests/test/simple/buildfile | 5 +- tests/test/simple/driver.cxx | 10 ++++ tests/test/simple/test.std | 1 + 15 files changed, 187 insertions(+), 42 deletions(-) create mode 100644 tests/test/simple/test.std diff --git a/build/cli/module.cxx b/build/cli/module.cxx index c1d0aab..f112cd4 100644 --- a/build/cli/module.cxx +++ b/build/cli/module.cxx @@ -102,7 +102,7 @@ namespace build string ver; try { - process pr (args, false, false, true); + process pr (args, 0, -1); // Open pipe to stdout. ifdstream is (pr.in_ofd); // The version should be the last word on the first line. diff --git a/build/config/operation.cxx b/build/config/operation.cxx index a79ddbf..a602325 100644 --- a/build/config/operation.cxx +++ b/build/config/operation.cxx @@ -211,7 +211,7 @@ namespace build "configure", "configure", "configuring", - "configured", + "is configured", nullptr, // meta-operation pre &configure_operation_pre, &load, // normal load @@ -381,7 +381,7 @@ namespace build targets.insert ( dir::static_type, root.path (), "", nullptr, trace).first); - info << diag_already_done (a, t); + info << diag_done (a, t); } } } @@ -402,7 +402,7 @@ namespace build "disfigure", "disfigure", "disfiguring", - "disfigured", + "is disfigured", nullptr, // meta-operation pre &disfigure_operation_pre, &disfigure_load, diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx index d0e6526..a12650f 100644 --- a/build/cxx/compile.cxx +++ b/build/cxx/compile.cxx @@ -459,7 +459,7 @@ namespace build try { - process pr (args.data (), false, false, true); + process pr (args.data (), 0, -1); // Open pipe to stdout. ifdstream is (pr.in_ofd); size_t skip (skip_count); diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx index b35a596..145a085 100644 --- a/build/cxx/link.cxx +++ b/build/cxx/link.cxx @@ -127,7 +127,7 @@ namespace build string l; try { - process pr (args.data (), false, false, true); + process pr (args.data (), 0, -1); // Open pipe to stdout. ifdstream is (pr.in_ofd); while (!is.eof ()) diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx index 3975ac5..882d4b0 100644 --- a/build/cxx/module.cxx +++ b/build/cxx/module.cxx @@ -110,7 +110,7 @@ namespace build string ver; try { - process pr (args, false, false, true); + process pr (args, 0, -1); // Open pipe to stdout. ifdstream is (pr.in_ofd); bool r (getline (is, ver)); diff --git a/build/diagnostics b/build/diagnostics index 3f05688..2eaaa84 100644 --- a/build/diagnostics +++ b/build/diagnostics @@ -5,6 +5,7 @@ #ifndef BUILD_DIAGNOSTICS #define BUILD_DIAGNOSTICS +#include // size_t #include #include #include @@ -52,7 +53,7 @@ namespace build // Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}", - // and "updating exe{foo} already configured". + // and "updating exe{foo} is configured". // struct action; class target; @@ -64,17 +65,25 @@ namespace build diag_doing (const action&, const target&); std::string - diag_already_done (const action&, const target&); + diag_done (const action&, const target&); - // Print process commmand line. + // Print process commmand line. If the number of elements is specified + // (or the second version is used), then it will print the piped multi- + // process command line, if present. In this case, the expected format + // is as follows: + // + // name1 arg arg ... nullptr + // name2 arg arg ... nullptr + // ... + // nameN arg arg ... nullptr nullptr // void - print_process (const char* const* args); + print_process (const char* const* args, std::size_t n = 0); inline void print_process (const cstrings& args) { - print_process (args.data ()); + print_process (args.data (), args.size ()); } // Trace verbosity level. diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx index f6d81bf..f12119c 100644 --- a/build/diagnostics.cxx +++ b/build/diagnostics.cxx @@ -128,44 +128,59 @@ namespace build } string - diag_already_done (const action&, const target& t) + diag_done (const action&, const target& t) { const meta_operation_info& mi (*current_mif); const operation_info& oi (*current_oif); ostringstream os; - // perform(update(x)) -> "x is already up to date" - // configure(update(x)) -> "updating x is already configured" + // perform(update(x)) -> "x is up to date" + // configure(update(x)) -> "updating x is configured" // - if (mi.name_already_done.empty ()) + if (mi.name_done.empty ()) { os << t; - if (!oi.name_already_done.empty ()) - os << " is already " << oi.name_already_done; + if (!oi.name_done.empty ()) + os << " " << oi.name_done; } else { if (!oi.name_doing.empty ()) os << oi.name_doing << ' '; - os << t << " is already " << mi.name_already_done; + os << t << " " << mi.name_done; } return os.str (); } void - print_process (const char* const* args) + print_process (const char* const* args, size_t n) { diag_record r (text); - for (const char* const* p (args); *p != nullptr; p++) - r << (p != args ? " " : "") - << (**p == '\0' ? "\"" : "") // Quote empty arguments. - << *p - << (**p == '\0' ? "\"" : ""); + size_t m (0); + const char* const* p (args); + do + { + if (m != 0) + r << " |"; // Trailing space will be added inside the loop. + + for (m++; *p != nullptr; p++, m++) + r << (p != args ? " " : "") + << (**p == '\0' ? "\"" : "") // Quote empty arguments. + << *p + << (**p == '\0' ? "\"" : ""); + + if (m < n) // Can we examine the next element? + { + p++; + m++; + } + + } while (*p != nullptr); } // Trace verbosity level. diff --git a/build/operation b/build/operation index 6fde9fd..55f901d 100644 --- a/build/operation +++ b/build/operation @@ -56,7 +56,7 @@ namespace build // If this is not a nested operation, then outer should be 0. // - action (meta_operation_id m, operation_id inner, operation_id outer) + action (meta_operation_id m, operation_id inner, operation_id outer = 0) : inner_id ((m << 4) | inner), outer_id (outer == 0 ? 0 : (m << 4) | outer) {} @@ -91,16 +91,13 @@ namespace build (x.outer_id != y.outer_id && y.outer_id != 0); } - /* + // Note that these ignore the outer operation. + // inline bool - operator== (action x, action y) - { - return x.inner_id == y.inner_id && x.outer_id == y.outer_id; - } + operator== (action x, action y) {return x.inner_id == y.inner_id;} inline bool operator!= (action x, action y) {return !(x == y);} - */ std::ostream& operator<< (std::ostream&, action); @@ -179,7 +176,7 @@ namespace build // const std::string name_do; // E.g., [to] 'configure'. const std::string name_doing; // E.g., [while] 'configuring'. - const std::string name_already_done; // E.g., [already] 'configured'. + const std::string name_done; // E.g., 'is configured'. // If operation_pre() is not NULL, then it may translate default_id // (and only default_id) to some other operation. If not translated, @@ -261,7 +258,7 @@ namespace build // const std::string name_do; // E.g., [to] 'update'. const std::string name_doing; // E.g., [while] 'updating'. - const std::string name_already_done; // E.g., [already] 'up to date'. + const std::string name_done; // E.g., 'is up to date'. const execution_mode mode; diff --git a/build/operation.cxx b/build/operation.cxx index 61111fe..9bfa8d2 100644 --- a/build/operation.cxx +++ b/build/operation.cxx @@ -139,7 +139,7 @@ namespace build // Be quiet in pre/post operations. // if (a.outer_operation () == 0) - info << diag_already_done (a, t); + info << diag_done (a, t); break; } case target_state::changed: @@ -176,7 +176,7 @@ namespace build // Be quiet in pre/post operations. // if (a.outer_operation () == 0) - info << diag_already_done (a, t); + info << diag_done (a, t); break; } case target_state::unknown: // Assume something was done to it. @@ -221,7 +221,7 @@ namespace build "update", "update", "updating", - "up to date", + "is up to date", execution_mode::first, nullptr, nullptr @@ -231,7 +231,7 @@ namespace build "clean", "clean", "cleaning", - "clean", + "is clean", execution_mode::last, nullptr, nullptr 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 +#include +#include + #include #include #include #include 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 (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 (); + } } } } diff --git a/tests/test/simple/buildfile b/tests/test/simple/buildfile index 6113968..518b672 100644 --- a/tests/test/simple/buildfile +++ b/tests/test/simple/buildfile @@ -4,7 +4,10 @@ hxx.ext = hxx cxx.ext = cxx lib{utility}: cxx{utility} -exe{driver}: cxx{driver} lib{utility} +exe{driver}: cxx{driver} #lib{utility} exe{driver}: test = true +test.exe = false + .: lib{utility} exe{driver} +#.: exe{driver} diff --git a/tests/test/simple/driver.cxx b/tests/test/simple/driver.cxx index 70b4146..3753821 100644 --- a/tests/test/simple/driver.cxx +++ b/tests/test/simple/driver.cxx @@ -1,4 +1,14 @@ +#include +#include + +using namespace std; + int main () { + cerr << "test is running (stderr)" << endl; + //assert (false); + cout << "test is running (stdout)" << endl; + return 0; + //return 1; } diff --git a/tests/test/simple/test.std b/tests/test/simple/test.std new file mode 100644 index 0000000..5d63fab --- /dev/null +++ b/tests/test/simple/test.std @@ -0,0 +1 @@ +test is running (stdout) -- cgit v1.1