Remove no-longer-used svn_*_get_mergeinfo_for_tree APIs.
[svn.git] / subversion / tests / libsvn_fs_base / skel-test.c
blob77d144319267afd861a46f97d4851a8dd9813e63
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 * ====================================================================
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <stdio.h>
23 #include <apr.h>
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. */
38 static svn_error_t *
39 fail(apr_pool_t *pool, const char *fmt, ...)
41 va_list ap;
42 char *msg;
44 va_start(ap, fmt);
45 msg = apr_pvsprintf(pool, fmt, ap);
46 va_end(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)
56 svn_pool_clear(pool);
58 return svn_stringbuf_ncreate(0, 0, pool);
61 /* Parse a skeleton from a Subversion string. */
62 static skel_t *
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. */
70 static skel_t *
71 parse_cstr(const char *str, apr_pool_t *pool)
73 return svn_fs_base__parse_skel(str, strlen(str), pool);
77 enum char_type {
78 type_nothing = 0,
79 type_space = 1,
80 type_digit = 2,
81 type_paren = 3,
82 type_name = 4
85 static int skel_char_map_initialized;
86 static enum char_type skel_char_map[256];
88 static void
89 init_char_types(void)
91 int i;
92 const char *c;
94 if (skel_char_map_initialized)
95 return;
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. */
118 static int
119 skel_is_space(char byte)
121 init_char_types();
123 return skel_char_map[(unsigned char) byte] == type_space;
126 #if 0
127 /* Return true iff BYTE is a digit byte. */
128 static int
129 skel_is_digit(char byte)
131 init_char_types();
133 return skel_char_map[(unsigned char) byte] == type_digit;
135 #endif
137 /* Return true iff BYTE is a paren byte. */
138 static int
139 skel_is_paren(char byte)
141 init_char_types();
143 return skel_char_map[(unsigned char) byte] == type_paren;
146 /* Return true iff BYTE is a name byte. */
147 static int
148 skel_is_name(char byte)
150 init_char_types();
152 return skel_char_map[(unsigned char) byte] == type_name;
156 /* Check that SKEL is an atom, and its contents match LEN bytes of
157 DATA. */
158 static int
159 check_atom(skel_t *skel, const char *data, apr_size_t len)
161 return (skel
162 && skel->is_atom
163 && skel->len == 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. */
174 static void
175 put_implicit_length_byte(svn_stringbuf_t *str, char byte, char term)
177 if (! skel_is_name(byte))
178 abort();
179 if (term != '\0'
180 && ! skel_is_space(term)
181 && ! skel_is_paren(term))
182 abort();
183 svn_stringbuf_appendbytes(str, &byte, 1);
184 if (term != '\0')
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. */
191 static int
192 check_implicit_length_byte(skel_t *skel, char byte)
194 if (! skel_is_name(byte))
195 abort();
197 return check_atom(skel, &byte, 1);
201 /* Subroutine for the *_implicit_length_all_chars functions. */
202 static char *
203 gen_implicit_length_all_chars(apr_size_t *len_p)
205 apr_size_t pos;
206 int i;
207 static char name[256];
209 /* Gotta start with a valid name character. */
210 pos = 0;
211 name[pos++] = 'x';
212 for (i = 0; i < 256; i++)
213 if (! skel_is_space( (apr_byte_t)i)
214 && ! skel_is_paren( (apr_byte_t)i))
215 name[pos++] = i;
217 *len_p = pos;
218 return name;
222 /* Append to STR an implicit-length atom containing every character
223 that's legal in such atoms, terminated by the valid atom terminator
224 TERM. */
225 static void
226 put_implicit_length_all_chars(svn_stringbuf_t *str, char term)
228 apr_size_t len;
229 char *name = gen_implicit_length_all_chars(&len);
231 if (term != '\0'
232 && ! skel_is_space(term)
233 && ! skel_is_paren(term))
234 abort();
236 svn_stringbuf_appendbytes(str, name, len);
237 if (term != '\0')
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. */
244 static int
245 check_implicit_length_all_chars(skel_t *skel)
247 apr_size_t len;
248 char *name = gen_implicit_length_all_chars(&len);
250 return check_atom(skel, name, len);
255 /* Test parsing of implicit-length atoms. */
257 static svn_error_t *
258 parse_implicit_length(const char **msg,
259 svn_boolean_t msg_only,
260 svn_test_opts_t *opts,
261 apr_pool_t *pool)
263 svn_stringbuf_t *str = get_empty_string(pool);
264 skel_t *skel;
266 *msg = "parse implicit-length atoms";
268 if (msg_only)
269 return SVN_NO_ERROR;
271 /* Try all valid single-byte atoms. */
273 const char *c;
274 int i;
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",
286 i, c);
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");
298 return SVN_NO_ERROR;
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. */
308 static void
309 put_explicit_length(svn_stringbuf_t *str, const char *data, apr_size_t len,
310 char sep)
312 char *buf = malloc(len + 100);
313 apr_size_t length_len;
315 if (! skel_is_space(sep))
316 abort();
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);
326 free(buf);
330 /* Return true iff SKEL is the parsed form of an atom generated by
331 put_explicit_length. */
332 static int
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. */
341 static svn_error_t *
342 try_explicit_length(const char *data, apr_size_t len, apr_size_t check_len,
343 apr_pool_t *pool)
345 int i;
346 svn_stringbuf_t *str = get_empty_string(pool);
347 skel_t *skel;
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");
360 return SVN_NO_ERROR;
364 static svn_error_t *
365 parse_explicit_length(const char **msg,
366 svn_boolean_t msg_only,
367 svn_test_opts_t *opts,
368 apr_pool_t *pool)
370 *msg = "parse explicit-length atoms";
372 if (msg_only)
373 return SVN_NO_ERROR;
375 /* Try to parse the empty atom. */
376 SVN_ERR(try_explicit_length("", 0, 0, pool));
378 /* Try to parse every one-character atom. */
380 int i;
382 for (i = 0; i < 256; i++)
384 char buf[1];
386 buf[0] = i;
387 SVN_ERR(try_explicit_length(buf, 1, 1, pool));
391 /* Try to parse an atom containing every character. */
393 int i;
394 char data[256];
396 for (i = 0; i < 256; i++)
397 data[i] = i;
399 SVN_ERR(try_explicit_length(data, 256, 256, pool));
402 return SVN_NO_ERROR;
407 /* Test parsing of invalid atoms. */
409 static struct invalid_atoms
411 int type;
412 apr_size_t len;
413 const char *data;
414 } invalid_atoms[] = { { 1, 1, "(" },
415 { 1, 1, ")" },
416 { 1, 1, "[" },
417 { 1, 1, "]" },
418 { 1, 1, " " },
419 { 1, 13, "Hello, World!" },
420 { 1, 8, "1mplicit" },
422 { 2, 2, "1" },
423 { 2, 1, "12" },
425 { 7, 0, NULL } };
427 static svn_error_t *
428 parse_invalid_atoms(const char **msg,
429 svn_boolean_t msg_only,
430 svn_test_opts_t *opts,
431 apr_pool_t *pool)
433 struct invalid_atoms *ia = invalid_atoms;
435 *msg = "parse invalid atoms";
437 if (msg_only)
438 return SVN_NO_ERROR;
440 while (ia->type != 7)
442 if (ia->type == 1)
444 skel_t *skel = parse_cstr(ia->data, pool);
445 if (check_atom(skel, ia->data, ia->len))
446 return fail(pool,
447 "failed to detect parsing error in '%s'", ia->data);
449 else
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);
458 ia++;
461 return SVN_NO_ERROR;
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. */
470 static void
471 put_list_start(svn_stringbuf_t *str, char space, int len)
473 int i;
475 if (len > 0 && ! skel_is_space(space))
476 abort();
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. */
486 static void
487 put_list_end(svn_stringbuf_t *str, char space, int len)
489 int i;
491 if (len > 0 && ! skel_is_space(space))
492 abort();
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. */
501 static int
502 check_list(skel_t *skel, int desired_len)
504 int len;
505 skel_t *child;
507 if (! (skel
508 && ! skel->is_atom))
509 return 0;
511 len = 0;
512 for (child = skel->children; child; child = child->next)
513 len++;
515 return len == desired_len;
520 /* Parse lists. */
522 static svn_error_t *
523 parse_list(const char **msg,
524 svn_boolean_t msg_only,
525 svn_test_opts_t *opts,
526 apr_pool_t *pool)
528 *msg = "parse lists";
530 if (msg_only)
531 return SVN_NO_ERROR;
534 /* Try lists of varying length. */
535 int list_len;
537 for (list_len = 0;
538 list_len < 30;
539 list_len < 4 ? list_len++ : (list_len *= 3))
541 /* Try lists with different separators. */
542 int sep;
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. */
549 int sep_count;
551 for (sep_count = 0;
552 sep_count < 30;
553 sep_count < 4 ? sep_count++ : (sep_count *= 3))
555 /* Try various single-byte implicit-length atoms
556 for elements. */
557 int atom_byte;
559 for (atom_byte = 0; atom_byte < 256; atom_byte++)
560 if (skel_is_name( (apr_byte_t)atom_byte))
562 int i;
563 svn_stringbuf_t *str = get_empty_string(pool);
564 skel_t *skel;
565 skel_t *child;
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,
571 (apr_byte_t)sep);
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;
578 child;
579 child = child->next)
580 if (! check_implicit_length_byte
581 (child, (apr_byte_t)atom_byte))
582 return fail(pool,
583 "list was reparsed incorrectly");
586 /* Try the atom containing every character that's
587 legal in an implicit-length atom as the element. */
589 int i;
590 svn_stringbuf_t *str = get_empty_string(pool);
591 skel_t *skel;
592 skel_t *child;
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;
603 child;
604 child = child->next)
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
610 an element. */
611 for (atom_byte = 0; atom_byte < 256; atom_byte++)
613 int i;
614 svn_stringbuf_t *str = get_empty_string(pool);
615 skel_t *skel;
616 skel_t *child;
617 char buf[1];
619 buf[0] = atom_byte;
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;
630 child;
631 child = child->next)
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
637 an element. */
639 int i;
640 svn_stringbuf_t *str = get_empty_string(pool);
641 skel_t *skel;
642 skel_t *child;
643 char data[256];
645 for (i = 0; i < 256; i++)
646 data[i] = 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;
657 child;
658 child = child->next)
659 if (! check_explicit_length(child, data, 256))
660 return fail(pool, "list was re-parsed incorrectly");
667 /* Try to parse some invalid lists. */
669 int sep;
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. */
677 int sep_count;
679 for (sep_count = 0;
680 sep_count < 100;
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");
708 return SVN_NO_ERROR;
713 /* Building interesting skels. */
715 /* Build an atom skel containing the LEN bytes at DATA. */
716 static skel_t *
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);
723 skel->is_atom = 1;
724 skel->len = len;
725 skel->data = copy;
727 return skel;
730 /* Build an empty list skel. */
731 static skel_t *
732 empty(apr_pool_t *pool)
734 skel_t *skel = apr_palloc(pool, sizeof(*skel));
736 skel->is_atom = 0;
737 skel->children = 0;
739 return skel;
742 /* Stick ELEMENT at the beginning of the list skeleton LIST. */
743 static void
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
752 skel B. */
753 static int
754 skel_equal(skel_t *a, skel_t *b)
756 if (a->is_atom != b->is_atom)
757 return 0;
759 if (a->is_atom)
760 return (a->len == b->len
761 && ! memcmp(a->data, b->data, a->len));
762 else
764 skel_t *a_child, *b_child;
766 for (a_child = a->children, b_child = b->children;
767 a_child && b_child;
768 a_child = a_child->next, b_child = b_child->next)
769 if (! skel_equal(a_child, b_child))
770 return 0;
772 if (a_child || b_child)
773 return 0;
776 return 1;
780 /* Unparsing implicit-length atoms. */
782 static svn_error_t *
783 unparse_implicit_length(const char **msg,
784 svn_boolean_t msg_only,
785 svn_test_opts_t *opts,
786 apr_pool_t *pool)
788 *msg = "unparse implicit-length atoms";
790 if (msg_only)
791 return SVN_NO_ERROR;
793 /* Unparse and check every single-byte implicit-length atom. */
795 int byte;
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);
806 if (! (str
807 && str->len == 1
808 && str->data[0] == (char)byte))
809 return fail(pool, "incorrectly unparsed single-byte "
810 "implicit-length atom");
814 return SVN_NO_ERROR;
819 /* Unparse some lists. */
821 static svn_error_t *
822 unparse_list(const char **msg,
823 svn_boolean_t msg_only,
824 svn_test_opts_t *opts,
825 apr_pool_t *pool)
827 *msg = "unparse lists";
829 if (msg_only)
830 return SVN_NO_ERROR;
832 /* Make a list of all the single-byte implicit-length atoms. */
834 svn_stringbuf_t *str = get_empty_string(pool);
835 int byte;
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))
842 char buf = byte;
843 add(build_atom(1, &buf, pool), list);
846 /* Unparse that, parse it again, and see if we got the same thing
847 back. */
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))
861 if (! (elt
862 && elt->is_atom
863 && elt->len == 1
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");
872 elt = elt->next;
875 /* We should have reached the end of the list at this point. */
876 if (elt)
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);
884 skel_t *reparsed;
885 int i;
887 for (i = 0; i < 10; i++)
889 skel_t *middle = empty(pool);
890 int j;
892 for (j = 0; j < 10; j++)
894 char buf[10];
895 apr_size_t k;
896 int val;
898 /* Make some interesting atom, containing lots of binary
899 characters. */
900 val = i * 10 + j;
901 for (k = 0; k < sizeof(buf); k++)
903 buf[k] = val;
904 val += j;
907 add(build_atom(sizeof(buf), buf, pool), middle);
910 add(middle, top);
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");
920 return SVN_NO_ERROR;
924 /* The test table. */
926 struct svn_test_descriptor_t test_funcs[] =
928 SVN_TEST_NULL,
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),
935 SVN_TEST_NULL