diff --git a/Makefile.am b/Makefile.am
index 1eeaaa416..496c0ff4e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,6 +37,7 @@ swupd_SOURCES = \
src/autoupdate.c \
src/binary_loader.c \
src/bundle.c \
+ src/bundle_info.c \
src/check_update.c \
src/clean.c \
src/clr_bundle_add.c \
@@ -187,6 +188,9 @@ BATS = \
test/functional/bundleadd/add-uses-fullfile.bats \
test/functional/bundleadd/add-uses-zeropack.bats \
test/functional/bundleadd/add-verify-fix-path.bats \
+ test/functional/bundleinfo/bundle-info-basic.bats \
+ test/functional/bundleinfo/bundle-info-files.bats \
+ test/functional/bundleinfo/bundle-info-includes.bats \
test/functional/bundlelist/list-all.bats \
test/functional/bundlelist/list-client-certificate.bats \
test/functional/bundlelist/list-deps-flat.bats \
diff --git a/docs/RELEASE_NOTES_DRAFT b/docs/RELEASE_NOTES_DRAFT
index 509735b2d..491847159 100644
--- a/docs/RELEASE_NOTES_DRAFT
+++ b/docs/RELEASE_NOTES_DRAFT
@@ -8,7 +8,19 @@ Bug fixes:
-
Feature:
- -
+ - Adds the bundle-info command to show detailed data related to a bundle.
+ Among the data currently displayed it includes:
+ - If the bundle is installed or not
+ - If the bundle is experimental or not
+ - If the bundle is up to date (when installed)
+ - The latest available version of the bundle
+ - The size of the bundle
+ - The max size needed in disk to install a bundle if not installed
+ - Using the --version flag a user can display bundle information in
+ a specific version, not only the current version.
+ - Using the --dependencies flag users can show all dependencies of the
+ bundle.
+ - Using the --files flag users can get the list of files in a bundle.
Test Improvements:
-
diff --git a/docs/swupd.1.rst b/docs/swupd.1.rst
index 440a656d3..93ba94ab9 100644
--- a/docs/swupd.1.rst
+++ b/docs/swupd.1.rst
@@ -223,7 +223,7 @@ SUBCOMMANDS
List all installed software bundles in the local system. Bundles available
can be listed with the `--all` option.
- - `-a, --all`
+ - `-a, --all`
Lists all available software bundles, either installed or not, that
are available. It will return 0 with succeeded and a different value
@@ -241,6 +241,23 @@ SUBCOMMANDS
not installed on the system. Combine with `--verbose` to show a tree of
these bundles.
+``bundle-info``
+
+ Display detailed information about a bundle.
+
+ - `--dependencies`
+
+ Show the bundle dependencies.
+
+ - `--files`
+
+ Show the files installed by this bundle.
+
+ - `-V, --version`
+
+ Show the bundle info for the specified version V, also accepts 'latest'.
+ It defaults to the current version if no version is specified.
+
``check-update``
Checks whether an update is available and prints out the information
diff --git a/src/bundle_info.c b/src/bundle_info.c
new file mode 100644
index 000000000..00bea2484
--- /dev/null
+++ b/src/bundle_info.c
@@ -0,0 +1,477 @@
+/*
+ * Software Updater - client side
+ *
+ * Copyright © 2012-2019 Intel Corporation.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 or later of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#define _GNU_SOURCE
+#include
+#include
+#include
+#include
+
+#include "swupd.h"
+
+#define FLAG_DEPENDENCIES 2000
+#define FLAG_FILES 2001
+
+static bool cmdline_option_dependencies = false;
+static bool cmdline_option_files = false;
+static int cmdline_option_version = 0;
+
+static char *bundle;
+
+struct bundle_info {
+ struct manifest *manifest;
+ struct list *direct_includes;
+ struct list *indirect_includes;
+ struct list *includes_manifests;
+ struct list *files;
+ bool is_installed;
+ bool is_experimental;
+ int version_latest_available;
+ long size_total;
+ long size_required;
+};
+
+static void print_help(void)
+{
+ print("Usage:\n");
+ print(" swupd bundle-info [OPTIONS...] BUNDLE\n\n");
+
+ global_print_help();
+
+ print("Options:\n");
+ print(" -V, --version=V Show the bundle info for the specified version V (current by default), also accepts 'latest'\n");
+ print(" --dependencies Show the bundle dependencies\n");
+ print(" --files Show the files installed by this bundle\n");
+ print("\n");
+}
+
+static const struct option prog_opts[] = {
+ { "version", required_argument, 0, 'V' },
+ { "dependencies", no_argument, 0, FLAG_DEPENDENCIES },
+ { "files", no_argument, 0, FLAG_FILES },
+};
+
+static bool parse_opt(int opt, UNUSED_PARAM char *optarg)
+{
+ int err;
+
+ switch (opt) {
+ case 'V':
+ if (strcmp("latest", optarg) == 0) {
+ cmdline_option_version = -1;
+ return true;
+ }
+ err = strtoi_err(optarg, &cmdline_option_version);
+ if (err < 0 || cmdline_option_version < 0) {
+ error("Invalid --version argument: %s\n\n", optarg);
+ return false;
+ }
+ return true;
+ case FLAG_DEPENDENCIES:
+ cmdline_option_dependencies = optarg_to_bool(optarg);
+ return true;
+ case FLAG_FILES:
+ cmdline_option_files = optarg_to_bool(optarg);
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+static const struct global_options opts = {
+ prog_opts,
+ sizeof(prog_opts) / sizeof(struct option),
+ parse_opt,
+ print_help,
+};
+
+static bool parse_options(int argc, char **argv)
+{
+ int ind = global_parse_options(argc, argv, &opts);
+
+ if (ind < 0) {
+ return false;
+ }
+
+ if (argc <= optind) {
+ error("Please specify the bundle you wish to display information from\n\n");
+ return false;
+ }
+
+ if (optind + 1 < argc) {
+ error("Please specify only one bundle at a time\n\n");
+ return false;
+ }
+
+ bundle = *(argv + optind);
+
+ return true;
+}
+
+static void print_bundle_dependencies(struct manifest *manifest, struct list *indirect_includes)
+{
+ struct list *iter;
+ char *dep;
+
+ /* print direct includes/also-add first */
+ if (manifest->includes || manifest->optional) {
+ info("\nDirect dependencies (%d):\n", list_len(manifest->includes) + list_len(manifest->optional));
+ for (iter = list_head(manifest->includes); iter; iter = iter->next) {
+ dep = iter->data;
+ info(" - ");
+ print("%s\n", dep);
+ }
+ for (iter = list_head(manifest->optional); iter; iter = iter->next) {
+ dep = iter->data;
+ /* optional dependencies can be installed or not, so we
+ * can also provide this info to the user, this only make
+ * sense if the requested bundle is also installed */
+ if (is_installed_bundle(manifest->component)) {
+ info(" - ");
+ print("%s", dep);
+ info(" (optional, %s)", is_installed_bundle(dep) ? "installed" : "not installed");
+ print("\n");
+ } else {
+ print(" - %s (optional)\n", dep);
+ }
+ }
+ }
+
+ /* now print indirect includes (if any) */
+ if (indirect_includes) {
+ info("\nIndirect dependencies (%d):\n", list_len(indirect_includes));
+ for (iter = list_head(indirect_includes); iter; iter = iter->next) {
+ dep = iter->data;
+ info(" - ");
+ print("%s\n", dep);
+ }
+ }
+}
+
+static void print_bundle_files(struct list *files)
+{
+ struct list *iter;
+ struct file *file;
+ long count = 0;
+
+ info("\nFiles in bundle");
+ if (cmdline_option_dependencies) {
+ info(" (includes dependencies):\n");
+ } else {
+ info(":\n");
+ }
+
+ for (iter = list_head(files); iter; iter = iter->next, count++) {
+ file = iter->data;
+ info(" - ");
+ print("%s\n", file->filename);
+ }
+
+ info("\nTotal files: %d\n", count);
+}
+
+static void print_bundle_size(struct manifest *manifest, long size, bool bundle_installed)
+{
+ char *pretty_size;
+
+ info("\nBundle size:\n");
+ prettify_size(manifest->contentsize, &pretty_size);
+ info(" - Size of bundle: %s\n", pretty_size);
+ free_string(&pretty_size);
+
+ prettify_size(size, &pretty_size);
+ if (bundle_installed) {
+ info(" - Size bundle takes on disk (includes dependencies): %s\n", pretty_size);
+ } else {
+ info(" - Maximum amount of disk size the bundle will take if installed (includes dependencies): %s\n", pretty_size);
+ }
+ free_string(&pretty_size);
+}
+
+static long get_bundle_size(struct manifest *mom, bool bundle_installed)
+{
+ int bundle_size;
+ struct list *bundles_not_installed = NULL;
+ struct list *iter;
+ struct manifest *manifest;
+ char *dep;
+
+ if (bundle_installed) {
+ /* if the bundle is installed, get the size the bundle
+ * takes on disk (considering its dependencies) */
+ bundle_size = get_manifest_list_contentsize(mom->submanifests);
+ } else {
+ /* if the bundle is not installed, get the max size it
+ * would take on disk if installed */
+ for (iter = list_head(mom->submanifests); iter; iter = iter->next) {
+ manifest = iter->data;
+ dep = manifest->component;
+ if (!is_installed_bundle(dep)) {
+ bundles_not_installed = list_prepend_data(bundles_not_installed, manifest);
+ }
+ }
+ bundle_size = get_manifest_list_contentsize(bundles_not_installed);
+ list_free_list(bundles_not_installed);
+ }
+
+ return bundle_size;
+}
+
+static enum swupd_code get_bundle_files(struct manifest *manifest, struct manifest *mom, struct list **files)
+{
+
+ /* when the --includes flag is also used, the list needs to include
+ * the files from all bundles pulled in by the requested bundle */
+ if (cmdline_option_dependencies) {
+ *files = consolidate_files_from_bundles(mom->submanifests);
+ } else {
+ *files = list_clone(manifest->files);
+ }
+ *files = filter_out_deleted_files(*files);
+
+ return SWUPD_OK;
+}
+
+static enum swupd_code get_bundle_dependencies(struct manifest *manifest, struct list *subs, struct list **indirect_includes)
+{
+ struct list *iter;
+ struct sub *sub;
+ char *included_bundle;
+
+ /* build a list of dependencies */
+ for (iter = list_head(subs); iter; iter = iter->next) {
+ sub = iter->data;
+ included_bundle = sub->component;
+ /* we don't need to add the requested bundle to the list */
+ if (strcmp(included_bundle, bundle) == 0) {
+ continue;
+ }
+ *indirect_includes = list_append_data(*indirect_includes, included_bundle);
+ }
+
+ /* from the list of dependencies, remove those that were
+ * directly pulled by the bundle so we are left only with the ones
+ * indirectly pulled */
+ *indirect_includes = list_sort(*indirect_includes, list_strcmp);
+ manifest->includes = list_sort(manifest->includes, list_strcmp);
+ manifest->optional = list_sort(manifest->optional, list_strcmp);
+ *indirect_includes = list_filter_common_elements(*indirect_includes, manifest->includes, list_strcmp, NULL);
+ *indirect_includes = list_filter_common_elements(*indirect_includes, manifest->optional, list_strcmp, NULL);
+
+ return SWUPD_OK;
+}
+
+enum swupd_code bundle_info(char *bundle)
+{
+ enum swupd_code ret = SWUPD_OK;
+ int requested_version, latest_version;
+ struct manifest *mom = NULL;
+ struct manifest *latest_mom = NULL;
+ struct manifest *manifest = NULL;
+ struct manifest *latest_manifest = NULL;
+ struct file *file = NULL;
+ struct list *subs = NULL;
+ long bundle_size;
+ bool mix_exists;
+ bool installed = is_installed_bundle(bundle);
+
+ /* get the very latest version available */
+ latest_version = get_latest_version(NULL);
+ if (latest_version < 0) {
+ error("Unable to determine the server version\n");
+ return SWUPD_SERVER_CONNECTION_ERROR;
+ }
+
+ /* if no version was specified,
+ * get the version the system is currently at */
+ if (cmdline_option_version == 0) {
+ requested_version = get_current_version(globals.path_prefix);
+ if (requested_version < 0) {
+ error("Unable to determine current OS version\n");
+ return SWUPD_CURRENT_VERSION_UNKNOWN;
+ }
+ } else if (cmdline_option_version == -1) {
+ /* user selected "latest" */
+ requested_version = latest_version;
+ } else {
+ /* user specified an arbitrary version */
+ requested_version = cmdline_option_version;
+ }
+
+ mix_exists = (check_mix_exists() & system_on_mix());
+
+ /* get the MoM for the requested version */
+ mom = load_mom(requested_version, mix_exists, NULL);
+ if (!mom) {
+ error("Cannot load official manifest MoM for version %i\n", requested_version);
+ return SWUPD_COULDNT_LOAD_MOM;
+ }
+
+ /* validate the bundle provided and get its manifest */
+ file = search_bundle_in_manifest(mom, bundle);
+ if (!file) {
+ error("Bundle \"%s\" is invalid\n", bundle);
+ ret = SWUPD_INVALID_BUNDLE;
+ goto clean;
+ }
+ manifest = load_manifest(file->last_change, file, mom, false, NULL);
+ if (!manifest) {
+ error("Unable to download the manifest for %s version %d, exiting now\n", bundle, file->last_change);
+ ret = SWUPD_COULDNT_LOAD_MANIFEST;
+ goto clean;
+ }
+
+ /* if there is a newer version of the bundle, get the manifests */
+ if (requested_version != latest_version) {
+ /* get mom from the latest version */
+ latest_mom = load_mom(latest_version, mix_exists, NULL);
+ if (!latest_mom) {
+ error("Cannot load official manifest MoM for version %i\n", latest_version);
+ ret = SWUPD_COULDNT_LOAD_MOM;
+ goto clean;
+ }
+
+ /* get the latest version of the bundle manifest */
+ file = search_bundle_in_manifest(latest_mom, bundle);
+ if (file) {
+ latest_manifest = load_manifest(file->last_change, file, latest_mom, false, NULL);
+ if (!latest_manifest) {
+ error("Unable to download the manifest for %s version %d, exiting now\n", bundle, file->last_change);
+ ret = SWUPD_COULDNT_LOAD_MANIFEST;
+ goto clean;
+ }
+ } else {
+ debug("Bundle \"%s\" is no longer available at version %d\n", bundle, latest_version);
+ }
+ }
+
+ /* get all bundles pulled in by the requested bundle */
+ struct list *bundle_temp = NULL;
+ bundle_temp = list_prepend_data(bundle_temp, bundle);
+ ret = add_subscriptions(bundle_temp, &subs, mom, true, 0);
+ list_free_list(bundle_temp);
+ if (ret & add_sub_ERR) {
+ ret = SWUPD_COULDNT_LOAD_MANIFEST;
+ goto clean;
+ } else if (ret & add_sub_BADNAME) {
+ ret = SWUPD_INVALID_BUNDLE;
+ goto clean;
+ } else {
+ /* add_subscriptions return add_sub_NEW when successful,
+ * convert it to a swupd_code */
+ ret = SWUPD_OK;
+ }
+ set_subscription_versions(mom, NULL, &subs);
+ mom->submanifests = recurse_manifest(mom, subs, NULL, false, NULL);
+
+ /* print header */
+ const int HEADER = 19;
+ int bundle_length = strlen(bundle);
+ print_pattern("_", HEADER + bundle_length);
+ info(" Info for bundle: %s \n", bundle);
+ print_pattern("_", HEADER + bundle_length);
+
+ /* status info */
+ info("\nStatus: %s%s\n", installed ? "Installed" : "Not installed", file->is_experimental ? " (experimental)" : "");
+
+ /* version info */
+ if (installed) {
+ if (manifest->version < file->last_change) {
+ info("\nThere is an update for bundle %s:\n", bundle);
+ } else {
+ info("\nBundle %s is up to date:\n", bundle);
+ }
+ info(" - Installed bundle last updated in version: %d\n", manifest->version);
+ info(" - ");
+ }
+ info("Latest available version: %d\n", file->last_change);
+
+ /* size info */
+ bundle_size = get_bundle_size(mom, installed);
+ print_bundle_size(manifest, bundle_size, installed);
+
+ /* optional info */
+
+ /* the --includes flag was used */
+ if (cmdline_option_dependencies) {
+
+ struct list *indirect_includes = NULL;
+ ret = get_bundle_dependencies(manifest, subs, &indirect_includes);
+ if (ret) {
+ error("Could not get all bundles included by %s\n", bundle);
+ goto clean;
+ }
+ print_bundle_dependencies(manifest, indirect_includes);
+ list_free_list(indirect_includes);
+ }
+
+ /* the --files flag was used */
+ if (cmdline_option_files) {
+
+ struct list *bundle_files = NULL;
+ ret = get_bundle_files(manifest, mom, &bundle_files);
+ if (ret) {
+ error("Could not get all files included by %s\n", bundle);
+ goto clean;
+ }
+ print_bundle_files(bundle_files);
+ list_free_list(bundle_files);
+ }
+
+ info("\n");
+
+clean:
+ free_subscriptions(&subs);
+ free_manifest(latest_manifest);
+ free_manifest(latest_mom);
+ free_manifest(manifest);
+ free_manifest(mom);
+
+ return ret;
+}
+
+enum swupd_code bundle_info_main(int argc, char **argv)
+{
+ int ret;
+ const int steps_in_bundleinfo = 1;
+
+ /* there is no need to report in progress for bundle-info at this time */
+
+ if (!parse_options(argc, argv)) {
+ print_help();
+ return SWUPD_INVALID_OPTION;
+ }
+
+ progress_init_steps("bundle-info", steps_in_bundleinfo);
+ ret = swupd_init(SWUPD_ALL);
+ if (ret != 0) {
+ error("Failed swupd initialization. Exiting now\n");
+ ret = SWUPD_CURL_INIT_FAILED;
+ goto exit;
+ }
+
+ ret = bundle_info(bundle);
+
+ swupd_deinit();
+exit:
+ progress_finish_steps(ret);
+
+ return ret;
+}
diff --git a/src/helpers.c b/src/helpers.c
index 7272d8329..8b2b350e6 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -1081,3 +1081,36 @@ int remove_files_from_fs(struct list *files)
return deleted;
}
+
+void print_pattern(const char *pattern, int times)
+{
+ while (times > 0) {
+ info("%s", pattern);
+ times--;
+ }
+ info("\n");
+}
+
+void prettify_size(long size_in_bytes, char **pretty_size)
+{
+ double size;
+
+ /* find the best way to show the provided size */
+ size = (double)size_in_bytes;
+ if (size < 1000) {
+ string_or_die(pretty_size, "%.2lf Bytes", size);
+ return;
+ }
+ size = size / 1000;
+ if (size < 1000) {
+ string_or_die(pretty_size, "%.2lf KB", size);
+ return;
+ }
+ size = size / 1000;
+ if (size < 1000) {
+ string_or_die(pretty_size, "%.2lf MB", size);
+ return;
+ }
+ size = size / 1000;
+ string_or_die(pretty_size, "%.2lf GB", size);
+}
diff --git a/src/main.c b/src/main.c
index 9df595cde..767b3a7ee 100644
--- a/src/main.c
+++ b/src/main.c
@@ -57,6 +57,7 @@ static struct subcmd commands[] = {
{ "bundle-add", "Install a new bundle", bundle_add_main },
{ "bundle-remove", "Uninstall a bundle", bundle_remove_main },
{ "bundle-list", "List installed bundles", bundle_list_main },
+ { "bundle-info", "Display information about a bundle", bundle_info_main },
#ifdef EXTERNAL_MODULES_SUPPORT
{ "search", "Searches for the best bundle to install a binary or library (depends on os-core-search bundle)", external_search_main },
#endif
diff --git a/src/swupd.h b/src/swupd.h
index 4204f024f..2edf02d9f 100644
--- a/src/swupd.h
+++ b/src/swupd.h
@@ -160,7 +160,6 @@ extern int main_verify(int current_version);
extern enum swupd_code walk_tree(struct manifest *, const char *, bool, const regex_t *, struct file_counts *);
extern int get_latest_version(char *v_url);
-extern int get_absolute_latest_version(void);
extern enum swupd_code read_versions(int *current_version, int *server_version, char *path_prefix);
extern int get_current_version(char *path_prefix);
extern bool get_distribution_string(char *path_prefix, char *dist);
@@ -310,6 +309,8 @@ extern void remove_trailing_slash(char *url);
extern int link_or_copy(const char *orig, const char *dest);
extern int link_or_copy_all(const char *orig, const char *dest);
extern int remove_files_from_fs(struct list *files);
+extern void print_pattern(const char *pattern, int times);
+extern void prettify_size(long size_in_bytes, char **pretty_size);
/* subscription.c */
struct list *free_list_file(struct list *item);
diff --git a/src/swupd_internal.h b/src/swupd_internal.h
index 4cad0833c..3132cc516 100644
--- a/src/swupd_internal.h
+++ b/src/swupd_internal.h
@@ -16,5 +16,6 @@ extern enum swupd_code binary_loader_main(int argc, char **argv);
extern enum swupd_code install_main(int argc, char **argv);
extern enum swupd_code repair_main(int argc, char **argv);
extern enum swupd_code diagnose_main(int argc, char **argv);
+extern enum swupd_code bundle_info_main(int argc, char **argv);
#endif
diff --git a/src/update.c b/src/update.c
index 143ac1bc6..b5fe9a769 100644
--- a/src/update.c
+++ b/src/update.c
@@ -663,7 +663,7 @@ static bool parse_opt(int opt, char *optarg)
err = strtoi_err(optarg, &requested_version);
if (err < 0 || requested_version < 0) {
- error("Invalid --manifest argument: %s\n\n", optarg);
+ error("Invalid --version argument: %s\n\n", optarg);
return false;
}
return true;
diff --git a/swupd.bash b/swupd.bash
index 83adc085a..fec515303 100644
--- a/swupd.bash
+++ b/swupd.bash
@@ -51,6 +51,9 @@ _swupd()
("bundle-list")
opts="$global --all --deps --has-dep "
break;;
+ ("bundle-info")
+ opts="$global --dependencies --files --version "
+ break;;
("search")
opts="--help --all --quiet --verbose "
break;;
diff --git a/swupd.zsh b/swupd.zsh
index e5ad07a0e..bb8607e23 100644
--- a/swupd.zsh
+++ b/swupd.zsh
@@ -236,6 +236,16 @@ if [[ -n "$state" ]]; then
)
_arguments $lsbundle && ret=0
;;
+ bundle-info)
+ local -a infobundle; infobundle=(
+ $global_opts
+ '(help -V --version)'{-V,--version=}'[Show the bundle info for the specified version V, also accepts "latest" and "current" (default)]:version:()'
+ '(help)--dependencies[Show the bundle dependencies]'
+ '(help)--files[Show the files installed by this bundle]'
+ '*:bundle-info: _swupd_all_bundles -f'
+ )
+ _arguments $infobundle && ret=0
+ ;;
search)
local -a searches; searches=(
'(help -v -vv --verbose -q --quiet)'{-v,-vv,--verbose}'[verbose mode]'
diff --git a/test/functional/bundleinfo/bundle-info-basic.bats b/test/functional/bundleinfo/bundle-info-basic.bats
new file mode 100755
index 000000000..14f528f83
--- /dev/null
+++ b/test/functional/bundleinfo/bundle-info-basic.bats
@@ -0,0 +1,150 @@
+#!/usr/bin/env bats
+
+# Author: Castulo Martinez
+# Email: castulo.martinez@intel.com
+
+load "../testlib"
+
+global_setup() {
+
+ create_test_environment "$TEST_NAME"
+ create_bundle -L -n test-bundle1 -f /file_1,/foo/file_2 "$TEST_NAME"
+ create_bundle -e -n test-bundle2 -f /file_3,/bar/file_4 "$TEST_NAME"
+ create_version "$TEST_NAME" 20 10
+ update_bundle "$TEST_NAME" test-bundle1 --update /file_1
+ create_version "$TEST_NAME" 30 20
+ update_bundle "$TEST_NAME" test-bundle1 --add /foo/file_5
+
+}
+
+test_setup() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+test_teardown() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+global_teardown() {
+
+ destroy_test_environment "$TEST_NAME"
+
+}
+
+@test "BIN001: Show info about a bundle installed in the system" {
+
+ # users can use the bundle-info command to see detailed info about a bundle
+ # if the bundle is installed it should show when it was last updated and
+ # how much disk space is taking
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Installed
+ There is an update for bundle test-bundle1:
+ - Installed bundle last updated in version: 10
+ - Latest available version: 30
+ Bundle size:
+ - Size of bundle: .* KB
+ - Size bundle takes on disk \(includes dependencies\): .* KB
+ EOM
+ )
+ assert_regex_in_output "$expected_output"
+
+}
+
+@test "BIN002: Show info about a bundle not installed in the system" {
+
+ # users can use the bundle-info command to see detailed info about a bundle
+ # if the bundle is not installed it should show the latest available version
+ # and how much disk space it would take if installed
+ # also bundles that are experimental are marked as such in the status
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle2"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle2
+ _______________________________
+ Status: Not installed \(experimental\)
+ Latest available version: 10
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
+
+@test "BIN003: Show info about a bundle for a particular system version" {
+
+ # a specific version can be specified to display info
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --version 20"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Installed
+ There is an update for bundle test-bundle1:
+ - Installed bundle last updated in version: 20
+ - Latest available version: 30
+ Bundle size:
+ - Size of bundle: .* KB
+ - Size bundle takes on disk \(includes dependencies\): .* KB
+ EOM
+ )
+ assert_regex_in_output "$expected_output"
+
+}
+
+@test "BIN004: Show info about a bundle for a particular system version" {
+
+ # version can take latest or current
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --version latest"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Installed
+ Bundle test-bundle1 is up to date:
+ - Installed bundle last updated in version: 30
+ - Latest available version: 30
+ Bundle size:
+ - Size of bundle: .* KB
+ - Size bundle takes on disk \(includes dependencies\): .* KB
+ EOM
+ )
+ assert_regex_in_output "$expected_output"
+
+}
+
+@test "BIN005: Try fo show nfo about an invalid bundle" {
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS bad-bundle"
+
+ assert_status_is "$SWUPD_INVALID_BUNDLE"
+ expected_output=$(cat <<-EOM
+ Error: Bundle "bad-bundle" is invalid
+ EOM
+ )
+ assert_is_output "$expected_output"
+
+}
diff --git a/test/functional/bundleinfo/bundle-info-files.bats b/test/functional/bundleinfo/bundle-info-files.bats
new file mode 100755
index 000000000..1d517bef6
--- /dev/null
+++ b/test/functional/bundleinfo/bundle-info-files.bats
@@ -0,0 +1,138 @@
+#!/usr/bin/env bats
+
+# Author: Castulo Martinez
+# Email: castulo.martinez@intel.com
+
+load "../testlib"
+
+global_setup() {
+
+ create_test_environment "$TEST_NAME"
+ create_bundle -n test-bundle1 -f /file_1,/foo/file_2 "$TEST_NAME"
+ create_bundle -n test-bundle2 -f /file_3,/bar/file_4 "$TEST_NAME"
+ # add test-bundle2 as a dependency of test-bundle1
+ add_dependency_to_manifest "$WEBDIR"/10/Manifest.test-bundle1 test-bundle2
+ create_version "$TEST_NAME" 20 10
+ update_bundle "$TEST_NAME" test-bundle1 --header-only
+ update_bundle "$TEST_NAME" test-bundle2 --add /file_5
+
+}
+
+test_setup() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+test_teardown() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+global_teardown() {
+
+ destroy_test_environment "$TEST_NAME"
+
+}
+
+@test "BIN008: Show the files that are part of a bundle" {
+
+ # bundle-info --files show the files that are part of that bundle
+ # it doesn't include files from dependencies
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --files"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Not installed
+ Latest available version: 20
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ Files in bundle:
+ - /usr/share/clear/bundles/test-bundle1
+ - /foo/file_2
+ - /foo
+ - /file_1
+ Total files: 4
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
+
+@test "BIN009: Show the files that are part of a bundle and its dependencies" {
+
+ # bundle-info --files show the files that are part of that bundle, if
+ # the --includes flag is also used it doest include files from dependencies
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --files --dependencies"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Not installed
+ Latest available version: 20
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ Directly included bundles \(1\):
+ - test-bundle2
+ Files in bundle \(includes dependencies\):
+ - /bar
+ - /bar/file_4
+ - /file_1
+ - /file_3
+ - /foo
+ - /foo/file_2
+ - /usr/share/clear/bundles/test-bundle1
+ - /usr/share/clear/bundles/test-bundle2
+ Total files: 8
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
+
+@test "BIN010: Show the files that are part of a bundle and its dependencies for a specific version" {
+
+ # the --version flag can be appended to the command to select a specific version
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --files --dependencies --version latest"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Not installed
+ Latest available version: 20
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ Directly included bundles \(1\):
+ - test-bundle2
+ Files in bundle \(includes dependencies\):
+ - /bar
+ - /bar/file_4
+ - /file_1
+ - /file_3
+ - /file_5
+ - /foo
+ - /foo/file_2
+ - /usr/share/clear/bundles/test-bundle1
+ - /usr/share/clear/bundles/test-bundle2
+ Total files: 9
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
diff --git a/test/functional/bundleinfo/bundle-info-includes.bats b/test/functional/bundleinfo/bundle-info-includes.bats
new file mode 100755
index 000000000..d92dce1b0
--- /dev/null
+++ b/test/functional/bundleinfo/bundle-info-includes.bats
@@ -0,0 +1,103 @@
+#!/usr/bin/env bats
+
+# Author: Castulo Martinez
+# Email: castulo.martinez@intel.com
+
+load "../testlib"
+
+global_setup() {
+
+ create_test_environment "$TEST_NAME"
+ create_bundle -n test-bundle1 -f /file_1 "$TEST_NAME"
+ create_bundle -n test-bundle2 -f /file_2 "$TEST_NAME"
+ create_bundle -n test-bundle3 -f /file_3 "$TEST_NAME"
+ create_bundle -n test-bundle4 -f /file_4 "$TEST_NAME"
+ # add test-bundle2 as a dependency of test-bundle1 and test-bundle3 as optional
+ add_dependency_to_manifest "$WEBDIR"/10/Manifest.test-bundle1 test-bundle2
+ add_dependency_to_manifest -o "$WEBDIR"/10/Manifest.test-bundle1 test-bundle3
+ # add test-bundle4 as a dependency of test-bundle2
+ add_dependency_to_manifest "$WEBDIR"/10/Manifest.test-bundle2 test-bundle4
+ # add one more dependency in a new version
+ create_version "$TEST_NAME" 20 10
+ update_bundle "$TEST_NAME" test-bundle1 --header-only
+ create_bundle -n test-bundle5 -f /file_5 "$TEST_NAME"
+ add_dependency_to_manifest "$WEBDIR"/20/Manifest.test-bundle1 test-bundle5
+
+}
+
+test_setup() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+test_teardown() {
+
+ # do nothing, just overwrite the lib test_setup
+ return
+
+}
+
+global_teardown() {
+
+ destroy_test_environment "$TEST_NAME"
+
+}
+
+@test "BIN006: Show info about a bundle including its dependencies" {
+
+ # the --includes flag can be used to show all directly and indirectly
+ # included and optional bundles
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --dependencies"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Not installed
+ Latest available version: 20
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ Directly included bundles \(2\):
+ - test-bundle2
+ - test-bundle3 \(optional\)
+ Indirectly included bundles \(1\):
+ - test-bundle4
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
+
+@test "BIN007: Show info about a specific version of bundle including its dependencies" {
+
+ # the --includes flag can be used to show all directly and indirectly
+ # included and optional bundles along with the --version flag
+
+ run sudo sh -c "$SWUPD bundle-info $SWUPD_OPTS test-bundle1 --dependencies --version latest"
+
+ assert_status_is "$SWUPD_OK"
+ expected_output=$(cat <<-EOM
+ _______________________________
+ Info for bundle: test-bundle1
+ _______________________________
+ Status: Not installed
+ Latest available version: 20
+ Bundle size:
+ - Size of bundle: .* KB
+ - Maximum amount of disk size the bundle will take if installed \(includes dependencies\): .* KB
+ Directly included bundles \(3\):
+ - test-bundle2
+ - test-bundle5
+ - test-bundle3 \(optional\)
+ Indirectly included bundles \(1\):
+ - test-bundle4
+ EOM
+ )
+ assert_regex_is_output "$expected_output"
+
+}
diff --git a/test/functional/testlib.bash b/test/functional/testlib.bash
index dd3c38880..2910792fc 100644
--- a/test/functional/testlib.bash
+++ b/test/functional/testlib.bash
@@ -3130,7 +3130,8 @@ generate_test() { # swupd_function
printf '\t# \n\n'
# shellcheck disable=SC2016
printf '\trun sudo sh -c "$SWUPD $SWUPD_OPTS "\n\n'
- printf '\t# assert_status_is 0\n'
+ # shellcheck disable=SC2016
+ printf '\t# assert_status_is "$SWUPD_OK"\n'
# shellcheck disable=SC2016
printf '\t# expected_output=$(cat <<-EOM\n'
printf '\t# \t\n'
@@ -3172,6 +3173,7 @@ get_next_available_id() { # swupd_function
bundleadd) group=ADD;;
bundleremove) group=REM;;
bundlelist) group=LST;;
+ bundleinfo) group=BIN;;
diagnose) group=DIA;;
update) group=UPD;;
checkupdate) group=CHK;;