From 8205a652a4616aea84f24ff31235ea9941f47db6 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 7 Mar 2017 10:24:02 +0200 Subject: Specify config.test.output variable --- build2/name | 4 +++ build2/parser.cxx | 1 + build2/test/common | 8 ++++++ build2/test/init.cxx | 35 +++++++++++++++++++++++ build2/variable | 33 ++++++++++++++++------ build2/variable.cxx | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ build2/variable.ixx | 22 +++++++++++++-- doc/testscript.cli | 29 +++++++++++++++++++ 8 files changed, 199 insertions(+), 11 deletions(-) diff --git a/build2/name b/build2/name index 2833a2b..ccceebe 100644 --- a/build2/name +++ b/build2/name @@ -147,6 +147,10 @@ namespace build2 inline ostream& operator<< (ostream& os, const names& ns) {return os << names_view (ns);} + + // Pair of names. + // + using name_pair = pair; } #include diff --git a/build2/parser.cxx b/build2/parser.cxx index 412b557..20d7bd2 100644 --- a/build2/parser.cxx +++ b/build2/parser.cxx @@ -1556,6 +1556,7 @@ namespace build2 n == "paths" ? ptr (value_traits::value_type) : n == "dir_paths" ? ptr (value_traits::value_type) : n == "names" ? ptr (value_traits>::value_type) : + n == "name_pair" ? ptr (value_traits::value_type) : nullptr; } diff --git a/build2/test/common b/build2/test/common index 44c7bf8..8e4235b 100644 --- a/build2/test/common +++ b/build2/test/common @@ -14,8 +14,16 @@ namespace build2 { namespace test { + enum class output_before {fail, warn, clean}; + enum class output_after {clean, keep}; + struct common { + // The config.test.output values. + // + output_before before; + output_after after; + // The config.test query interface. // const names* test_ = nullptr; // The config.test value if any. diff --git a/build2/test/init.cxx b/build2/test/init.cxx index fd8db99..a367206 100644 --- a/build2/test/init.cxx +++ b/build2/test/init.cxx @@ -48,6 +48,11 @@ namespace build2 // vp.insert ("config.test", true); + // Test working directory before/after cleanup (see Testscript spec for + // semantics). + // + vp.insert ("config.test.output", true); + // Note: none are overridable. // // The test variable is a name which can be a path (with the @@ -130,6 +135,36 @@ namespace build2 m.root_ = s; } + // config.test.output + // + if (lookup l = config::omitted (rs, "config.test.output").first) + { + const name_pair& p (cast (l)); + + // If second half is empty, then first is the after value. + // + const name& a (p.second.empty () ? p.first : p.second); // after + const name& b (p.second.empty () ? p.second : p.first); // before + + // Parse and validate. + // + if (!b.simple ()) + fail << "invalid config.test.output before value '" << b << "'"; + + if (!a.simple ()) + fail << "invalid config.test.output after value '" << a << "'"; + + if (a.value == "clean") m.after = output_after::clean; + else if (a.value == "keep") m.after = output_after::keep; + else fail << "invalid config.test.output after value '" << a << "'"; + + if (b.value == "fail") m.before = output_before::fail; + else if (b.value == "warn") m.before = output_before::warn; + else if (b.value == "clean") m.before = output_before::clean; + else if (b.value == "") m.before = output_before::clean; + else fail << "invalid config.test.output before value '" << b << "'"; + } + //@@ TODO: Need ability to specify extra diff options (e.g., // --strip-trailing-cr, now hardcoded). // diff --git a/build2/variable b/build2/variable index 690c45d..ea1912a 100644 --- a/build2/variable +++ b/build2/variable @@ -233,13 +233,7 @@ namespace build2 // specialization below). Types that don't fit will have to be handled // with an extra dynamic allocation. // - // std::max() is not constexpr on GCC 4.9. - // - static constexpr size_t size_ = - sizeof (names) > sizeof (target_triplet) - ? sizeof (names) - : sizeof (target_triplet); - + static constexpr size_t size_ = sizeof (name_pair); std::aligned_storage::type data_; // Make sure we have sufficient storage for untyped values. @@ -653,7 +647,7 @@ namespace build2 static name convert (name&&, name*); static void assign (value&, name&&); static name reverse (const name& x) {return x;} - static int compare (const name&, const name&); + static int compare (const name& l, const name& r) {return l.compare (r);} static bool empty (const name& x) {return x.empty ();} static const bool empty_value = true; @@ -661,6 +655,29 @@ namespace build2 static const build2::value_type value_type; }; + // name_pair + // + // An empty first or second half of a pair is treated as unspecified (this + // way it can be usage-specific whether a single value is first or second + // half of a pair). If both are empty then this is an empty value (and not a + // pair of two empties). + // + template <> + struct value_traits + { + static_assert (sizeof (name_pair) <= value::size_, "insufficient space"); + + static name_pair convert (name&&, name*); + static void assign (value&, name_pair&&); + static int compare (const name_pair&, const name_pair&); + static bool empty (const name_pair& x) { + return x.first.empty () && x.second.empty ();} + + static const bool empty_value = true; + static const char* const type_name; + static const build2::value_type value_type; + }; + // process_path // // Note that instances that we store always have non-empty recall and diff --git a/build2/variable.cxx b/build2/variable.cxx index 09790b4..f282dc5 100644 --- a/build2/variable.cxx +++ b/build2/variable.cxx @@ -723,6 +723,84 @@ namespace build2 &default_empty }; + // name_pair + // + name_pair value_traits:: + convert (name&& n, name* r) + { + n.pair = '\0'; // Keep "unpaired" in case r is empty. + return name_pair (move (n), r != nullptr ? move (*r) : name ()); + } + + void + name_pair_assign (value& v, names&& ns, const variable* var) + { + using traits = value_traits; + + size_t n (ns.size ()); + + if (n <= 2) + { + try + { + traits::assign ( + v, + (n == 0 + ? name_pair () + : traits::convert (move (ns[0]), n == 2 ? &ns[1] : nullptr))); + return; + } + catch (const invalid_argument&) {} // Fall through. + } + + diag_record dr (fail); + dr << "invalid name_pair value '" << ns << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + } + + static names_view + name_pair_reverse (const value& v, names& ns) + { + const name_pair& p (v.as ()); + const name& f (p.first); + const name& s (p.second); + + if (f.empty () && s.empty ()) + return names_view (nullptr, 0); + + if (f.empty ()) + return names_view (&s, 1); + + if (s.empty ()) + return names_view (&f, 1); + + ns.push_back (f); + ns.back ().pair = '@'; + ns.push_back (s); + return ns; + } + + const char* const value_traits::type_name = "name_pair"; + + const value_type value_traits::value_type + { + type_name, + sizeof (name_pair), + nullptr, // No base. + &default_dtor, + &default_copy_ctor, + &default_copy_assign, + &name_pair_assign, + nullptr, // Append not supported. + nullptr, // Prepend not supported. + &name_pair_reverse, + nullptr, // No cast (cast data_ directly). + &simple_compare, + &default_empty + }; + // process_path value // process_path value_traits:: diff --git a/build2/variable.ixx b/build2/variable.ixx index dfe5c84..8656a04 100644 --- a/build2/variable.ixx +++ b/build2/variable.ixx @@ -490,10 +490,26 @@ namespace build2 new (&v.data_) name (move (x)); } - inline int value_traits:: - compare (const name& l, const name& r) + // name_pair value + // + inline void value_traits:: + assign (value& v, name_pair&& x) { - return l.compare (r); + if (v) + v.as () = move (x); + else + new (&v.data_) name_pair (move (x)); + } + + inline int value_traits:: + compare (const name_pair& x, const name_pair& y) + { + int r (x.first.compare (y.first)); + + if (r == 0) + r = x.second.compare (y.second); + + return r; } // process_path value diff --git a/doc/testscript.cli b/doc/testscript.cli index 5b4e8ee..692ff57 100644 --- a/doc/testscript.cli +++ b/doc/testscript.cli @@ -1047,6 +1047,35 @@ $ b test config.test=basics/foo # Only foo $ b test 'config.test=basics/foo basics/fox/bar' # Only foo and bar \ +The script working directory may exist before the execution (for example, +because of a failed previous run) or it may be desirable not to clean it up +after the execution (for example, to examine test setup, output, etc). Before +the execution the default behavior is to warn and then automatically remove +the working directory if it exists. After the execution the default behavior +is to perform all the cleanups and teardowns and then remove the working +directory failing if it is not empty. This default behaviors can, however, be +overridden with the \c{config.test.output} variable. + +The \c{config.test.output} variable contains a pair of values with the first +signifying the \i{before} behavior and the second \- \i{after}. The valid +\i{before} values are \c{fail} (fail if the directory exists), \c{warn} +(warn if the directory exists then remove), \c{clean} (silently remove +the existing directory). The valid \i{after} values are \c{clean} (remove +the directory failing if it is not empty) and \c{keep} (do not run cleanups +and teardowns and do not remove the working directory). The default behavior +is thus equivalent to specifying the \c{warn@clean} pair. + +If only a single value is specified in \c{config.test.output} then it is +assumed to be the \i{after} value and the \i{before} value is assumed to +be \c{clean}. In other words: + +\ +$ b test config.test.output=clean # config.test.output=clean@clean +$ b test config.test.output=keep # config.test.output=clean@keep +\ + +Note also that selecting the \c{keep} behavior may result in some test +failures due to unexpected output to go undetected. \h1#lexical|Lexical Structure| -- cgit v1.1