aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/test/common.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/test/common.cxx')
-rw-r--r--libbuild2/test/common.cxx220
1 files changed, 220 insertions, 0 deletions
diff --git a/libbuild2/test/common.cxx b/libbuild2/test/common.cxx
new file mode 100644
index 0000000..11c5d90
--- /dev/null
+++ b/libbuild2/test/common.cxx
@@ -0,0 +1,220 @@
+// file : libbuild2/test/common.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/test/common.hxx>
+
+#include <libbuild2/target.hxx>
+#include <libbuild2/algorithm.hxx>
+
+using namespace std;
+
+namespace build2
+{
+ namespace test
+ {
+ // Determine if we have the target (first), id path (second), or both (in
+ // which case we also advance the iterator).
+ //
+ static pair<const name*, const name*>
+ sense (names::const_iterator& i)
+ {
+ const name* tn (nullptr);
+ const name* pn (nullptr);
+
+ if (i->pair)
+ {
+ tn = &*i++;
+ pn = &*i;
+ }
+ else
+ {
+ // If it has a type (exe{hello}) or a directory (basics/), then
+ // we assume it is a target.
+ //
+ (i->typed () || !i->dir.empty () ? tn : pn) = &*i;
+ }
+
+ // Validate the target.
+ //
+ if (tn != nullptr)
+ {
+ if (tn->qualified ())
+ fail << "project-qualified target '" << *tn << " in config.test";
+ }
+
+ // Validate the id path.
+ //
+ if (pn != nullptr)
+ {
+ if (!pn->simple () || pn->empty ())
+ fail << "invalid id path '" << *pn << " in config.test";
+ }
+
+ return make_pair (tn, pn);
+ }
+
+ bool common::
+ pass (const target& a) const
+ {
+ if (test_ == nullptr)
+ return true;
+
+ // We need to "enable" aliases that "lead up" to the targets we are
+ // interested in. So see if any target is in a subdirectory of this
+ // alias.
+ //
+ // If we don't see any targets (e.g., only id paths), then we assume all
+ // targets match and therefore we always pass.
+ //
+ bool r (true);
+
+ // Directory part from root to this alias (the same in src and out).
+ //
+ const dir_path d (a.out_dir ().leaf (root_->out_path ()));
+
+ for (auto i (test_->begin ()); i != test_->end (); ++i)
+ {
+ if (const name* n = sense (i).first)
+ {
+ // Reset result to false if no match (but we have seen a target).
+ //
+ r = n->dir.sub (d);
+
+ // See test() below for details on this special case.
+ //
+ if (!r && !n->typed ())
+ r = d.sub (n->dir);
+
+ if (r)
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ bool common::
+ test (const target& t) const
+ {
+ if (test_ == nullptr)
+ return true;
+
+ // If we don't see any targets (e.g., only id paths), then we assume
+ // all of them match.
+ //
+ bool r (true);
+
+ // Directory part from root to this alias (the same in src and out).
+ //
+ const dir_path d (t.out_dir ().leaf (root_->out_path ()));
+ const target_type& tt (t.type ());
+
+ for (auto i (test_->begin ()); i != test_->end (); ++i)
+ {
+ if (const name* n = sense (i).first)
+ {
+ // Reset result to false if no match (but we have seen a target).
+ //
+
+ // When specifying a directory, for example, config.tests=tests/,
+ // one would intuitively expect that all the tests under it will
+ // run. But that's not what will happen with the below test: while
+ // the dir{tests/} itself will match, any target underneath won't.
+ // So we are going to handle this type if a target specially by
+ // making it match any target in or under it.
+ //
+ // Note that we only do this for tests/, not dir{tests/} since it is
+ // not always the semantics that one wants. Sometimes one may want
+ // to run tests (scripts) just for the tests/ target but not for any
+ // of its prerequisites. So dir{tests/} is a way to disable this
+ // special logic.
+ //
+ // Note: the same code as in test() below.
+ //
+ if (!n->typed ())
+ r = d.sub (n->dir);
+ else
+ // First quickly and cheaply weed out names that cannot possibly
+ // match. Only then search for a target (as if it was a
+ // prerequisite), which can be expensive.
+ //
+ // We cannot specify an src target in config.test since we used
+ // the pair separator for ids. As a result, we search for both
+ // out and src targets.
+ //
+ r =
+ t.name == n->value && // Name matches.
+ tt.name == n->type && // Target type matches.
+ d == n->dir && // Directory matches.
+ (search_existing (*n, *root_) == &t ||
+ search_existing (*n, *root_, d) == &t);
+
+ if (r)
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ bool common::
+ test (const target& t, const path& id) const
+ {
+ if (test_ == nullptr)
+ return true;
+
+ // If we don't see any id paths (e.g., only targets), then we assume
+ // all of them match.
+ //
+ bool r (true);
+
+ // Directory part from root to this alias (the same in src and out).
+ //
+ const dir_path d (t.out_dir ().leaf (root_->out_path ()));
+ const target_type& tt (t.type ());
+
+ for (auto i (test_->begin ()); i != test_->end (); ++i)
+ {
+ auto p (sense (i));
+
+ if (const name* n = p.second)
+ {
+ // If there is a target, check that it matches ours.
+ //
+ if (const name* n = p.first)
+ {
+ // Note: the same code as in test() above.
+ //
+ bool r;
+
+ if (!n->typed ())
+ r = d.sub (n->dir);
+ else
+ r =
+ t.name == n->value &&
+ tt.name == n->type &&
+ d == n->dir &&
+ (search_existing (*n, *root_) == &t ||
+ search_existing (*n, *root_, d) == &t);
+
+ if (!r)
+ continue; // Not our target.
+ }
+
+ // If the id (group) "leads up" to what we want to run or we
+ // (group) lead up to the id, then match.
+ //
+ const path p (n->value);
+
+ // Reset result to false if no match (but we have seen an id path).
+ //
+ if ((r = p.sub (id) || id.sub (p)))
+ break;
+ }
+ }
+
+ return r;
+ }
+ }
+}