2 * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
21 * Author: Dustin J. Mitchell <dustin@zmanda.com>
25 #include "testutils.h"
31 safestr(const char *str) {
32 static char hex[] = "0123456789abcdef";
34 char *result = malloc(3 + strlen(str) * 3);
38 for (p = str; *p; p++) {
39 if (isprint((int)*p)) {
43 *(r++) = hex[((*p)&0xf0) >> 4];
44 *(r++) = hex[(*p)&0xf];
53 char * quotable_strings[] = {
61 "balanced \"internal\" quotes",
62 "\"already quoted\" string",
63 "string that's \"already quoted\"",
68 "\t", "\r", "\n", "\f", "\004",
79 * Round-trip testing of quoting functions
86 gboolean success = TRUE;
88 for (strp = quotable_strings; *strp; strp++) {
89 char *quoted, *unquoted;
91 quoted = quote_string(*strp);
92 unquoted = unquote_string(quoted);
94 /* if they're not the same, complain */
95 if (0 != strcmp(*strp, unquoted)) {
96 char *safe_orig = safestr(*strp);
97 char *safe_quoted = safestr(quoted);
98 char *safe_unquoted = safestr(unquoted);
100 printf(" bad round-trip: %s -quote_string-> %s -unquote_string-> %s\n",
101 safe_orig, safe_quoted, safe_unquoted);
105 amfree(safe_unquoted);
118 * Test that the new split_quoted_strings acts identically to
119 * the old split(), albeit with a different set of arguments and
120 * return value. Note that we only test with a delimiter of " ",
121 * as split() is not used with any other delimiter.
129 const char *original)
131 const char **a = exp;
134 if (0 != strcmp(*a, *b))
139 /* did we exit the loop early, or were they different lengths? */
143 safe = safestr(original);
144 g_printf(" %s: expected [", safe);
146 for (a = exp; *a; a++) {
148 g_printf("%s%s", safe, *(a+1)? ", " : "");
151 g_printf("] but got [");
152 for (b = got; *b; b++) {
154 g_printf("%s%s", safe, *(b+1)? ", " : "");
157 g_printf("] using %s.\n", source);
166 test_split_quoted_strings(void)
168 char **iter1, **iter2, **iter3;
169 gboolean success = TRUE;
170 char *middle_strings[] = {
178 /* the idea here is to loop over all triples of strings, forming a
179 * string by quoting them with quote_string and inserting a space, then
180 * re-splitting with split_quoted_string. This should get us back to our
183 for (iter1 = quotable_strings; *iter1; iter1++) {
184 for (iter2 = middle_strings; *iter2; iter2++) {
185 for (iter3 = quotable_strings; *iter3; iter3++) {
186 char *q1 = quote_string(*iter1);
187 char *q2 = quote_string(*iter2);
188 char *q3 = quote_string(*iter3);
189 const char *expected[4] = { *iter1, *iter2, *iter3, NULL };
190 char *combined = vstralloc(q1, " ", q2, " ", q3, NULL);
193 tokens = split_quoted_strings(combined);
195 success = compare_strv(expected, tokens, "split_quoted_strings", combined)
211 * Test splitting some edge cases and invalid strings
215 const char *combined;
216 const char *expected[5];
220 test_split_quoted_strings_edge(void)
222 gboolean success = TRUE;
223 struct trial trials[] = {
224 { "", { "", NULL, } },
225 { " ", { "", "", NULL } },
226 { " x", { "", "x", NULL } },
227 { "x ", { "x", "", NULL } },
228 { "x\\ y", { "x y", NULL } },
229 { "\\", { "", NULL } }, /* inv */
230 { "z\\", { "z", NULL } }, /* inv */
231 { "z\"", { "z", NULL } }, /* inv */
232 { "\" \" \"", { " ", "", NULL } }, /* inv */
235 struct trial *trial = trials;
237 while (trial->combined) {
238 char **tokens = split_quoted_strings(trial->combined);
240 success = compare_strv(trial->expected, tokens,
241 "split_quoted_strings", trial->combined)
252 * Test unquoting of some pathological strings
255 test_unquote_string(void)
257 gboolean success = TRUE;
260 "\"quoted\"", "quoted",
261 "s p a c e", "s p a c e",
263 /* special escape characters */
264 "esc \\\" quote", "esc \" quote",
265 "esc \\t tab", "esc \t tab",
266 "esc \\\\ esc", "esc \\ esc",
267 "esc \\02 oct", "esc \02 oct",
268 "esc \\7 oct", "esc \7 oct",
269 "esc \\17 oct", "esc \17 oct",
270 "esc \\117 oct", "esc \117 oct",
271 "esc \\1117 oct", "esc \1117 oct", /* '7' is distinct char */
273 /* same, but pre-quoted */
274 "\"esc \\\" quote\"", "esc \" quote",
275 "\"esc \\t tab\"", "esc \t tab",
276 "\"esc \\\\ esc\"", "esc \\ esc",
277 "\"esc \\02 oct\"", "esc \02 oct",
278 "\"esc \\7 oct\"", "esc \7 oct",
279 "\"esc \\17 oct\"", "esc \17 oct",
280 "\"esc \\117 oct\"", "esc \117 oct",
281 "\"esc \\1117 oct\"", "esc \1117 oct", /* '7' is distinct char */
283 /* strips balanced quotes, even inside the string */
284 ">>\"x\"<<", ">>x<<",
285 ">>\"x\"-\"y\"<<", ">>x-y<<",
287 /* pathological, but valid */
294 /* invalid strings (handling here is arbitrary, but these tests
295 * will alert us if the handling changes) */
296 "\\", "", /* trailing backslash is ignored */
297 "xx\\", "xx", /* ditto */
298 "\\\\\\\\\\\\\\", "\\\\\\", /* ditto */
299 "\\777", "\377", /* 0777 & 0xff = 0xff */
300 "\"--", "--", /* leading quote is dropped */
301 "--\"", "--", /* trailing quote is dropped */
307 for (strp = tests; *strp;) {
308 char *quoted = *(strp++);
309 char *expected = *(strp++);
310 char *unquoted = unquote_string(quoted);
312 /* if they're not the same, complain */
313 if (0 != strcmp(expected, unquoted)) {
314 char *safe_quoted = safestr(quoted);
315 char *safe_unquoted = safestr(unquoted);
316 char *safe_expected = safestr(expected);
318 printf(" %s unquoted to %s; expected %s.\n",
319 safe_quoted, safe_unquoted, safe_expected);
322 amfree(safe_unquoted);
323 amfree(safe_expected);
335 * Test the strquotedstr function
338 test_strquotedstr_skipping(void)
340 char **iter1, **iter2;
341 gboolean success = TRUE;
343 /* the idea here is to loop over all pairs of strings, forming a
344 * string by quoting them with quote_string and inserting a space, then
345 * re-splitting with strquotedstr. This should get us back to our
346 * starting point. Note that we have to begin with a non-quoted identifier,
347 * becuse strquotedstr requires that strtok_r has already been called. */
349 for (iter1 = quotable_strings; *iter1; iter1++) {
350 for (iter2 = quotable_strings; *iter2; iter2++) {
351 char *q1 = quote_string(*iter1);
352 char *q2 = quote_string(*iter2);
353 char *combined = vstralloc("START ", q1, " ", q2, NULL);
354 char *copy = g_strdup(combined);
355 char *saveptr = NULL;
359 tok = strtok_r(copy, " ", &saveptr);
361 for (i = 1; i <= 2; i++) {
362 char *expected = (i == 1)? q1:q2;
363 tok = strquotedstr(&saveptr);
365 g_fprintf(stderr, "while parsing '%s', call %d to strquotedstr returned NULL\n",
370 if (0 != strcmp(tok, expected)) {
371 char *safe = safestr(tok);
373 g_fprintf(stderr, "while parsing '%s', call %d to strquotedstr returned '%s' "
374 "but '%s' was expected.\n",
375 combined, i, safe, expected);
381 if (strquotedstr(&saveptr) != NULL) {
382 g_fprintf(stderr, "while parsing '%s', call 3 to strquotedstr did not return NULL\n",
399 test_strquotedstr_edge_invalid(void)
401 gboolean success = TRUE;
403 "X \"abc", /* unterminated */
404 "X \"ab cd", /* unterminated second token */
405 "X a\"b cd", /* unterminated second token with internal quote */
406 "X b\\", /* trailing backslash */
407 "X \"b\\", /* trailing backslash in quote */
408 "X \"b\\\"", /* backslash'd ending quote */
413 /* run strquotedstr on a bunch of invalid tokens. It should return NULL */
415 for (iter = invalid; *iter; iter++) {
416 char *copy = g_strdup(*iter);
418 char *saveptr = NULL;
420 tok = strtok_r(copy, " ", &saveptr);
421 tok = strquotedstr(&saveptr);
423 g_fprintf(stderr, "while parsing invalid '%s', strquotedstr did not return NULL\n",
435 test_strquotedstr_edge_valid(void)
437 gboolean success = TRUE;
439 /* input */ /* expected (omitting "X") */
440 "X abc\\ def", "abc\\ def", /* backslashed space */
441 "X \"abc\\ def\"", "\"abc\\ def\"", /* quoted, backslashed space */
442 "X a\" \"b", "a\" \"b", /* quoted spaces */
447 /* run strquotedstr on a bunch of valid, but tricky, tokens. It should return NULL */
449 for (iter = valid; *iter; iter += 2) {
450 char *copy = g_strdup(*iter);
451 char *expected = *(iter+1);
453 char *saveptr = NULL;
455 tok = strtok_r(copy, " ", &saveptr);
456 tok = strquotedstr(&saveptr);
458 g_fprintf(stderr, "while parsing valid '%s', strquotedstr returned NULL\n",
461 } else if (0 != strcmp(tok, expected)) {
462 g_fprintf(stderr, "while parsing valid '%s', strquotedstr returned '%s' while "
463 "'%s' was expected\n",
464 *iter, tok, expected);
479 main(int argc, char **argv)
481 static TestUtilsTest tests[] = {
482 TU_TEST(test_round_trip, 90),
483 TU_TEST(test_unquote_string, 90),
484 TU_TEST(test_split_quoted_strings, 90),
485 TU_TEST(test_split_quoted_strings_edge, 90),
486 TU_TEST(test_strquotedstr_skipping, 90),
487 TU_TEST(test_strquotedstr_edge_invalid, 90),
488 TU_TEST(test_strquotedstr_edge_valid, 90),
492 return testutils_run_tests(argc, argv, tests);