From 561629f9a4398f9faea4466868a3a4ba8a367481 Mon Sep 17 00:00:00 2001
From: Ludovico de Nittis <ludovico.denittis@collabora.com>
Date: Wed, 21 Apr 2021 16:37:00 +0200
Subject: [PATCH] tests: Add coverage for the dynamic linker expansions

Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com>
---
 tests/meson.build        |  19 ++++++
 tests/mock-bad-detect.c  |  45 +++++++++++++
 tests/mock-good-detect.c |  43 ++++++++++++
 tests/system-info.c      | 141 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+)
 create mode 100644 tests/mock-bad-detect.c
 create mode 100644 tests/mock-good-detect.c

diff --git a/tests/meson.build b/tests/meson.build
index ce0955bf3..59b035546 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -287,6 +287,25 @@ foreach helper : [
   )
 endforeach
 
+foreach token : [
+  'LIB',
+  'PLATFORM',
+]
+  foreach helper : [
+    'mock-good-detect',
+    'mock-bad-detect',
+  ]
+    executable(
+      helper + '-' + token.to_lower(),
+      helper + '.c',
+      c_args : ['-DMOCK_LIBDL_TOKEN_' + token],
+      include_directories : project_include_dirs,
+      install: true,
+      install_dir: tests_dir
+    )
+  endforeach
+endforeach
+
 # These need to be the same as their -good- counterparts, to exercise
 # the case where the preliminary check succeeds, but actually rendering
 # a window fails (hence "mixed").
diff --git a/tests/mock-bad-detect.c b/tests/mock-bad-detect.c
new file mode 100644
index 000000000..556f4987e
--- /dev/null
+++ b/tests/mock-bad-detect.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2021 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 <stdio.h>
+
+int
+main (int argc,
+      char **argv)
+{
+
+#if defined(MOCK_LIBDL_TOKEN_PLATFORM)
+  const char *output = "Unable to find the library: ${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-platform.so: "
+                       "cannot open shared object file: No such file or directory";
+#else
+  const char *output = "Unable to find the library: ${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-lib.so: "
+                       "cannot open shared object file: No such file or directory";
+#endif
+
+  // Give bad output
+  fprintf (stderr, "%s\n", output);
+  return 1;
+}
+
diff --git a/tests/mock-good-detect.c b/tests/mock-good-detect.c
new file mode 100644
index 000000000..ae9b8df7b
--- /dev/null
+++ b/tests/mock-good-detect.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2021 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 <stdio.h>
+
+int
+main (int argc,
+      char **argv)
+{
+
+#if defined(MOCK_LIBDL_TOKEN_PLATFORM)
+  const char *output = "x86_64";
+#else
+  const char *output = "lib";
+#endif
+
+  // Give good output
+  printf ("%s\n", output);
+  return 0;
+}
+
diff --git a/tests/system-info.c b/tests/system-info.c
index 47b377d4a..ca964d7ff 100644
--- a/tests/system-info.c
+++ b/tests/system-info.c
@@ -165,6 +165,53 @@ test_object (Fixture *f,
   g_object_unref (info);
 }
 
