aboutsummaryrefslogtreecommitdiff
path: root/build/test
diff options
context:
space:
mode:
Diffstat (limited to 'build/test')
-rw-r--r--build/test/operation.cxx2
-rw-r--r--build/test/rule3
-rw-r--r--build/test/rule.cxx111
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 ();
+ }
}
}
}