From b2f117c1a1024ec57250bb4e37e75345b9121b78 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Wed, 28 Jul 2021 12:19:34 +0100
Subject: [PATCH] tests: Add test coverage for pv-adverb LD_AUDIT, LD_PRELOAD
 handling

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/adverb.c        |  23 +++++--
 tests/pressure-vessel/adverb.py | 105 ++++++++++++++++++++++++++++++++
 2 files changed, 122 insertions(+), 6 deletions(-)

diff --git a/pressure-vessel/adverb.c b/pressure-vessel/adverb.c
index b5ef2b9a0..b3e2f4759 100644
--- a/pressure-vessel/adverb.c
+++ b/pressure-vessel/adverb.c
@@ -480,12 +480,23 @@ generate_lib_temp_dirs (LibTempDirs *lib_temp_dirs,
     {
       g_autofree gchar *libdl_platform = NULL;
       g_autofree gchar *abi_path = NULL;
-      libdl_platform = srt_system_info_dup_libdl_platform (info,
-                                                           pv_multiarch_details[abi].tuple,
-                                                           error);
-      if (!libdl_platform)
-        return glnx_prefix_error (error,
-                                  "Unknown expansion of the dl string token $PLATFORM");
+
+      if (g_getenv ("PRESSURE_VESSEL_TEST_STANDARDIZE_PLATFORM") != NULL)
+        {
+          /* In unit tests it isn't straightforward to find the real
+           * ${PLATFORM}, so we use a predictable mock implementation:
+           * whichever platform happens to be listed first. */
+          libdl_platform = g_strdup (pv_multiarch_details[abi].platforms[0]);
+        }
+      else
+        {
+          libdl_platform = srt_system_info_dup_libdl_platform (info,
+                                                               pv_multiarch_details[abi].tuple,
+                                                               error);
+          if (!libdl_platform)
+            return glnx_prefix_error (error,
+                                      "Unknown expansion of the dl string token $PLATFORM");
+        }
 
       abi_path = g_build_filename (lib_temp_dirs->root_path, libdl_platform, NULL);
 
diff --git a/tests/pressure-vessel/adverb.py b/tests/pressure-vessel/adverb.py
index 9088e43e7..4b466ac88 100755
--- a/tests/pressure-vessel/adverb.py
+++ b/tests/pressure-vessel/adverb.py
@@ -5,6 +5,7 @@
 
 import logging
 import os
+import re
 import signal
 import subprocess
 import sys
@@ -18,6 +19,7 @@ except ImportError:
 
 from testutils import (
     BaseTest,
+    run_subprocess,
     test_main,
 )
 
@@ -26,20 +28,123 @@ logger = logging.getLogger('test-adverb')
 
 
 class TestAdverb(BaseTest):
+    def run_subprocess(
+        self,
+        args,           # type: typing.Union[typing.List[str], str]
+        check=False,
+        input=None,     # type: typing.Optional[bytes]
+        timeout=None,   # type: typing.Optional[int]
+        **kwargs        # type: typing.Any
+    ):
+        logger.info('Running: %r', args)
+        return run_subprocess(
+            args, check=check, input=input, timeout=timeout, **kwargs
+        )
+
     def setUp(self) -> None:
         super().setUp()
 
         if 'PRESSURE_VESSEL_UNINSTALLED' in os.environ:
             self.adverb = self.command_prefix + [
+                'env',
+                '-u', 'LD_AUDIT',
+                '-u', 'LD_PRELOAD',
+                # In unit tests it isn't straightforward to find the real
+                # ${PLATFORM}, so we use a predictable mock implementation
+                # that always uses PvMultiarchDetails.platforms[0].
+                'PRESSURE_VESSEL_TEST_STANDARDIZE_PLATFORM=1',
                 os.path.join(
                     self.top_builddir,
                     'pressure-vessel',
                     'pressure-vessel-adverb'
                 ),
             ]
+            self.helper = self.command_prefix + [
+                os.path.join(
+                    self.top_builddir,
+                    'tests',
+                    'pressure-vessel',
+                    'test-helper'
+                ),
+            ]
         else:
             self.skipTest('Not available as an installed-test')
 
+    def test_ld_preload(self) -> None:
+        completed = run_subprocess(
+            self.adverb + [
+                '--ld-audit=/nonexistent/libaudit.so',
+                '--ld-preload=/nonexistent/libpreload.so',
+                '--ld-preload=/nonexistent/ubuntu12_32/gameoverlayrenderer.so',
+                '--ld-preload=/nonexistent/ubuntu12_64/gameoverlayrenderer.so',
+                '--',
+                'sh', '-euc',
+                # The hard-coded i686 and xeon_phi here must match up with
+                # pv_multiarch_details[i].platforms[0], which is what is used
+                # as a mock implementation under
+                # PRESSURE_VESSEL_TEST_STANDARDIZE_PLATFORM=1.
+                r'''
+                ld_audit="$LD_AUDIT"
+                ld_preload="$LD_PRELOAD"
+                unset LD_AUDIT
+                unset LD_PRELOAD
+
+                echo "LD_AUDIT=$ld_audit"
+                echo "LD_PRELOAD=$ld_preload"
+
+                IFS=:
+                for item in $ld_preload; do
+                    case "$item" in
+                        (*\$\{PLATFORM\}*)
+                            i686="$(echo "$item" |
+                                sed -e 's/[$]{PLATFORM}/i686/g')"
+                            xeon_phi="$(echo "$item" |
+                                sed -e 's/[$]{PLATFORM}/xeon_phi/g')"
+                            printf "i686: symlink to "
+                            readlink "$i686"
+                            printf "xeon_phi: symlink to "
+                            readlink "$xeon_phi"
+                            ;;
+                        (*)
+                            echo "literal $item"
+                            ;;
+                    esac
+                done
+                ''',
+            ],
+            check=True,
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=2,
+            universal_newlines=True,
+        )
+
+        stdout = completed.stdout
+        assert stdout is not None
+        lines = stdout.splitlines()
+        self.assertEqual(
+            lines[0],
+            'LD_AUDIT=/nonexistent/libaudit.so',
+        )
+        self.assertEqual(
+            re.sub(r':/[^:]*?/pressure-vessel-libs-....../',
+                   r':/tmp/pressure-vessel-libs-XXXXXX/',
+                   lines[1]),
+            ('LD_PRELOAD=/nonexistent/libpreload.so'
+             ':/tmp/pressure-vessel-libs-XXXXXX/'
+             '${PLATFORM}/gameoverlayrenderer.so')
+        )
+        self.assertEqual(lines[2], 'literal /nonexistent/libpreload.so')
+        self.assertEqual(
+            lines[3],
+            'i686: symlink to /nonexistent/ubuntu12_32/gameoverlayrenderer.so',
+        )
+        self.assertEqual(
+            lines[4],
+            ('xeon_phi: symlink to '
+             '/nonexistent/ubuntu12_64/gameoverlayrenderer.so'),
+        )
+
     def test_stdio_passthrough(self) -> None:
         proc = subprocess.Popen(
             self.adverb + [
-- 
GitLab