+static void
+test_libdl (Fixture *f,
+            gconstpointer context)
+{
+  g_autoptr(SrtSystemInfo) info = NULL;
+  g_autofree gchar *libdl = NULL;
+  g_autoptr(GError) error = NULL;
+
+  info = srt_system_info_new (NULL);
+  g_assert_nonnull (info);
+  srt_system_info_set_helpers_path (info, f->builddir);
+  libdl = srt_system_info_dup_libdl_lib (info, "mock-good", &error);
+  g_assert_cmpstr (libdl, ==, "lib");
+  g_assert_no_error (error);
+  g_free (libdl);
+  /* Test cache */
+  libdl = srt_system_info_dup_libdl_lib (info, "mock-good", &error);
+  g_assert_cmpstr (libdl, ==, "lib");
+  g_assert_no_error (error);
+  g_free (libdl);
+
+  libdl = srt_system_info_dup_libdl_platform (info, "mock-good", &error);
+  g_assert_cmpstr (libdl, ==, "x86_64");
+  g_assert_no_error (error);
+  g_free (libdl);
+  /* Test cache */
+  libdl = srt_system_info_dup_libdl_platform (info, "mock-good", &error);
+  g_assert_cmpstr (libdl, ==, "x86_64");
+  g_assert_no_error (error);
+  g_free (libdl);
+
+  libdl = srt_system_info_dup_libdl_lib (info, "mock-bad", &error);
+  g_assert_cmpstr (libdl, ==, NULL);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+  g_assert_cmpstr (error->message, ==, "Unable to find the library: "
+                                       "${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-lib.so: "
+                                       "cannot open shared object file: No such file or directory\n");
+
+  g_clear_error (&error);
+  libdl = srt_system_info_dup_libdl_platform (info, "mock-bad", &error);
+  g_assert_cmpstr (libdl, ==, NULL);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+  g_assert_cmpstr (error->message, ==, "Unable to find the library: "
+                                       "${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-platform.so: "
+                                       "cannot open shared object file: No such file or directory\n");
+}
+
 static void
 check_libraries_result (GList *libraries)
 {
@@ -2440,6 +2487,13 @@ assert_equal_strings_arrays (const gchar * const *array1,
 
 static const char * const multiarch_tuples[] = { SRT_ABI_I386, SRT_ABI_X86_64 };
 
+typedef enum
+{
+  LIBDL_TOKEN_SKIP = 0,
+  LIBDL_TOKEN_LIB,
+  LIBDL_TOKEN_PLATFORM,
+} LibdlToken;
+
 typedef struct
 {
   const gchar *path;
@@ -2519,9 +2573,19 @@ typedef struct
   const char *error_message;
 } RuntimeLinkerTest;
 
+typedef struct
+{
+  LibdlToken libdl_token;
+  const char *expansion_value;
+  const char *error_domain;
+  int error_code;
+  const char *error_message;
+} LibdlTest;
+
 typedef struct
 {
   gboolean can_run;
+  LibdlTest libdl[3];
   SrtLibraryIssues issues;
   RuntimeLinkerTest runtime_linker;
   DriverTest dri_drivers[5];
@@ -2670,6 +2734,20 @@ static const JsonTest json_test[] =
     {
       {
         .can_run = FALSE,
+        .libdl =
+        {
+          {
+            .libdl_token = LIBDL_TOKEN_LIB,
+            .expansion_value = "lib32",
+          },
+          {
+            .libdl_token = LIBDL_TOKEN_PLATFORM,
+            .error_domain = "g-io-error-quark",
+            .error_code = G_IO_ERROR_NOT_FOUND,
+            .error_message = "Unable to find the library: ${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-platform.so: "
+                             "cannot open shared object file: No such file or directory",
+          },
+        },
         .runtime_linker =
         {
           .path = "/lib/ld-linux.so.2",
@@ -2762,6 +2840,17 @@ static const JsonTest json_test[] =
 
       {
         .can_run = TRUE,
+        .libdl =
+        {
+          {
+            .libdl_token = LIBDL_TOKEN_LIB,
+            .expansion_value = "lib",
+          },
+          {
+            .libdl_token = LIBDL_TOKEN_PLATFORM,
+            .expansion_value = "x86_64",
+          },
+        },
         .runtime_linker =
         {
           .path = "/lib64/ld-linux-x86-64.so.2",
@@ -3341,6 +3430,53 @@ assert_expected_runtime_linker (SrtSystemInfo *info,
     }
 }
 
+static void
+assert_expected_libdl (SrtSystemInfo *info,
+                       const char *multiarch_tuple,
+                       const LibdlTest *libdl)
+{
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *libdl_expanded = NULL;
+
+  switch (libdl->libdl_token)
+    {
+      case LIBDL_TOKEN_LIB:
+        libdl_expanded = srt_system_info_dup_libdl_lib (info, multiarch_tuple,
+                                                        &error);
+        break;
+
+      case LIBDL_TOKEN_PLATFORM:
+        libdl_expanded = srt_system_info_dup_libdl_platform (info, multiarch_tuple,
+                                                             &error);
+        break;
+
+      case LIBDL_TOKEN_SKIP:
+      default:
+        g_return_if_reached ();
+    }
+
+  g_assert_cmpstr (libdl->expansion_value, ==, libdl_expanded);
+
+  if (libdl->expansion_value != NULL)
+    {
+      g_assert (libdl->error_domain == NULL);
+      g_assert (libdl->error_code == 0);
+      g_assert_cmpstr (libdl->error_message, ==, NULL);
+
+      g_assert_no_error (error);
+    }
+  else
+    {
+      g_assert_cmpstr (libdl->expansion_value, ==, NULL);
+      g_assert_cmpstr (libdl->error_domain, !=, NULL);
+      g_assert_cmpstr (libdl->error_message, !=, NULL);
+
+      g_assert_error (error, g_quark_from_string (libdl->error_domain),
+                      libdl->error_code);
+      g_assert_cmpstr (error->message, ==, libdl->error_message);
+    }
+}
+
 static void
 json_parsing (Fixture *f,
               gconstpointer context)
@@ -3450,6 +3586,9 @@ json_parsing (Fixture *f,
           g_assert_cmpint (this_arch.issues, ==,
                            srt_system_info_check_libraries (info, multiarch_tuples[j], NULL));
 
+          for (jj = 0; this_arch.libdl[jj].libdl_token != LIBDL_TOKEN_SKIP; jj++)
+            assert_expected_libdl (info, multiarch_tuples[j], &this_arch.libdl[jj]);
+
           assert_expected_runtime_linker (info,
                                           multiarch_tuples[j],
                                           &this_arch.runtime_linker);
@@ -3876,6 +4015,8 @@ main (int argc,
 
   g_test_add ("/system-info/object", Fixture, NULL,
               setup, test_object, teardown);
+  g_test_add ("/system-info/test_libdl", Fixture, NULL,
+              setup, test_libdl, teardown);
   g_test_add ("/system-info/libraries_presence", Fixture, NULL,
               setup, libraries_presence, teardown);
   g_test_add ("/system-info/auto_expectations", Fixture, NULL,
-- 
GitLab