Asterisk - The Open Source Telephony Project  18.5.0
test_func_file.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * Tilghman Lesher <tlesher AT digium DOT 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 Function FILE tests
22  *
23  * \author\verbatim Tilghman Lesher <tlesher AT digium DOT com> \endverbatim
24  *
25  * \ingroup tests
26  */
27 
28 /*** MODULEINFO
29  <depend>TEST_FRAMEWORK</depend>
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include "asterisk/utils.h"
36 #include "asterisk/app.h"
37 #include "asterisk/module.h"
38 #include "asterisk/test.h"
39 #include "asterisk/pbx.h"
40 
41 #define C1024 \
42  "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF"
43 
44 static struct {
45  const char *contents;
46  const char *args;
47  const char *value;
48 } read_tests[] = {
49  /* 4 different ways of specifying the first character */
50  { "123456789", "0,1", "1" },
51  { "123456789", "0,-8", "1" },
52  { "123456789", "-9,1", "1" },
53  { "123456789", "-9,-8", "1" },
54  /* Does 0-length work? */
55  { "123456789", "0,0", "" },
56  { "123456789", "-9,0", "" },
57  { "123456789", "-9,-9", "" },
58  /* Does negative length work? */
59  { "123456789", "5,-6", "" },
60  { "123456789", "-5,-6", "" },
61  /* No length */
62  { "123456789", "-5", "56789" },
63  { "123456789", "4", "56789" },
64  /* Passed file length */
65  { "123456789", "8,10", "9" },
66  { "123456789", "10,1", "" },
67  /* Middle of file */
68  { "123456789", "2,5", "34567" },
69  { "123456789", "-7,5", "34567" },
70  /* Line mode, 4 ways of specifying the first character */
71  { "123\n456\n789\n", "0,1,l", "123\n" },
72  { "123\n456\n789\n", "-3,1,l", "123\n" },
73  { "123\n456\n789\n", "0,-2,l", "123\n" },
74  { "123\n456\n789\n", "-3,-2,l", "123\n" },
75  /* Line mode, 0-length */
76  { "123\n456\n789\n", "0,0,l", "" },
77  { "123\n456\n789\n", "-3,0,l", "" },
78  { "123\n456\n789\n", "-3,-3,l", "" },
79  /* Line mode, negative length */
80  { "123\n456\n789\n", "2,-2,l", "" },
81  { "123\n456\n789\n", "-2,-3,l", "" },
82  /* No length */
83  { "123\n456\n789\n", "1,,l", "456\n789\n" },
84  { "123\n456\n789\n", "-2,,l", "456\n789\n" },
85 };
86 
87 static struct {
88  const char *contents;
89  const char *args;
90  const char *value;
91  const char *contents2;
92 } write_tests[] = {
93  /* Single character replace */
94  { "123456789", "0,1", "a", "a23456789" },
95  { "123456789", "-9,1", "a", "a23456789" },
96  { "123456789", "0,-8", "a", "a23456789" },
97  { "123456789", "-9,-8", "a", "a23456789" },
98  { "123456789", "5,1", "b", "12345b789" },
99  { "123456789", "-4,1", "b", "12345b789" },
100  { "123456789", "5,-3", "b", "12345b789" },
101  { "123456789", "-4,-3", "b", "12345b789" },
102  /* Replace 2 characters with 1 */
103  { "123456789", "0,2", "c", "c3456789" },
104  { "123456789", "-9,2", "c", "c3456789" },
105  { "123456789", "0,-7", "c", "c3456789" },
106  { "123456789", "-9,-7", "c", "c3456789" },
107  { "123456789", "4,2", "d", "1234d789" },
108  { "123456789", "-5,2", "d", "1234d789" },
109  { "123456789", "4,-3", "d", "1234d789" },
110  { "123456789", "-5,-3", "d", "1234d789" },
111  /* Truncate file */
112  { "123456789", "5", "e", "12345e" },
113  { "123456789", "5", "", "12345" },
114  { "123456789", "-4", "e", "12345e" },
115  { "123456789", "-4", "", "12345" },
116  /* Replace 1 character with 2 */
117  { "123456789", "0,1", "fg", "fg23456789" },
118  { "123456789", "0,-8", "fg", "fg23456789" },
119  { "123456789", "-9,1", "fg", "fg23456789" },
120  { "123456789", "-9,-8", "fg", "fg23456789" },
121  /* Overwrite file */
122  { "123456789", "", "h", "h" },
123  { "123456789", ",,,", "h", "h" },
124  { "123\n456\n789\n", ",,l", "h", "h\n" },
125  { "123\n456\n789\n", ",,ld", "h", "h" },
126  /* Single line replace, same length */
127  { "123\n456\n789\n", "0,1,l", "abc", "abc\n456\n789\n" },
128  { "123\n456\n789\n", "-3,1,l", "abc", "abc\n456\n789\n" },
129  { "123\n456\n789\n", "0,-2,l", "abc", "abc\n456\n789\n" },
130  { "123\n456\n789\n", "-3,-2,l", "abc", "abc\n456\n789\n" },
131  { "123\n456\n789\n", "1,1,l", "abc", "123\nabc\n789\n" },
132  { "123\n456\n789\n", "1,-1,l", "abc", "123\nabc\n789\n" },
133  { "123\n456\n789\n", "-2,1,l", "abc", "123\nabc\n789\n" },
134  { "123\n456\n789\n", "-2,-1,l", "abc", "123\nabc\n789\n" },
135  /* Single line replace, one character short */
136  { "123\n456\n789\n", "0,1,l", "ab", "ab\n456\n789\n" },
137  { "123\n456\n789\n", "-3,1,l", "ab", "ab\n456\n789\n" },
138  { "123\n456\n789\n", "0,-2,l", "ab", "ab\n456\n789\n" },
139  { "123\n456\n789\n", "-3,-2,l", "ab", "ab\n456\n789\n" },
140  { "123\n456\n789\n", "1,1,l", "ab", "123\nab\n789\n" },
141  { "123\n456\n789\n", "1,-1,l", "ab", "123\nab\n789\n" },
142  { "123\n456\n789\n", "-2,1,l", "ab", "123\nab\n789\n" },
143  { "123\n456\n789\n", "-2,-1,l", "ab", "123\nab\n789\n" },
144  /* Single line replace, one character long */
145  { "123\n456\n789\n", "0,1,l", "abcd", "abcd\n456\n789\n" },
146  { "123\n456\n789\n", "-3,1,l", "abcd", "abcd\n456\n789\n" },
147  { "123\n456\n789\n", "0,-2,l", "abcd", "abcd\n456\n789\n" },
148  { "123\n456\n789\n", "-3,-2,l", "abcd", "abcd\n456\n789\n" },
149  { "123\n456\n789\n", "1,1,l", "abcd", "123\nabcd\n789\n" },
150  { "123\n456\n789\n", "1,-1,l", "abcd", "123\nabcd\n789\n" },
151  { "123\n456\n789\n", "-2,1,l", "abcd", "123\nabcd\n789\n" },
152  { "123\n456\n789\n", "-2,-1,l", "abcd", "123\nabcd\n789\n" },
153  /* Multi-line replace, same number of characters, 2 lines for 1 */
154  { "123\n456\n789\n", "0,2,l", "abcdefg", "abcdefg\n789\n" },
155  { "123\n456\n789\n", "-3,2,l", "abcdefg", "abcdefg\n789\n" },
156  { "123\n456\n789\n", "0,-1,l", "abcdefg", "abcdefg\n789\n" },
157  { "123\n456\n789\n", "-3,-1,l", "abcdefg", "abcdefg\n789\n" },
158  { "123\n456\n789\n", "1,2,l", "abcdefg", "123\nabcdefg\n" },
159  { "123\n456\n789\n", "1,,l", "abcdefg", "123\nabcdefg\n" },
160  { "123\n456\n789\n", "-2,2,l", "abcdefg", "123\nabcdefg\n" },
161  { "123\n456\n789\n", "-2,,l", "abcdefg", "123\nabcdefg\n" },
162  /* Multi-line replace, shorter number of characters, 2 lines for 1 */
163  { "123\n456\n789\n", "0,2,l", "abcd", "abcd\n789\n" },
164  { "123\n456\n789\n", "-3,2,l", "abcd", "abcd\n789\n" },
165  { "123\n456\n789\n", "0,-1,l", "abcd", "abcd\n789\n" },
166  { "123\n456\n789\n", "-3,-1,l", "abcd", "abcd\n789\n" },
167  { "123\n456\n789\n", "1,2,l", "abcd", "123\nabcd\n" },
168  { "123\n456\n789\n", "1,,l", "abcd", "123\nabcd\n" },
169  { "123\n456\n789\n", "-2,2,l", "abcd", "123\nabcd\n" },
170  { "123\n456\n789\n", "-2,,l", "abcd", "123\nabcd\n" },
171  /* Multi-line replace, longer number of characters, 2 lines for 1 */
172  { "123\n456\n789\n", "0,2,l", "abcdefghijklmnop", "abcdefghijklmnop\n789\n" },
173  { "123\n456\n789\n", "-3,2,l", "abcdefghijklmnop", "abcdefghijklmnop\n789\n" },
174  { "123\n456\n789\n", "0,-1,l", "abcdefghijklmnop", "abcdefghijklmnop\n789\n" },
175  { "123\n456\n789\n", "-3,-1,l", "abcdefghijklmnop", "abcdefghijklmnop\n789\n" },
176  { "123\n456\n789\n", "1,2,l", "abcdefghijklmnop", "123\nabcdefghijklmnop\n" },
177  { "123\n456\n789\n", "1,,l", "abcdefghijklmnop", "123\nabcdefghijklmnop\n" },
178  { "123\n456\n789\n", "-2,2,l", "abcdefghijklmnop", "123\nabcdefghijklmnop\n" },
179  { "123\n456\n789\n", "-2,,l", "abcdefghijklmnop", "123\nabcdefghijklmnop\n" },
180  /* Insert line */
181  { "123\n456\n789\n", "0,0,l", "abcd", "abcd\n123\n456\n789\n" },
182  { "123\n456\n789\n", "-3,0,l", "abcd", "abcd\n123\n456\n789\n" },
183  { "123\n456\n789\n", "1,0,l", "abcd", "123\nabcd\n456\n789\n" },
184  { "123\n456\n789\n", "-2,0,l", "abcd", "123\nabcd\n456\n789\n" },
185  { "123\n456\n789\n", "2,0,l", "abcd", "123\n456\nabcd\n789\n" },
186  { "123\n456\n789\n", "-1,0,l", "abcd", "123\n456\nabcd\n789\n" },
187  { "123\n456\n789\n", "3,0,l", "abcd", "123\n456\n789\nabcd\n" },
188  { "123\n456\n789\n", ",,la", "abcd", "123\n456\n789\nabcd\n" },
189  /* Single line, replace with blank line */
190  { "123\n456\n789\n", "0,1,l", "", "\n456\n789\n" },
191  { "123\n456\n789\n", "-3,1,l", "", "\n456\n789\n" },
192  { "123\n456\n789\n", "0,-2,l", "", "\n456\n789\n" },
193  { "123\n456\n789\n", "-3,-2,l", "", "\n456\n789\n" },
194  { "123\n456\n789\n", "1,1,l", "", "123\n\n789\n" },
195  { "123\n456\n789\n", "1,-1,l", "", "123\n\n789\n" },
196  { "123\n456\n789\n", "-2,1,l", "", "123\n\n789\n" },
197  { "123\n456\n789\n", "-2,-1,l", "", "123\n\n789\n" },
198  /* Single line, delete */
199  { "123\n456\n789\n", "0,1,ld", "", "456\n789\n" },
200  { "123\n456\n789\n", "-3,1,ld", "", "456\n789\n" },
201  { "123\n456\n789\n", "0,-2,ld", "", "456\n789\n" },
202  { "123\n456\n789\n", "-3,-2,ld", "", "456\n789\n" },
203  { "123\n456\n789\n", "1,1,ld", "", "123\n789\n" },
204  { "123\n456\n789\n", "1,-1,ld", "", "123\n789\n" },
205  { "123\n456\n789\n", "-2,1,ld", "", "123\n789\n" },
206  { "123\n456\n789\n", "-2,-1,ld", "", "123\n789\n" },
207  /* Really long tests */
208  { "1234567890ABCDEF" C1024 C1024 C1024 C1024 C1024,
209  "0,1", "a",
210  "a234567890ABCDEF" C1024 C1024 C1024 C1024 C1024 },
211  { "1234567890ABCDEF" C1024 C1024 C1024 C1024 C1024,
212  "0,1", "abcd",
213  "abcd234567890ABCDEF" C1024 C1024 C1024 C1024 C1024 },
214  { "1234567890ABCDEF" C1024 C1024 C1024 C1024 C1024,
215  "0,10", "abcd",
216  "abcdABCDEF" C1024 C1024 C1024 C1024 C1024 },
217  { "1" C1024 "\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n",
218  "0,1,l", "abcd",
219  "abcd\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n" },
220  { "1234\n1" C1024 "\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n",
221  "0,1,l", "abcd",
222  "abcd\n1" C1024 "\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n" },
223  { "1234\n1" C1024 "\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n",
224  "0,1,l", "a",
225  "a\n1" C1024 "\n2" C1024 "\n3" C1024 "\n4" C1024 "\n5" C1024 "\n6" C1024 "\n" },
226 };
227 
228 static char *file2display(struct ast_str **buf, ssize_t len, const char *input)
229 {
230  const char *ptr;
231  ast_str_reset(*buf);
232  for (ptr = input; *ptr; ptr++) {
233  if (*ptr == '\n') {
234  ast_str_append(buf, len, "\\n");
235  } else if (*ptr == '\r') {
236  ast_str_append(buf, len, "\\r");
237  } else if (*ptr == '\t') {
238  ast_str_append(buf, len, "\\t");
239  } else if (*ptr < ' ' || *ptr > 125) {
240  ast_str_append(buf, len, "\\x%hhX", *ptr);
241  } else {
242  ast_str_append(buf, len, "%c", *ptr);
243  }
244  }
245  return ast_str_buffer(*buf);
246 }
247 
248 AST_TEST_DEFINE(test_func_file)
249 {
250  int res = AST_TEST_PASS;
251  int i;
252  char dir[] = "/tmp/test_func_file.XXXXXX";
253  char file[80], expression[256];
254  struct ast_str *buf, *disp[2] = { NULL, NULL };
255  char fbuf[8192];
256  FILE *fh;
257 
258  switch (cmd) {
259  case TEST_INIT:
260  info->name = "func_file";
261  info->category = "/funcs/func_env/";
262  info->summary = "Verify behavior of the FILE() dialplan function";
263  info->description =
264  "Verifies that the examples of the FILE() dialplan function documentation work as described.";
265  return AST_TEST_NOT_RUN;
266  case TEST_EXECUTE:
267  break;
268  }
269 
270  if (!mkdtemp(dir)) {
271  ast_test_status_update(test, "Cannot create temporary directory: %s\n", strerror(errno));
272  return AST_TEST_FAIL;
273  }
274 
275  disp[0] = ast_str_create(16);
276  disp[1] = ast_str_create(16);
277  if (!(buf = ast_str_create(16)) || !disp[0] || !disp[1]) {
278  ast_free(buf);
279  ast_free(disp[0]);
280  ast_free(disp[1]);
281  rmdir(dir);
282  return AST_TEST_FAIL;
283  }
284 
285  snprintf(file, sizeof(file), "%s/test.txt", dir);
286 
287  for (i = 0; i < ARRAY_LEN(read_tests); i++) {
288  if (!(fh = fopen(file, "w"))) {
289  ast_test_status_update(test, "Cannot open test file: %s\n", strerror(errno));
290  ast_free(buf);
291  ast_free(disp[0]);
292  ast_free(disp[1]);
293  unlink(file);
294  rmdir(dir);
295  return AST_TEST_FAIL;
296  }
297 
298  if (fwrite(read_tests[i].contents, 1, strlen(read_tests[i].contents), fh) < strlen(read_tests[i].contents)) {
299  ast_test_status_update(test, "Cannot write initial values into test file: %s\n", strerror(errno));
300  ast_free(buf);
301  ast_free(disp[0]);
302  ast_free(disp[1]);
303  fclose(fh);
304  unlink(file);
305  rmdir(dir);
306  return AST_TEST_FAIL;
307  }
308 
309  fclose(fh);
310 
311  snprintf(expression, sizeof(expression), "${FILE(%s,%s)}", file, read_tests[i].args);
312  ast_str_substitute_variables(&buf, 0, NULL, expression);
313 
314  if (strcmp(ast_str_buffer(buf), read_tests[i].value)) {
315  ast_test_status_update(test, "Expression '${FILE(...,%s)}' did not produce ('%s') the expected value ('%s')\n",
316  read_tests[i].args, file2display(&disp[0], 0, ast_str_buffer(buf)), file2display(&disp[1], 0, read_tests[i].value));
317  res = AST_TEST_FAIL;
318  }
319  }
320 
321  ast_free(buf);
322 
323  for (i = 0; i < ARRAY_LEN(write_tests); i++) {
324  if (!(fh = fopen(file, "w"))) {
325  ast_test_status_update(test, "Cannot open test file: %s\n", strerror(errno));
326  ast_free(disp[0]);
327  ast_free(disp[1]);
328  unlink(file);
329  rmdir(dir);
330  return AST_TEST_FAIL;
331  }
332 
333  if (fwrite(write_tests[i].contents, 1, strlen(write_tests[i].contents), fh) < strlen(write_tests[i].contents)) {
334  ast_test_status_update(test, "Cannot write initial values into test file: %s\n", strerror(errno));
335  ast_free(disp[0]);
336  ast_free(disp[1]);
337  fclose(fh);
338  unlink(file);
339  rmdir(dir);
340  return AST_TEST_FAIL;
341  }
342 
343  fclose(fh);
344 
345  snprintf(expression, sizeof(expression), "FILE(%s,%s)", file, write_tests[i].args);
346  ast_func_write(NULL, expression, write_tests[i].value);
347 
348  if (!(fh = fopen(file, "r"))) {
349  ast_test_status_update(test, "Cannot open test file: %s\n", strerror(errno));
350  ast_free(disp[0]);
351  ast_free(disp[1]);
352  unlink(file);
353  rmdir(dir);
354  return AST_TEST_FAIL;
355  }
356 
357  memset(fbuf, 0, sizeof(fbuf));
358  if (!fread(fbuf, 1, sizeof(fbuf), fh)) {
359  ast_test_status_update(test, "Cannot read write results from test file: %s\n", strerror(errno));
360  ast_free(disp[0]);
361  ast_free(disp[1]);
362  fclose(fh);
363  unlink(file);
364  rmdir(dir);
365  return AST_TEST_FAIL;
366  }
367 
368  fclose(fh);
369 
370  if (strcmp(fbuf, write_tests[i].contents2)) {
371  ast_test_status_update(test, "Expression 'FILE(...,%s)=%s' did not produce ('%s') the expected result ('%s')\n",
372  write_tests[i].args, write_tests[i].value, file2display(&disp[0], 0, fbuf), file2display(&disp[1], 0, write_tests[i].contents2));
373  res = AST_TEST_FAIL;
374  } else {
375  ast_test_status_update(test, "Expression 'FILE(...,%s)=%s'... OK!\n", write_tests[i].args, write_tests[i].value);
376  }
377  }
378 
379  ast_free(disp[0]);
380  ast_free(disp[1]);
381  unlink(file);
382  rmdir(dir);
383 
384  return res;
385 }
386 
387 static int unload_module(void)
388 {
389  AST_TEST_UNREGISTER(test_func_file);
390  return 0;
391 }
392 
393 static int load_module(void)
394 {
395  AST_TEST_REGISTER(test_func_file);
397 }
398 
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define C1024
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
const char * args
#define NULL
Definition: resample.c:96
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1584
static char * file2display(struct ast_str **buf, ssize_t len, const char *input)
Utility functions.
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
const char * contents
Core PBX routines and definitions.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
char * mkdtemp(char *template_s)
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
const char * value
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
#define ast_free(a)
Definition: astmm.h:182
static struct @510 write_tests[]
static int load_module(void)
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:653
const char * contents2
static int unload_module(void)
AST_TEST_DEFINE(test_func_file)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
static struct @509 read_tests[]
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620