1
/* skel-test.c --- tests for the skeleton functions
3
* ====================================================================
4
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
6
* This software is licensed as described in the file COPYING, which
7
* you should have received as part of this distribution. The terms
8
* are also available at http://subversion.tigris.org/license-1.html.
9
* If newer versions of this license are posted there, you may use a
10
* newer version instead, at your option.
12
* This software consists of voluntary contributions made by many
13
* individuals. For exact contribution history, see the revision
14
* history and logs, available at http://subversion.tigris.org/.
15
* ====================================================================
25
#include "svn_pools.h"
26
#include "svn_string.h"
28
#include "../../libsvn_fs_base/fs.h"
29
#include "../../libsvn_fs_base/util/skel.h"
32
/* Some utility functions. */
35
/* A quick way to create error messages. */
37
fail (apr_pool_t *pool, const char *fmt, ...)
43
msg = apr_pvsprintf (pool, fmt, ap);
46
return svn_error_create (SVN_ERR_TEST_FAILED, 0, msg);
50
/* Free everything from pool, and return an empty Subversion string. */
51
static svn_stringbuf_t *
52
get_empty_string (apr_pool_t *pool)
54
svn_pool_clear (pool);
56
return svn_stringbuf_ncreate (0, 0, pool);
59
/* Parse a skeleton from a Subversion string. */
61
parse_str (svn_stringbuf_t *str, apr_pool_t *pool)
63
return svn_fs_base__parse_skel (str->data, str->len, pool);
67
/* Parse a skeleton from a C string. */
69
parse_cstr (const char *str, apr_pool_t *pool)
71
return svn_fs_base__parse_skel (str, strlen (str), pool);
83
static int skel_char_map_initialized;
84
static enum char_type skel_char_map[256];
87
init_char_types (void)
92
if (skel_char_map_initialized)
95
for (i = 0; i < 256; i++)
96
skel_char_map[i] = type_nothing;
98
for (i = '0'; i <= '9'; i++)
99
skel_char_map[i] = type_digit;
101
for (c = "\t\n\f\r "; *c; c++)
102
skel_char_map[(unsigned char) *c] = type_space;
104
for (c = "()[]"; *c; c++)
105
skel_char_map[(unsigned char) *c] = type_paren;
107
for (i = 'A'; i <= 'Z'; i++)
108
skel_char_map[i] = type_name;
109
for (i = 'a'; i <= 'z'; i++)
110
skel_char_map[i] = type_name;
112
skel_char_map_initialized = 1;
115
/* Return true iff BYTE is a whitespace byte. */
117
skel_is_space (char byte)
121
return skel_char_map[(unsigned char) byte] == type_space;
125
/* Return true iff BYTE is a digit byte. */
127
skel_is_digit (char byte)
131
return skel_char_map[(unsigned char) byte] == type_digit;
135
/* Return true iff BYTE is a paren byte. */
137
skel_is_paren (char byte)
141
return skel_char_map[(unsigned char) byte] == type_paren;
144
/* Return true iff BYTE is a name byte. */
146
skel_is_name (char byte)
150
return skel_char_map[(unsigned char) byte] == type_name;
154
/* Check that SKEL is an atom, and its contents match LEN bytes of
157
check_atom (skel_t *skel, const char *data, apr_size_t len)
162
&& ! memcmp (skel->data, data, len));
166
/* Functions that generate/check interesting implicit-length atoms. */
169
/* Append to STR an implicit-length atom consisting of the byte BYTE,
170
terminated by the character TERM. BYTE must be a name byte,
171
and TERM must be a valid skel separator, or NULL. */
173
put_implicit_length_byte (svn_stringbuf_t *str, char byte, char term)
175
if (! skel_is_name (byte))
178
&& ! skel_is_space (term)
179
&& ! skel_is_paren (term))
181
svn_stringbuf_appendbytes (str, &byte, 1);
183
svn_stringbuf_appendbytes (str, &term, 1);
187
/* Return true iff SKEL is the parsed form of the atom produced by
188
calling put_implicit_length with BYTE. */
190
check_implicit_length_byte (skel_t *skel, char byte)
192
if (! skel_is_name (byte))
195
return check_atom (skel, &byte, 1);
199
/* Subroutine for the *_implicit_length_all_chars functions. */
201
gen_implicit_length_all_chars (apr_size_t *len_p)
205
static char name[256];
207
/* Gotta start with a valid name character. */
210
for (i = 0; i < 256; i++)
211
if (! skel_is_space ( (apr_byte_t)i)
212
&& ! skel_is_paren ( (apr_byte_t)i))
220
/* Append to STR an implicit-length atom containing every character
221
that's legal in such atoms, terminated by the valid atom terminator
224
put_implicit_length_all_chars (svn_stringbuf_t *str, char term)
227
char *name = gen_implicit_length_all_chars (&len);
230
&& ! skel_is_space (term)
231
&& ! skel_is_paren (term))
234
svn_stringbuf_appendbytes (str, name, len);
236
svn_stringbuf_appendbytes (str, &term, 1);
240
/* Return true iff SKEL is the parsed form of the atom produced by
241
calling put_implicit_length_all_chars. */
243
check_implicit_length_all_chars (skel_t *skel)
246
char *name = gen_implicit_length_all_chars (&len);
248
return check_atom (skel, name, len);
253
/* Test parsing of implicit-length atoms. */
256
parse_implicit_length (const char **msg,
257
svn_boolean_t msg_only,
258
svn_test_opts_t *opts,
261
svn_stringbuf_t *str = get_empty_string (pool);
264
*msg = "parse implicit-length atoms";
269
/* Try all valid single-byte atoms. */
274
for (c = "\t\n\f\r ()[]"; *c; c++)
275
for (i = 0; i < 256; i++)
276
if (skel_is_name((apr_byte_t)i))
278
svn_stringbuf_setempty (str);
279
put_implicit_length_byte (str, (apr_byte_t)i, *c);
280
skel = parse_str (str, pool);
281
if (! check_implicit_length_byte (skel, (apr_byte_t)i))
282
return fail (pool, "single-byte implicit-length skel 0x%02x"
283
" with terminator 0x%02x",
288
/* Try an atom that contains every character that's legal in an
289
implicit-length atom. */
290
svn_stringbuf_setempty (str);
291
put_implicit_length_all_chars (str, '\0');
292
skel = parse_str (str, pool);
293
if (! check_implicit_length_all_chars (skel))
294
return fail (pool, "implicit-length skel containing all legal chars");
300
/* Functions that generate/check interesting explicit-length atoms. */
303
/* Append to STR the representation of the atom containing the LEN
304
bytes at DATA, in explicit-length form, using SEP as the separator
305
between the length and the data. */
307
put_explicit_length (svn_stringbuf_t *str, const char *data, apr_size_t len,
310
char *buf = malloc (len + 100);
311
apr_size_t length_len;
313
if (! skel_is_space (sep))
316
/* Generate the length and separator character. */
317
sprintf (buf, "%"APR_SIZE_T_FMT"%c", len, sep);
318
length_len = strlen(buf);
320
/* Copy in the real data (which may contain nulls). */
321
memcpy (buf + length_len, data, len);
323
svn_stringbuf_appendbytes (str, buf, length_len + len);
328
/* Return true iff SKEL is the parsed form of an atom generated by
329
put_explicit_length. */
331
check_explicit_length (skel_t *skel, const char *data, apr_size_t len)
333
return check_atom (skel, data, len);
337
/* Test parsing of explicit-length atoms. */
340
try_explicit_length (const char *data, apr_size_t len, apr_size_t check_len,
344
svn_stringbuf_t *str = get_empty_string (pool);
347
/* Try it with every possible separator character. */
348
for (i = 0; i < 256; i++)
349
if (skel_is_space ( (apr_byte_t)i))
351
svn_stringbuf_setempty (str);
352
put_explicit_length (str, data, len, (apr_byte_t)i);
353
skel = parse_str (str, pool);
354
if (! check_explicit_length (skel, data, check_len))
355
return fail (pool, "failed to reparse explicit-length atom");
363
parse_explicit_length (const char **msg,
364
svn_boolean_t msg_only,
365
svn_test_opts_t *opts,
368
*msg = "parse explicit-length atoms";
373
/* Try to parse the empty atom. */
374
SVN_ERR (try_explicit_length ("", 0, 0, pool));
376
/* Try to parse every one-character atom. */
380
for (i = 0; i < 256; i++)
385
SVN_ERR (try_explicit_length (buf, 1, 1, pool));
389
/* Try to parse an atom containing every character. */
394
for (i = 0; i < 256; i++)
397
SVN_ERR (try_explicit_length (data, 256, 256, pool));
405
/* Test parsing of invalid atoms. */
407
static struct invalid_atoms
412
} invalid_atoms[] = { { 1, 1, "(" },
417
{ 1, 13, "Hello, World!" },
418
{ 1, 8, "1mplicit" },
426
parse_invalid_atoms (const char **msg,
427
svn_boolean_t msg_only,
428
svn_test_opts_t *opts,
431
struct invalid_atoms *ia = invalid_atoms;
433
*msg = "parse invalid atoms";
438
while (ia->type != 7)
442
skel_t *skel = parse_cstr (ia->data, pool);
443
if (check_atom (skel, ia->data, ia->len))
445
"failed to detect parsing error in '%s'", ia->data);
449
svn_error_t *err = try_explicit_length (ia->data, ia->len,
450
strlen (ia->data), pool);
451
if (err == SVN_NO_ERROR)
452
return fail (pool, "got wrong length in explicit-length atom");
453
svn_error_clear (err);
464
/* Functions that generate/check interesting lists. */
466
/* Append the start of a list to STR, using LEN bytes of the
467
whitespace character SPACE. */
469
put_list_start (svn_stringbuf_t *str, char space, int len)
473
if (len > 0 && ! skel_is_space (space))
476
svn_stringbuf_appendcstr (str, "(");
477
for (i = 0; i < len; i++)
478
svn_stringbuf_appendbytes (str, &space, 1);
482
/* Append the end of a list to STR, using LEN bytes of the
483
whitespace character SPACE. */
485
put_list_end (svn_stringbuf_t *str, char space, int len)
489
if (len > 0 && ! skel_is_space (space))
492
for (i = 0; i < len; i++)
493
svn_stringbuf_appendbytes (str, &space, 1);
494
svn_stringbuf_appendcstr (str, ")");
498
/* Return true iff SKEL is a list of length DESIRED_LEN. */
500
check_list (skel_t *skel, int desired_len)
510
for (child = skel->children; child; child = child->next)
513
return len == desired_len;
521
parse_list (const char **msg,
522
svn_boolean_t msg_only,
523
svn_test_opts_t *opts,
526
*msg = "parse lists";
532
/* Try lists of varying length. */
537
list_len < 4 ? list_len++ : (list_len *= 3))
539
/* Try lists with different separators. */
542
for (sep = 0; sep < 256; sep++)
543
if (skel_is_space ( (apr_byte_t)sep))
545
/* Try lists with different numbers of separator
546
characters between the elements. */
551
sep_count < 4 ? sep_count++ : (sep_count *= 3))
553
/* Try various single-byte implicit-length atoms
557
for (atom_byte = 0; atom_byte < 256; atom_byte++)
558
if (skel_is_name ( (apr_byte_t)atom_byte))
561
svn_stringbuf_t *str = get_empty_string (pool);
565
put_list_start (str, (apr_byte_t)sep, sep_count);
566
for (i = 0; i < list_len; i++)
567
put_implicit_length_byte (str,
568
(apr_byte_t)atom_byte,
570
put_list_end (str, (apr_byte_t)sep, sep_count);
572
skel = parse_str (str, pool);
573
if (! check_list (skel, list_len))
574
return fail (pool, "couldn't parse list");
575
for (child = skel->children;
578
if (! check_implicit_length_byte
579
(child, (apr_byte_t)atom_byte))
581
"list was reparsed incorrectly");
584
/* Try the atom containing every character that's
585
legal in an implicit-length atom as the element. */
588
svn_stringbuf_t *str = get_empty_string (pool);
592
put_list_start (str, (apr_byte_t)sep, sep_count);
593
for (i = 0; i < list_len; i++)
594
put_implicit_length_all_chars (str, (apr_byte_t)sep);
595
put_list_end (str, (apr_byte_t)sep, sep_count);
597
skel = parse_str (str, pool);
598
if (! check_list (skel, list_len))
599
return fail (pool, "couldn't parse list");
600
for (child = skel->children;
603
if (! check_implicit_length_all_chars (child))
604
return fail (pool, "couldn't parse list");
607
/* Try using every one-byte explicit-length atom as
609
for (atom_byte = 0; atom_byte < 256; atom_byte++)
612
svn_stringbuf_t *str = get_empty_string (pool);
619
put_list_start (str, (apr_byte_t)sep, sep_count);
620
for (i = 0; i < list_len; i++)
621
put_explicit_length (str, buf, 1, (apr_byte_t)sep);
622
put_list_end (str, (apr_byte_t)sep, sep_count);
624
skel = parse_str (str, pool);
625
if (! check_list (skel, list_len))
626
return fail (pool, "couldn't parse list");
627
for (child = skel->children;
630
if (! check_explicit_length (child, buf, 1))
631
return fail (pool, "list was reparsed incorrectly");
634
/* Try using an atom containing every character as
638
svn_stringbuf_t *str = get_empty_string (pool);
643
for (i = 0; i < 256; i++)
646
put_list_start (str, (apr_byte_t)sep, sep_count);
647
for (i = 0; i < list_len; i++)
648
put_explicit_length (str, data, 256, (apr_byte_t)sep);
649
put_list_end (str, (apr_byte_t)sep, sep_count);
651
skel = parse_str (str, pool);
652
if (! check_list (skel, list_len))
653
return fail (pool, "couldn't parse list");
654
for (child = skel->children;
657
if (! check_explicit_length (child, data, 256))
658
return fail (pool, "list was re-parsed incorrectly");
665
/* Try to parse some invalid lists. */
669
/* Try different separators. */
670
for (sep = 0; sep < 256; sep++)
671
if (skel_is_space ( (apr_byte_t)sep))
673
/* Try lists with different numbers of separator
674
characters between the elements. */
679
sep_count < 10 ? sep_count++ : (sep_count *= 3))
681
svn_stringbuf_t *str;
683
/* A list with only a separator. */
684
str = get_empty_string (pool);
685
put_list_start (str, (apr_byte_t)sep, sep_count);
686
if (parse_str (str, pool))
687
return fail (pool, "failed to detect syntax error");
689
/* A list with only a terminator. */
690
str = get_empty_string (pool);
691
put_list_end (str, (apr_byte_t)sep, sep_count);
692
if (parse_str (str, pool))
693
return fail (pool, "failed to detect syntax error");
695
/* A list containing an invalid element. */
696
str = get_empty_string (pool);
697
put_list_start (str, (apr_byte_t)sep, sep_count);
698
svn_stringbuf_appendcstr (str, "100 ");
699
put_list_end (str, (apr_byte_t)sep, sep_count);
700
if (parse_str (str, pool))
701
return fail (pool, "failed to detect invalid element");
711
/* Building interesting skels. */
713
/* Build an atom skel containing the LEN bytes at DATA. */
715
build_atom (apr_size_t len, char *data, apr_pool_t *pool)
717
char *copy = apr_palloc (pool, len);
718
skel_t *skel = apr_palloc (pool, sizeof (*skel));
720
memcpy (copy, data, len);
728
/* Build an empty list skel. */
730
empty (apr_pool_t *pool)
732
skel_t *skel = apr_palloc (pool, sizeof (*skel));
740
/* Stick ELEMENT at the beginning of the list skeleton LIST. */
742
add (skel_t *element, skel_t *list)
744
element->next = list->children;
745
list->children = element;
749
/* Return true if the contents of skel A are identical to those of
752
skel_equal (skel_t *a, skel_t *b)
754
if (a->is_atom != b->is_atom)
758
return (a->len == b->len
759
&& ! memcmp (a->data, b->data, a->len));
762
skel_t *a_child, *b_child;
764
for (a_child = a->children, b_child = b->children;
766
a_child = a_child->next, b_child = b_child->next)
767
if (! skel_equal (a_child, b_child))
770
if (a_child || b_child)
778
/* Unparsing implicit-length atoms. */
781
unparse_implicit_length (const char **msg,
782
svn_boolean_t msg_only,
783
svn_test_opts_t *opts,
786
*msg = "unparse implicit-length atoms";
791
/* Unparse and check every single-byte implicit-length atom. */
795
for (byte = 0; byte < 256; byte++)
796
if (skel_is_name ( (apr_byte_t)byte))
798
svn_stringbuf_t *str = get_empty_string (pool);
799
char buf = (char)byte;
800
skel_t *skel = build_atom (1, &buf, pool);
802
str = svn_fs_base__unparse_skel (skel, pool);
806
&& str->data[0] == (char)byte))
807
return fail (pool, "incorrectly unparsed single-byte "
808
"implicit-length atom");
817
/* Unparse some lists. */
820
unparse_list (const char **msg,
821
svn_boolean_t msg_only,
822
svn_test_opts_t *opts,
825
*msg = "unparse lists";
830
/* Make a list of all the single-byte implicit-length atoms. */
832
svn_stringbuf_t *str = get_empty_string (pool);
834
skel_t *list = empty (pool);
835
skel_t *reparsed, *elt;
837
for (byte = 0; byte < 256; byte++)
838
if (skel_is_name ( (apr_byte_t)byte))
841
add (build_atom (1, &buf, pool), list);
844
/* Unparse that, parse it again, and see if we got the same thing
846
str = svn_fs_base__unparse_skel (list, pool);
847
reparsed = svn_fs_base__parse_skel (str->data, str->len, pool);
849
if (! reparsed || reparsed->is_atom)
850
return fail (pool, "result is syntactically misformed, or not a list");
852
if (! skel_equal (list, reparsed))
853
return fail (pool, "unparsing and parsing didn't preserve contents");
855
elt = reparsed->children;
856
for (byte = 255; byte >= 0; byte--)
857
if (skel_is_name ( (apr_byte_t)byte))
862
&& elt->data[0] == byte))
863
return fail (pool, "bad element");
865
/* Verify that each element's data falls within the string. */
866
if (elt->data < str->data
867
|| elt->data + elt->len > str->data + str->len)
868
return fail (pool, "bad element");
873
/* We should have reached the end of the list at this point. */
875
return fail (pool, "list too long");
878
/* Make a list of lists. */
880
svn_stringbuf_t *str = get_empty_string (pool);
881
skel_t *top = empty (pool);
885
for (i = 0; i < 10; i++)
887
skel_t *middle = empty (pool);
890
for (j = 0; j < 10; j++)
896
/* Make some interesting atom, containing lots of binary
899
for (k = 0; k < sizeof (buf); k++)
905
add (build_atom (sizeof (buf), buf, pool), middle);
911
str = svn_fs_base__unparse_skel (top, pool);
912
reparsed = svn_fs_base__parse_skel (str->data, str->len, pool);
914
if (! skel_equal (top, reparsed))
915
return fail (pool, "failed to reparse list of lists");
922
/* The test table. */
924
struct svn_test_descriptor_t test_funcs[] =
927
SVN_TEST_PASS (parse_implicit_length),
928
SVN_TEST_PASS (parse_explicit_length),
929
SVN_TEST_PASS (parse_invalid_atoms),
930
SVN_TEST_PASS (parse_list),
931
SVN_TEST_PASS (unparse_implicit_length),
932
SVN_TEST_PASS (unparse_list),