2 * The software in this package is distributed under the GNU General
3 * Public License version 2 (with a special exception described below).
5 * A copy of GNU General Public License (GPL) is included in this distribution,
6 * in the file COPYING.GPL.
8 * As a special exception, if other files instantiate templates or use macros
9 * or inline functions from this file, or you compile this file and link it
10 * with other works to produce a work based on this file, this file
11 * does not by itself cause the resulting work to be covered
12 * by the GNU General Public License.
14 * However the source code for this file must still be made available
15 * in accordance with section (3) of the GNU General Public License.
17 * This exception does not invalidate any other reasons why a work based
18 * on this file might be covered by the GNU General Public License.
22 * unit tests for the module "pipestream"
24 * Copyright 2018 by Intra2net AG
27 #include <boost/version.hpp>
28 #if BOOST_VERSION > /* guessed */ 104400
30 * Boost overeagerly terminates a unit test when a child exits non-zero
31 * without offering a means of disabling this behavior locally. All we
32 * have is below macro which isn’t even available on older versions of
33 * the unittest runner.
35 # define BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE
37 /* Boost too old; skip test that validate error handling. */
38 # define NO_CHILD_FAIL_TESTS
41 #define BOOST_TEST_DYN_LINK
42 #include <boost/test/unit_test.hpp>
44 #include "stringfunc.hxx"
45 #include "pipestream.hxx"
47 #define TO_CHARP_TOK(x) #x
48 #define TO_CHARP(x) TO_CHARP_TOK(x)
50 #define I2N_EXTRA_ENV "I2N_EXTRA_ENV"
52 struct Test_Pipestream_Fixture
56 Test_Pipestream_Fixture (void) : saved_environ (environ) { }
58 ~Test_Pipestream_Fixture (void) {
59 environ = this->saved_environ;
60 (void)unsetenv (I2N_EXTRA_ENV);
64 BOOST_FIXTURE_TEST_SUITE(pipestream, Test_Pipestream_Fixture)
66 BOOST_AUTO_TEST_SUITE(read)
68 # define ENOUGH_ZEROS 42
69 const char *const zero_bytes_argv [] =
70 { "/usr/bin/head", "-c", TO_CHARP(ENOUGH_ZEROS), "/dev/zero", NULL };
72 BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok)
74 const std::string result =
75 capture_exec (I2n::join_string (zero_bytes_argv, " "));
77 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
80 BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok_result)
82 ExecResult exres = ExecResult ();
83 const std::string result =
84 capture_exec (I2n::join_string (zero_bytes_argv, " "),
87 BOOST_CHECK(exres.normal_exit);
88 BOOST_CHECK_EQUAL(exres.return_code, 0);
89 BOOST_CHECK(!exres.terminated_by_signal);
90 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
93 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok)
95 const std::string result = capture_exec (zero_bytes_argv);
97 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
100 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok_strvec)
102 std::vector<std::string> argvec;
103 const char *const *argp = zero_bytes_argv;
104 const char * cur = NULL;
106 while ((cur = *argp++) != NULL) {
107 argvec.push_back (std::string (cur));
110 const std::string result = capture_exec (argvec);
112 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
115 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok_result)
117 ExecResult exres = ExecResult ();
118 const std::string result = capture_exec (zero_bytes_argv, exres);
120 BOOST_CHECK(exres.normal_exit);
121 BOOST_CHECK_EQUAL(exres.return_code, 0);
122 BOOST_CHECK(!exres.terminated_by_signal);
123 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
126 const char *const bad_command [] = { "/does_not_exist", NULL };
128 # ifndef NO_CHILD_FAIL_TESTS
129 BOOST_AUTO_TEST_CASE(abspath_bad_shell_fail)
131 assert (access(bad_command [0], X_OK) != 0);
133 ExecResult exres = ExecResult ();
135 * Note that the next line will make the unit test spew a message
136 * to stderr which cannot be prevented due to the limitations of
139 const std::string result =
140 capture_exec (I2n::join_string (bad_command, " "));
142 BOOST_CHECK(!exres.normal_exit);
143 BOOST_CHECK_EQUAL(result.size (), 0);
145 # endif /* [!NO_CHILD_FAIL_TESTS] */
147 # ifndef NO_CHILD_FAIL_TESTS
148 BOOST_AUTO_TEST_CASE(abspath_bad_noshell_fail)
150 assert (access(bad_command [0], X_OK) != 0);
152 ExecResult exres = ExecResult ();
153 const std::string result = capture_exec (bad_command, exres);
155 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
156 BOOST_CHECK(!exres.terminated_by_signal);
157 BOOST_CHECK_EQUAL(result.size (), 0);
158 BOOST_CHECK_EQUAL(exres.error_message,
159 "child failed to exec(): "
160 "error 2 (No such file or directory)");
162 # endif /* [!NO_CHILD_FAIL_TESTS] */
164 # ifndef NO_CHILD_FAIL_TESTS
165 BOOST_AUTO_TEST_CASE(abspath_bad_noshell_stderr)
167 assert (access(bad_command [0], X_OK) != 0);
169 ExecResult exres = ExecResult ();
170 const std::string result = capture_exec (bad_command, exres, false, true);
172 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
173 BOOST_CHECK(!exres.terminated_by_signal);
174 BOOST_CHECK_EQUAL(result.size (), 0);
175 BOOST_CHECK_EQUAL(exres.error_message,
176 "child failed to exec(): "
177 "error 2 (No such file or directory)");
179 # endif /* [!NO_CHILD_FAIL_TESTS] */
181 const char *const false_argv_abs [] = { "/bin/false", NULL };
182 const char *const true_argv_abs [] = { "/bin/true" , NULL };
183 const char *const false_argv_rel [] = { "false" , NULL };
184 const char *const true_argv_rel [] = { "true" , NULL };
186 # ifndef NO_CHILD_FAIL_TESTS
187 BOOST_AUTO_TEST_CASE(abspath_false_noshell_fail_exit)
189 ExecResult exres = ExecResult ();
190 const std::string result =
191 capture_exec (false_argv_abs, exres, true, false, false);
193 BOOST_CHECK(exres.normal_exit);
194 BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
195 BOOST_CHECK_EQUAL(result.size (), 0);
197 # endif /* [!NO_CHILD_FAIL_TESTS] */
199 # ifndef NO_CHILD_FAIL_TESTS
200 BOOST_AUTO_TEST_CASE(abspath_false_shell_fail_exit)
202 ExecResult exres = ExecResult ();
203 const std::string result =
204 capture_exec (std::string (false_argv_abs [0]), exres);
206 BOOST_CHECK(exres.normal_exit);
207 BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
208 BOOST_CHECK_EQUAL(result.size (), 0);
210 # endif /* [!NO_CHILD_FAIL_TESTS] */
212 BOOST_AUTO_TEST_CASE(relpath_true_noshell_ok)
214 ExecResult exres = ExecResult ();
215 const std::string result =
216 capture_exec (true_argv_rel, exres, true, false, true);
218 BOOST_CHECK(exres.normal_exit);
219 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
220 BOOST_CHECK_EQUAL(result.size (), 0);
223 # ifndef NO_CHILD_FAIL_TESTS
224 BOOST_AUTO_TEST_CASE(relpath_true_noshell_fail)
226 ExecResult exres = ExecResult ();
227 const std::string result =
228 capture_exec (true_argv_rel, exres, true, false, false);
230 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
231 /* no return code check since we couldn't exit */
232 BOOST_CHECK_EQUAL(result.size (), 0);
233 BOOST_CHECK_EQUAL(exres.error_message,
234 "child failed to exec(): "
235 "error 2 (No such file or directory)");
237 # endif /* [!NO_CHILD_FAIL_TESTS] */
239 BOOST_AUTO_TEST_CASE(abspath_true_noshell_ok)
241 ExecResult exres = ExecResult ();
242 const std::string result =
243 capture_exec (true_argv_abs, exres, true, false, true);
245 BOOST_CHECK(exres.normal_exit);
246 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
247 BOOST_CHECK_EQUAL(result.size (), 0);
250 # ifndef NO_CHILD_FAIL_TESTS
251 BOOST_AUTO_TEST_CASE(relpath_false_noshell_fail)
253 ExecResult exres = ExecResult ();
254 const std::string result =
255 capture_exec (false_argv_rel, exres, true, false, true);
257 BOOST_CHECK(exres.normal_exit);
258 /* no return code check since we couldn't exit */
259 BOOST_CHECK_EQUAL(result.size (), 0);
261 # endif /* [!NO_CHILD_FAIL_TESTS] */
263 const char *const echo_abs = "/bin/echo";
264 const char *const echo_rel = "echo";
266 static std::vector<std::string>
267 mk_echo_argv (const std::string &text, const bool absolute=true)
269 std::vector<std::string> ret;
271 ret.push_back (absolute ? echo_abs : echo_rel);
272 ret.push_back ("-n");
273 ret.push_back (text);
278 BOOST_AUTO_TEST_CASE(abspath_echo_noshell_capture_ok)
280 ExecResult exres = ExecResult ();
281 const std::string text = "The significant owl hoots in the night.";
282 const std::vector<std::string> argv = mk_echo_argv (text);
283 const std::string result = capture_exec (argv, exres, true, false, true);
285 BOOST_CHECK(exres.normal_exit);
286 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
287 BOOST_CHECK_EQUAL(result, text);
290 BOOST_AUTO_TEST_CASE(relpath_echo_noshell_capture_ok)
292 ExecResult exres = ExecResult ();
293 const std::string text = "Yet many grey lords go sadly to the masterless men.";
294 const std::vector<std::string> argv = mk_echo_argv (text, false);
295 const std::string result = capture_exec (argv, exres, true, false, true);
297 BOOST_CHECK(exres.normal_exit);
298 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
299 BOOST_CHECK_EQUAL(result, text);
302 static std::vector<std::string>
303 mk_errcho_argv (const std::string &text)
306 * Hack cause there’s no way to make echo print to stderr without
309 std::vector<std::string> ret;
311 ret.push_back ("/bin/sh");
312 ret.push_back ("-c");
313 ret.push_back (std::string ("1>&- 1>&2 echo -n '") + text + "'"); /* brr */
318 BOOST_AUTO_TEST_CASE(sh_errcho_capture_ok)
320 ExecResult exres = ExecResult ();
321 const std::string text = "Hooray, hooray for the spinster’s sister’s daughter.";
322 const std::vector<std::string> argv = mk_errcho_argv (text);
323 const std::string result = capture_exec (argv, exres, false, true, true);
325 BOOST_CHECK(exres.normal_exit);
326 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
327 BOOST_CHECK_EQUAL(result, text);
330 BOOST_AUTO_TEST_CASE(sh_errcho_stdout_empty_ok)
332 ExecResult exres = ExecResult ();
333 const std::string text = "To the axeman, all supplicants are the same height.";
334 const std::vector<std::string> argv = mk_errcho_argv (text);
335 const std::string result = capture_exec (argv, exres, true, false, true);
337 BOOST_CHECK(exres.normal_exit);
338 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
339 BOOST_CHECK_EQUAL(result.size (), 0);
342 BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
344 BOOST_AUTO_TEST_SUITE(env)
346 static const char *const env_argv_abs [] = { "/usr/bin/env", NULL };
348 #define I2N_EXTRA_ENVIRON I2N_EXTRA_ENV "=Yet verily, the rose is within the thorn."
349 static char *i2n_extra_environ [] =
350 { const_cast<char*> (I2N_EXTRA_ENVIRON)
354 BOOST_AUTO_TEST_CASE(env_passthrough)
356 ExecResult exres = ExecResult ();
357 environ = i2n_extra_environ;
359 const std::string result =
360 capture_exec (env_argv_abs, exres, true, true, false, true);
362 BOOST_CHECK(exres.normal_exit);
363 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
364 BOOST_CHECK_EQUAL(result, (std::string)(I2N_EXTRA_ENVIRON "\n"));
367 BOOST_AUTO_TEST_CASE(env_nil)
369 ExecResult exres = ExecResult ();
370 environ = i2n_extra_environ;
372 const std::string result =
373 capture_exec (env_argv_abs, exres, true, true, false, false);
375 BOOST_CHECK(exres.normal_exit);
376 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
377 BOOST_CHECK_EQUAL(result, std::string());
380 BOOST_AUTO_TEST_SUITE_END() /* [pipestream->env] */
382 BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */