skip unit tests for handling child errors in pipestream with ancient boost
[libi2ncommon] / test / test_pipestream.cpp
CommitLineData
3ed2cc9b
PG
1/*
2 * The software in this package is distributed under the GNU General
3 * Public License version 2 (with a special exception described below).
4 *
5 * A copy of GNU General Public License (GPL) is included in this distribution,
6 * in the file COPYING.GPL.
7 *
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.
13 *
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.
16 *
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.
19 *
20 * @file
21 *
22 * unit tests for the module "pipestream"
23 *
24 * Copyright 2018 by Intra2net AG
25 */
26
0c68bd0f
PG
27#include <boost/version.hpp>
28#if BOOST_VERSION > /* guessed */ 104400
29/*
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.
34 */
35# define BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE
36#else
37/* Boost too old; skip test that validate error handling. */
38# define NO_CHILD_FAIL_TESTS
39#endif
3ed2cc9b
PG
40
41#define BOOST_TEST_DYN_LINK
42#include <boost/test/unit_test.hpp>
3ed2cc9b
PG
43
44#include "stringfunc.hxx"
45#include "pipestream.hxx"
46
47#define TO_CHARP_TOK(x) #x
48#define TO_CHARP(x) TO_CHARP_TOK(x)
49
50BOOST_AUTO_TEST_SUITE(pipestream)
51
52 BOOST_AUTO_TEST_SUITE(read)
53
54 # define ENOUGH_ZEROS 42
55 const char *const zero_bytes_argv [] =
56 { "/usr/bin/head", "-c", TO_CHARP(ENOUGH_ZEROS), "/dev/zero", NULL };
57
58 BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok)
59 {
60 const std::string result =
61 capture_exec (I2n::join_string (zero_bytes_argv, " "));
62
63 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
64 }
65
c2c29997
PG
66 BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok_result)
67 {
ad4490f1 68 ExecResult exres = ExecResult ();
c2c29997
PG
69 const std::string result =
70 capture_exec (I2n::join_string (zero_bytes_argv, " "),
71 exres);
72
73 BOOST_CHECK(exres.normal_exit);
74 BOOST_CHECK_EQUAL(exres.return_code, 0);
75 BOOST_CHECK(!exres.terminated_by_signal);
76 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
77 }
78
3ed2cc9b
PG
79 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok)
80 {
81 const std::string result = capture_exec (zero_bytes_argv);
82
83 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
84 }
85
c2c29997
PG
86 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok_strvec)
87 {
88 std::vector<std::string> argvec;
89 const char *const *argp = zero_bytes_argv;
90 const char * cur = NULL;
91
92 while ((cur = *argp++) != NULL) {
93 argvec.push_back (std::string (cur));
94 }
95
96 const std::string result = capture_exec (argvec);
97
98 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
99 }
100
101 BOOST_AUTO_TEST_CASE(abspath_zeros_noshell_ok_result)
102 {
ad4490f1 103 ExecResult exres = ExecResult ();
c2c29997
PG
104 const std::string result = capture_exec (zero_bytes_argv, exres);
105
106 BOOST_CHECK(exres.normal_exit);
107 BOOST_CHECK_EQUAL(exres.return_code, 0);
108 BOOST_CHECK(!exres.terminated_by_signal);
109 BOOST_CHECK_EQUAL(result.size (), ENOUGH_ZEROS);
110 }
111
cc917897
PG
112 const char *const bad_command [] = { "/does_not_exist", NULL };
113
0c68bd0f 114# ifndef NO_CHILD_FAIL_TESTS
7f58145b 115 BOOST_AUTO_TEST_CASE(abspath_bad_shell_fail)
cc917897
PG
116 {
117 assert (access(bad_command [0], X_OK) != 0);
118
ad4490f1
PG
119 ExecResult exres = ExecResult ();
120 /*
121 * Note that the next line will make the unit test spew a message
122 * to stderr which cannot be prevented due to the limitations of
123 * popen(3).
124 */
cc917897
PG
125 const std::string result =
126 capture_exec (I2n::join_string (bad_command, " "));
127
ff5191e6 128 BOOST_CHECK(!exres.normal_exit);
cc917897
PG
129 BOOST_CHECK_EQUAL(result.size (), 0);
130 }
0c68bd0f 131# endif /* [!NO_CHILD_FAIL_TESTS] */
cc917897 132
0c68bd0f 133# ifndef NO_CHILD_FAIL_TESTS
7f58145b 134 BOOST_AUTO_TEST_CASE(abspath_bad_noshell_fail)
cc917897
PG
135 {
136 assert (access(bad_command [0], X_OK) != 0);
137
ad4490f1 138 ExecResult exres = ExecResult ();
cc917897
PG
139 const std::string result = capture_exec (bad_command, exres);
140
08199c66 141 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
cc917897
PG
142 BOOST_CHECK(!exres.terminated_by_signal);
143 BOOST_CHECK_EQUAL(result.size (), 0);
08199c66
PG
144 BOOST_CHECK_EQUAL(exres.error_message,
145 "child failed to exec(): "
146 "error 2 (No such file or directory)");
cc917897 147 }
0c68bd0f 148# endif /* [!NO_CHILD_FAIL_TESTS] */
cc917897 149
0c68bd0f 150# ifndef NO_CHILD_FAIL_TESTS
7f58145b 151 BOOST_AUTO_TEST_CASE(abspath_bad_noshell_stderr)
cc917897
PG
152 {
153 assert (access(bad_command [0], X_OK) != 0);
154
ad4490f1 155 ExecResult exres = ExecResult ();
cc917897
PG
156 const std::string result = capture_exec (bad_command, exres, false, true);
157
08199c66 158 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
ad4490f1
PG
159 BOOST_CHECK(!exres.terminated_by_signal);
160 BOOST_CHECK_EQUAL(result.size (), 0);
08199c66
PG
161 BOOST_CHECK_EQUAL(exres.error_message,
162 "child failed to exec(): "
163 "error 2 (No such file or directory)");
cc917897 164 }
0c68bd0f 165# endif /* [!NO_CHILD_FAIL_TESTS] */
cc917897 166
ad4490f1
PG
167 const char *const false_argv_abs [] = { "/bin/false", NULL };
168 const char *const true_argv_abs [] = { "/bin/true" , NULL };
169 const char *const false_argv_rel [] = { "false" , NULL };
170 const char *const true_argv_rel [] = { "true" , NULL };
7f58145b 171
0c68bd0f 172# ifndef NO_CHILD_FAIL_TESTS
7f58145b
PG
173 BOOST_AUTO_TEST_CASE(abspath_false_noshell_fail_exit)
174 {
ad4490f1
PG
175 ExecResult exres = ExecResult ();
176 const std::string result =
177 capture_exec (false_argv_abs, exres, true, false, false);
7f58145b
PG
178
179 BOOST_CHECK(exres.normal_exit);
180 BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
181 BOOST_CHECK_EQUAL(result.size (), 0);
182 }
0c68bd0f 183# endif /* [!NO_CHILD_FAIL_TESTS] */
7f58145b 184
0c68bd0f 185# ifndef NO_CHILD_FAIL_TESTS
7f58145b
PG
186 BOOST_AUTO_TEST_CASE(abspath_false_shell_fail_exit)
187 {
ad4490f1 188 ExecResult exres = ExecResult ();
7f58145b 189 const std::string result =
ad4490f1 190 capture_exec (std::string (false_argv_abs [0]), exres);
7f58145b
PG
191
192 BOOST_CHECK(exres.normal_exit);
193 BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
194 BOOST_CHECK_EQUAL(result.size (), 0);
195 }
0c68bd0f 196# endif /* [!NO_CHILD_FAIL_TESTS] */
7f58145b 197
ad4490f1
PG
198 BOOST_AUTO_TEST_CASE(relpath_true_noshell_ok)
199 {
200 ExecResult exres = ExecResult ();
201 const std::string result =
202 capture_exec (true_argv_rel, exres, true, false, true);
203
204 BOOST_CHECK(exres.normal_exit);
205 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
206 BOOST_CHECK_EQUAL(result.size (), 0);
207 }
208
0c68bd0f 209# ifndef NO_CHILD_FAIL_TESTS
ad4490f1
PG
210 BOOST_AUTO_TEST_CASE(relpath_true_noshell_fail)
211 {
212 ExecResult exres = ExecResult ();
213 const std::string result =
214 capture_exec (true_argv_rel, exres, true, false, false);
215
08199c66
PG
216 BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
217 /* no return code check since we couldn't exit */
ad4490f1 218 BOOST_CHECK_EQUAL(result.size (), 0);
08199c66
PG
219 BOOST_CHECK_EQUAL(exres.error_message,
220 "child failed to exec(): "
221 "error 2 (No such file or directory)");
ad4490f1 222 }
0c68bd0f 223# endif /* [!NO_CHILD_FAIL_TESTS] */
ad4490f1
PG
224
225 BOOST_AUTO_TEST_CASE(abspath_true_noshell_ok)
226 {
227 ExecResult exres = ExecResult ();
228 const std::string result =
229 capture_exec (true_argv_abs, exres, true, false, true);
230
231 BOOST_CHECK(exres.normal_exit);
232 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
233 BOOST_CHECK_EQUAL(result.size (), 0);
234 }
235
0c68bd0f 236# ifndef NO_CHILD_FAIL_TESTS
ad4490f1
PG
237 BOOST_AUTO_TEST_CASE(relpath_false_noshell_fail)
238 {
239 ExecResult exres = ExecResult ();
240 const std::string result =
241 capture_exec (false_argv_rel, exres, true, false, true);
242
243 BOOST_CHECK(exres.normal_exit);
08199c66 244 /* no return code check since we couldn't exit */
ad4490f1
PG
245 BOOST_CHECK_EQUAL(result.size (), 0);
246 }
0c68bd0f 247# endif /* [!NO_CHILD_FAIL_TESTS] */
ad4490f1 248
ff5191e6
PG
249 const char *const echo_abs = "/bin/echo";
250 const char *const echo_rel = "echo";
251
252 static std::vector<std::string>
253 mk_echo_argv (const std::string &text, const bool absolute=true)
254 {
255 std::vector<std::string> ret;
256
257 ret.push_back (absolute ? echo_abs : echo_rel);
258 ret.push_back ("-n");
259 ret.push_back (text);
260
261 return ret;
262 }
263
264 BOOST_AUTO_TEST_CASE(abspath_echo_noshell_capture_ok)
265 {
266 ExecResult exres = ExecResult ();
267 const std::string text = "The significant owl hoots in the night.";
268 const std::vector<std::string> argv = mk_echo_argv (text);
269 const std::string result = capture_exec (argv, exres, true, false, true);
270
271 BOOST_CHECK(exres.normal_exit);
272 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
273 BOOST_CHECK_EQUAL(result, text);
274 }
275
276 BOOST_AUTO_TEST_CASE(relpath_echo_noshell_capture_ok)
277 {
278 ExecResult exres = ExecResult ();
279 const std::string text = "Yet many grey lords go sadly to the masterless men.";
280 const std::vector<std::string> argv = mk_echo_argv (text, false);
281 const std::string result = capture_exec (argv, exres, true, false, true);
282
283 BOOST_CHECK(exres.normal_exit);
284 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
285 BOOST_CHECK_EQUAL(result, text);
286 }
287
288 static std::vector<std::string>
289 mk_errcho_argv (const std::string &text)
290 {
291 /*
292 * Hack cause there’s no way to make echo print to stderr without
293 * redirection.
294 */
295 std::vector<std::string> ret;
296
297 ret.push_back ("/bin/sh");
298 ret.push_back ("-c");
299 ret.push_back (std::string ("1>&- 1>&2 echo -n '") + text + "'"); /* brr */
300
301 return ret;
302 }
303
304 BOOST_AUTO_TEST_CASE(sh_errcho_capture_ok)
305 {
306 ExecResult exres = ExecResult ();
307 const std::string text = "Hooray, hooray for the spinster’s sister’s daughter.";
308 const std::vector<std::string> argv = mk_errcho_argv (text);
309 const std::string result = capture_exec (argv, exres, false, true, true);
310
311 BOOST_CHECK(exres.normal_exit);
312 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
313 BOOST_CHECK_EQUAL(result, text);
314 }
315
316 BOOST_AUTO_TEST_CASE(sh_errcho_stdout_empty_ok)
317 {
318 ExecResult exres = ExecResult ();
319 const std::string text = "To the axeman, all supplicants are the same height.";
320 const std::vector<std::string> argv = mk_errcho_argv (text);
321 const std::string result = capture_exec (argv, exres, true, false, true);
322
323 BOOST_CHECK(exres.normal_exit);
324 BOOST_CHECK_EQUAL(exres.return_code, EXIT_SUCCESS);
325 BOOST_CHECK_EQUAL(result.size (), 0);
326 }
327
3ed2cc9b
PG
328 BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
329
330BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */
331