aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2022-02-10 20:35:15 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2022-02-14 12:47:30 +0300
commit75845ef500cfc02531a7da80d2f41650f5db9bb1 (patch)
treed6ea0a3eaec9e173ed9eb6101f61afe54b6d84d5
parent54b357fc6e0b5ebbde52b2dd26cdb01690633971 (diff)
Add support for reflect clause in tests package manifest value
-rw-r--r--libbpkg/manifest.cxx107
-rw-r--r--libbpkg/manifest.hxx26
-rw-r--r--tests/manifest/testscript107
3 files changed, 227 insertions, 13 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index d9e8299..35c1217 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -3063,14 +3063,117 @@ namespace bpkg
{
using std::string;
+ // We will use the dependency alternatives parser to parse the
+ // `<name> [<version-constraint>] [<reflect-config>]` representation into
+ // a temporary dependency alternatives object. Then we will verify that
+ // the result has no multiple alternatives/dependency packages and
+ // unexpected clauses and will move the required information (dependency,
+ // reflection, etc) into the being created test dependency object.
+
+ // Verify that there is no newline characters to forbid the multi-line
+ // dependency alternatives representation.
+ //
+ if (v.find ('\n') != string::npos)
+ throw invalid_argument ("unexpected <newline>");
+
buildtime = (v[0] == '*');
+
size_t p (v.find_first_not_of (spaces, buildtime ? 1 : 0));
if (p == string::npos)
throw invalid_argument ("no package name specified");
- static_cast<dependency&> (*this) =
- dependency (p == 0 ? move (v) : string (v, p));
+ string::const_iterator b (v.begin () + p);
+ string::const_iterator e (v.end ());
+
+ // Extract the dependency package name in advance, to pass it to the
+ // parser which will use it to verify the reflection variable name.
+ //
+ // Note that multiple packages can only be specified in {} to be accepted
+ // by the parser. In our case such '{' would be interpreted as a part of
+ // the package name and so would fail complaining about an invalid
+ // character. Let's handle this case manually to avoid the potentially
+ // confusing error description.
+ //
+ assert (b != e); // We would fail earlier otherwise.
+
+ if (*b == '{')
+ throw invalid_argument ("only single package allowed");
+
+ package_name dn;
+
+ try
+ {
+ p = v.find_first_of (" \t=<>[(~^", p); // End of the package name.
+ dn = package_name (string (b, p == string::npos ? e : v.begin () + p));
+ }
+ catch (const invalid_argument& e)
+ {
+ throw invalid_argument (string ("invalid package name: ") + e.what ());
+ }
+
+ // Parse the value into the temporary dependency alternatives object.
+ //
+ dependency_alternatives das;
+
+ try
+ {
+ dependency_alternatives_parser p;
+ istringstream is (b == v.begin () ? v : string (b, e));
+ p.parse (dn, is, "" /* name */, 1, 1, das);
+ }
+ catch (const manifest_parsing& e)
+ {
+ throw invalid_argument (e.description);
+ }
+
+ // Verify that there are no multiple dependency alternatives.
+ //
+ assert (!das.empty ()); // Enforced by the parser.
+
+ if (das.size () != 1)
+ throw invalid_argument ("unexpected '|'");
+
+ dependency_alternative& da (das[0]);
+
+ // Verify that there are no multiple dependencies in the alternative.
+ //
+ // The parser can never end up with no dependencies in an alternative and
+ // we already verified that there can't be multiple of them (see above).
+ //
+ assert (da.size () == 1);
+
+ // Verify that there are no unexpected clauses.
+ //
+ // Note that the require, prefer, and accept clauses can only be present
+ // in the multi-line representation and we have already verified that this
+ // is not the case.
+ //
+ if (da.enable)
+ throw invalid_argument ("unexpected enable clause");
+
+ // Move the dependency and the reflect clause into the being created test
+ // dependency object.
+ //
+ static_cast<dependency&> (*this) = move (da[0]);
+
+ reflect = move (da.reflect);
+ }
+
+ string test_dependency::
+ string () const
+ {
+ std::string r (buildtime
+ ? "* " + dependency::string ()
+ : dependency::string ());
+
+ if (reflect)
+ {
+ r += ' ';
+ r += *reflect;
+ }
+
+ return r;
}
// pkg_package_manifest
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index 6428e3a..94206f3 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -1021,27 +1021,31 @@ namespace bpkg
{
test_dependency_type type;
bool buildtime;
+ butl::optional<std::string> reflect;
test_dependency () = default;
test_dependency (package_name n,
test_dependency_type t,
bool b,
- butl::optional<version_constraint> c)
- : dependency {std::move (n), std::move (c)}, type (t), buildtime (b) {}
+ butl::optional<version_constraint> c,
+ butl::optional<std::string> r)
+ : dependency {std::move (n), std::move (c)},
+ type (t),
+ buildtime (b),
+ reflect (std::move (r)) {}
// Parse the test dependency string representation in the
- // `[*] <name> [<version-constraint>]` form. Throw std::invalid_argument
- // if the value is invalid.
+ // `[*] <name> [<version-constraint>] [<reflect-config>]` form. Throw
+ // std::invalid_argument if the value is invalid.
+ //
+ // Verify that the reflect clause, if present, refers to the test
+ // dependency package configuration variable. Note that such variable
+ // value normally signals the dependent package being tested.
//
test_dependency (std::string, test_dependency_type);
- inline std::string
- string () const
- {
- return buildtime
- ? "* " + dependency::string ()
- : dependency::string ();
- }
+ std::string
+ string () const;
};
class LIBBPKG_EXPORT package_manifest
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 100f47d..50e7e51 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -3066,6 +3066,113 @@
license: LGPLv2
tests: bar == $
EOF
+
+ : reflect
+ :
+ {
+ : after-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar == 1.0.0 config.bar.test = foo
+ EOF
+
+ : no-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar config.bar.test = foo
+ EOF
+
+ : invalid-variable
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar config.foo.test = bar
+ EOI
+ stdin:6:8: error: config.bar.* variable assignment expected instead of <buildfile fragment>
+ EOE
+ }
+
+ : newline
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests:\
+ *
+ bar
+ \
+ EOI
+ stdin:7:1: error: unexpected <newline>
+ EOE
+
+ : no-package
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: *
+ EOI
+ stdin:6:8: error: no package name specified
+ EOE
+
+ : multiple-alternatives
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar | baz
+ EOI
+ stdin:6:8: error: unexpected '|'
+ EOE
+
+ : multiple-dependencies
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: {bar baz}
+ EOI
+ stdin:6:8: error: only single package allowed
+ EOE
+
+ : enable
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar ? (windows)
+ EOI
+ stdin:6:8: error: unexpected enable clause
+ EOE
}
}