[glib/wip/avoid-searching-path] Add test coverage for G_SPAWN_SEARCH_PATH



commit 3505c31fad5f1fcafa965e19cd7e59945472e306
Author: Simon McVittie <smcv collabora com>
Date:   Thu Jan 28 18:16:36 2021 +0000

    Add test coverage for G_SPAWN_SEARCH_PATH
    
    For manual test coverage that would reproduce the bug fixed in !1902,
    copy /bin/true (or any other harmless executable) to
    /usr/bin/spawn-test-helper.
    
    Signed-off-by: Simon McVittie <smcv collabora com>

 glib/tests/meson.build                          |  17 ++
 glib/tests/path-test-subdir/meson.build         |   6 +
 glib/tests/path-test-subdir/spawn-test-helper.c |   7 +
 glib/tests/spawn-path-search-helper.c           | 156 +++++++++++++++
 glib/tests/spawn-path-search.c                  | 249 ++++++++++++++++++++++++
 glib/tests/spawn-test-helper.c                  |   7 +
 6 files changed, 442 insertions(+)
---
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index 567f5eda4..19e4571f2 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -89,6 +89,7 @@ glib_tests = {
   'slist' : {},
   'sort' : {},
   'spawn-multithreaded' : {},
+  'spawn-path-search' : {},
   'spawn-singlethread' : {},
   'strfuncs' : {},
   'string' : {},
@@ -236,6 +237,20 @@ foreach test_name, extra_args : glib_tests
   test(test_name, exe, env : test_env, timeout : timeout, suite : suite)
 endforeach
 
+executable('spawn-path-search-helper', 'spawn-path-search-helper.c',
+  c_args : test_cargs,
+  dependencies : test_deps,
+  install_dir: installed_tests_execdir,
+  install: installed_tests_enabled,
+)
+
+executable('spawn-test-helper', 'spawn-test-helper.c',
+  c_args : test_cargs,
+  dependencies : test_deps,
+  install_dir: installed_tests_execdir,
+  install: installed_tests_enabled,
+)
+
 # test-spawn-echo helper binary required by the spawn tests above
 executable('test-spawn-echo', 'test-spawn-echo.c',
   c_args : test_cargs,
@@ -267,3 +282,5 @@ if not meson.is_cross_build() and host_system != 'windows'
     )
   endif
 endif
+
+subdir('path-test-subdir')
diff --git a/glib/tests/path-test-subdir/meson.build b/glib/tests/path-test-subdir/meson.build
new file mode 100644
index 000000000..351254cd8
--- /dev/null
+++ b/glib/tests/path-test-subdir/meson.build
@@ -0,0 +1,6 @@
+executable('spawn-test-helper', 'spawn-test-helper.c',
+  c_args : test_cargs,
+  dependencies : test_deps,
+  install_dir: join_paths(installed_tests_execdir, 'path-test-subdir'),
+  install: installed_tests_enabled,
+)
diff --git a/glib/tests/path-test-subdir/spawn-test-helper.c b/glib/tests/path-test-subdir/spawn-test-helper.c
new file mode 100644
index 000000000..f9f2cee84
--- /dev/null
+++ b/glib/tests/path-test-subdir/spawn-test-helper.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main (void)
+{
+  fprintf (stderr, "this is spawn-test-helper from path-test-subdir\n");
+  return 5;
+}
diff --git a/glib/tests/spawn-path-search-helper.c b/glib/tests/spawn-path-search-helper.c
new file mode 100644
index 000000000..b417c7896
--- /dev/null
+++ b/glib/tests/spawn-path-search-helper.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2021 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#ifdef G_OS_UNIX
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+static void
+child_setup (gpointer user_data)
+{
+}
+
+typedef struct
+{
+  int wait_status;
+  gboolean done;
+} ChildStatus;
+
+static ChildStatus child_status = { -1, FALSE };
+
+static void
+child_watch_cb (GPid pid,
+                gint status,
+                gpointer user_data)
+{
+  child_status.wait_status = status;
+  child_status.done = TRUE;
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  gboolean search_path = FALSE;
+  gboolean search_path_from_envp = FALSE;
+  gboolean slow_path = FALSE;
+  gchar *chdir_child = NULL;
+  gchar *set_path_in_envp = NULL;
+  gchar **envp = NULL;
+  GOptionEntry entries[] =
+  {
+    { "chdir-child", '\0',
+      G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child,
+      "Run PROGRAM in this working directory", NULL },
+    { "search-path", '\0',
+      G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path,
+      "Search PATH for PROGRAM", NULL },
+    { "search-path-from-envp", '\0',
+      G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp,
+      "Search PATH from specified environment", NULL },
+    { "set-path-in-envp", '\0',
+      G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp,
+      "Set PATH in specified environment to this value", "PATH", },
+    { "slow-path", '\0',
+      G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path,
+      "Use a child-setup function to avoid the posix_spawn fast path", NULL },
+    { NULL }
+  };
+  GError *error = NULL;
+  int ret = 1;
+  GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD;
+  GPid pid;
+  GOptionContext *context = NULL;
+
+  context = g_option_context_new ("PROGRAM [ARGS...]");
+  g_option_context_add_main_entries (context, entries, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      ret = 2;
+      goto out;
+    }
+
+  if (argc < 2)
+    {
+      g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
+                   "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]);
+      ret = 2;
+      goto out;
+    }
+
+  envp = g_get_environ ();
+
+  if (set_path_in_envp != NULL)
+    envp = g_environ_setenv (envp, "PATH", set_path_in_envp, TRUE);
+
+  if (search_path)
+    spawn_flags |= G_SPAWN_SEARCH_PATH;
+
+  if (search_path_from_envp)
+    spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
+
+  if (!g_spawn_async_with_pipes (chdir_child,
+                                 &argv[1],
+                                 envp,
+                                 spawn_flags,
+                                 slow_path ? child_setup : NULL,
+                                 NULL,  /* user_data */
+                                 &pid,
+                                 NULL,  /* stdin */
+                                 NULL,  /* stdout */
+                                 NULL,  /* stderr */
+                                 &error))
+    {
+      ret = 1;
+      goto out;
+    }
+
+  g_child_watch_add (pid, child_watch_cb, NULL);
+
+  while (!child_status.done)
+    g_main_context_iteration (NULL, TRUE);
+
+  g_spawn_close_pid (pid);
+
+#ifdef G_OS_UNIX
+  if (WIFEXITED (child_status.wait_status))
+    ret = WEXITSTATUS (child_status.wait_status);
+  else
+    ret = 1;
+#else
+  ret = child_status.wait_status;
+#endif
+
+out:
+  if (error != NULL)
+    fprintf (stderr, "%s\n", error->message);
+
+  g_free (set_path_in_envp);
+  g_free (chdir_child);
+  g_clear_error (&error);
+  g_strfreev (envp);
+  g_option_context_free (context);
+  return ret;
+}
diff --git a/glib/tests/spawn-path-search.c b/glib/tests/spawn-path-search.c
new file mode 100644
index 000000000..13415f8d0
--- /dev/null
+++ b/glib/tests/spawn-path-search.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2021 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+static void
+test_do_not_search (void)
+{
+  GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
+  gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
+  gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
+  gchar **envp = g_get_environ ();
+  gchar *out = NULL;
+  gchar *err = NULL;
+  GError *error = NULL;
+  int wait_status = -1;
+
+  g_test_summary ("Without G_SPAWN_SEARCH_PATH, spawn-test-helper "
+                  "means ./spawn-test-helper.");
+
+  envp = g_environ_setenv (envp, "PATH", subdir, TRUE);
+
+  g_ptr_array_add (argv,
+                   g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
+  g_ptr_array_add (argv, g_strdup ("--"));
+  g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
+  g_ptr_array_add (argv, NULL);
+
+  g_spawn_sync (here,
+                (char **) argv->pdata,
+                envp,
+                G_SPAWN_DEFAULT,
+                NULL,  /* child setup */
+                NULL,  /* user data */
+                &out,
+                &err,
+                &wait_status,
+                &error);
+  g_assert_no_error (error);
+
+  g_test_message ("%s", out);
+  g_test_message ("%s", err);
+  g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
+
+#ifdef G_OS_UNIX
+  g_assert_true (WIFEXITED (wait_status));
+  g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
+#endif
+
+  g_strfreev (envp);
+  g_free (here);
+  g_free (subdir);
+  g_free (out);
+  g_free (err);
+  g_ptr_array_unref (argv);
+}
+
+static void
+test_search_path (void)
+{
+  GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
+  gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
+  gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
+  gchar **envp = g_get_environ ();
+  gchar *out = NULL;
+  gchar *err = NULL;
+  GError *error = NULL;
+  int wait_status = -1;
+
+  g_test_summary ("With G_SPAWN_SEARCH_PATH, spawn-test-helper "
+                  "means $PATH/spawn-test-helper.");
+
+  envp = g_environ_setenv (envp, "PATH", subdir, TRUE);
+
+  g_ptr_array_add (argv,
+                   g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
+  g_ptr_array_add (argv, g_strdup ("--search-path"));
+  g_ptr_array_add (argv, g_strdup ("--"));
+  g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
+  g_ptr_array_add (argv, NULL);
+
+  g_spawn_sync (here,
+                (char **) argv->pdata,
+                envp,
+                G_SPAWN_DEFAULT,
+                NULL,  /* child setup */
+                NULL,  /* user data */
+                &out,
+                &err,
+                &wait_status,
+                &error);
+  g_assert_no_error (error);
+
+  g_test_message ("%s", out);
+  g_test_message ("%s", err);
+  g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
+
+#ifdef G_OS_UNIX
+  g_assert_true (WIFEXITED (wait_status));
+  g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
+#endif
+
+  g_strfreev (envp);
+  g_free (here);
+  g_free (subdir);
+  g_free (out);
+  g_free (err);
+  g_ptr_array_unref (argv);
+}
+
+static void
+test_search_path_from_envp (void)
+{
+  GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
+  gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
+  gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
+  gchar **envp = g_get_environ ();
+  gchar *out = NULL;
+  gchar *err = NULL;
+  GError *error = NULL;
+  int wait_status = -1;
+
+  g_test_summary ("With G_SPAWN_SEARCH_PATH_FROM_ENVP, spawn-test-helper "
+                  "means $PATH/spawn-test-helper with $PATH from envp.");
+
+  envp = g_environ_setenv (envp, "PATH", here, TRUE);
+
+  g_ptr_array_add (argv,
+                   g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
+  g_ptr_array_add (argv, g_strdup ("--search-path-from-envp"));
+  g_ptr_array_add (argv, g_strdup ("--set-path-in-envp"));
+  g_ptr_array_add (argv, g_strdup (subdir));
+  g_ptr_array_add (argv, g_strdup ("--"));
+  g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
+  g_ptr_array_add (argv, NULL);
+
+  g_spawn_sync (here,
+                (char **) argv->pdata,
+                envp,
+                G_SPAWN_DEFAULT,
+                NULL,  /* child setup */
+                NULL,  /* user data */
+                &out,
+                &err,
+                &wait_status,
+                &error);
+  g_assert_no_error (error);
+
+  g_test_message ("%s", out);
+  g_test_message ("%s", err);
+  g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
+
+#ifdef G_OS_UNIX
+  g_assert_true (WIFEXITED (wait_status));
+  g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
+#endif
+
+  g_strfreev (envp);
+  g_free (here);
+  g_free (subdir);
+  g_free (out);
+  g_free (err);
+  g_ptr_array_unref (argv);
+}
+
+static void
+test_search_path_ambiguous (void)
+{
+  GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
+  gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
+  gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
+  gchar **envp = g_get_environ ();
+  gchar *out = NULL;
+  gchar *err = NULL;
+  GError *error = NULL;
+  int wait_status = -1;
+
+  g_test_summary ("With G_SPAWN_SEARCH_PATH and G_SPAWN_SEARCH_PATH_FROM_ENVP, "
+                  "the latter wins.");
+
+  envp = g_environ_setenv (envp, "PATH", here, TRUE);
+
+  g_ptr_array_add (argv,
+                   g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
+  g_ptr_array_add (argv, g_strdup ("--search-path"));
+  g_ptr_array_add (argv, g_strdup ("--search-path-from-envp"));
+  g_ptr_array_add (argv, g_strdup ("--set-path-in-envp"));
+  g_ptr_array_add (argv, g_strdup (subdir));
+  g_ptr_array_add (argv, g_strdup ("--"));
+  g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
+  g_ptr_array_add (argv, NULL);
+
+  g_spawn_sync (here,
+                (char **) argv->pdata,
+                envp,
+                G_SPAWN_DEFAULT,
+                NULL,  /* child setup */
+                NULL,  /* user data */
+                &out,
+                &err,
+                &wait_status,
+                &error);
+  g_assert_no_error (error);
+
+  g_test_message ("%s", out);
+  g_test_message ("%s", err);
+  g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
+
+#ifdef G_OS_UNIX
+  g_assert_true (WIFEXITED (wait_status));
+  g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
+#endif
+
+  g_strfreev (envp);
+  g_free (here);
+  g_free (subdir);
+  g_free (out);
+  g_free (err);
+  g_ptr_array_unref (argv);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add_func ("/spawn/do-not-search", test_do_not_search);
+  g_test_add_func ("/spawn/search-path", test_search_path);
+  g_test_add_func ("/spawn/search-path-from-envp", test_search_path_from_envp);
+  g_test_add_func ("/spawn/search-path-ambiguous", test_search_path_ambiguous);
+
+  return g_test_run ();
+}
diff --git a/glib/tests/spawn-test-helper.c b/glib/tests/spawn-test-helper.c
new file mode 100644
index 000000000..301f3f31c
--- /dev/null
+++ b/glib/tests/spawn-test-helper.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main (void)
+{
+  fprintf (stderr, "this is spawn-test-helper from glib/tests\n");
+  return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]