2 * Copyright (c) 2008,2009 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
18 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20 * Author: Dustin J. Mitchell <dustin@zmanda.com>
24 #include "testutils.h"
30 safestr(const char *str) {
31 static char hex[] = "0123456789abcdef";
33 char *result = malloc(3 + strlen(str) * 3);
37 for (p = str; *p; p++) {
38 if (isprint((int)*p)) {
42 *(r++) = hex[((*p)&0xf0) >> 4];
43 *(r++) = hex[(*p)&0xf];
52 char * quotable_strings[] = {
60 "balanced \"internal\" quotes",
61 "\"already quoted\" string",
62 "string that's \"already quoted\"",
67 "\t", "\r", "\n", "\f", "\004",
78 * Round-trip testing of quoting functions
85 gboolean success = TRUE;
87 for (strp = quotable_strings; *strp; strp++) {
88 char *quoted, *unquoted;
90 quoted = quote_string(*strp);
91 unquoted = unquote_string(quoted);
93 /* if they're not the same, complain */
94 if (0 != strcmp(*strp, unquoted)) {
95 char *safe_orig = safestr(*strp);
96 char *safe_quoted = safestr(quoted);
97 char *safe_unquoted = safestr(unquoted);
99 printf(" bad round-trip: %s -quote_string-> %s -unquote_string-> %s\n",
100 safe_orig, safe_quoted, safe_unquoted);
104 amfree(safe_unquoted);
117 * Test that the new split_quoted_strings acts identically to
118 * the old split(), albeit with a different set of arguments and
119 * return value. Note that we only test with a delimiter of " ",
120 * as split() is not used with any other delimiter.
128 const char *original)
130 const char **a = exp;
133 if (0 != strcmp(*a, *b))
138 /* did we exit the loop early, or were they different lengths? */
142 safe = safestr(original);
143 g_printf(" %s: expected [", safe);
145 for (a = exp; *a; a++) {
147 g_printf("%s%s", safe, *(a+1)? ", " : "");
150 g_printf("] but got [");
151 for (b = got; *b; b++) {
153 g_printf("%s%s", safe, *(b+1)? ", " : "");
156 g_printf("] using %s.\n", source);
165 test_split_quoted_strings(void)
167 char **iter1, **iter2, **iter3;
168 gboolean success = TRUE;
169 char *middle_strings[] = {
177 /* the idea here is to loop over all triples of strings, forming a
178 * string by quoting them with quote_string and inserting a space, then
179 * re-splitting with split_quoted_string. This should get us back to our
182 for (iter1 = quotable_strings; *iter1; iter1++) {
183 for (iter2 = middle_strings; *iter2; iter2++) {
184 for (iter3 = quotable_strings; *iter3; iter3++) {
185 char *q1 = quote_string(*iter1);
186 char *q2 = quote_string(*iter2);
187 char *q3 = quote_string(*iter3);
188 const char *expected[4] = { *iter1, *iter2, *iter3, NULL };
189 char *combined = vstralloc(q1, " ", q2, " ", q3, NULL);
192 tokens = split_quoted_strings(combined);
194 success = compare_strv(expected, tokens, "split_quoted_strings", combined)
210 * Test splitting some edge cases and invalid strings
214 const char *combined;
215 const char *expected[5];
219 test_split_quoted_strings_edge(void)
221 gboolean success = TRUE;
222 struct trial trials[] = {
223 { "", { "", NULL, } },
224 { " ", { "", "", NULL } },
225 { " x", { "", "x", NULL } },
226 { "x ", { "x", "", NULL } },
227 { "x\\ y", { "x y", NULL } },
228 { "\\", { "", NULL } }, /* inv */
229 { "z\\", { "z", NULL } }, /* inv */
230 { "z\"", { "z", NULL } }, /* inv */
231 { "\" \" \"", { " ", "", NULL } }, /* inv */
234 struct trial *trial = trials;
236 while (trial->combined) {
237 char **tokens = split_quoted_strings(trial->combined);
239 success = compare_strv(trial->expected, tokens,
240 "split_quoted_strings", trial->combined)
251 * Test unquoting of some pathological strings
254 test_unquote_string(void)
256 gboolean success = TRUE;
259 "\"quoted\"", "quoted",
260 "s p a c e", "s p a c e",
262 /* special escape characters */
263 "esc \\\" quote", "esc \" quote",
264 "esc \\t tab", "esc \t tab",
265 "esc \\\\ esc", "esc \\ esc",
266 "esc \\02 oct", "esc \02 oct",
267 "esc \\7 oct", "esc \7 oct",
268 "esc \\17 oct", "esc \17 oct",
269 "esc \\117 oct", "esc \117 oct",
270 "esc \\1117 oct", "esc \1117 oct", /* '7' is distinct char */
272 /* same, but pre-quoted */
273 "\"esc \\\" quote\"", "esc \" quote",
274 "\"esc \\t tab\"", "esc \t tab",
275 "\"esc \\\\ esc\"", "esc \\ esc",
276 "\"esc \\02 oct\"", "esc \02 oct",
277 "\"esc \\7 oct\"", "esc \7 oct",
278 "\"esc \\17 oct\"", "esc \17 oct",
279 "\"esc \\117 oct\"", "esc \117 oct",
280 "\"esc \\1117 oct\"", "esc \1117 oct", /* '7' is distinct char */
282 /* strips balanced quotes, even inside the string */
283 ">>\"x\"<<", ">>x<<",
284 ">>\"x\"-\"y\"<<", ">>x-y<<",
286 /* pathological, but valid */
293 /* invalid strings (handling here is arbitrary, but these tests
294 * will alert us if the handling changes) */
295 "\\", "", /* trailing backslash is ignored */
296 "xx\\", "xx", /* ditto */
297 "\\\\\\\\\\\\\\", "\\\\\\", /* ditto */
298 "\\777", "\377", /* 0777 & 0xff = 0xff */
299 "\"--", "--", /* leading quote is dropped */
300 "--\"", "--", /* trailing quote is dropped */
306 for (strp = tests; *strp;) {
307 char *quoted = *(strp++);
308 char *expected = *(strp++);
309 char *unquoted = unquote_string(quoted);
311 /* if they're not the same, complain */
312 if (0 != strcmp(expected, unquoted)) {
313 char *safe_quoted = safestr(quoted);
314 char *safe_unquoted = safestr(unquoted);
315 char *safe_expected = safestr(expected);
317 printf(" %s unquoted to %s; expected %s.\n",
318 safe_quoted, safe_unquoted, safe_expected);
321 amfree(safe_unquoted);
322 amfree(safe_expected);
334 * Test the strquotedstr function
337 test_strquotedstr_skipping(void)
339 char **iter1, **iter2;
340 gboolean success = TRUE;
342 /* the idea here is to loop over all pairs of strings, forming a
343 * string by quoting them with quote_string and inserting a space, then
344 * re-splitting with strquotedstr. This should get us back to our
345 * starting point. Note that we have to begin with a non-quoted identifier,
346 * becuse strquotedstr requires that strtok_r has already been called. */
348 for (iter1 = quotable_strings; *iter1; iter1++) {
349 for (iter2 = quotable_strings; *iter2; iter2++) {
350 char *q1 = quote_string(*iter1);
351 char *q2 = quote_string(*iter2);
352 char *combined = vstralloc("START ", q1, " ", q2, NULL);
353 char *copy = g_strdup(combined);
354 char *saveptr = NULL;
358 tok = strtok_r(copy, " ", &saveptr);
360 for (i = 1; i <= 2; i++) {
361 char *expected = (i == 1)? q1:q2;
362 tok = strquotedstr(&saveptr);
364 g_fprintf(stderr, "while parsing '%s', call %d to strquotedstr returned NULL\n",
369 if (0 != strcmp(tok, expected)) {
370 char *safe = safestr(tok);
372 g_fprintf(stderr, "while parsing '%s', call %d to strquotedstr returned '%s' "
373 "but '%s' was expected.\n",
374 combined, i, safe, expected);
380 if (strquotedstr(&saveptr) != NULL) {
381 g_fprintf(stderr, "while parsing '%s', call 3 to strquotedstr did not return NULL\n",
398 test_strquotedstr_edge_invalid(void)
400 gboolean success = TRUE;
402 "X \"abc", /* unterminated */
403 "X \"ab cd", /* unterminated second token */
404 "X a\"b cd", /* unterminated second token with internal quote */
405 "X b\\", /* trailing backslash */
406 "X \"b\\", /* trailing backslash in quote */
407 "X \"b\\\"", /* backslash'd ending quote */
412 /* run strquotedstr on a bunch of invalid tokens. It should return NULL */
414 for (iter = invalid; *iter; iter++) {
415 char *copy = g_strdup(*iter);
417 char *saveptr = NULL;
419 tok = strtok_r(copy, " ", &saveptr);
420 tok = strquotedstr(&saveptr);
422 g_fprintf(stderr, "while parsing invalid '%s', strquotedstr did not return NULL\n",
434 test_strquotedstr_edge_valid(void)
436 gboolean success = TRUE;
438 /* input */ /* expected (omitting "X") */
439 "X abc\\ def", "abc\\ def", /* backslashed space */
440 "X \"abc\\ def\"", "\"abc\\ def\"", /* quoted, backslashed space */
441 "X a\" \"b", "a\" \"b", /* quoted spaces */
446 /* run strquotedstr on a bunch of valid, but tricky, tokens. It should return NULL */
448 for (iter = valid; *iter; iter += 2) {
449 char *copy = g_strdup(*iter);
450 char *expected = *(iter+1);
452 char *saveptr = NULL;
454 tok = strtok_r(copy, " ", &saveptr);
455 tok = strquotedstr(&saveptr);
457 g_fprintf(stderr, "while parsing valid '%s', strquotedstr returned NULL\n",
460 } else if (0 != strcmp(tok, expected)) {
461 g_fprintf(stderr, "while parsing valid '%s', strquotedstr returned '%s' while "
462 "'%s' was expected\n",
463 *iter, tok, expected);
478 main(int argc, char **argv)
480 static TestUtilsTest tests[] = {
481 TU_TEST(test_round_trip, 90),
482 TU_TEST(test_unquote_string, 90),
483 TU_TEST(test_split_quoted_strings, 90),
484 TU_TEST(test_split_quoted_strings_edge, 90),
485 TU_TEST(test_strquotedstr_skipping, 90),
486 TU_TEST(test_strquotedstr_edge_invalid, 90),
487 TU_TEST(test_strquotedstr_edge_valid, 90),
491 return testutils_run_tests(argc, argv, tests);