From 6a6b36bbc7d46bb787064c792635a933ac258929 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 23 Nov 2023 12:08:14 +0000 Subject: [PATCH] gtestutils: Add g_test_trap_subprocess_with_envp() for testing envs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a variant of `g_test_trap_subprocess()` which allows the environment for the child process to be specified. This is quite useful when you want to test code which reads environment variables, as it’s not safe to set those after the start of `main()`. This will be useful within and outwith GLib for testing such code. Signed-off-by: Philip Withnall Helps: #1618 --- docs/reference/glib/glib-sections.txt.in | 1 + glib/gtestutils.c | 61 +++++++++++++++++++++--- glib/gtestutils.h | 5 ++ glib/tests/testing.c | 20 ++++++++ 4 files changed, 81 insertions(+), 6 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index db8f47230..52029b861 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -3363,6 +3363,7 @@ g_test_assert_expected_messages GTestTrapFlags GTestSubprocessFlags g_test_trap_subprocess +g_test_trap_subprocess_with_envp g_test_trap_has_passed g_test_trap_reached_timeout g_test_trap_assert_passed diff --git a/glib/gtestutils.c b/glib/gtestutils.c index 9d5b1cfbb..704f5ce0e 100644 --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@ -3850,6 +3850,32 @@ G_GNUC_END_IGNORE_DEPRECATIONS * @test_flags: Flags to modify subprocess behaviour. * * Respawns the test program to run only @test_path in a subprocess. + * + * This is equivalent to calling g_test_trap_subprocess_with_envp() with `envp` + * set to %NULL. See the documentation for that function for full details. + * + * Since: 2.38 + */ +void +g_test_trap_subprocess (const char *test_path, + guint64 usec_timeout, + GTestSubprocessFlags test_flags) +{ + g_test_trap_subprocess_with_envp (test_path, NULL, usec_timeout, test_flags); +} + +/** + * g_test_trap_subprocess_with_envp: + * @test_path: (nullable): Test to run in a subprocess + * @envp: (array zero-terminated=1) (nullable) (element-type filename): Environment + * to run the test in, or %NULL to inherit the parent’s environment. This must + * be in the GLib filename encoding. + * @usec_timeout: Timeout for the subprocess test in micro seconds. + * @test_flags: Flags to modify subprocess behaviour. + * + * Respawns the test program to run only @test_path in a subprocess with the + * given @envp environment. + * * This can be used for a test case that might not return, or that * might abort. * @@ -3863,6 +3889,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS * tests with names of this form will automatically be skipped in the * parent process. * + * If @envp is %NULL, the parent process’ environment will be inherited. + * * If @usec_timeout is non-0, the test subprocess is aborted and * considered failing if its run time exceeds it. * @@ -3905,23 +3933,44 @@ G_GNUC_END_IGNORE_DEPRECATIONS * g_test_trap_assert_stderr ("*ERROR*too large*"); * } * + * static void + * test_different_username (void) + * { + * if (g_test_subprocess ()) + * { + * // Code under test goes here + * g_message ("Username is now simulated as %s", g_getenv ("USER")); + * return; + * } + * + * // Reruns this same test in a subprocess + * g_autoptr(GStrv) envp = g_get_environ (); + * envp = g_environ_setenv (g_steal_pointer (&envp), "USER", "charlie", TRUE); + * g_test_trap_subprocess_with_envp (NULL, envp, 0, G_TEST_SUBPROCESS_DEFAULT); + * g_test_trap_assert_passed (); + * g_test_trap_assert_stdout ("Username is now simulated as charlie"); + * } + * * int * main (int argc, char **argv) * { * g_test_init (&argc, &argv, NULL); * - * g_test_add_func ("/myobject/create_large_object", + * g_test_add_func ("/myobject/create-large-object", * test_create_large_object); + * g_test_add_func ("/myobject/different-username", + * test_different_username); * return g_test_run (); * } * ]| * - * Since: 2.38 + * Since: 2.80 */ void -g_test_trap_subprocess (const char *test_path, - guint64 usec_timeout, - GTestSubprocessFlags test_flags) +g_test_trap_subprocess_with_envp (const char *test_path, + const char * const *envp, + guint64 usec_timeout, + GTestSubprocessFlags test_flags) { GError *error = NULL; GPtrArray *argv; @@ -3980,7 +4029,7 @@ g_test_trap_subprocess (const char *test_path, if (!g_spawn_async_with_pipes (test_initial_cwd, (char **)argv->pdata, - NULL, flags, + (char **) envp, flags, NULL, NULL, &pid, NULL, &stdout_fd, &stderr_fd, &error)) diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 30ede2588..69ca9c9b7 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -515,6 +515,11 @@ GLIB_AVAILABLE_IN_2_38 void g_test_trap_subprocess (const char *test_path, guint64 usec_timeout, GTestSubprocessFlags test_flags); +GLIB_AVAILABLE_IN_2_80 +void g_test_trap_subprocess_with_envp (const char *test_path, + const char * const *envp, + guint64 usec_timeout, + GTestSubprocessFlags test_flags); GLIB_AVAILABLE_IN_ALL gboolean g_test_trap_has_passed (void); diff --git a/glib/tests/testing.c b/glib/tests/testing.c index acbc1be22..83aaad9bb 100644 --- a/glib/tests/testing.c +++ b/glib/tests/testing.c @@ -404,6 +404,25 @@ test_subprocess_timeout (void) g_assert_true (g_test_trap_reached_timeout ()); } +static void +test_subprocess_envp (void) +{ + char **envp = NULL; + + if (g_test_subprocess ()) + { + g_assert_cmpstr (g_getenv ("TEST_SUBPROCESS_VARIABLE"), ==, "definitely set"); + return; + } + + envp = g_get_environ (); + envp = g_environ_setenv (g_steal_pointer (&envp), "TEST_SUBPROCESS_VARIABLE", "definitely set", TRUE); + g_test_trap_subprocess_with_envp (NULL, (const gchar * const *) envp, + 0, G_TEST_SUBPROCESS_DEFAULT); + g_test_trap_assert_passed (); + g_strfreev (envp); +} + /* run a test with fixture setup and teardown */ typedef struct { guint seed; @@ -2909,6 +2928,7 @@ main (int argc, g_test_add_func ("/trap_subprocess/no-such-test", test_subprocess_no_such_test); if (g_test_slow ()) g_test_add_func ("/trap_subprocess/timeout", test_subprocess_timeout); + g_test_add_func ("/trap_subprocess/envp", test_subprocess_envp); g_test_add_func ("/trap_subprocess/patterns", test_subprocess_patterns);