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 "../svn_test.h"
29 #include "../svn_test_fs.h"
30 #include "../../libsvn_fs_base/fs.h"
31 #include "../../libsvn_fs_base/util/skel.h"
34 /* Some utility functions. */
37 /* A quick way to create error messages. */
39 fail(apr_pool_t
*pool
, const char *fmt
, ...)
45 msg
= apr_pvsprintf(pool
, fmt
, ap
);
48 return svn_error_create(SVN_ERR_TEST_FAILED
, 0, msg
);
52 /* Free everything from pool, and return an empty Subversion string. */
53 static svn_stringbuf_t
*
54 get_empty_string(apr_pool_t
*pool
)
58 return svn_stringbuf_ncreate(0, 0, pool
);
61 /* Parse a skeleton from a Subversion string. */
63 parse_str(svn_stringbuf_t
*str
, apr_pool_t
*pool
)
65 return svn_fs_base__parse_skel(str
->data
, str
->len
, pool
);
69 /* Parse a skeleton from a C string. */
71 parse_cstr(const char *str
, apr_pool_t
*pool
)
73 return svn_fs_base__parse_skel(str
, strlen(str
), pool
);
85 static int skel_char_map_initialized
;
86 static enum char_type skel_char_map
[256];
94 if (skel_char_map_initialized
)
97 for (i
= 0; i
< 256; i
++)
98 skel_char_map
[i
] = type_nothing
;
100 for (i
= '0'; i
<= '9'; i
++)
101 skel_char_map
[i
] = type_digit
;
103 for (c
= "\t\n\f\r "; *c
; c
++)
104 skel_char_map
[(unsigned char) *c
] = type_space
;
106 for (c
= "()[]"; *c
; c
++)
107 skel_char_map
[(unsigned char) *c
] = type_paren
;
109 for (i
= 'A'; i
<= 'Z'; i
++)
110 skel_char_map
[i
] = type_name
;
111 for (i
= 'a'; i
<= 'z'; i
++)
112 skel_char_map
[i
] = type_name
;
114 skel_char_map_initialized
= 1;
117 /* Return true iff BYTE is a whitespace byte. */
119 skel_is_space(char byte
)
123 return skel_char_map
[(unsigned char) byte
] == type_space
;
127 /* Return true iff BYTE is a digit byte. */
129 skel_is_digit(char byte
)
133 return skel_char_map
[(unsigned char) byte
] == type_digit
;
137 /* Return true iff BYTE is a paren byte. */
139 skel_is_paren(char byte
)
143 return skel_char_map
[(unsigned char) byte
] == type_paren
;
146 /* Return true iff BYTE is a name byte. */
148 skel_is_name(char byte
)
152 return skel_char_map
[(unsigned char) byte
] == type_name
;
156 /* Check that SKEL is an atom, and its contents match LEN bytes of
159 check_atom(skel_t
*skel
, const char *data
, apr_size_t len
)
164 && ! memcmp(skel
->data
, data
, len
));
168 /* Functions that generate/check interesting implicit-length atoms. */
171 /* Append to STR an implicit-length atom consisting of the byte BYTE,
172 terminated by the character TERM. BYTE must be a name byte,
173 and TERM must be a valid skel separator, or NULL. */
175 put_implicit_length_byte(svn_stringbuf_t
*str
, char byte
, char term
)
177 if (! skel_is_name(byte
))
180 && ! skel_is_space(term
)
181 && ! skel_is_paren(term
))
183 svn_stringbuf_appendbytes(str
, &byte
, 1);
185 svn_stringbuf_appendbytes(str
, &term
, 1);
189 /* Return true iff SKEL is the parsed form of the atom produced by
190 calling put_implicit_length with BYTE. */
192 check_implicit_length_byte(skel_t
*skel
, char byte
)
194 if (! skel_is_name(byte
))
197 return check_atom(skel
, &byte
, 1);
201 /* Subroutine for the *_implicit_length_all_chars functions. */
203 gen_implicit_length_all_chars(apr_size_t
*len_p
)
207 static char name
[256];
209 /* Gotta start with a valid name character. */
212 for (i
= 0; i
< 256; i
++)
213 if (! skel_is_space( (apr_byte_t
)i
)
214 && ! skel_is_paren( (apr_byte_t
)i
))
222 /* Append to STR an implicit-length atom containing every character
223 that's legal in such atoms, terminated by the valid atom terminator
226 put_implicit_length_all_chars(svn_stringbuf_t
*str
, char term
)
229 char *name
= gen_implicit_length_all_chars(&len
);
232 && ! skel_is_space(term
)
233 && ! skel_is_paren(term
))
236 svn_stringbuf_appendbytes(str
, name
, len
);
238 svn_stringbuf_appendbytes(str
, &term
, 1);
242 /* Return true iff SKEL is the parsed form of the atom produced by
243 calling put_implicit_length_all_chars. */
245 check_implicit_length_all_chars(skel_t
*skel
)
248 char *name
= gen_implicit_length_all_chars(&len
);
250 return check_atom(skel
, name
, len
);
255 /* Test parsing of implicit-length atoms. */
258 parse_implicit_length(const char **msg
,
259 svn_boolean_t msg_only
,
260 svn_test_opts_t
*opts
,
263 svn_stringbuf_t
*str
= get_empty_string(pool
);
266 *msg
= "parse implicit-length atoms";
271 /* Try all valid single-byte atoms. */
276 for (c
= "\t\n\f\r ()[]"; *c
; c
++)
277 for (i
= 0; i
< 256; i
++)
278 if (skel_is_name((apr_byte_t
)i
))
280 svn_stringbuf_setempty(str
);
281 put_implicit_length_byte(str
, (apr_byte_t
)i
, *c
);
282 skel
= parse_str(str
, pool
);
283 if (! check_implicit_length_byte(skel
, (apr_byte_t
)i
))
284 return fail(pool
, "single-byte implicit-length skel 0x%02x"
285 " with terminator 0x%02x",
290 /* Try an atom that contains every character that's legal in an
291 implicit-length atom. */
292 svn_stringbuf_setempty(str
);
293 put_implicit_length_all_chars(str
, '\0');
294 skel
= parse_str(str
, pool
);
295 if (! check_implicit_length_all_chars(skel
))
296 return fail(pool
, "implicit-length skel containing all legal chars");
302 /* Functions that generate/check interesting explicit-length atoms. */
305 /* Append to STR the representation of the atom containing the LEN
306 bytes at DATA, in explicit-length form, using SEP as the separator
307 between the length and the data. */
309 put_explicit_length(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
312 char *buf
= malloc(len
+ 100);
313 apr_size_t length_len
;
315 if (! skel_is_space(sep
))
318 /* Generate the length and separator character. */
319 sprintf(buf
, "%"APR_SIZE_T_FMT
"%c", len
, sep
);
320 length_len
= strlen(buf
);
322 /* Copy in the real data (which may contain nulls). */
323 memcpy(buf
+ length_len
, data
, len
);
325 svn_stringbuf_appendbytes(str
, buf
, length_len
+ len
);
330 /* Return true iff SKEL is the parsed form of an atom generated by
331 put_explicit_length. */
333 check_explicit_length(skel_t
*skel
, const char *data
, apr_size_t len
)
335 return check_atom(skel
, data
, len
);
339 /* Test parsing of explicit-length atoms. */
342 try_explicit_length(const char *data
, apr_size_t len
, apr_size_t check_len
,
346 svn_stringbuf_t
*str
= get_empty_string(pool
);
349 /* Try it with every possible separator character. */
350 for (i
= 0; i
< 256; i
++)
351 if (skel_is_space( (apr_byte_t
)i
))
353 svn_stringbuf_setempty(str
);
354 put_explicit_length(str
, data
, len
, (apr_byte_t
)i
);
355 skel
= parse_str(str
, pool
);
356 if (! check_explicit_length(skel
, data
, check_len
))
357 return fail(pool
, "failed to reparse explicit-length atom");
365 parse_explicit_length(const char **msg
,
366 svn_boolean_t msg_only
,
367 svn_test_opts_t
*opts
,
370 *msg
= "parse explicit-length atoms";
375 /* Try to parse the empty atom. */
376 SVN_ERR(try_explicit_length("", 0, 0, pool
));
378 /* Try to parse every one-character atom. */
382 for (i
= 0; i
< 256; i
++)
387 SVN_ERR(try_explicit_length(buf
, 1, 1, pool
));
391 /* Try to parse an atom containing every character. */
396 for (i
= 0; i
< 256; i
++)
399 SVN_ERR(try_explicit_length(data
, 256, 256, pool
));
407 /* Test parsing of invalid atoms. */
409 static struct invalid_atoms
414 } invalid_atoms
[] = { { 1, 1, "(" },
419 { 1, 13, "Hello, World!" },
420 { 1, 8, "1mplicit" },
428 parse_invalid_atoms(const char **msg
,
429 svn_boolean_t msg_only
,
430 svn_test_opts_t
*opts
,
433 struct invalid_atoms
*ia
= invalid_atoms
;
435 *msg
= "parse invalid atoms";
440 while (ia
->type
!= 7)
444 skel_t
*skel
= parse_cstr(ia
->data
, pool
);
445 if (check_atom(skel
, ia
->data
, ia
->len
))
447 "failed to detect parsing error in '%s'", ia
->data
);
451 svn_error_t
*err
= try_explicit_length(ia
->data
, ia
->len
,
452 strlen(ia
->data
), pool
);
453 if (err
== SVN_NO_ERROR
)
454 return fail(pool
, "got wrong length in explicit-length atom");
455 svn_error_clear(err
);
466 /* Functions that generate/check interesting lists. */
468 /* Append the start of a list to STR, using LEN bytes of the
469 whitespace character SPACE. */
471 put_list_start(svn_stringbuf_t
*str
, char space
, int len
)
475 if (len
> 0 && ! skel_is_space(space
))
478 svn_stringbuf_appendcstr(str
, "(");
479 for (i
= 0; i
< len
; i
++)
480 svn_stringbuf_appendbytes(str
, &space
, 1);
484 /* Append the end of a list to STR, using LEN bytes of the
485 whitespace character SPACE. */
487 put_list_end(svn_stringbuf_t
*str
, char space
, int len
)
491 if (len
> 0 && ! skel_is_space(space
))
494 for (i
= 0; i
< len
; i
++)
495 svn_stringbuf_appendbytes(str
, &space
, 1);
496 svn_stringbuf_appendcstr(str
, ")");
500 /* Return true iff SKEL is a list of length DESIRED_LEN. */
502 check_list(skel_t
*skel
, int desired_len
)
512 for (child
= skel
->children
; child
; child
= child
->next
)
515 return len
== desired_len
;
523 parse_list(const char **msg
,
524 svn_boolean_t msg_only
,
525 svn_test_opts_t
*opts
,
528 *msg
= "parse lists";
534 /* Try lists of varying length. */
539 list_len
< 4 ? list_len
++ : (list_len
*= 3))
541 /* Try lists with different separators. */
544 for (sep
= 0; sep
< 256; sep
++)
545 if (skel_is_space( (apr_byte_t
)sep
))
547 /* Try lists with different numbers of separator
548 characters between the elements. */
553 sep_count
< 4 ? sep_count
++ : (sep_count
*= 3))
555 /* Try various single-byte implicit-length atoms
559 for (atom_byte
= 0; atom_byte
< 256; atom_byte
++)
560 if (skel_is_name( (apr_byte_t
)atom_byte
))
563 svn_stringbuf_t
*str
= get_empty_string(pool
);
567 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
568 for (i
= 0; i
< list_len
; i
++)
569 put_implicit_length_byte(str
,
570 (apr_byte_t
)atom_byte
,
572 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
574 skel
= parse_str(str
, pool
);
575 if (! check_list(skel
, list_len
))
576 return fail(pool
, "couldn't parse list");
577 for (child
= skel
->children
;
580 if (! check_implicit_length_byte
581 (child
, (apr_byte_t
)atom_byte
))
583 "list was reparsed incorrectly");
586 /* Try the atom containing every character that's
587 legal in an implicit-length atom as the element. */
590 svn_stringbuf_t
*str
= get_empty_string(pool
);
594 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
595 for (i
= 0; i
< list_len
; i
++)
596 put_implicit_length_all_chars(str
, (apr_byte_t
)sep
);
597 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
599 skel
= parse_str(str
, pool
);
600 if (! check_list(skel
, list_len
))
601 return fail(pool
, "couldn't parse list");
602 for (child
= skel
->children
;
605 if (! check_implicit_length_all_chars(child
))
606 return fail(pool
, "couldn't parse list");
609 /* Try using every one-byte explicit-length atom as
611 for (atom_byte
= 0; atom_byte
< 256; atom_byte
++)
614 svn_stringbuf_t
*str
= get_empty_string(pool
);
621 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
622 for (i
= 0; i
< list_len
; i
++)
623 put_explicit_length(str
, buf
, 1, (apr_byte_t
)sep
);
624 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
626 skel
= parse_str(str
, pool
);
627 if (! check_list(skel
, list_len
))
628 return fail(pool
, "couldn't parse list");
629 for (child
= skel
->children
;
632 if (! check_explicit_length(child
, buf
, 1))
633 return fail(pool
, "list was reparsed incorrectly");
636 /* Try using an atom containing every character as
640 svn_stringbuf_t
*str
= get_empty_string(pool
);
645 for (i
= 0; i
< 256; i
++)
648 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
649 for (i
= 0; i
< list_len
; i
++)
650 put_explicit_length(str
, data
, 256, (apr_byte_t
)sep
);
651 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
653 skel
= parse_str(str
, pool
);
654 if (! check_list(skel
, list_len
))
655 return fail(pool
, "couldn't parse list");
656 for (child
= skel
->children
;
659 if (! check_explicit_length(child
, data
, 256))
660 return fail(pool
, "list was re-parsed incorrectly");
667 /* Try to parse some invalid lists. */
671 /* Try different separators. */
672 for (sep
= 0; sep
< 256; sep
++)
673 if (skel_is_space( (apr_byte_t
)sep
))
675 /* Try lists with different numbers of separator
676 characters between the elements. */
681 sep_count
< 10 ? sep_count
++ : (sep_count
*= 3))
683 svn_stringbuf_t
*str
;
685 /* A list with only a separator. */
686 str
= get_empty_string(pool
);
687 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
688 if (parse_str(str
, pool
))
689 return fail(pool
, "failed to detect syntax error");
691 /* A list with only a terminator. */
692 str
= get_empty_string(pool
);
693 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
694 if (parse_str(str
, pool
))
695 return fail(pool
, "failed to detect syntax error");
697 /* A list containing an invalid element. */
698 str
= get_empty_string(pool
);
699 put_list_start(str
, (apr_byte_t
)sep
, sep_count
);
700 svn_stringbuf_appendcstr(str
, "100 ");
701 put_list_end(str
, (apr_byte_t
)sep
, sep_count
);
702 if (parse_str(str
, pool
))
703 return fail(pool
, "failed to detect invalid element");
713 /* Building interesting skels. */
715 /* Build an atom skel containing the LEN bytes at DATA. */
717 build_atom(apr_size_t len
, char *data
, apr_pool_t
*pool
)
719 char *copy
= apr_palloc(pool
, len
);
720 skel_t
*skel
= apr_palloc(pool
, sizeof(*skel
));
722 memcpy(copy
, data
, len
);
730 /* Build an empty list skel. */
732 empty(apr_pool_t
*pool
)
734 skel_t
*skel
= apr_palloc(pool
, sizeof(*skel
));
742 /* Stick ELEMENT at the beginning of the list skeleton LIST. */
744 add(skel_t
*element
, skel_t
*list
)
746 element
->next
= list
->children
;
747 list
->children
= element
;
751 /* Return true if the contents of skel A are identical to those of
754 skel_equal(skel_t
*a
, skel_t
*b
)
756 if (a
->is_atom
!= b
->is_atom
)
760 return (a
->len
== b
->len
761 && ! memcmp(a
->data
, b
->data
, a
->len
));
764 skel_t
*a_child
, *b_child
;
766 for (a_child
= a
->children
, b_child
= b
->children
;
768 a_child
= a_child
->next
, b_child
= b_child
->next
)
769 if (! skel_equal(a_child
, b_child
))
772 if (a_child
|| b_child
)
780 /* Unparsing implicit-length atoms. */
783 unparse_implicit_length(const char **msg
,
784 svn_boolean_t msg_only
,
785 svn_test_opts_t
*opts
,
788 *msg
= "unparse implicit-length atoms";
793 /* Unparse and check every single-byte implicit-length atom. */
797 for (byte
= 0; byte
< 256; byte
++)
798 if (skel_is_name( (apr_byte_t
)byte
))
800 svn_stringbuf_t
*str
= get_empty_string(pool
);
801 char buf
= (char)byte
;
802 skel_t
*skel
= build_atom(1, &buf
, pool
);
804 str
= svn_fs_base__unparse_skel(skel
, pool
);
808 && str
->data
[0] == (char)byte
))
809 return fail(pool
, "incorrectly unparsed single-byte "
810 "implicit-length atom");
819 /* Unparse some lists. */
822 unparse_list(const char **msg
,
823 svn_boolean_t msg_only
,
824 svn_test_opts_t
*opts
,
827 *msg
= "unparse lists";
832 /* Make a list of all the single-byte implicit-length atoms. */
834 svn_stringbuf_t
*str
= get_empty_string(pool
);
836 skel_t
*list
= empty(pool
);
837 skel_t
*reparsed
, *elt
;
839 for (byte
= 0; byte
< 256; byte
++)
840 if (skel_is_name( (apr_byte_t
)byte
))
843 add(build_atom(1, &buf
, pool
), list
);
846 /* Unparse that, parse it again, and see if we got the same thing
848 str
= svn_fs_base__unparse_skel(list
, pool
);
849 reparsed
= svn_fs_base__parse_skel(str
->data
, str
->len
, pool
);
851 if (! reparsed
|| reparsed
->is_atom
)
852 return fail(pool
, "result is syntactically misformed, or not a list");
854 if (! skel_equal(list
, reparsed
))
855 return fail(pool
, "unparsing and parsing didn't preserve contents");
857 elt
= reparsed
->children
;
858 for (byte
= 255; byte
>= 0; byte
--)
859 if (skel_is_name( (apr_byte_t
)byte
))
864 && elt
->data
[0] == byte
))
865 return fail(pool
, "bad element");
867 /* Verify that each element's data falls within the string. */
868 if (elt
->data
< str
->data
869 || elt
->data
+ elt
->len
> str
->data
+ str
->len
)
870 return fail(pool
, "bad element");
875 /* We should have reached the end of the list at this point. */
877 return fail(pool
, "list too long");
880 /* Make a list of lists. */
882 svn_stringbuf_t
*str
= get_empty_string(pool
);
883 skel_t
*top
= empty(pool
);
887 for (i
= 0; i
< 10; i
++)
889 skel_t
*middle
= empty(pool
);
892 for (j
= 0; j
< 10; j
++)
898 /* Make some interesting atom, containing lots of binary
901 for (k
= 0; k
< sizeof(buf
); k
++)
907 add(build_atom(sizeof(buf
), buf
, pool
), middle
);
913 str
= svn_fs_base__unparse_skel(top
, pool
);
914 reparsed
= svn_fs_base__parse_skel(str
->data
, str
->len
, pool
);
916 if (! skel_equal(top
, reparsed
))
917 return fail(pool
, "failed to reparse list of lists");
924 /* The test table. */
926 struct svn_test_descriptor_t test_funcs
[] =
929 SVN_TEST_PASS(parse_implicit_length
),
930 SVN_TEST_PASS(parse_explicit_length
),
931 SVN_TEST_PASS(parse_invalid_atoms
),
932 SVN_TEST_PASS(parse_list
),
933 SVN_TEST_PASS(unparse_implicit_length
),
934 SVN_TEST_PASS(unparse_list
),