diff --git a/examples/meson.build b/examples/meson.build index 56d300e66732c1c6773c58b449f216f427ad1246..83af1519d55213073b5bed7d42dd816e93050245 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -24,7 +24,7 @@ executable( 'steam-runtime-system-info', 'system-info.c', - dependencies : [glib, libsteamrt_dep], + dependencies : [glib, libsteamrt_dep, json_glib], install : true, ) diff --git a/examples/system-info.c b/examples/system-info.c index aca0ed52c50fbbb6b7a2bbfc2b6c2a3be5eab64e..2946d0f6f47950105d6b018d1c2e9aeb6719e930 100644 --- a/examples/system-info.c +++ b/examples/system-info.c @@ -27,30 +27,337 @@ * Output basic information about the system on which the tool is run. * The output is a JSON object with the following keys: * - * can-run: - * An object. The keys are strings representing multiarch tuples, as - * used in Debian and the freedesktop.org SDK runtime. The values are - * boolean: true if we can definitely run executables of this - * architecture, or false if we cannot prove that we can do so. + * can-write-uinput: + * The values are boolean: true if we can write to `/dev/uninput`, + * false if we are not able to do it. + * + * architectures: + * An object. The keys are multiarch tuples like %SRT_ABI_I386, + * as used in Debian and the freedesktop.org SDK runtime. + * The values are objects with more details of the architecture: + * + * can-run: + * The values are boolean: true if we can definitely run + * executables of this architecture, or false if we cannot prove + * that we can do so. + * + * library-issues-summary: + * A string array listing all the libraries problems that has been found + * in the running system. Possible values can be: "cannot-load", + * "missing-symbols", "misversioned-symbols" and "internal-error". + * If "can-run" for this architecture is false we skip the library check + * and this "library-issues-summary" will not be printed at all. + * + * library-details: + * An object. The keys are library SONAMEs, such as `libjpeg.so.62`. + * The values are objects with more details of the library: + * + * path: + * The value is a string representing the full path about where the + * @library has been found. The value is `null` if the library is + * not available in the system. + * + * issues: + * A string array listing all the @library problems that has been + * found in the running system. For possible values check + * `library-issues-summary`. This object will be available only if + * there are some issues. + * + * missing-symbols: + * A string array listing all the symbols that were expected to be + * provided by @library but were not found. This object will be + * available only if there are sone missing symbols. + * + * misversioned-symbols: + * A string array listing all the symbols that were expected to be + * provided by @library but were available with a different version. + * This object will be available only if there are some misversioned + * symbols. */ #include <steam-runtime-tools/steam-runtime-tools.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> #include <glib.h> +#include <json-glib/json-glib.h> + +enum +{ + OPTION_HELP = 1, + OPTION_EXPECTATION, + OPTION_VERBOSE, +}; + +struct option long_options[] = +{ + { "expectations", required_argument, NULL, OPTION_EXPECTATION }, + { "verbose", no_argument, NULL, OPTION_VERBOSE }, + { "help", no_argument, NULL, OPTION_HELP }, + { NULL, 0, NULL, 0 } +}; + +static void usage (int code) __attribute__((__noreturn__)); + +/* + * Print usage information and exit with status @code. + */ +static void +usage (int code) +{ + FILE *fp; + + if (code == 0) + fp = stdout; + else + fp = stderr; + + fprintf (fp, "Usage: %s [OPTIONS]\n", + program_invocation_short_name); + exit (code); +} + +static FILE * +divert_stdout_to_stderr (GError **error) +{ + int original_stdout_fd; + FILE *original_stdout; + + /* Duplicate the original stdout so that we still have a way to write + * machine-readable output. */ + original_stdout_fd = dup (STDOUT_FILENO); + + if (original_stdout_fd < 0) + { + int saved_errno = errno; + + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Unable to duplicate fd %d: %s", + STDOUT_FILENO, g_strerror (saved_errno)); + return NULL; + } + + /* If something like g_debug writes to stdout, make it come out of + * our original stderr. */ + if (dup2 (STDERR_FILENO, STDOUT_FILENO) != STDOUT_FILENO) + { + int saved_errno = errno; + + close (original_stdout_fd); + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Unable to make fd %d a copy of fd %d: %s", + STDOUT_FILENO, STDERR_FILENO, g_strerror (saved_errno)); + return NULL; + } + + /* original_stdout takes ownership of original_stdout_fd on success */ + original_stdout = fdopen (original_stdout_fd, "w"); + + if (original_stdout == NULL) + { + int saved_errno = errno; + + close (original_stdout_fd); + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Unable to create a stdio wrapper for fd %d: %s", + original_stdout_fd, g_strerror (saved_errno)); + return NULL; + } + + return original_stdout; +} + +static void +jsonify_library_issues (JsonBuilder *builder, + SrtLibraryIssues issues) +{ + if ((issues & SRT_LIBRARY_ISSUES_CANNOT_LOAD) != 0) + json_builder_add_string_value (builder, "cannot-load"); + + if ((issues & SRT_LIBRARY_ISSUES_MISSING_SYMBOLS) != 0) + json_builder_add_string_value (builder, "missing-symbols"); + + if ((issues & SRT_LIBRARY_ISSUES_MISVERSIONED_SYMBOLS) != 0) + json_builder_add_string_value (builder, "misversioned-symbols"); + + if ((issues & SRT_LIBRARY_ISSUES_INTERNAL_ERROR) != 0) + json_builder_add_string_value (builder, "internal-error"); +} + +static void +print_libraries_details (JsonBuilder *builder, + GList *libraries, + gboolean verbose) +{ + json_builder_set_member_name (builder, "library-details"); + json_builder_begin_object (builder); + for (GList *l = libraries; l != NULL; l = l->next) + { + if (verbose || srt_library_get_issues (l->data) != SRT_LIBRARY_ISSUES_NONE) + { + const char * const *missing_symbols; + const char * const *misversioned_symbols; + json_builder_set_member_name (builder, srt_library_get_soname (l->data)); + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "path"); + json_builder_add_string_value (builder, srt_library_get_absolute_path (l->data)); + + if (srt_library_get_issues (l->data) != SRT_LIBRARY_ISSUES_NONE) + { + json_builder_set_member_name (builder, "issues"); + json_builder_begin_array (builder); + jsonify_library_issues (builder, srt_library_get_issues (l->data)); + json_builder_end_array (builder); + } + + missing_symbols = srt_library_get_missing_symbols (l->data); + if (missing_symbols[0] != NULL) + { + json_builder_set_member_name (builder, "missing-symbols"); + json_builder_begin_array (builder); + for (gsize i = 0; missing_symbols[i] != NULL; i++) + json_builder_add_string_value (builder, missing_symbols[i]); + json_builder_end_array (builder); + } + + misversioned_symbols = srt_library_get_misversioned_symbols (l->data); + if (misversioned_symbols[0] != NULL) + { + json_builder_set_member_name (builder, "misversioned-symbols"); + json_builder_begin_array (builder); + for (gsize i = 0; misversioned_symbols[i] != NULL; i++) + json_builder_add_string_value (builder, misversioned_symbols[i]); + json_builder_end_array (builder); + } + + json_builder_end_object (builder); + } + } + json_builder_end_object (builder); + + return; +} + int main (int argc, char **argv) { - g_print ("{\n"); + FILE *original_stdout = NULL; + GError *error = NULL; + SrtSystemInfo *info; + SrtLibraryIssues issues = SRT_LIBRARY_ISSUES_NONE; + char *expectations = NULL; + gboolean verbose = FALSE; + JsonBuilder *builder; + JsonGenerator *generator; + gboolean can_run = FALSE; + gchar *json_output; + GList *libraries = NULL; + GList *detailed_errors = NULL; + int opt; + static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; + + while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1) + { + switch (opt) + { + case OPTION_EXPECTATION: + expectations = optarg; + break; + + case OPTION_VERBOSE: + verbose = TRUE; + break; + + case OPTION_HELP: + usage (0); + break; + + case '?': + default: + usage (1); + break; /* not reached */ + } + } + + if (optind != argc) + usage (1); + + /* stdout is reserved for machine-readable output, so avoid having + * things like g_debug() pollute it. */ + original_stdout = divert_stdout_to_stderr (&error); + + if (original_stdout == NULL) + { + g_warning ("%s", error->message); + g_clear_error (&error); + return 1; + } + + info = srt_system_info_new (expectations); + + builder = json_builder_new (); + json_builder_begin_object (builder); + + json_builder_set_member_name (builder, "can-write-uinput"); + json_builder_add_boolean_value (builder, srt_system_info_can_write_to_uinput (info)); + + json_builder_set_member_name (builder, "architectures"); + json_builder_begin_object (builder); + + for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++) + { + json_builder_set_member_name (builder, multiarch_tuples[i]); + json_builder_begin_object (builder); + json_builder_set_member_name (builder, "can-run"); + can_run = srt_system_info_can_run (info, multiarch_tuples[i]); + json_builder_add_boolean_value (builder, can_run); + + if (can_run && expectations != NULL) + { + json_builder_set_member_name (builder, "library-issues-summary"); + json_builder_begin_array (builder); + issues = srt_system_info_check_libraries (info, + multiarch_tuples[i], + &libraries); + jsonify_library_issues (builder, issues); + json_builder_end_array (builder); + } + + if (libraries != NULL && (issues != SRT_LIBRARY_ISSUES_NONE || verbose)) + print_libraries_details (builder, libraries, verbose); + + json_builder_end_object (builder); + g_list_free_full (detailed_errors, g_free); + g_list_free_full (libraries, g_object_unref); + } + + json_builder_end_object (builder); + json_builder_end_object (builder); + + JsonNode *root = json_builder_get_root (builder); + generator = json_generator_new (); + json_generator_set_root (generator, root); + json_generator_set_pretty (generator, TRUE); + json_output = json_generator_to_data (generator, NULL); + + if (fputs (json_output, original_stdout) < 0) + g_warning ("Unable to write output: %s", g_strerror (errno)); + + if (fputs ("\n", original_stdout) < 0) + g_warning ("Unable to write final newline: %s", g_strerror (errno)); + + if (fclose (original_stdout) != 0) + g_warning ("Unable to close stdout: %s", g_strerror (errno)); - g_print (" \"can-run\": {\n"); - g_print (" \"%s\": %s,\n", SRT_ABI_I386, - srt_architecture_can_run_i386 () ? "true" : "false"); - g_print (" \"%s\": %s\n", SRT_ABI_X86_64, - srt_architecture_can_run_x86_64 () ? "true" : "false"); - g_print (" }\n"); + g_free (json_output); + g_object_unref (generator); + json_node_free (root); + g_object_unref (builder); + g_object_unref (info); - g_print ("}\n"); return 0; } diff --git a/helpers/check-gl.c b/helpers/check-gl.c index 3d81ad9d472fbfdfb600a83418a8bfd7f84df8af..b2ac307f980ee27d2ff02988bd711aeec71b8a78 100644 --- a/helpers/check-gl.c +++ b/helpers/check-gl.c @@ -69,9 +69,6 @@ * [3] http://git.compiz.org/fusion/misc/compiz-manager/tree/compiz-manager */ -/* for strcasestr */ -#define _GNU_SOURCE - #include <ctype.h> #include <locale.h> #include <stdio.h> diff --git a/helpers/check-gles.c b/helpers/check-gles.c index e8571470d927ebd15fed41ffd961094bb12f3613..39192c8d2a025fddbad1a54c83e88c410ed0178c 100644 --- a/helpers/check-gles.c +++ b/helpers/check-gles.c @@ -23,9 +23,6 @@ * Cosimo Cecchi <cosimo@endlessm.com> */ -/* for strcasestr */ -#define _GNU_SOURCE - #include <locale.h> #include <stdlib.h> #include <string.h> diff --git a/helpers/meson.build b/helpers/meson.build index dc993675d01675b187a717794d59e6322a2dd167..1e94285861befeafde76801ca04481cfdbdde85e 100644 --- a/helpers/meson.build +++ b/helpers/meson.build @@ -35,9 +35,6 @@ executable( multiarch + '-inspect-library', 'inspect-library.c', dependencies : [libdl], - c_args : [ - '-D_GNU_SOURCE', - ], install : true, install_dir : join_paths( get_option('libexecdir'), diff --git a/meson.build b/meson.build index dc8c9e606d1236bf0eb0e4b01d9f050ab318ab66..1a2c000cd0883282d5dc5577013ad5f411b41de4 100644 --- a/meson.build +++ b/meson.build @@ -91,6 +91,8 @@ endforeach add_project_arguments(['-std=c99'], language : 'c') +add_project_arguments(['-D_GNU_SOURCE'], language : 'c') + glib = dependency( 'glib-2.0', version : '>= 2.32', diff --git a/steam-runtime-tools/meson.build b/steam-runtime-tools/meson.build index 1211aebc96a0134578611dec3caa341dfba1d525..5d3d0a0dccc5873c31f09f7652067e18b79537e6 100644 --- a/steam-runtime-tools/meson.build +++ b/steam-runtime-tools/meson.build @@ -56,7 +56,6 @@ libsteamrt = library( c_args : [ '-DG_LOG_DOMAIN="' + meson.project_name() + '"', '-D_SRT_COMPILATION', - '-D_GNU_SOURCE', '-D_SRT_SONAME="libsteam-runtime-tools-' + api_major + '.so.' + abi_major + '"', '-D_SRT_MULTIARCH="' + multiarch + '"', '-D_SRT_API_MAJOR="' + api_major + '"', diff --git a/tests/examples-system-info.c b/tests/examples-system-info.c new file mode 100644 index 0000000000000000000000000000000000000000..2a330d5ddd241b05c00e900b0c3a99380e414a79 --- /dev/null +++ b/tests/examples-system-info.c @@ -0,0 +1,457 @@ +/* + * Copyright © 2019 Collabora Ltd. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <steam-runtime-tools/steam-runtime-tools.h> + +#include <steam-runtime-tools/glib-compat.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <json-glib/json-glib.h> + +#include "test-utils.h" + +static const char *argv0; +static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; + +typedef struct +{ + gchar *srcdir; + gchar *builddir; +} Fixture; + +typedef struct +{ + int unused; +} Config; + +static void +setup (Fixture *f, + gconstpointer context) +{ + G_GNUC_UNUSED const Config *config = context; + + f->srcdir = g_strdup (g_getenv ("G_TEST_SRCDIR")); + f->builddir = g_strdup (g_getenv ("G_TEST_BUILDDIR")); + + if (f->srcdir == NULL) + f->srcdir = g_path_get_dirname (argv0); + + if (f->builddir == NULL) + f->builddir = g_path_get_dirname (argv0); +} + +static void +teardown (Fixture *f, + gconstpointer context) +{ + G_GNUC_UNUSED const Config *config = context; + + g_free (f->srcdir); + g_free (f->builddir); +} + +/* + * Test if the expected libraries are available in the + * running system. + */ +static void +libraries_presence (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_arch; + GError *error = NULL; + gchar *output = NULL; + gchar *examples = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + gchar *expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + const gchar *argv[] = + { + "steam-runtime-system-info", "--expectations", expectations_in, NULL + }; + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + NULL, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + g_assert_true (json_object_has_member (json, "architectures")); + json = json_object_get_object_member (json, "architectures"); + + for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++) + { + g_assert_true (json_object_has_member (json, multiarch_tuples[i])); + json_arch = json_object_get_object_member (json, multiarch_tuples[i]); + g_assert_true (json_object_has_member (json_arch, "can-run")); + g_assert_cmpint (json_object_get_boolean_member (json_arch, "can-run"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + g_assert_cmpint (json_object_has_member (json_arch, "library-issues-summary"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + if (json_object_has_member (json_arch, "library-issues-summary")) + { + JsonArray *array; + array = json_object_get_array_member (json_arch, "library-issues-summary"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + } + } + + g_object_unref (parser); + g_object_unref (info); + g_free (examples); + g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +static void +check_libraries_missing (JsonObject *json_arch) +{ + JsonArray *array; + JsonObject *json_details; + JsonObject *json_lib; + if (json_object_has_member (json_arch, "library-issues-summary")) + { + array = json_object_get_array_member (json_arch, "library-issues-summary"); + g_assert_cmpint (json_array_get_length (array), ==, 3); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, "cannot-load"); + g_assert_cmpstr (json_array_get_string_element (array, 1), ==, "missing-symbols"); + g_assert_cmpstr (json_array_get_string_element (array, 2), ==, "misversioned-symbols"); + + g_assert_true (json_object_has_member (json_arch, "library-details")); + json_details = json_object_get_object_member (json_arch, "library-details"); + + g_assert_true (json_object_has_member (json_details, "libgio-MISSING-2.0.so.0")); + json_lib = json_object_get_object_member (json_details, "libgio-MISSING-2.0.so.0"); + g_assert_true (json_object_has_member (json_lib, "path")); + g_assert_cmpstr (json_object_get_string_member (json_lib, "path"), ==, NULL); + g_assert_true (json_object_has_member (json_lib, "issues")); + array = json_object_get_array_member (json_lib, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 1); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, "cannot-load"); + g_assert_false (json_object_has_member (json_lib, "missing-symbols")); + g_assert_false (json_object_has_member (json_lib, "misversioned-symbols")); + + g_assert_true (json_object_has_member (json_details, "libz.so.1")); + json_lib = json_object_get_object_member (json_details, "libz.so.1"); + g_assert_true (json_object_has_member (json_lib, "path")); + g_assert_cmpstr (json_object_get_string_member (json_lib, "path"), !=, NULL); + g_assert_true (json_object_has_member (json_lib, "issues")); + array = json_object_get_array_member (json_lib, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 2); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, "missing-symbols"); + g_assert_cmpstr (json_array_get_string_element (array, 1), ==, "misversioned-symbols"); + array = json_object_get_array_member (json_lib, "missing-symbols"); + g_assert_cmpint (json_array_get_length (array), ==, 1); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, "missing@NotAvailable"); + array = json_object_get_array_member (json_lib, "misversioned-symbols"); + g_assert_cmpint (json_array_get_length (array), ==, 1); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, "crc32@WRONG_VERSION"); + } +} + +/* + * Test libraries that are either not available or with missing and + * misversioned symbols. + */ +static void +libraries_missing (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_arch; + GError *error = NULL; + gchar *output = NULL; + gchar *examples = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + gchar *expectations_in = g_build_filename (f->srcdir, "expectations_with_missings", NULL); + const gchar *argv[] = + { + "steam-runtime-system-info", "--expectations", expectations_in, NULL + }; + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + NULL, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + g_assert_true (json_object_has_member (json, "architectures")); + json = json_object_get_object_member (json, "architectures"); + + for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++) + { + g_assert_true (json_object_has_member (json, multiarch_tuples[i])); + json_arch = json_object_get_object_member (json, multiarch_tuples[i]); + g_assert_true (json_object_has_member (json_arch, "can-run")); + g_assert_cmpint (json_object_get_boolean_member (json_arch, "can-run"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + g_assert_cmpint (json_object_has_member (json_arch, "library-issues-summary"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + + check_libraries_missing (json_arch); + } + + g_object_unref (parser); + g_object_unref (info); + g_free (examples); + g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +static void +check_library_no_errors (JsonObject *json_details, + const char *library_soname) +{ + JsonObject *json_lib; + + g_assert_true (json_object_has_member (json_details, library_soname)); + json_lib = json_object_get_object_member (json_details, library_soname); + g_assert_true (json_object_has_member (json_lib, "path")); + g_assert_cmpstr (json_object_get_string_member (json_lib, "path"), !=, NULL); + g_assert_false (json_object_has_member (json_lib, "issues")); + g_assert_false (json_object_has_member (json_lib, "missing-symbols")); + g_assert_false (json_object_has_member (json_lib, "misversioned-symbols")); +} + +static void +check_libraries_verbose (JsonObject *json_arch) +{ + JsonArray *array; + JsonObject *json_details; + if (json_object_has_member (json_arch, "library-issues-summary")) + { + array = json_object_get_array_member (json_arch, "library-issues-summary"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + g_assert_true (json_object_has_member (json_arch, "library-details")); + json_details = json_object_get_object_member (json_arch, "library-details"); + + check_library_no_errors (json_details, "libgio-2.0.so.0"); + + check_library_no_errors (json_details, "libglib-2.0.so.0"); + + check_library_no_errors (json_details, "libz.so.1"); + } +} + +/* + * Test the presence of libraries with the verbose option + */ +static void +libraries_presence_verbose (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_arch; + GError *error = NULL; + gchar *output = NULL; + gchar *examples = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + gchar *expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + const gchar *argv[] = + { + "steam-runtime-system-info", "--expectations", expectations_in, "--verbose", NULL + }; + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + NULL, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + g_assert_true (json_object_has_member (json, "architectures")); + json = json_object_get_object_member (json, "architectures"); + + for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++) + { + g_assert_true (json_object_has_member (json, multiarch_tuples[i])); + json_arch = json_object_get_object_member (json, multiarch_tuples[i]); + g_assert_true (json_object_has_member (json_arch, "can-run")); + g_assert_cmpint (json_object_get_boolean_member (json_arch, "can-run"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + g_assert_cmpint (json_object_has_member (json_arch, "library-issues-summary"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + + check_libraries_verbose (json_arch); + } + + g_object_unref (parser); + g_object_unref (info); + g_free (examples); + g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +/* + * Test `steam-runtime-system-info` with no additional arguments + */ +static void +no_arguments (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_arch; + GError *error = NULL; + gchar *output = NULL; + gchar *examples = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + gchar *expectations_in = g_build_filename (f->srcdir, "expectations", NULL); + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + NULL, /* envp */ + G_SPAWN_SEARCH_PATH, + NULL, /* child setup */ + NULL, /* user data */ + &output, /* stdout */ + NULL, /* stderr */ + &exit_status, + &error)); + g_assert_cmpint (exit_status, ==, 0); + g_assert_null (error); + g_assert_nonnull (output); + + /* We can't use `json_from_string()` directly because we are targeting an + * older json-glib version */ + parser = json_parser_new (); + g_assert_true (json_parser_load_from_data (parser, output, -1, NULL)); + node = json_parser_get_root (parser); + json = json_node_get_object (node); + + g_assert_true (json_object_has_member (json, "can-write-uinput")); + + g_assert_true (json_object_has_member (json, "architectures")); + json = json_object_get_object_member (json, "architectures"); + + for (gsize i = 0; i < G_N_ELEMENTS (multiarch_tuples); i++) + { + g_assert_true (json_object_has_member (json, multiarch_tuples[i])); + json_arch = json_object_get_object_member (json, multiarch_tuples[i]); + g_assert_true (json_object_has_member (json_arch, "can-run")); + g_assert_cmpint (json_object_get_boolean_member (json_arch, "can-run"), + ==, srt_system_info_can_run (info, multiarch_tuples[i])); + } + + g_object_unref (parser); + g_object_unref (info); + g_free (examples); + g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +int +main (int argc, + char **argv) +{ + argv0 = argv[0]; + + g_test_init (&argc, &argv, NULL); + g_test_add ("/system-info/libraries_presence", Fixture, NULL, + setup, libraries_presence, teardown); + g_test_add ("/system-info/libraries_missing", Fixture, NULL, + setup, libraries_missing, teardown); + g_test_add ("/system-info/libraries_presence_verbose", Fixture, NULL, + setup, libraries_presence_verbose, teardown); + g_test_add ("/system-info/no_arguments", Fixture, NULL, + setup, no_arguments, teardown); + + return g_test_run (); +} diff --git a/tests/meson.build b/tests/meson.build index 5ff5cf52ef99b9a57f71ecfb243e17c4c1c26eec..4a9d6e0d3dc28a3aa969e49af6a1c820aeef62fd 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -25,9 +25,11 @@ test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) test_env.set('SRT_HELPERS_PATH', join_paths(meson.current_build_dir(), '..', 'helpers')) +test_env.prepend('PATH', join_paths(meson.current_build_dir(), '..', 'examples')) tests = [ 'architecture', + 'examples-system-info', 'library', 'system-info', ] @@ -53,7 +55,7 @@ foreach test_name : tests c_args : [ '-D_SRT_MULTIARCH="' + multiarch + '"', ], - dependencies : [glib, gobject, libsteamrt_dep], + dependencies : [glib, gobject, libsteamrt_dep, json_glib], install : get_option('installed_tests'), install_dir : tests_dir, )