From 8ff6314283396a60ae9806a03f1c017bdc3ec4cc Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 4 Mar 2022 17:19:18 +0300 Subject: Add support for --stdout-format to pkg-status command --- bpkg/common.cli | 76 +++++ bpkg/options-types.hxx | 6 + bpkg/pkg-status.cli | 95 +++++- bpkg/pkg-status.cxx | 437 +++++++++++++++++-------- bpkg/types-parsers.cxx | 18 + bpkg/types-parsers.hxx | 10 + tests/pkg-status.testscript | 337 ++++++++++++------- tests/pkg-status/testing/libbar-1.0.0+1.tar.gz | Bin 325 -> 363 bytes tests/pkg-status/testing/libbar-1.1.0.tar.gz | Bin 322 -> 373 bytes tests/pkg-status/testing/libbaz-1.0.0.tar.gz | Bin 0 -> 360 bytes 10 files changed, 719 insertions(+), 260 deletions(-) create mode 100644 tests/pkg-status/testing/libbaz-1.0.0.tar.gz diff --git a/bpkg/common.cli b/bpkg/common.cli index dd0417d..cccf1d3 100644 --- a/bpkg/common.cli +++ b/bpkg/common.cli @@ -80,6 +80,14 @@ namespace bpkg \li|Even more detailed information.||" } + bpkg::stdout_format --stdout-format = bpkg::stdout_format::lines + { + "", + "Representation format to use for printing to \cb{stdout}. Valid values + for this option are \cb{lines} (default) and \cb{json}. See the JSON + OUTPUT section below for details on the \cb{json} format." + } + size_t --jobs|-j { "", @@ -409,4 +417,72 @@ namespace bpkg "Don't load default options files." } }; + + { + "", + " + \h|JSON OUTPUT| + + Commands that support the JSON output specify their formats as a + serialized representation of a C++ \cb{struct} or an array thereof. For + example: + + \ + struct package + { + string name; + }; + + struct configuration + { + uint64_t id; + string path; + optional name; + bool default; + vector packages; + }; + \ + + An example of the serialized JSON representation of \cb{struct} + \cb{configuration}: + + \ + { + \"id\": 1, + \"path\": \"/tmp/hello-gcc\", + \"name\": \"gcc\", + \"default\": true, + \"packages\": [ + { + \"name\": \"hello\" + } + ] + } + \ + + This sections provides details on the overall properties of such formats + and the semantics of the \cb{struct} serialization. + + The order of members in a JSON object is fixed as specified in the + corresponding \cb{struct}. While new members may be added in the + future (and should be ignored by older consumers), the semantics of the + existing members (including whether the top-level entry is an object or + array) may not change. + + An object member is required unless its type is \cb{optional<>}, + \cb{bool}, or \cb{vector<>} (array). For \cb{bool} members absent means + \cb{false}. For \cb{vector<>} members absent means empty. An empty + top-level array is always present. + + For example, the following JSON text is a possible serialization of + the above \cb{struct} \cb{configuration}: + + \ + { + \"id\": 1, + \"path\": \"/tmp/hello-gcc\" + } + \ + " + } } diff --git a/bpkg/options-types.hxx b/bpkg/options-types.hxx index 741e93c..6576060 100644 --- a/bpkg/options-types.hxx +++ b/bpkg/options-types.hxx @@ -21,6 +21,12 @@ namespace bpkg all }; + enum class stdout_format + { + lines, + json + }; + // Qualified options. // // An option that uses this type can have its values qualified using the diff --git a/bpkg/pkg-status.cli b/bpkg/pkg-status.cli index d2eb644..59319bf 100644 --- a/bpkg/pkg-status.cli +++ b/bpkg/pkg-status.cli @@ -32,12 +32,13 @@ namespace bpkg \c{\b{--recursive}|\b{-r}} options, respectively. Note that the status is written to \cb{stdout}, not \cb{stderr}. - The status output format is regular with components separated with - spaces. Each line starts with the package name followed by one of the - status words listed below. Some of them can be optionally followed by - '\cb{,}' (no spaces) and a sub-status word. Lines corresponding to - dependencies from linked configurations will additionally mention the - configuration directory in square brackets after the package name. + The default output format (see the \cb{--stdout-format} common option) is + regular with components separated with spaces. Each line starts with the + package name followed by one of the status words listed below. Some of + them can be optionally followed by '\cb{,}' (no spaces) and a sub-status + word. Lines corresponding to dependencies from linked configurations will + additionally mention the configuration directory in square brackets after + the package name. \dl| @@ -83,11 +84,13 @@ namespace bpkg package may or may not be available from the system and that its version is unknown. - If the package version was specified, then the status word is always - followed by this version (or its revision). + The \cb{fetched}, \cb{unpacked}, \cb{configured}, and \cb{broken} status + words are followed by the version of the package. If the package version + was specified, then the \cb{unknown} status word is also followed by the + version. If the status is \cb{fetched}, \cb{unpacked}, \cb{configured}, or - \cb{broken} and newer versions are available, then this version is + \cb{broken} and newer versions are available, then the package version is followed by the \cb{available} status word and the list of newer versions. To instead see a list of all versions, including the older ones, specify the \c{\b{--old-available}|\b{-o}} option. In this case the @@ -160,6 +163,80 @@ namespace bpkg libbar configured 2.0.0 \ + If the output format is \cb{json}, then the output is a JSON array of + objects which are the serialized representation of the following C++ + \cb{struct} \cb{package_status}: + + \ + struct available_version + { + string version; + bool system; + bool dependency; + }; + + struct package_status + { + string name; + optional configuration; + optional constraint; + string status; + optional sub_status; + optional version; + bool hold_package; + bool hold_version; + vector available_versions; + vector dependencies; + }; + \ + + For example: + + \ + [ + { + \"name\": \"hello\", + \"status\": \"configured\", + \"version\": \"1.0.0\", + \"hold_package\": true, + \"available_versions\": [ + { + \"version\": \"1.0.1\" + }, + { + \"version\": \"2.0.0\" + } + ], + \"dependencies\": [ + { + \"name\": \"libhello\", + \"status\": \"configured\", + \"version\": \"1.0.2\", + } + ] + } + ] + \ + + See the JSON OUTPUT section in \l{bdep-common-options(1)} for details on + the overall properties of this format and the semantics of the + \cb{struct} serialization. + + In \cb{package_status}, the \cb{configuration} member contains the + absolute directory of a linked configuration if this package resides in a + linked configuration. The \cb{constraint} member is present only if the + \cb{--constraint} option is specified. The \cb{version} member is absent + if the \cb{status} member is \cb{unknown} or \cb{available} and no + package version is specified on the command line. If the \cb{sub_status} + member is \cb{system}, then the \cb{version} member can be special + \cb{*}. The \cb{dependencies} member is present only if the + \cb{--immediate|-i} or \cb{--recursive|-r} options are specified. + + In \cb{available_version}, if the \cb{system} member is \cb{true}, then + this version is available from the system, in which case the \cb{version} + member can be special \cb{?} or \cb{*}. If the \cb{dependency} member is + \cb{true}, then this version is only available as a dependency from + prerequisite repositories of other repositories. " } diff --git a/bpkg/pkg-status.cxx b/bpkg/pkg-status.cxx index 2475fa1..93008a8 100644 --- a/bpkg/pkg-status.cxx +++ b/bpkg/pkg-status.cxx @@ -5,6 +5,8 @@ #include // cout +#include + #include #include #include @@ -27,102 +29,147 @@ namespace bpkg }; using packages = vector; - // If recursive or immediate is true, then print status for dependencies - // indented by two spaces. - // - static void - pkg_status (const pkg_status_options& o, - const packages& pkgs, - string& indent, - bool recursive, - bool immediate) + struct available_package_status { - tracer trace ("pkg_status"); + shared_ptr package; - for (const package& p: pkgs) - { - l4 ([&]{trace << "package " << p.name << "; version " << p.version;}); + // Can only be built as a dependency. + // + // True if this package version doesn't belong to the repositories that + // were explicitly added to the configuration and their complements, + // recursively. + // + bool dependency; + }; + + class available_package_statuses: public vector + { + public: + // Empty if the package is not available from the system. Can be `?`. + // + string system_package_version; + + // Can only be built as a dependency. + // + // True if there are no package versions available from the repositories + // that were explicitly added to the configuration and their complements, + // recursively. + // + bool dependency = true; + }; - database& pdb (p.pdb); - database& rdb (p.rdb); + static available_package_statuses + pkg_statuses (const pkg_status_options& o, const package& p) + { + database& rdb (p.rdb); + const shared_ptr& s (p.selected); - // Can't be both. - // - assert (p.version.empty () || !p.constraint); + available_package_statuses r; - const shared_ptr& s (p.selected); + bool known (false); + + shared_ptr root ( + rdb.load ("")); - // Look for available packages. + using query = query; + + query q (query::id.name == p.name); + { + auto qr (rdb.query (q)); + known = !qr.empty (); + r.dependency = (filter_one (root, move (qr)).first == nullptr); + } + + if (known) + { + // If the user specified the version, then only look for that + // specific version (we still do it since there might be other + // revisions). // - // Some of them are only available to upgrade/downgrade as dependencies. + if (!p.version.empty ()) + q = q && compare_version_eq (query::id.version, + canonical_version (p.version), + p.version.revision.has_value (), + false /* iteration */); + + // And if we found an existing package, then only look for versions + // greater than to what already exists unless we were asked to show + // old versions. // - struct apkg - { - shared_ptr package; - bool build; - }; - vector apkgs; - - // A package with this name is known in available packages potentially - // for build. + // Note that for a system wildcard version we will always show all + // available versions (since it is 0). // - bool known (false); - bool build (false); + if (s != nullptr && !o.old_available ()) + q = q && query::id.version > canonical_version (s->version); + + q += order_by_version_desc (query::id.version); + + for (shared_ptr ap: + pointer_result (rdb.query (q))) { - shared_ptr root ( - rdb.load ("")); + bool dependency (filter (root, ap) == nullptr); + r.push_back (available_package_status {move (ap), dependency}); + } - using query = query; + // The idea is that in the future we will try to auto-discover a system + // version. For now we just say "maybe available from the system" even + // if the version was specified by the user. We will later compare it if + // the user did specify the version. + // + if (o.system ()) + r.system_package_version = "?"; - query q (query::id.name == p.name); + // Get rid of stubs. + // + for (auto i (r.begin ()); i != r.end (); ++i) + { + if (i->package->stub ()) { - auto r (rdb.query (q)); - known = !r.empty (); - build = filter_one (root, move (r)).first != nullptr; + // All the rest are stubs so bail out. + // + r.erase (i, r.end ()); + break; } + } + } - if (known) - { - // If the user specified the version, then only look for that - // specific version (we still do it since there might be other - // revisions). - // - if (!p.version.empty ()) - q = q && compare_version_eq (query::id.version, - canonical_version (p.version), - p.version.revision.has_value (), - false /* iteration */); + return r; + } - // And if we found an existing package, then only look for versions - // greater than to what already exists unless we were asked to show - // old versions. - // - // Note that for a system wildcard version we will always show all - // available versions (since it is 0). - // - if (s != nullptr && !o.old_available ()) - q = q && query::id.version > canonical_version (s->version); + static packages + pkg_prerequisites (const shared_ptr& s, database& rdb) + { + packages r; + for (const auto& pair: s->prerequisites) + { + shared_ptr d (pair.first.load ()); + database& db (pair.first.database ()); + const optional& c (pair.second); + r.push_back (package {db, rdb, d->name, version (), move (d), c}); + } + return r; + } - q += order_by_version_desc (query::id.version); + static void + pkg_status_lines (const pkg_status_options& o, + const packages& pkgs, + string& indent, + bool recursive, + bool immediate) + { + tracer trace ("pkg_status_lines"); - // Packages that are in repositories that were explicitly added to - // the configuration and their complements, recursively, are also - // available to build. - // - for (shared_ptr ap: - pointer_result ( - rdb.query (q))) - { - bool build (filter (root, ap)); - apkgs.push_back (apkg {move (ap), build}); - } - } - } + for (const package& p: pkgs) + { + l4 ([&]{trace << "package " << p.name << "; version " << p.version;}); + + available_package_statuses ps (pkg_statuses (o, p)); cout << indent; // Selected. // + const shared_ptr& s (p.selected); // Hold package status. // @@ -134,7 +181,7 @@ namespace bpkg // If the package name is selected, then print its exact spelling. // - cout << (s != nullptr ? s->name : p.name) << pdb; + cout << (s != nullptr ? s->name : p.name) << p.pdb; if (o.constraint () && p.constraint) cout << ' ' << *p.constraint; @@ -158,77 +205,188 @@ namespace bpkg // Available. // - bool available (false); - if (known) + if (!ps.empty () || !ps.system_package_version.empty ()) { - // Available from the system. - // - // The idea is that in the future we will try to auto-discover a - // system version and then print that. For now we just say "maybe - // available from the system" even if the version was specified by - // the user. We will later compare it if the user did specify the - // version. - // - string sys; - if (o.system ()) + cout << (s != nullptr ? " " : "") << "available"; + + for (const available_package_status& a: ps) { - sys = "?"; - available = true; + const version& v (a.package->version); + + // Show the currently selected version in parenthesis. + // + bool cur (s != nullptr && v == s->version); + + cout << ' ' + << (cur ? "(" : a.dependency ? "[" : "") + << v + << (cur ? ")" : a.dependency ? "]" : ""); } - // Get rid of stubs. + if (!ps.system_package_version.empty ()) + cout << ' ' + << (ps.dependency ? "[" : "") + << "sys:" << ps.system_package_version + << (ps.dependency ? "]" : ""); + } + // + // Unknown. + // + else if (s == nullptr) + { + cout << "unknown"; + + // Print the user's version if specified. // - for (auto i (apkgs.begin ()); i != apkgs.end (); ++i) + if (!p.version.empty ()) + cout << ' ' << p.version; + } + + cout << endl; + + if (recursive || immediate) + { + // Collect and recurse. + // + // Let's propagate the repository information source database from the + // dependent to its prerequisites. + // + if (s != nullptr) { - if (i->package->stub ()) + packages dpkgs (pkg_prerequisites (s, p.rdb)); + + if (!dpkgs.empty ()) { - // All the rest are stubs so bail out. - // - apkgs.erase (i, apkgs.end ()); - break; + indent += " "; + pkg_status_lines (o, dpkgs, indent, recursive, false /* immediate */); + indent.resize (indent.size () - 2); } + } + } + } + } + + static void + pkg_status_json (const pkg_status_options& o, + const packages& pkgs, + json::stream_serializer& ss, + bool recursive, + bool immediate) + { + tracer trace ("pkg_status_json"); + + ss.begin_array (); + + for (const package& p: pkgs) + { + l4 ([&]{trace << "package " << p.name << "; version " << p.version;}); + + available_package_statuses ps (pkg_statuses (o, p)); + + const shared_ptr& s (p.selected); + + // Note that we won't check some values for being valid UTF-8 (package + // names, etc), since their characters belong to even stricter character + // sets. + // + ss.begin_object (); + + // If the package name is selected, then print its exact spelling. + // + ss.member ("name", + (s != nullptr ? s->name : p.name).string (), + false /* check */); + + if (!p.pdb.string.empty ()) + ss.member ("configuration", p.pdb.string); + + if (o.constraint () && p.constraint) + ss.member ("constraint", p.constraint->string (), false /* check */); + + // Selected. + // + if (s != nullptr) + { + ss.member ("status", to_string (s->state), false /* check */); + + if (s->substate != package_substate::none) + ss.member ("sub_status", to_string (s->substate), false /* check */); + + ss.member ("version", s->version_string (), false /* check */); + + if (s->hold_package) + ss.member ("hold_package", true); + + if (s->hold_version) + ss.member ("hold_version", true); + } + + // Available. + // + if (!ps.empty () || !ps.system_package_version.empty ()) + { + if (s == nullptr) + { + ss.member ("status", "available", false /* check */); - available = true; + // Print the user's version if specified. + // + if (!p.version.empty ()) + ss.member ("version", p.version.string (), false /* check */); } - if (available) + // Print the list of available versions, unless a specific available + // version is already printed. + // + if (s != nullptr || p.version.empty ()) { - cout << (s != nullptr ? " " : "") << "available"; + ss.member_name ("available_versions"); - for (const apkg& a: apkgs) + // Serialize an available package version. + // + auto serialize = [&ss] (const string& v, bool s, bool d) { - const version& v (a.package->version); + ss.begin_object (); - // Show the currently selected version in parenthesis. - // - bool cur (s != nullptr && v == s->version); + ss.member ("version", v, false /* check */); - cout << ' ' - << (cur ? "(" : a.build ? "" : "[") - << v - << (cur ? ")" : a.build ? "" : "]"); - } + if (s) + ss.member ("system", s); + + if (d) + ss.member ("dependency", d); + + ss.end_object (); + }; + + ss.begin_array (); + + for (const available_package_status& a: ps) + serialize (a.package->version.string (), + false /* system */, + a.dependency); + + if (!ps.system_package_version.empty ()) + serialize (ps.system_package_version, + true /* system */, + ps.dependency); - if (!sys.empty ()) - cout << ' ' - << (build ? "" : "[") - << "sys:" << sys - << (build ? "" : "]"); + ss.end_array (); } } - - if (s == nullptr && !available) + // + // Unknown. + // + else if (s == nullptr) { - cout << "unknown"; + ss.member ("status", "unknown", false /* check */); // Print the user's version if specified. // if (!p.version.empty ()) - cout << ' ' << p.version; + ss.member ("version", p.version.string (), false /* check */); } - cout << endl; - if (recursive || immediate) { // Collect and recurse. @@ -236,27 +394,22 @@ namespace bpkg // Let's propagate the repository information source database from the // dependent to its prerequisites. // - packages dpkgs; if (s != nullptr) { - for (const auto& pair: s->prerequisites) + packages dpkgs (pkg_prerequisites (s, p.rdb)); + + if (!dpkgs.empty ()) { - shared_ptr d (pair.first.load ()); - database& db (pair.first.database ()); - const optional& c (pair.second); - dpkgs.push_back ( - package {db, rdb, d->name, version (), move (d), c}); + ss.member_name ("dependencies"); + pkg_status_json (o, dpkgs, ss, recursive, false /* immediate */); } } - - if (!dpkgs.empty ()) - { - indent += " "; - pkg_status (o, dpkgs, indent, recursive, false /* immediate */); - indent.resize (indent.size () - 2); - } } + + ss.end_object (); } + + ss.end_array (); } int @@ -377,8 +530,22 @@ namespace bpkg } } - string indent; - pkg_status (o, pkgs, indent, o.recursive (), o.immediate ()); + switch (o.stdout_format ()) + { + case stdout_format::lines: + { + string indent; + pkg_status_lines (o, pkgs, indent, o.recursive (), o.immediate ()); + break; + } + case stdout_format::json: + { + json::stream_serializer s (cout); + pkg_status_json (o, pkgs, s, o.recursive (), o.immediate ()); + cout << endl; + break; + } + } t.commit (); return 0; diff --git a/bpkg/types-parsers.cxx b/bpkg/types-parsers.cxx index 97ebafe..e27f050 100644 --- a/bpkg/types-parsers.cxx +++ b/bpkg/types-parsers.cxx @@ -141,6 +141,24 @@ namespace bpkg throw invalid_value (o, v); } + void parser:: + parse (stdout_format& x, bool& xs, scanner& s) + { + xs = true; + const char* o (s.next ()); + + if (!s.more ()) + throw missing_value (o); + + const string v (s.next ()); + if (v == "lines") + x = stdout_format::lines; + else if (v == "json") + x = stdout_format::json; + else + throw invalid_value (o, v); + } + void parser:: parse (repository_type& x, bool& xs, scanner& s) { diff --git a/bpkg/types-parsers.hxx b/bpkg/types-parsers.hxx index 007d754..dba459a 100644 --- a/bpkg/types-parsers.hxx +++ b/bpkg/types-parsers.hxx @@ -84,6 +84,16 @@ namespace bpkg }; template <> + struct parser + { + static void + parse (stdout_format&, bool&, scanner&); + + static void + merge (stdout_format& b, const stdout_format& a) {b = a;} + }; + + template <> struct parser { static void diff --git a/tests/pkg-status.testscript b/tests/pkg-status.testscript index 86a85d4..e90e3bf 100644 --- a/tests/pkg-status.testscript +++ b/tests/pkg-status.testscript @@ -54,183 +54,288 @@ $git_extract $src/git/style-basic.tar &$out_git/state0/*** end -pkg_fetch += 2>! -pkg_purge += -d cfg 2>! rep_add += -d cfg 2>! rep_fetch += -d cfg --auth all --trust-yes 2>! +pkg_fetch += 2>! +pkg_build += -d cfg --yes 2>! +pkg_purge += -d cfg 2>! +pkg_drop += -d cfg --yes 2>! -: basics +: lines : { +$clone_cfg - : not-fetched + : basics : { +$clone_cfg - : libfoo-1.0.0 - : - $clone_cfg; - $* libfoo/1.0.0 >'libfoo unknown 1.0.0' - - : libfoo - : - $clone_cfg; - $* libfoo >'libfoo unknown' - } - - : rep-fetched - : - { - +$clone_cfg && $rep_add $rep/stable && $rep_fetch - - +cp -r cfg ./fetched - +$pkg_fetch libfoo/1.0.0 -d fetched &fetched/libfoo-1.0.0.tar.gz - - : libfoo-1.0.0 + : not-fetched : - $clone_cfg; - $* libfoo/1.0.0 >'libfoo available 1.0.0' + { + +$clone_cfg - : libfoo-1.0.0+0 - : - $clone_cfg; - $* libfoo/1.0.0+0 >'libfoo available 1.0.0' + : libfoo-1.0.0 + : + $clone_cfg; + $* libfoo/1.0.0 >'libfoo unknown 1.0.0' - : libfoo - : - $clone_cfg; - $* libfoo >'libfoo available 1.0.0' + : libfoo + : + $clone_cfg; + $* libfoo >'libfoo unknown' + } - : pkg-fetched + : rep-fetched : { - clone_cfg = cp -r ../../fetched cfg + +$clone_cfg && $rep_add $rep/stable && $rep_fetch + + +cp -r cfg ./fetched + +$pkg_fetch libfoo/1.0.0 -d fetched &fetched/libfoo-1.0.0.tar.gz : libfoo-1.0.0 : $clone_cfg; - $* libfoo/1.0.0 >'libfoo fetched 1.0.0' + $* libfoo/1.0.0 >'libfoo available 1.0.0' + + : libfoo-1.0.0+0 + : + $clone_cfg; + $* libfoo/1.0.0+0 >'libfoo available 1.0.0' : libfoo : $clone_cfg; - $* libfoo >'libfoo fetched 1.0.0' + $* libfoo >'libfoo available 1.0.0' + + : pkg-fetched + : + { + clone_cfg = cp -r ../../fetched cfg + + : libfoo-1.0.0 + : + $clone_cfg; + $* libfoo/1.0.0 >'libfoo fetched 1.0.0' + + : libfoo + : + $clone_cfg; + $* libfoo >'libfoo fetched 1.0.0' + } } } -} - -: multiple-versions -{ - # Prepare the nested tests to copy the root configuration. Note that they - # must provide the destination directory name as an argument. - # - clone_cfg = cp -r $~/../cfg - : extra - : + : multiple-versions { - # Here we, first, prepare 2 configurations that derive from each other, and - # then spawn 2 tests on them. + # Prepare the nested tests to copy the root configuration. Note that they + # must provide the destination directory name as an argument. # - +$clone_cfg extra && $rep_add -d extra $rep/extra && $rep_fetch -d extra + clone_cfg = cp -r $~/../cfg - +cp -r extra extra-stable - +$rep_add -d extra-stable $rep/stable && $rep_fetch -d extra-stable - - : libbar + : extra : - $* -d ../extra libbar >'libbar available 1.1.0+1 [1.0.0]' - - : libbar-stable - : - $* -d ../extra-stable libbar >'libbar available 1.1.0+1 1.0.0' - } + { + # Here we, first, prepare 2 configurations that derive from each other, + # and then spawn 2 tests on them. + # + +$clone_cfg extra && $rep_add -d extra $rep/extra && $rep_fetch -d extra - : testing - : - { - +$clone_cfg ./ && $rep_add $rep/testing && $rep_fetch + +cp -r extra extra-stable + +$rep_add -d extra-stable $rep/stable && $rep_fetch -d extra-stable - clone_cfg = cp -r ../cfg ./ + : libbar + : + $* -d ../extra libbar >'libbar available 1.1.0+1 [1.0.0]' - : no-version - : - { - $clone_cfg; - $* libbar >'libbar available [1.1.0+1] 1.1.0 1.0.0+1 1.0.0' + : libbar-stable + : + $* -d ../extra-stable libbar >'libbar available 1.1.0+1 1.0.0' } - : no-revision + : testing : { - $clone_cfg; - $* libbar/1.0.0 >'libbar available 1.0.0+1 1.0.0' + +$clone_cfg ./ && $rep_add $rep/testing && $rep_fetch + + clone_cfg = cp -r ../cfg ./ + + : no-version + : + { + $clone_cfg; + $* libbar >'libbar available [1.1.0+1] 1.1.0 1.0.0+1 1.0.0' + } + + : no-revision + : + { + $clone_cfg; + $* libbar/1.0.0 >'libbar available 1.0.0+1 1.0.0' + } + + : zero-revision + : + { + $clone_cfg; + $* libbar/1.0.0+0 >'libbar available 1.0.0' + } + + : recursive + : + { + $clone_cfg; + + $pkg_build libbar; + + $* libbar --recursive >>EOO; + !libbar configured 1.1.0 available [1.1.0+1] + libbaz configured 1.0.0 + EOO + + $pkg_drop libbar + } } - : zero-revision + : unstable : { - $clone_cfg; - $* libbar/1.0.0+0 >'libbar available 1.0.0' + # Here we, first, prepare 3 configurations that derive from each other, + # and then spawn 3 tests on them. + # + +$clone_cfg ./ && $rep_add $rep/unstable && $rep_fetch + + +cp -r cfg fetched1 + +$pkg_fetch libbar/1.0.0+1 -d fetched1 &fetched1/libbar-1.0.0+1.tar.gz + + +cp -r fetched1 fetched2 + +$pkg_purge -d fetched2 libbar &!fetched2/libbar-1.0.0+1.tar.gz + +$pkg_fetch libbar/2.0.0 -d fetched2 &fetched2/libbar-2.0.0.tar.gz + + : not-fetched + : + $* -d ../cfg libbar >'libbar available 2.0.0 [1.1.0+1] 1.1.0 1.0.0+1 1.0.0' + + : fetched-1 + : + $* -d ../fetched1 libbar >'libbar fetched 1.0.0+1 available 2.0.0 [1.1.0+1] 1.1.0' + + : fetched-2 + : + $* -d ../fetched2 libbar >'libbar fetched 2.0.0' } } - : unstable + : git-rep : + if! $git_supported { - # Here we, first, prepare 3 configurations that derive from each other, and - # then spawn 3 tests on them. + # Skip git repository tests. # - +$clone_cfg ./ && $rep_add $rep/unstable && $rep_fetch - - +cp -r cfg fetched1 - +$pkg_fetch libbar/1.0.0+1 -d fetched1 &fetched1/libbar-1.0.0+1.tar.gz - - +cp -r fetched1 fetched2 - +$pkg_purge -d fetched2 libbar &!fetched2/libbar-1.0.0+1.tar.gz - +$pkg_fetch libbar/2.0.0 -d fetched2 &fetched2/libbar-2.0.0.tar.gz + } + else + { + rep = "$rep_git/state0" + test.cleanups += &cfg/.bpkg/repos/*/*** - : not-fetched + : complement-cycle : - $* -d ../cfg libbar >'libbar available 2.0.0 [1.1.0+1] 1.1.0 1.0.0+1 1.0.0' - - : fetched-1 + : Make sure that we properly handle the root<->style repository dependency + : cycle while searching for the style-basic package, that is an available + : package but not from the user-added repository (or its complement), and + : so is not detected as buildable by the status command. Note that the root + : repository is the default complement for git repositories (see + : rep_fetch() implementation for the reasoning). : - $* -d ../fetched1 libbar >'libbar fetched 1.0.0+1 available 2.0.0 [1.1.0+1] 1.1.0' + $clone_root_cfg; + $rep_add "$rep/libbar.git#master" && $rep_add "$rep/style.git#master"; - : fetched-2 - : - $* -d ../fetched2 libbar >'libbar fetched 2.0.0' + $rep_fetch 2>!; + + $* style-basic >~'%style-basic available \[1\.1\.0-a\.0\..+\]%' } } -: git-rep +: json : -if! $git_supported { - # Skip git repository tests. - # -} -else -{ - rep = "$rep_git/state0" - test.cleanups += &cfg/.bpkg/repos/*/*** + test.arguments += --stdout-format json + + +$clone_cfg - : complement-cycle + : not-fetched : - : Make sure that we properly handle the root<->style repository dependency - : cycle while searching for the style-basic package, that is an available - : package but not from the user-added repository (or its complement), and so - : is not detected as buildable by the status command. Note that the root - : repository is the default complement for git repositories (see rep_fetch() - : implementation for the reasoning). + { + +$clone_cfg + + : libfoo-1.0.0 + : + $clone_cfg; + $* libfoo/1.0.0 >>EOO + [ + { + "name": "libfoo", + "status": "unknown", + "version": "1.0.0" + } + ] + EOO + + : libfoo + : + $clone_cfg; + $* libfoo >>EOO + [ + { + "name": "libfoo", + "status": "unknown" + } + ] + EOO + } + + : fetched : - $clone_root_cfg; - $rep_add "$rep/libbar.git#master" && $rep_add "$rep/style.git#master"; + { + +$clone_cfg - $rep_fetch 2>!; + +$rep_add $rep/testing && $rep_fetch - $* style-basic >~'%style-basic available \[1\.1\.0-a\.0\..+\]%' + : recursive + : + { + $clone_cfg; + + $pkg_build libbar; + + $* libbar --recursive --constraint >>EOO; + [ + { + "name": "libbar", + "status": "configured", + "version": "1.1.0", + "hold_package": true, + "available_versions": [ + { + "version": "1.1.0+1", + "dependency": true + } + ], + "dependencies": [ + { + "name": "libbaz", + "constraint": "^1.0.0", + "status": "configured", + "version": "1.0.0" + } + ] + } + ] + EOO + + $pkg_drop libbar + } + } } diff --git a/tests/pkg-status/testing/libbar-1.0.0+1.tar.gz b/tests/pkg-status/testing/libbar-1.0.0+1.tar.gz index 9c2d0ed..d38cbbd 100644 Binary files a/tests/pkg-status/testing/libbar-1.0.0+1.tar.gz and b/tests/pkg-status/testing/libbar-1.0.0+1.tar.gz differ diff --git a/tests/pkg-status/testing/libbar-1.1.0.tar.gz b/tests/pkg-status/testing/libbar-1.1.0.tar.gz index 6ca773a..a5e060d 100644 Binary files a/tests/pkg-status/testing/libbar-1.1.0.tar.gz and b/tests/pkg-status/testing/libbar-1.1.0.tar.gz differ diff --git a/tests/pkg-status/testing/libbaz-1.0.0.tar.gz b/tests/pkg-status/testing/libbaz-1.0.0.tar.gz new file mode 100644 index 0000000..8d4c2f3 Binary files /dev/null and b/tests/pkg-status/testing/libbaz-1.0.0.tar.gz differ -- cgit v1.1