b03d6c0508e403ce8fb4a373bd552845b9d99155
[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 #define BOOST_TEST_IGNORE_NON_ZERO_CHILD_CODE
28
29 #define BOOST_TEST_DYN_LINK
30 #include <boost/test/unit_test.hpp>
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
38 BOOST_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
54         BOOST_AUTO_TEST_CASE(abspath_zeros_shell_ok_result)
55         {
56             ExecResult exres = ExecResult ();
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
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
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         {
91             ExecResult exres = ExecResult ();
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
100         const char *const bad_command [] = { "/does_not_exist", NULL };
101
102         BOOST_AUTO_TEST_CASE(abspath_bad_shell_fail)
103         {
104             assert (access(bad_command [0], X_OK) != 0);
105
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              */
112             const std::string result =
113                     capture_exec (I2n::join_string (bad_command, " "));
114
115             BOOST_CHECK(!exres.normal_exit);
116             BOOST_CHECK_EQUAL(result.size (), 0);
117         }
118
119         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_fail)
120         {
121             assert (access(bad_command [0], X_OK) != 0);
122
123             ExecResult exres = ExecResult ();
124             const std::string result = capture_exec (bad_command, exres);
125
126             BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
127             BOOST_CHECK(!exres.terminated_by_signal);
128             BOOST_CHECK_EQUAL(result.size (), 0);
129             BOOST_CHECK_EQUAL(exres.error_message,
130                               "child failed to exec(): "
131                               "error 2 (No such file or directory)");
132         }
133
134         BOOST_AUTO_TEST_CASE(abspath_bad_noshell_stderr)
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, false, true);
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
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 };
153
154         BOOST_AUTO_TEST_CASE(abspath_false_noshell_fail_exit)
155         {
156             ExecResult exres = ExecResult ();
157             const std::string result =
158                 capture_exec (false_argv_abs, exres, true, false, false);
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         {
167             ExecResult exres = ExecResult ();
168             const std::string result =
169                     capture_exec (std::string (false_argv_abs [0]), exres);
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
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
193             BOOST_CHECK(!exres.normal_exit); /* failed to exec() */
194             /* no return code check since we couldn't exit */
195             BOOST_CHECK_EQUAL(result.size (), 0);
196             BOOST_CHECK_EQUAL(exres.error_message,
197                               "child failed to exec(): "
198                               "error 2 (No such file or directory)");
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);
219             /* no return code check since we couldn't exit */
220             BOOST_CHECK_EQUAL(result.size (), 0);
221         }
222
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
302     BOOST_AUTO_TEST_SUITE_END() /* [pipestream->read] */
303
304 BOOST_AUTO_TEST_SUITE_END() /* [pipestream] */
305