add option to forward environment to pipestream
[libi2ncommon] / test / test_pipestream.cpp
index 1b4bf8e..8924861 100644 (file)
  * Copyright 2018 by Intra2net AG
  */
 
-#define BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE
+#include <boost/version.hpp>
+#if BOOST_VERSION > /* guessed */ 104400
+/*
+ * Boost overeagerly terminates a unit test when a child exits non-zero
+ * without offering a means of disabling this behavior locally. All we
+ * have is below macro which isn’t even available on older versions of
+ * the unittest runner.
+ */
+#   define BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE
+#else
+/* Boost too old; skip test that validate error handling. */
+#   define NO_CHILD_FAIL_TESTS
+#endif
 
 #define BOOST_TEST_DYN_LINK
 #include <boost/test/unit_test.hpp>
 #define TO_CHARP_TOK(x) #x
 #define TO_CHARP(x) TO_CHARP_TOK(x)
 
-BOOST_AUTO_TEST_SUITE(pipestream)
+#define I2N_EXTRA_ENV "I2N_EXTRA_ENV"
+
+struct Test_Pipestream_Fixture
+{
+    char **saved_environ;
+
+    Test_Pipestream_Fixture (void) : saved_environ (environ) { }
+
+    ~Test_Pipestream_Fixture (void) {
+        environ = this->saved_environ;
+        (void)unsetenv (I2N_EXTRA_ENV);
+    }
+};
+
+BOOST_FIXTURE_TEST_SUITE(pipestream, Test_Pipestream_Fixture)
 
     BOOST_AUTO_TEST_SUITE(read)
 
@@ -99,6 +125,7 @@ BOOST_AUTO_TEST_SUITE(pipestream)
 
         const char *const bad_command [] = { "/does_not_exist", NULL };
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(abspath_bad_shell_fail)
         {
             assert (access(bad_command [0], X_OK) != 0);
@@ -115,7 +142,9 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             BOOST_CHECK(!exres.normal_exit);
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_fail)
         {
             assert (access(bad_command [0], X_OK) != 0);
@@ -123,11 +152,16 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             ExecResult exres = ExecResult ();
             const std::string result = capture_exec (bad_command, exres);
 
-            BOOST_CHECK(exres.normal_exit);
+            BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
             BOOST_CHECK(!exres.terminated_by_signal);
             BOOST_CHECK_EQUAL(result.size (), 0);
+            BOOST_CHECK_EQUAL(exres.error_message,
+                              "child failed to exec(): "
+                              "error 2 (No such file or directory)");
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_stderr)
         {
             assert (access(bad_command [0], X_OK) != 0);
@@ -135,17 +169,21 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             ExecResult exres = ExecResult ();
             const std::string result = capture_exec (bad_command, exres, false, true);
 
-            BOOST_CHECK(exres.normal_exit);
+            BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
             BOOST_CHECK(!exres.terminated_by_signal);
-            BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
             BOOST_CHECK_EQUAL(result.size (), 0);
+            BOOST_CHECK_EQUAL(exres.error_message,
+                              "child failed to exec(): "
+                              "error 2 (No such file or directory)");
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
         const char *const false_argv_abs [] = { "/bin/false", NULL };
         const char *const true_argv_abs  [] = { "/bin/true" , NULL };
         const char *const false_argv_rel [] = { "false"     , NULL };
         const char *const true_argv_rel  [] = { "true"      , NULL };
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(abspath_false_noshell_fail_exit)
         {
             ExecResult exres = ExecResult ();
@@ -156,7 +194,9 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(abspath_false_shell_fail_exit)
         {
             ExecResult exres = ExecResult ();
@@ -167,6 +207,7 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
         BOOST_AUTO_TEST_CASE(relpath_true_noshell_ok)
         {
@@ -179,16 +220,21 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(relpath_true_noshell_fail)
         {
             ExecResult exres = ExecResult ();
             const std::string result =
                 capture_exec (true_argv_rel, exres, true, false, false);
 
-            BOOST_CHECK(exres.normal_exit);
-            /* no return code check since we couln't exit */
+            BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
+            /* no return code check since we couldn't exit */
             BOOST_CHECK_EQUAL(result.size (), 0);
+            BOOST_CHECK_EQUAL(exres.error_message,
+                              "child failed to exec(): "
+                              "error 2 (No such file or directory)");
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
         BOOST_AUTO_TEST_CASE(abspath_true_noshell_ok)
         {
@@ -201,6 +247,7 @@ BOOST_AUTO_TEST_SUITE(pipestream)
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
 
+# ifndef NO_CHILD_FAIL_TESTS
         BOOST_AUTO_TEST_CASE(relpath_false_noshell_fail)
         {
             ExecResult exres = ExecResult ();
@@ -208,9 +255,10 @@ BOOST_AUTO_TEST_SUITE(pipestream)
                 capture_exec (false_argv_rel, exres, true, false, true);
 
             BOOST_CHECK(exres.normal_exit);
-            /* no return code check since we couln't exit */
+            /* no return code check since we couldn't exit */
             BOOST_CHECK_EQUAL(result.size (), 0);
         }
+# endif /* [!NO_CHILD_FAIL_TESTS] */
 
         const char *const echo_abs = "/bin/echo";
         const char *const echo_rel = "echo";
@@ -293,5 +341,43 @@ BOOST_AUTO_TEST_SUITE(pipestream)
 
     BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
 
+    BOOST_AUTO_TEST_SUITE(env)
+
+        static const char *const env_argv_abs [] = { "/usr/bin/env", NULL };
+
+        #define I2N_EXTRA_ENVIRON I2N_EXTRA_ENV "=Yet verily, the rose is within the thorn."
+        static char *i2n_extra_environ [] =
+            { const_cast<char*> (I2N_EXTRA_ENVIRON)
+            , NULL
+            };
+
+        BOOST_AUTO_TEST_CASE(env_passthrough)
+        {
+            ExecResult exres = ExecResult ();
+            environ = i2n_extra_environ;
+
+            const std::string result =
+                capture_exec (env_argv_abs, exres, true, true, false, true);
+
+            BOOST_CHECK(exres.normal_exit);
+            BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
+            BOOST_CHECK_EQUAL(result, (std::string)(I2N_EXTRA_ENVIRON "\n"));
+        }
+
+        BOOST_AUTO_TEST_CASE(env_nil)
+        {
+            ExecResult exres = ExecResult ();
+            environ = i2n_extra_environ;
+
+            const std::string result =
+                capture_exec (env_argv_abs, exres, true, true, false, false);
+
+            BOOST_CHECK(exres.normal_exit);
+            BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
+            BOOST_CHECK_EQUAL(result, std::string());
+        }
+
+    BOOST_AUTO_TEST_SUITE_END() /* [pipestream->env] */
+
 BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */