From 7ee3f0d9edc026af2fe1aed57363460c1cee5826 Mon Sep 17 00:00:00 2001 From: Castulo Martinez Date: Fri, 6 Sep 2019 14:30:39 -0700 Subject: [PATCH] Implement bundle-info command The bundle-info command shows detailed data related to a specified 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 installed, it shows if there is an existing update for the bundle - The latest available version of the bundle - The size of the bundle and all its dependencies - The max size needed in disk to install a bundle if not installed This commit also adds the following command flags: --version: so a user can display information for a bundle in a specific version, not only the current version. --dependencies: this flag can be used to show all optional and required bundles that are directly and indirectly included by the specified bundle. --files: this flag can be used to show all files that are part of a given bundle. All flags can be combined to show specific data. This is the first of a series of PRs to implement the bundle-info features referred to at #461. Signed-off-by: Castulo Martinez --- Makefile.am | 4 + docs/RELEASE_NOTES_DRAFT | 14 +- docs/swupd.1.rst | 19 +- src/bundle_info.c | 477 ++++++++++++++++++ src/helpers.c | 33 ++ src/main.c | 1 + src/swupd.h | 3 +- src/swupd_internal.h | 1 + src/update.c | 2 +- swupd.bash | 3 + swupd.zsh | 10 + .../bundleinfo/bundle-info-basic.bats | 150 ++++++ .../bundleinfo/bundle-info-files.bats | 138 +++++ .../bundleinfo/bundle-info-includes.bats | 103 ++++ test/functional/testlib.bash | 4 +- 15 files changed, 957 insertions(+), 5 deletions(-) create mode 100644 src/bundle_info.c create mode 100755 test/functional/bundleinfo/bundle-info-basic.bats create mode 100755 test/functional/bundleinfo/bundle-info-files.bats create mode 100755 test/functional/bundleinfo/bundle-info-includes.bats 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;;