Asterisk - The Open Source Telephony Project  21.4.1
test_capture.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2022, Philip Prindeville
5  *
6  * Philip Prindeville <philipp@redfish-solutions.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*!
20  * \file
21  * \brief Make basic use of capture capability in test framework.
22  *
23  * \author\verbatim Philip Prindeville <philipp@redfish-solutions.com> \endverbatim
24  *
25  * Exercise the capture capabilities built into the test framework so
26  * that external commands might be used to generate validating results
27  * used on corroborating tests.
28  * \ingroup tests
29  */
30 
31 /*** MODULEINFO
32  <depend>TEST_FRAMEWORK</depend>
33  <support_level>core</support_level>
34  ***/
35 
36 #include "asterisk.h"
37 
38 #include "asterisk/utils.h"
39 #include "asterisk/module.h"
40 #include "asterisk/test.h"
41 
42 AST_TEST_DEFINE(test_capture_true)
43 {
44  int status = AST_TEST_FAIL;
45  struct ast_test_capture cap;
46  const char *command = "true";
47  char *const args[] = { "true", NULL };
48 
49  switch (cmd) {
50  case TEST_INIT:
51  info->name = "test_capture_true";
52  info->category = "/main/test_capture/";
53  info->summary = "capture true exit unit test";
54  info->description =
55  "Capture exit code from true command.";
56  return AST_TEST_NOT_RUN;
57  case TEST_EXECUTE:
58  break;
59  }
60 
61  ast_test_status_update(test, "Executing true exit test...\n");
62 
63  if (!ast_check_command_in_path(command)) {
64  ast_test_status_update(test, "couldn't find %s\n", command);
65  return status;
66  }
67 
68  if (ast_test_capture_command(&cap, command, args, NULL, 0) != 1) {
69  ast_test_status_update(test, "ast_test_capture_command() failed\n");
70  return status;
71  }
72 
73  if (cap.outlen != 0) {
74  ast_test_status_update(test, "unexpected value for stdout\n");
75  goto cleanup;
76  }
77 
78  if (cap.errlen != 0) {
79  ast_test_status_update(test, "unexpected value for stderr\n");
80  goto cleanup;
81  }
82 
83  if (cap.pid == -1) {
84  ast_test_status_update(test, "invalid process id\n");
85  goto cleanup;
86  }
87 
88  if (cap.exitcode != 0) {
89  ast_test_status_update(test, "child exited %d\n", cap.exitcode);
90  goto cleanup;
91  }
92 
93  status = AST_TEST_PASS;
94 
95 cleanup:
96  ast_test_capture_free(&cap);
97 
98  return status;
99 }
100 
101 AST_TEST_DEFINE(test_capture_false)
102 {
103  int status = AST_TEST_FAIL;
104  struct ast_test_capture cap;
105  const char *command = "false";
106  char *const args[] = { "false", NULL };
107 
108  switch (cmd) {
109  case TEST_INIT:
110  info->name = "test_capture_false";
111  info->category = "/main/test_capture/";
112  info->summary = "capture false exit unit test";
113  info->description =
114  "Capture exit code from false command.";
115  return AST_TEST_NOT_RUN;
116  case TEST_EXECUTE:
117  break;
118  }
119 
120  ast_test_status_update(test, "Executing false exit test...\n");
121 
122  if (!ast_check_command_in_path(command)) {
123  ast_test_status_update(test, "couldn't find %s\n", command);
124  return status;
125  }
126 
127  if (ast_test_capture_command(&cap, command, args, NULL, 0) != 1) {
128  ast_test_status_update(test, "ast_test_capture_command() failed\n");
129  return status;
130  }
131 
132  if (cap.outlen != 0) {
133  ast_test_status_update(test, "unexpected value for stdout\n");
134  goto cleanup;
135  }
136 
137  if (cap.errlen != 0) {
138  ast_test_status_update(test, "unexpected value for stderr\n");
139  goto cleanup;
140  }
141 
142  if (cap.pid == -1) {
143  ast_test_status_update(test, "invalid process id\n");
144  goto cleanup;
145  }
146 
147  if (cap.exitcode != 1) {
148  ast_test_status_update(test, "child exited %d\n", cap.exitcode);
149  goto cleanup;
150  }
151 
152  status = AST_TEST_PASS;
153 
154 cleanup:
155  ast_test_capture_free(&cap);
156 
157  return status;
158 }
159 
160 AST_TEST_DEFINE(test_capture_with_stdin)
161 {
162  int status = AST_TEST_FAIL;
163  struct ast_test_capture cap;
164  const char *command = "base64";
165  char *const args[] = { "base64", NULL };
166  const char data[] = "Mary had a little lamb.";
167  const unsigned datalen = sizeof(data) - 1;
168  const char output[] = "TWFyeSBoYWQgYSBsaXR0bGUgbGFtYi4=\n";
169  const unsigned outputlen = sizeof(output) - 1;
170 
171  switch (cmd) {
172  case TEST_INIT:
173  info->name = "test_capture_with_stdin";
174  info->category = "/main/test_capture/";
175  info->summary = "capture with stdin unit test";
176  info->description =
177  "Capture output from stdin transformation command.";
178  return AST_TEST_NOT_RUN;
179  case TEST_EXECUTE:
180  break;
181  }
182 
183  ast_test_status_update(test, "Executing stdin test...\n");
184 
185  if (!ast_check_command_in_path(command)) {
186  ast_test_status_update(test, "couldn't find %s\n", command);
187  return status;
188  }
189 
190  if (ast_test_capture_command(&cap, command, args, data, datalen) != 1) {
191  ast_test_status_update(test, "ast_test_capture_command() failed\n");
192  return status;
193  }
194 
195  if (cap.outlen != outputlen || memcmp(cap.outbuf, output, cap.outlen)) {
196  ast_test_status_update(test, "unexpected value for stdout\n");
197  goto cleanup;
198  }
199 
200  if (cap.errlen != 0) {
201  ast_test_status_update(test, "unexpected value for stderr\n");
202  goto cleanup;
203  }
204 
205  if (cap.pid == -1) {
206  ast_test_status_update(test, "invalid process id\n");
207  goto cleanup;
208  }
209 
210  if (cap.exitcode != 0) {
211  ast_test_status_update(test, "child exited %d\n", cap.exitcode);
212  goto cleanup;
213  }
214 
215  status = AST_TEST_PASS;
216 
217 cleanup:
218  ast_test_capture_free(&cap);
219 
220  return status;
221 }
222 
223 AST_TEST_DEFINE(test_capture_with_dynamic)
224 {
225  int status = AST_TEST_FAIL;
226  struct ast_test_capture cap;
227  const char *command = "date";
228  char *args[] = { "date", "DATE", "FORMAT", NULL };
229  char date[40];
230  const char format[] = "+%a, %d %b %y %T %z";
231  const char format2[] = "%a, %d %b %y %T %z\n";
232  char myresult[64];
233  unsigned myresultlen;
234  time_t now;
235  struct tm *tm;
236 
237  switch (cmd) {
238  case TEST_INIT:
239  info->name = "test_capture_with_dynamic";
240  info->category = "/main/test_capture/";
241  info->summary = "capture with dynamic argument unit test";
242  info->description =
243  "Capture output from dynamic transformation command.";
244  return AST_TEST_NOT_RUN;
245  case TEST_EXECUTE:
246  break;
247  }
248 
249  ast_test_status_update(test, "Executing dynamic argument test...\n");
250 
251  if (!ast_check_command_in_path(command)) {
252  ast_test_status_update(test, "couldn't find %s\n", command);
253  return status;
254  }
255 
256  time(&now);
257  snprintf(date, sizeof(date), "--date=@%lu", now);
258 
259  tm = localtime(&now);
260  strftime(myresult, sizeof(myresult), format2, tm);
261  myresultlen = strlen(myresult);
262 
263  args[1] = date;
264  args[2] = (char *)format;
265 
266  if (ast_test_capture_command(&cap, command, args, NULL, 0) != 1) {
267  ast_test_status_update(test, "ast_test_capture_command() failed\n");
268  return status;
269  }
270 
271  if (cap.outlen != myresultlen || memcmp(cap.outbuf, myresult, cap.outlen)) {
272  ast_test_status_update(test, "unexpected value for stdout\n");
273  goto cleanup;
274  }
275 
276  if (cap.errlen != 0) {
277  ast_test_status_update(test, "unexpected value for stderr\n");
278  goto cleanup;
279  }
280 
281  if (cap.pid == -1) {
282  ast_test_status_update(test, "invalid process id\n");
283  goto cleanup;
284  }
285 
286  if (cap.exitcode != 0) {
287  ast_test_status_update(test, "child exited %d\n", cap.exitcode);
288  goto cleanup;
289  }
290 
291  status = AST_TEST_PASS;
292 
293 cleanup:
294  ast_test_capture_free(&cap);
295 
296  return status;
297 }
298 
299 AST_TEST_DEFINE(test_capture_stdout_stderr)
300 {
301  int status = AST_TEST_FAIL;
302  struct ast_test_capture cap;
303  const char *command = "sh";
304  char *const args[] = { "sh", "-c", "echo -n 'foo' >&2 ; echo -n 'zzz' >&1 ; echo -n 'bar' >&2", NULL };
305 
306  switch (cmd) {
307  case TEST_INIT:
308  info->name = "test_capture_stdout_stderr";
309  info->category = "/main/test_capture/";
310  info->summary = "capture stdout & stderr unit test";
311  info->description =
312  "Capture both stdout and stderr from shell.";
313  return AST_TEST_NOT_RUN;
314  case TEST_EXECUTE:
315  break;
316  }
317 
318  ast_test_status_update(test, "Executing stdout/stderr test...\n");
319 
320  if (!ast_check_command_in_path(command)) {
321  ast_test_status_update(test, "couldn't find %s\n", command);
322  return status;
323  }
324 
325  if (ast_test_capture_command(&cap, command, args, NULL, 0) != 1) {
326  ast_test_status_update(test, "ast_test_capture_command() failed\n");
327  return status;
328  }
329 
330  if (cap.outlen != 3 || memcmp(cap.outbuf, "zzz", 3)) {
331  ast_test_status_update(test, "unexpected value for stdout\n");
332  goto cleanup;
333  }
334 
335  if (cap.errlen != 6 || memcmp(cap.errbuf, "foobar", 6)) {
336  ast_test_status_update(test, "unexpected value for stderr\n");
337  goto cleanup;
338  }
339 
340  if (cap.pid == -1) {
341  ast_test_status_update(test, "invalid process id\n");
342  goto cleanup;
343  }
344 
345  if (cap.exitcode != 0) {
346  ast_test_status_update(test, "child exited %d\n", cap.exitcode);
347  goto cleanup;
348  }
349 
350  status = AST_TEST_PASS;
351 
352 cleanup:
353  ast_test_capture_free(&cap);
354 
355  return status;
356 }
357 
358 static int unload_module(void)
359 {
360  AST_TEST_UNREGISTER(test_capture_with_stdin);
361  AST_TEST_UNREGISTER(test_capture_with_dynamic);
362  AST_TEST_UNREGISTER(test_capture_stdout_stderr);
363  AST_TEST_UNREGISTER(test_capture_true);
364  AST_TEST_UNREGISTER(test_capture_false);
365  return 0;
366 }
367 
368 static int load_module(void)
369 {
370  AST_TEST_REGISTER(test_capture_with_stdin);
371  AST_TEST_REGISTER(test_capture_with_dynamic);
372  AST_TEST_REGISTER(test_capture_stdout_stderr);
373  AST_TEST_REGISTER(test_capture_true);
374  AST_TEST_REGISTER(test_capture_false);
376 }
377 
378 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Capture support test");
379 
Asterisk main include file. File version handling, generic pbx functions.
Test Framework API.
Utility functions.
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
int ast_check_command_in_path(const char *cmd)
Test for the presence of an executable command in $PATH.
Definition: utils.c:3263
A capture of running an external process.
Definition: test.h:217
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.