skip unit tests for handling child errors in pipestream with ancient boost
[libi2ncommon] / test / test_pipestream.cpp
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
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
40
41 #define BOOST_TEST_DYN_LINK
42 #include <boost/test/unit_test.hpp>
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
50 BOOST_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
66         BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok_result)
67         {
68             ExecResult exres = ExecResult ();
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
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
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         {
103             ExecResult exres = ExecResult ();
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
112         const char *const bad_command [] = { "/does_not_exist", NULL };
113
114 # ifndef NO_CHILD_FAIL_TESTS
115         BOOST_AUTO_TEST_CASE(abspath_bad_shell_fail)
116         {
117             assert (access(bad_command [0], X_OK) != 0);
118
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              */
125             const std::string result =
126                     capture_exec (I2n::join_string (bad_command, " "));
127
128             BOOST_CHECK(!exres.normal_exit);
129             BOOST_CHECK_EQUAL(result.size (), 0);
130         }
131 # endif /* [!NO_CHILD_FAIL_TESTS] */
132
133 # ifndef NO_CHILD_FAIL_TESTS
134         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_fail)
135         {
136             assert (access(bad_command [0], X_OK) != 0);
137
138             ExecResult exres = ExecResult ();
139             const std::string result = capture_exec (bad_command, exres);
140
141             BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
142             BOOST_CHECK(!exres.terminated_by_signal);
143             BOOST_CHECK_EQUAL(result.size (), 0);
144             BOOST_CHECK_EQUAL(exres.error_message,
145                               "child failed to exec(): "
146                               "error 2 (No such file or directory)");
147         }
148 # endif /* [!NO_CHILD_FAIL_TESTS] */
149
150 # ifndef NO_CHILD_FAIL_TESTS
151         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_stderr)
152         {
153             assert (access(bad_command [0], X_OK) != 0);
154
155             ExecResult exres = ExecResult ();
156             const std::string result = capture_exec (bad_command, exres, false, true);
157
158             BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
159             BOOST_CHECK(!exres.terminated_by_signal);
160             BOOST_CHECK_EQUAL(result.size (), 0);
161             BOOST_CHECK_EQUAL(exres.error_message,
162                               "child failed to exec(): "
163                               "error 2 (No such file or directory)");
164         }
165 # endif /* [!NO_CHILD_FAIL_TESTS] */
166
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 };
171
172 # ifndef NO_CHILD_FAIL_TESTS
173         BOOST_AUTO_TEST_CASE(abspath_false_noshell_fail_exit)
174         {
175             ExecResult exres = ExecResult ();
176             const std::string result =
177                 capture_exec (false_argv_abs, exres, true, false, false);
178
179             BOOST_CHECK(exres.normal_exit);
180             BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
181             BOOST_CHECK_EQUAL(result.size (), 0);
182         }
183 # endif /* [!NO_CHILD_FAIL_TESTS] */
184
185 # ifndef NO_CHILD_FAIL_TESTS
186         BOOST_AUTO_TEST_CASE(abspath_false_shell_fail_exit)
187         {
188             ExecResult exres = ExecResult ();
189             const std::string result =
190                     capture_exec (std::string (false_argv_abs [0]), exres);
191
192             BOOST_CHECK(exres.normal_exit);
193             BOOST_CHECK_EQUAL(exres.return_code, EXIT_FAILURE);
194             BOOST_CHECK_EQUAL(result.size (), 0);
195         }
196 # endif /* [!NO_CHILD_FAIL_TESTS] */
197
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
209 # ifndef NO_CHILD_FAIL_TESTS
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
216             BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
217             /* no return code check since we couldn't exit */
218             BOOST_CHECK_EQUAL(result.size (), 0);
219             BOOST_CHECK_EQUAL(exres.error_message,
220                               "child failed to exec(): "
221                               "error 2 (No such file or directory)");
222         }
223 # endif /* [!NO_CHILD_FAIL_TESTS] */
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
236 # ifndef NO_CHILD_FAIL_TESTS
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);
244             /* no return code check since we couldn't exit */
245             BOOST_CHECK_EQUAL(result.size (), 0);
246         }
247 # endif /* [!NO_CHILD_FAIL_TESTS] */
248
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
328     BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
329
330 BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */
331