diff --git a/tests/fake-home.c b/tests/fake-home.c new file mode 100644 index 0000000000000000000000000000000000000000..c60da94021c8acdb0e7e448af7eedd013bf785f8 --- /dev/null +++ b/tests/fake-home.c @@ -0,0 +1,295 @@ +/* + * 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 "fake-home.h" + +#include <errno.h> +#include <fcntl.h> +#include <ftw.h> +#include <gio/gio.h> +#include <glib.h> +#include <glib/gstdio.h> + +#include "test-utils.h" + +/** + * fake_home_new: + * + * Create a new #FakeHome object and a temporary folder in the file system + * + * Returns: the newely created #FakeHome object + */ +FakeHome * +fake_home_new (void) +{ + GError *error = NULL; + FakeHome *fake_home = g_slice_new (FakeHome); + fake_home->home = g_dir_make_tmp ("fake-home-XXXXXX", &error); + g_assert_no_error (error); + + fake_home->create_pinning_libs = TRUE; + fake_home->create_i386_folders = TRUE; + fake_home->create_amd64_folders = TRUE; + fake_home->create_steam_symlink = TRUE; + fake_home->create_steamrt_files = TRUE; + fake_home->add_environments = TRUE; + fake_home->has_debian_bug_916303 = FALSE; + + return fake_home; +} + +/** + * fake_home_create_structure: + * @f: a #FakeHome object used to create the folder structure + * + * Create folders and files like a canonical Steam Installation. + * As the home directory, the temporary folder stored in `home` is used. + * A custom environ is also created and stored under `env`. + * + * Returns: %TRUE if the creation was successful + */ +gboolean +fake_home_create_structure (FakeHome *f) +{ + gboolean ret = FALSE; + GFile *symlink = NULL; + gchar *dot_steam = NULL; + gchar *scripts = NULL; + gchar *run = NULL; + gchar *setup = NULL; + gchar *version = NULL; + gchar *dot_steam_bin32 = NULL; + gchar *dot_steam_steam = NULL; + gchar *local_share = NULL; + gchar *ld_path = NULL; + gchar *prepended_path = NULL; + gchar *ubuntu12_32 = NULL; + GError *error = NULL; + int saved_errno; + + g_return_val_if_fail (f != NULL, FALSE); + + dot_steam = g_build_filename (f->home, ".steam", NULL); + + if (f->has_debian_bug_916303) + f->steam_base_folder = g_strdup (dot_steam); + else + f->steam_base_folder = g_build_filename (f->home, ".local", "share", + "Steam", NULL); + + f->runtime = g_build_filename (f->steam_base_folder, "ubuntu12_32", + "steam-runtime", NULL); + + scripts = g_build_filename (f->runtime, "scripts", NULL); + + if (g_mkdir_with_parents (dot_steam, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->steam_base_folder, 0755) != 0) + goto out; + if (g_mkdir_with_parents (scripts, 0755) != 0) + goto out; + + f->pinned_32 = g_build_filename (f->runtime, "pinned_libs_32", NULL); + f->pinned_64 = g_build_filename (f->runtime, "pinned_libs_64", NULL); + f->i386_lib_i386 = g_build_filename (f->runtime, "i386", "lib", "i386-linux-gnu", NULL); + f->i386_lib = g_build_filename (f->runtime, "i386", "lib", NULL); + f->i386_usr_lib_i386 = g_build_filename (f->runtime, "i386", "usr", "lib", "i386-linux-gnu", NULL); + f->i386_usr_lib = g_build_filename (f->runtime, "i386", "usr", "lib", NULL); + f->i386_usr_bin = g_build_filename (f->runtime, "i386", "usr", "bin", NULL); + f->amd64_lib_64 = g_build_filename (f->runtime, "amd64", "lib", "x86_64-linux-gnu", NULL); + f->amd64_lib = g_build_filename (f->runtime, "amd64", "lib", NULL); + f->amd64_usr_lib_64 = g_build_filename (f->runtime, "amd64", "usr", "lib", "x86_64-linux-gnu", NULL); + f->amd64_usr_lib = g_build_filename (f->runtime, "amd64", "usr", "lib", NULL); + f->amd64_bin = g_build_filename (f->runtime, "amd64", "bin", NULL); + f->amd64_usr_bin = g_build_filename (f->runtime, "amd64", "usr", "bin", NULL); + + if (f->create_pinning_libs) + { + if (g_mkdir_with_parents (f->pinned_32, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->pinned_64, 0755) != 0) + goto out; + } + + if (f->create_i386_folders) + { + if (g_mkdir_with_parents (f->i386_lib_i386, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->i386_usr_lib_i386, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->i386_usr_bin, 0755) != 0) + goto out; + } + + if (f->create_amd64_folders) + { + if (g_mkdir_with_parents (f->amd64_lib_64, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->amd64_usr_lib_64, 0755) != 0) + goto out; + if (g_mkdir_with_parents (f->amd64_usr_bin, 0755) != 0) + goto out; + } + + if (f->create_steamrt_files) + { + run = g_build_filename (f->runtime, "run.sh", NULL); + setup = g_build_filename (f->runtime, "setup.sh", NULL); + version = g_build_filename (f->runtime, "version.txt", NULL); + + if (g_creat (run, 0755) < 0) + goto out; + if (g_creat (setup, 0755) < 0) + goto out; + if (g_creat (version, 0755) < 0) + goto out; + + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + } + + if (f->has_debian_bug_916303) + { + dot_steam_steam = g_build_filename (dot_steam, "steam", NULL); + if (g_mkdir_with_parents (dot_steam_steam, 0755) != 0) + goto out; + } + + if (f->create_steam_symlink) + { + dot_steam_steam = g_build_filename (dot_steam, "steam", NULL); + symlink = g_file_new_for_path (dot_steam_steam); + + g_file_make_symbolic_link (symlink, f->steam_base_folder, NULL, &error); + g_object_unref (symlink); + if (f->has_debian_bug_916303) + { + g_error_free (error); + error = NULL; + } + else + { + g_assert_no_error (error); + } + + dot_steam_bin32 = g_build_filename (dot_steam, "bin32", NULL); + symlink = g_file_new_for_path (dot_steam_bin32); + + ubuntu12_32 = g_build_filename (f->steam_base_folder, "ubuntu12_32", NULL); + g_file_make_symbolic_link (symlink, ubuntu12_32, NULL, &error); + g_object_unref (symlink); + g_assert_no_error (error); + } + + local_share = g_build_filename (f->home, ".local", "share", NULL); + f->env = g_get_environ (); + f->env = g_environ_setenv (f->env, "HOME", f->home, TRUE); + f->env = g_environ_setenv (f->env, "XDG_DATA_HOME", local_share, TRUE); + + if (f->add_environments) + { + const gchar *path; + + f->env = g_environ_setenv (f->env, "STEAM_RUNTIME", f->runtime, TRUE); + + ld_path = g_strjoin (":", + f->pinned_32, + f->pinned_64, + f->i386_lib_i386, + f->i386_lib, + f->i386_usr_lib_i386, + f->i386_usr_lib, + f->amd64_lib_64, + f->amd64_lib, + f->amd64_usr_lib_64, + f->amd64_usr_lib, + NULL); + f->env = g_environ_setenv (f->env, "LD_LIBRARY_PATH", ld_path, TRUE); + + path = g_environ_getenv (f->env, "PATH"); + + prepended_path = g_strjoin (":", + f->amd64_bin, + f->amd64_usr_bin, + path, + NULL); + + f->env = g_environ_setenv (f->env, "PATH", prepended_path, TRUE); + } + + ret = TRUE; + + out: + saved_errno = errno; + g_free (dot_steam); + g_free (scripts); + g_free (run); + g_free (setup); + g_free (version); + g_free (dot_steam_steam); + g_free (local_share); + g_free (ld_path); + g_free (prepended_path); + + if (!ret) + g_warning ("Unable to create directories: %s", g_strerror (saved_errno)); + + return ret; + +} + +/** + * fake_home_clean_up: + * @f: the #FakeHome object to clean up and finalize + * + * Clean up a #FakeHome object recursively removing the created + * home directory and freeing @f. + */ +void +fake_home_clean_up (FakeHome *f) +{ + if (!rm_rf (f->home)) + g_debug ("Unable to remove the fake home directory: %s", f->home); + + g_free (f->home); + g_free (f->steam_base_folder); + g_free (f->runtime); + g_free (f->pinned_32); + g_free (f->pinned_64); + g_free (f->i386_lib_i386); + g_free (f->i386_lib); + g_free (f->i386_usr_lib_i386); + g_free (f->i386_usr_lib); + g_free (f->i386_usr_bin); + g_free (f->amd64_lib_64); + g_free (f->amd64_lib); + g_free (f->amd64_usr_lib_64); + g_free (f->amd64_usr_lib); + g_free (f->amd64_bin); + g_free (f->amd64_usr_bin); + g_strfreev (f->env); + + g_slice_free (FakeHome, f); +} diff --git a/tests/fake-home.h b/tests/fake-home.h new file mode 100644 index 0000000000000000000000000000000000000000..60a9846c0ace729554c7f7263eff9d0a3014d833 --- /dev/null +++ b/tests/fake-home.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#pragma once + +#include <glib.h> +#include <glib-object.h> + +typedef struct +{ + gboolean create_pinning_libs; + gboolean create_i386_folders; + gboolean create_amd64_folders; + gboolean create_steam_symlink; + gboolean create_steamrt_files; + gboolean add_environments; + gboolean has_debian_bug_916303; + + gchar *home; + gchar *steam_base_folder; + gchar *runtime; + gchar *pinned_32; + gchar *pinned_64; + gchar *i386_lib_i386; + gchar *i386_lib; + gchar *i386_usr_lib_i386; + gchar *i386_usr_lib; + gchar *i386_usr_bin; + gchar *amd64_lib_64; + gchar *amd64_lib; + gchar *amd64_usr_lib_64; + gchar *amd64_usr_lib; + gchar *amd64_bin; + gchar *amd64_usr_bin; + GStrv env; +} FakeHome; + +FakeHome * fake_home_new (void); +gboolean fake_home_create_structure (FakeHome *fake_home); +void fake_home_clean_up (FakeHome *f); diff --git a/tests/meson.build b/tests/meson.build index 53a323a8aaf07477412fdbcad6ae68cad1947451..12f2363e29678d044babd1916af99b14abe0f400 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -34,6 +34,11 @@ tests = [ 'system-info-cli', ] +tests_utils = [ + 'fake-home.c', + 'test-utils.c', +] + tests_dir = join_paths( get_option('libexecdir'), 'installed-tests', @@ -52,7 +57,7 @@ install_subdir('fake-steam-runtime', install_dir : tests_dir) foreach test_name : tests exe = executable( 'test-' + test_name, - files(test_name + '.c'), + files(test_name + '.c') + tests_utils, c_args : [ '-D_SRT_MULTIARCH="' + multiarch + '"', ], diff --git a/tests/system-info-cli.c b/tests/system-info-cli.c index f80e912f9f91daa7a0b87269f8a7504743ccfb22..93615bb96f8336ee7d0edafffd9a9d4860551b0d 100644 --- a/tests/system-info-cli.c +++ b/tests/system-info-cli.c @@ -39,6 +39,7 @@ #include <json-glib/json-glib.h> #include "test-utils.h" +#include "fake-home.h" static const char *argv0; static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 }; @@ -346,6 +347,10 @@ libraries_presence_verbose (Fixture *f, g_assert_true (json_object_has_member (json, "can-write-uinput")); + g_assert_true (json_object_has_member (json, "steam-installation")); + + g_assert_true (json_object_has_member (json, "runtime")); + g_assert_true (json_object_has_member (json, "architectures")); json = json_object_get_object_member (json, "architectures"); @@ -384,7 +389,6 @@ no_arguments (Fixture *f, GError *error = NULL; gchar *output = 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 */ @@ -424,7 +428,186 @@ no_arguments (Fixture *f, g_object_unref (parser); g_object_unref (info); - g_free (expectations_in); + g_free (output); + g_clear_error (&error); +} + +/* + * Test a system with a good Steam installation. + */ +static void +steam_presence (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_sub_object; + JsonArray *array; + GError *error = NULL; + gchar *output = NULL; + const gchar *path = NULL; + const gchar *version = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + fake_home->env, /* 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, "steam-installation")); + json_sub_object = json_object_get_object_member (json, "steam-installation"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + g_assert_true (json_object_has_member (json, "runtime")); + json_sub_object = json_object_get_object_member (json, "runtime"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "version")); + version = json_object_get_string_member (json_sub_object, "version"); + g_assert_cmpstr (version, !=, NULL); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 0); + + g_assert_true (json_object_has_member (json, "architectures")); + + fake_home_clean_up (fake_home); + g_object_unref (parser); + g_object_unref (info); + g_free (output); + g_clear_error (&error); +} + +/* + * Test a system with a Steam installation with issues. + */ +static void +steam_issues (Fixture *f, + gconstpointer context) +{ + int exit_status = -1; + JsonParser *parser = NULL; + JsonNode *node = NULL; + JsonObject *json; + JsonObject *json_sub_object; + JsonArray *array; + GError *error = NULL; + gchar *output = NULL; + const gchar *path = NULL; + const gchar *version = NULL; + SrtSystemInfo *info = srt_system_info_new (NULL); + const gchar *argv[] = { "steam-runtime-system-info", NULL }; + FakeHome *fake_home; + + g_assert_true (TRUE); + + fake_home = fake_home_new (); + fake_home->create_pinning_libs = FALSE; + fake_home->create_steam_symlink = FALSE; + fake_home->create_steamrt_files = FALSE; + fake_home_create_structure (fake_home); + + g_assert_true (g_spawn_sync (NULL, /* working directory */ + (gchar **) argv, + fake_home->env, /* 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, "steam-installation")); + json_sub_object = json_object_get_object_member (json, "steam-installation"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 1); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, + "dot-steam-steam-not-symlink"); + + g_assert_true (json_object_has_member (json, "runtime")); + json_sub_object = json_object_get_object_member (json, "runtime"); + + g_assert_true (json_object_has_member (json_sub_object, "path")); + path = json_object_get_string_member (json_sub_object, "path"); + g_assert_cmpstr (path, !=, NULL); + g_assert_true (path[0] == '/'); + + g_assert_true (json_object_has_member (json_sub_object, "version")); + version = json_object_get_string_member (json_sub_object, "version"); + g_assert_cmpstr (version, ==, NULL); + + g_assert_true (json_object_has_member (json_sub_object, "issues")); + array = json_object_get_array_member (json_sub_object, "issues"); + g_assert_cmpint (json_array_get_length (array), ==, 2); + g_assert_cmpstr (json_array_get_string_element (array, 0), ==, + "not-runtime"); + g_assert_cmpstr (json_array_get_string_element (array, 1), ==, + "not-using-newer-host-libraries"); + + g_assert_true (json_object_has_member (json, "architectures")); + + fake_home_clean_up (fake_home); + g_object_unref (parser); + g_object_unref (info); g_free (output); g_clear_error (&error); } @@ -436,14 +619,18 @@ main (int argc, argv0 = argv[0]; g_test_init (&argc, &argv, NULL); - g_test_add ("/system-info/libraries_presence", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_presence", Fixture, NULL, setup, libraries_presence, teardown); - g_test_add ("/system-info/libraries_missing", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_missing", Fixture, NULL, setup, libraries_missing, teardown); - g_test_add ("/system-info/libraries_presence_verbose", Fixture, NULL, + g_test_add ("/system-info-cli/libraries_presence_verbose", Fixture, NULL, setup, libraries_presence_verbose, teardown); - g_test_add ("/system-info/no_arguments", Fixture, NULL, + g_test_add ("/system-info-cli/no_arguments", Fixture, NULL, setup, no_arguments, teardown); + g_test_add ("/system-info-cli/steam_presence", Fixture, NULL, + setup, steam_presence, teardown); + g_test_add ("/system-info-cli/steam_issues", Fixture, NULL, + setup, steam_issues, teardown); return g_test_run (); } diff --git a/tests/system-info.c b/tests/system-info.c index afe4d58b1c1a9da6bc8c40b04630d6eb941dcda9..b7d73821e6c75483bd62cbc13609d329c32ed7bc 100644 --- a/tests/system-info.c +++ b/tests/system-info.c @@ -29,14 +29,17 @@ #include <glib.h> #include <glib/gstdio.h> +#include <gio/gio.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> +#include <ftw.h> #include "test-utils.h" +#include "fake-home.h" static const char *argv0; @@ -679,6 +682,497 @@ wrong_expectations (Fixture *f, g_object_unref (info); } +static void +steam_runtime (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues runtime_issues; + SrtSteamIssues steam_issues; + gchar *runtime_path = NULL; + gchar *installation_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + /* Check for runtime issues */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NONE); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, fake_home->runtime); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, fake_home->steam_base_folder); + g_free (runtime_path); + g_free (installation_path); + + /* Do the check again, this time using the cache */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NONE); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, fake_home->runtime); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, fake_home->steam_base_folder); + + /* Check for Steam issues */ + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Do the check again, this time using the cache */ + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_RUNTIME_ISSUES_NONE); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (runtime_path); + g_free (installation_path); +} + +static void +steam_runtime_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues runtime_issues; + SrtSteamIssues steam_issues; + gchar *full_ld_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + full_ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + + info = srt_system_info_new (NULL); + + /* Unset LD_LIBRARY_PATH */ + fake_home->env = g_environ_unsetenv (fake_home->env, "LD_LIBRARY_PATH"); + srt_system_info_set_environ (info, fake_home->env); + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues, ==, SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH); + + /* Re set LD_LIBRARY_PATH and remove a required folder from the runtime */ + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", full_ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + g_assert_cmpint (g_rmdir (fake_home->amd64_usr_lib_64), ==, 0); + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_RUNTIME, !=, 0); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH, !=, 0); + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_STEAM_ISSUES_NONE); + + /* Do the check again, this time using the cache */ + runtime_issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_RUNTIME, !=, 0); + g_assert_cmpint (runtime_issues & SRT_RUNTIME_ISSUES_NOT_IN_LD_PATH, !=, 0); + steam_issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (steam_issues, ==, SRT_STEAM_ISSUES_NONE); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (full_ld_path); +} + +static void +steam_runtime_pinned (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *full_ld_path = NULL; + gchar *ld_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + full_ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + + info = srt_system_info_new (NULL); + + /* Move the pinned libraries at the end of LD_LIBRARY_PATH */ + ld_path = g_strjoin (":", + fake_home->i386_lib_i386, + fake_home->i386_lib, + fake_home->i386_usr_lib_i386, + fake_home->i386_usr_lib, + fake_home->amd64_lib_64, + fake_home->amd64_lib, + fake_home->amd64_usr_lib_64, + fake_home->amd64_usr_lib, + fake_home->pinned_32, + fake_home->pinned_64, + NULL); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + g_free (ld_path); + + /* Remove the pinned library folder */ + g_assert_cmpint (g_rmdir (fake_home->pinned_32), ==, 0); + g_assert_cmpint (g_rmdir (fake_home->pinned_64), ==, 0); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", full_ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + + /* Remove pinned libraries from LD_LIBRARY_PATH */ + ld_path = g_strjoin (":", + fake_home->i386_lib_i386, + fake_home->i386_lib, + fake_home->i386_usr_lib_i386, + fake_home->i386_usr_lib, + fake_home->amd64_lib_64, + fake_home->amd64_lib, + fake_home->amd64_usr_lib_64, + fake_home->amd64_usr_lib, + NULL); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES, ==, issues); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (full_ld_path); + g_free (ld_path); +} + +static void +runtime_disabled_or_missing (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *runtime_path = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steamrt_files = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + + /* Completely disable the runtime */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", "0", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_DISABLED); + runtime_path = srt_system_info_dup_runtime_path (info); + g_assert_cmpstr (runtime_path, ==, NULL); + + /* Set the runtime to a relative position. + * Test if we can revover using the expected path. + * We didn't create SteamRT files so expect to receive a "not_runtime" + * issue. */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", "my/not/absolute/runtime/path", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + /* Remove the STEAM_RUNTIME environment. */ + fake_home->env = g_environ_unsetenv (fake_home->env, "STEAM_RUNTIME"); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + /* Disable prefer host libraries */ + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME_PREFER_HOST_LIBRARIES", "0", TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_USING_NEWER_HOST_LIBRARIES | + SRT_RUNTIME_ISSUES_NOT_IN_ENVIRONMENT | + SRT_RUNTIME_ISSUES_NOT_RUNTIME, ==, issues); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + +static void +runtime_version (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *version = NULL; + gchar *dup_version = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home_create_structure (fake_home); + + version = g_build_filename (fake_home->runtime, "version.txt", NULL); + info = srt_system_info_new (NULL); + + /* Check version with a trailing new line */ + g_file_set_contents (version, "steam-runtime_0.20190711.3\n", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check version with an empty number */ + g_file_set_contents (version, "steam-runtime_", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + + /* Check version without underscore */ + g_file_set_contents (version, "steam-runtime", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, NULL); + + /* Check version with a custom prefix */ + g_file_set_contents (version, "custom-steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + + /* Check version with a custom prefix and multiple underscores */ + g_file_set_contents (version, "custom_steam_runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, "0.20190711.3"); + g_free (dup_version); + + /* Check an empty version file */ + g_file_set_contents (version, "", -1, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NOT_RUNTIME); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, NULL); + + /* Check expected version */ + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check expected version with trailing new line */ + g_file_set_contents (version, "steam-runtime_0.20190711.3\n", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_NONE); + + /* Check wrong expected version */ + g_file_set_contents (version, "steam-runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20210813.4"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION); + + /* Check wrong expected version */ + g_file_set_contents (version, "steam-runtime_", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20180101.2"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (SRT_RUNTIME_ISSUES_NOT_RUNTIME | + SRT_RUNTIME_ISSUES_UNEXPECTED_VERSION, ==, issues); + dup_version = srt_system_info_dup_runtime_version (info); + g_assert_cmpstr (dup_version, ==, ""); + + /* Check expected version with custom prefix */ + g_file_set_contents (version, "my-custom_steam_runtime_0.20190711.3", -1, &error); + g_assert_no_error (error); + srt_system_info_set_expected_runtime_version (info, "0.20190711.3"); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNOFFICIAL); + + fake_home_clean_up (fake_home); + g_object_unref (info); + g_free (version); + g_free (dup_version); +} + +static void +runtime_unexpected_location (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtRuntimeIssues issues; + gchar *dot_steam_steam = NULL; + gchar *my_runtime = NULL; + gchar *ld_path = NULL; + gchar *env_path = NULL; + gchar **parts = NULL; + GFile *symlink = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steam_symlink = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + dot_steam_steam = g_build_filename (fake_home->home, ".steam", "steam", NULL); + + my_runtime = g_build_filename (fake_home->steam_base_folder, "ubuntu12_32", + "my-runtime", NULL); + + + /* Create a new homedir/.steam/steam symlink that doesn't point to + * the expected steam runtime path. */ + symlink = g_file_new_for_path (dot_steam_steam); + g_file_make_symbolic_link (symlink, fake_home->pinned_64, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION); + g_object_unref (symlink); + + /* Move the steam-runtime to another location called "my-runtime" and + * adjust all the environment variables accordingly. */ + ld_path = g_strdup (g_environ_getenv (fake_home->env, "LD_LIBRARY_PATH")); + parts = g_strsplit (ld_path, "steam-runtime", -1); + g_free (ld_path); + ld_path = g_strjoinv ("my-runtime", parts); + g_strfreev (parts); + + env_path = g_strdup (g_environ_getenv (fake_home->env, "PATH")); + parts = g_strsplit (env_path, "steam-runtime", -1); + g_free (env_path); + env_path = g_strjoinv ("my-runtime", parts); + + g_rename (fake_home->runtime, my_runtime); + g_remove (dot_steam_steam); + symlink = g_file_new_for_path (dot_steam_steam); + g_file_make_symbolic_link (symlink, my_runtime, NULL, &error); + g_assert_no_error (error); + fake_home->env = g_environ_setenv (fake_home->env, "LD_LIBRARY_PATH", ld_path, TRUE); + fake_home->env = g_environ_setenv (fake_home->env, "STEAM_RUNTIME", my_runtime, TRUE); + fake_home->env = g_environ_setenv (fake_home->env, "PATH", env_path, TRUE); + + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_runtime_issues (info); + g_assert_cmpint (issues, ==, SRT_RUNTIME_ISSUES_UNEXPECTED_LOCATION); + + fake_home_clean_up (fake_home); + g_object_unref (symlink); + g_object_unref (info); + g_free (dot_steam_steam); + g_free (my_runtime); + g_free (ld_path); + g_free (env_path); + g_strfreev (parts); +} + +static void +steam_symlink (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtSteamIssues issues; + gchar *data_home = NULL; + gchar *dot_steam_steam = NULL; + gchar *dot_steam_root = NULL; + gchar *dot_steam_bin32 = NULL; + gchar *installation_path = NULL; + gchar *ubuntu12_32 = NULL; + GFile *symlink = NULL; + GError *error = NULL; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->create_steam_symlink = FALSE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + dot_steam_steam = g_build_filename (fake_home->home, ".steam", "steam", NULL); + dot_steam_root = g_build_filename (fake_home->home, ".steam", "root", NULL); + dot_steam_bin32 = g_build_filename (fake_home->home, ".steam", "bin32", NULL); + ubuntu12_32 = g_build_filename (fake_home->steam_base_folder, "ubuntu12_32", NULL); + + /* We don't have a homedir/.steam/steam symlink. */ + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + + /* Create the homedir/.steam/root symlink. */ + symlink = g_file_new_for_path (dot_steam_root); + g_file_make_symbolic_link (symlink, fake_home->steam_base_folder, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + g_object_unref (symlink); + + /* Remove homedir/.steam/root symlink and create homedir/.steam/bin32 symlink. */ + g_remove (dot_steam_root); + symlink = g_file_new_for_path (dot_steam_bin32); + g_file_make_symbolic_link (symlink, ubuntu12_32, NULL, &error); + g_assert_no_error (error); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + + /* Remove the homedir/.steam/bin32 symlink and set XDG_DATA_HOME env to a + * folder that is not the expected homedir/.local/share */ + g_remove (dot_steam_bin32); + data_home = g_build_filename (fake_home->home, "DataHome", NULL); + fake_home->env = g_environ_setenv (fake_home->env, "XDG_DATA_HOME", data_home, TRUE); + srt_system_info_set_environ (info, fake_home->env); + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (SRT_STEAM_ISSUES_CANNOT_FIND | + SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK, ==, issues); + installation_path = srt_system_info_dup_steam_installation_path (info); + g_assert_cmpstr (installation_path, ==, NULL); + + fake_home_clean_up (fake_home); + g_object_unref (symlink); + g_object_unref (info); + g_free (data_home); + g_free (dot_steam_steam); + g_free (dot_steam_root); + g_free (dot_steam_bin32); + g_free (ubuntu12_32); +} + +/* Recreate the conditions that triggered the Debian bug 916303. + * Steam was installed into "~/.steam", which meant that the "steam/" + * directory inside the Steam installation collided with the "~/.steam/steam" + * symlink, preventing the symlink from being created. */ +static void +debian_bug_916303 (Fixture *f, + gconstpointer context) +{ + SrtSystemInfo *info; + SrtSteamIssues issues; + FakeHome *fake_home; + + fake_home = fake_home_new (); + fake_home->has_debian_bug_916303 = TRUE; + fake_home_create_structure (fake_home); + + info = srt_system_info_new (NULL); + srt_system_info_set_environ (info, fake_home->env); + + issues = srt_system_info_get_steam_issues (info); + g_assert_cmpint (issues, ==, SRT_STEAM_ISSUES_DOT_STEAM_STEAM_NOT_SYMLINK); + + fake_home_clean_up (fake_home); + g_object_unref (info); +} + int main (int argc, char **argv) @@ -701,5 +1195,22 @@ main (int argc, g_test_add ("/system-info/wrong_expectations", Fixture, NULL, setup, wrong_expectations, teardown); + g_test_add ("/system-info/steam_runtime", Fixture, NULL, + setup, steam_runtime, teardown); + g_test_add ("/system-info/steam_runtime_missing", Fixture, NULL, + setup, steam_runtime_missing, teardown); + g_test_add ("/system-info/steam_runtime_pinned", Fixture, NULL, + setup, steam_runtime_pinned, teardown); + g_test_add ("/system-info/runtime_disabled_or_missing", Fixture, NULL, + setup, runtime_disabled_or_missing, teardown); + g_test_add ("/system-info/runtime_version", Fixture, NULL, + setup, runtime_version, teardown); + g_test_add ("/system-info/runtime_unexpected_location", Fixture, NULL, + setup, runtime_unexpected_location, teardown); + g_test_add ("/system-info/steam_symlink", Fixture, NULL, + setup, steam_symlink, teardown); + g_test_add ("/system-info/debian_bug_916303", Fixture, NULL, + setup, debian_bug_916303, teardown); + return g_test_run (); } diff --git a/tests/test-utils.c b/tests/test-utils.c new file mode 100644 index 0000000000000000000000000000000000000000..969c790a8198b673764b0a9cec5f00be5a8ee28c --- /dev/null +++ b/tests/test-utils.c @@ -0,0 +1,61 @@ +/* + * 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 "test-utils.h" + +#include <ftw.h> +#include <glib.h> +#include <stdio.h> + +static gint +ftw_remove (const gchar *path, + const struct stat *sb, + gint typeflags, + struct FTW *ftwbuf) +{ + if (remove (path) < 0) + return -1; + + return 0; +} + +/** + * rm_rf: + * @directory: (type filename): The folder to remove. + * + * Recursively delete @directory within the same file system and + * without following symbolic links. + * + * Returns: %TRUE if the removal was successful + */ +gboolean rm_rf (char *directory) +{ + g_return_val_if_fail (directory != NULL, FALSE); + + if (nftw (directory, ftw_remove, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0) + return FALSE; + + return TRUE; +} \ No newline at end of file diff --git a/tests/test-utils.h b/tests/test-utils.h index e963a25495cc255fcff308d79ac4162e1ff079f8..8bb481e427984417e999b1d7dbf8836e10c45a44 100644 --- a/tests/test-utils.h +++ b/tests/test-utils.h @@ -64,3 +64,5 @@ #if !GLIB_CHECK_VERSION(2, 38, 0) #define g_test_skip(msg) g_test_message ("SKIP: %s", msg) #endif + +gboolean rm_rf (char *directory);