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