1 /* fs-test.c --- tests for the filesystem
3 * ====================================================================
4 * Copyright (c) 2000-2007 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 * ====================================================================
20 #include <apr_pools.h>
23 #include "svn_pools.h"
25 #include "svn_string.h"
28 #include "svn_mergeinfo.h"
30 #include "../svn_test.h"
31 #include "../svn_test_fs.h"
33 #include "../../libsvn_delta/delta.h"
35 #define SET_STR(ps, s) ((ps)->data = (s), (ps)->len = strlen(s))
38 /*-----------------------------------------------------------------*/
40 /** The actual fs-tests called by `make check` **/
42 /* Helper: commit TXN, expecting either success or failure:
44 * If EXPECTED_CONFLICT is null, then the commit is expected to
45 * succeed. If it does succeed, set *NEW_REV to the new revision;
48 * If EXPECTED_CONFLICT is non-null, it is either the empty string or
49 * the expected path of the conflict. If it is the empty string, any
50 * conflict is acceptable. If it is a non-empty string, the commit
51 * must fail due to conflict, and the conflict path must match
52 * EXPECTED_CONFLICT. If they don't match, return error.
54 * If a conflict is expected but the commit succeeds anyway, return
58 test_commit_txn(svn_revnum_t
*new_rev
,
60 const char *expected_conflict
,
66 err
= svn_fs_commit_txn(&conflict
, new_rev
, txn
, pool
);
68 if (err
&& (err
->apr_err
== SVN_ERR_FS_CONFLICT
))
71 if (! expected_conflict
)
73 return svn_error_createf
74 (SVN_ERR_FS_CONFLICT
, NULL
,
75 "commit conflicted at '%s', but no conflict expected",
76 conflict
? conflict
: "(missing conflict info!)");
78 else if (conflict
== NULL
)
80 return svn_error_createf
81 (SVN_ERR_FS_CONFLICT
, NULL
,
82 "commit conflicted as expected, "
83 "but no conflict path was returned ('%s' expected)",
86 else if ((strcmp(expected_conflict
, "") != 0)
87 && (strcmp(conflict
, expected_conflict
) != 0))
89 return svn_error_createf
90 (SVN_ERR_FS_CONFLICT
, NULL
,
91 "commit conflicted at '%s', but expected conflict at '%s')",
92 conflict
, expected_conflict
);
95 else if (err
) /* commit failed, but not due to conflict */
97 return svn_error_quick_wrap
98 (err
, "commit failed due to something other than a conflict");
100 else /* err == NULL, so commit succeeded */
102 if (expected_conflict
)
104 return svn_error_createf
105 (SVN_ERR_FS_GENERAL
, NULL
,
106 "commit succeeded that was expected to fail at '%s'",
116 /* Begin a txn, check its name, then close it */
118 trivial_transaction(const char **msg
,
119 svn_boolean_t msg_only
,
120 svn_test_opts_t
*opts
,
125 const char *txn_name
;
126 int is_invalid_char
[256];
130 *msg
= "begin a txn, check its name, then close it";
135 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-trivial-txn",
136 opts
->fs_type
, pool
));
138 /* Begin a new transaction that is based on revision 0. */
139 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
141 /* Test that the txn name is non-null. */
142 SVN_ERR(svn_fs_txn_name(&txn_name
, txn
, pool
));
145 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
146 "Got a NULL txn name.");
148 /* Test that the txn name contains only valid characters. See
149 svn_fs.h for the list of valid characters. */
150 for (i
= 0; i
< sizeof(is_invalid_char
)/sizeof(*is_invalid_char
); ++i
)
151 is_invalid_char
[i
] = 1;
152 for (i
= '0'; i
<= '9'; ++i
)
153 is_invalid_char
[i
] = 0;
154 for (i
= 'a'; i
<= 'z'; ++i
)
155 is_invalid_char
[i
] = 0;
156 for (i
= 'A'; i
<= 'Z'; ++i
)
157 is_invalid_char
[i
] = 0;
158 for (p
= "-."; *p
; ++p
)
159 is_invalid_char
[(unsigned char) *p
] = 0;
161 for (p
= txn_name
; *p
; ++p
)
163 if (is_invalid_char
[(unsigned char) *p
])
164 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
165 "The txn name '%s' contains an illegal '%c' "
166 "character", txn_name
, *p
);
174 /* Open an existing transaction by name. */
176 reopen_trivial_transaction(const char **msg
,
177 svn_boolean_t msg_only
,
178 svn_test_opts_t
*opts
,
183 const char *txn_name
;
184 apr_pool_t
*subpool
= svn_pool_create(pool
);
186 *msg
= "open an existing transaction by name";
191 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-reopen-trivial-txn",
192 opts
->fs_type
, pool
));
194 /* Begin a new transaction that is based on revision 0. */
195 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
197 /* Don't use the subpool, txn_name must persist beyond the current txn */
198 SVN_ERR(svn_fs_txn_name(&txn_name
, txn
, pool
));
200 /* Close the transaction. */
201 svn_pool_clear(subpool
);
203 /* Reopen the transaction by name */
204 SVN_ERR(svn_fs_open_txn(&txn
, fs
, txn_name
, subpool
));
206 /* Close the transaction ... again. */
207 svn_pool_destroy(subpool
);
216 create_file_transaction(const char **msg
,
217 svn_boolean_t msg_only
,
218 svn_test_opts_t
*opts
,
223 svn_fs_root_t
*txn_root
;
225 *msg
= "begin a txn, get the txn root, and add a file";
230 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-create-file-txn",
231 opts
->fs_type
, pool
));
233 /* Begin a new transaction that is based on revision 0. */
234 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
236 /* Get the txn root */
237 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
239 /* Create a new file in the root directory. */
240 SVN_ERR(svn_fs_make_file(txn_root
, "beer.txt", pool
));
246 /* Make sure we get txn lists correctly. */
248 verify_txn_list(const char **msg
,
249 svn_boolean_t msg_only
,
250 svn_test_opts_t
*opts
,
255 svn_fs_txn_t
*txn1
, *txn2
;
256 const char *name1
, *name2
;
257 apr_array_header_t
*txn_list
;
259 *msg
= "create 2 txns, list them, and verify the list";
264 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-verify-txn-list",
265 opts
->fs_type
, pool
));
267 /* Begin a new transaction, get its name (in the top pool), close it. */
268 subpool
= svn_pool_create(pool
);
269 SVN_ERR(svn_fs_begin_txn(&txn1
, fs
, 0, subpool
));
270 SVN_ERR(svn_fs_txn_name(&name1
, txn1
, pool
));
271 svn_pool_destroy(subpool
);
273 /* Begin *another* transaction, get its name (in the top pool), close it. */
274 subpool
= svn_pool_create(pool
);
275 SVN_ERR(svn_fs_begin_txn(&txn2
, fs
, 0, subpool
));
276 SVN_ERR(svn_fs_txn_name(&name2
, txn2
, pool
));
277 svn_pool_destroy(subpool
);
279 /* Get the list of active transactions from the fs. */
280 SVN_ERR(svn_fs_list_transactions(&txn_list
, fs
, pool
));
282 /* Check the list. It should have *exactly* two entries. */
283 if (txn_list
->nelts
!= 2)
286 /* We should be able to find our 2 txn names in the list, in some
288 if ((! strcmp(name1
, APR_ARRAY_IDX(txn_list
, 0, const char *)))
289 && (! strcmp(name2
, APR_ARRAY_IDX(txn_list
, 1, const char *))))
292 else if ((! strcmp(name2
, APR_ARRAY_IDX(txn_list
, 0, const char *)))
293 && (! strcmp(name1
, APR_ARRAY_IDX(txn_list
, 1, const char *))))
298 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
299 "Got a bogus txn list.");
306 /* Generate N consecutive transactions, then abort them all. Return
307 the list of transaction names. */
309 txn_names_are_not_reused_helper1(apr_hash_t
**txn_names
,
313 apr_hash_index_t
*hi
;
317 *txn_names
= apr_hash_make(pool
);
319 /* Create the transactions and store in a hash table the transaction
320 name as the key and the svn_fs_txn_t * as the value. */
321 for (i
= 0; i
< N
; ++i
)
325 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
326 SVN_ERR(svn_fs_txn_name(&name
, txn
, pool
));
327 if (apr_hash_get(*txn_names
, name
, APR_HASH_KEY_STRING
) != NULL
)
328 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
329 "beginning a new transaction used an "
330 "existing transaction name '%s'",
332 apr_hash_set(*txn_names
, name
, APR_HASH_KEY_STRING
, txn
);
336 for (hi
= apr_hash_first(pool
, *txn_names
); hi
; hi
= apr_hash_next(hi
))
339 apr_hash_this(hi
, NULL
, NULL
, &val
);
340 SVN_ERR(svn_fs_abort_txn((svn_fs_txn_t
*)val
, pool
));
345 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
346 "created %d transactions, but only aborted %d",
352 /* Compare two hash tables and ensure that no keys in the first hash
353 table appear in the second hash table. */
355 txn_names_are_not_reused_helper2(apr_hash_t
*ht1
,
359 apr_hash_index_t
*hi
;
361 for (hi
= apr_hash_first(pool
, ht1
); hi
; hi
= apr_hash_next(hi
))
364 const char *key_string
;
365 apr_hash_this(hi
, &key
, NULL
, NULL
);
367 if (apr_hash_get(ht2
, key
, APR_HASH_KEY_STRING
) != NULL
)
368 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
369 "the transaction name '%s' was reused",
376 /* Make sure that transaction names are not reused. */
378 txn_names_are_not_reused(const char **msg
,
379 svn_boolean_t msg_only
,
380 svn_test_opts_t
*opts
,
385 apr_hash_t
*txn_names1
, *txn_names2
;
387 *msg
= "check that transaction names are not reused";
392 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-txn-names-are-not-reused",
393 opts
->fs_type
, pool
));
395 subpool
= svn_pool_create(pool
);
397 /* Create N transactions, abort them all, and collect the generated
398 transaction names. Do this twice. */
399 SVN_ERR(txn_names_are_not_reused_helper1(&txn_names1
, fs
, subpool
));
400 SVN_ERR(txn_names_are_not_reused_helper1(&txn_names2
, fs
, subpool
));
402 /* Check that no transaction names appear in both hash tables. */
403 SVN_ERR(txn_names_are_not_reused_helper2(txn_names1
, txn_names2
, subpool
));
404 SVN_ERR(txn_names_are_not_reused_helper2(txn_names2
, txn_names1
, subpool
));
406 svn_pool_destroy(subpool
);
413 /* Test writing & reading a file's contents. */
415 write_and_read_file(const char **msg
,
416 svn_boolean_t msg_only
,
417 svn_test_opts_t
*opts
,
422 svn_fs_root_t
*txn_root
;
423 svn_stream_t
*rstream
;
424 svn_stringbuf_t
*rstring
;
425 svn_stringbuf_t
*wstring
;
427 *msg
= "write and read a file's contents";
432 wstring
= svn_stringbuf_create("Wicki wild, wicki wicki wild.", pool
);
433 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-read-and-write-file",
434 opts
->fs_type
, pool
));
435 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
436 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
438 /* Add an empty file. */
439 SVN_ERR(svn_fs_make_file(txn_root
, "beer.txt", pool
));
441 /* And write some data into this file. */
442 SVN_ERR(svn_test__set_file_contents(txn_root
, "beer.txt",
443 wstring
->data
, pool
));
445 /* Now let's read the data back from the file. */
446 SVN_ERR(svn_fs_file_contents(&rstream
, txn_root
, "beer.txt", pool
));
447 SVN_ERR(svn_test__stream_to_string(&rstring
, rstream
, pool
));
449 /* Compare what was read to what was written. */
450 if (! svn_stringbuf_compare(rstring
, wstring
))
451 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
452 "data read != data written.");
459 /* Create a file, a directory, and a file in that directory! */
461 create_mini_tree_transaction(const char **msg
,
462 svn_boolean_t msg_only
,
463 svn_test_opts_t
*opts
,
468 svn_fs_root_t
*txn_root
;
470 *msg
= "test basic file and subdirectory creation";
475 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-create-mini-tree-txn",
476 opts
->fs_type
, pool
));
478 /* Begin a new transaction that is based on revision 0. */
479 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
481 /* Get the txn root */
482 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
484 /* Create a new file in the root directory. */
485 SVN_ERR(svn_fs_make_file(txn_root
, "wine.txt", pool
));
487 /* Create a new directory in the root directory. */
488 SVN_ERR(svn_fs_make_dir(txn_root
, "keg", pool
));
490 /* Now, create a file in our new directory. */
491 SVN_ERR(svn_fs_make_file(txn_root
, "keg/beer.txt", pool
));
497 /* Create a file, a directory, and a file in that directory! */
499 create_greek_tree_transaction(const char **msg
,
500 svn_boolean_t msg_only
,
501 svn_test_opts_t
*opts
,
506 svn_fs_root_t
*txn_root
;
508 *msg
= "make The Official Subversion Test Tree";
513 /* Prepare a txn to receive the greek tree. */
514 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-create-greek-tree-txn",
515 opts
->fs_type
, pool
));
516 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
517 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
519 /* Create and verify the greek tree. */
520 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
526 /* Verify that entry KEY is present in ENTRIES, and that its value is
527 an svn_fs_dirent_t whose name and id are not null. */
529 verify_entry(apr_hash_t
*entries
, const char *key
)
531 svn_fs_dirent_t
*ent
= apr_hash_get(entries
, key
,
532 APR_HASH_KEY_STRING
);
535 return svn_error_createf
536 (SVN_ERR_FS_GENERAL
, NULL
,
537 "didn't find dir entry for \"%s\"", key
);
539 if ((ent
->name
== NULL
) && (ent
->id
== NULL
))
540 return svn_error_createf
541 (SVN_ERR_FS_GENERAL
, NULL
,
542 "dir entry for \"%s\" has null name and null id", key
);
544 if (ent
->name
== NULL
)
545 return svn_error_createf
546 (SVN_ERR_FS_GENERAL
, NULL
,
547 "dir entry for \"%s\" has null name", key
);
550 return svn_error_createf
551 (SVN_ERR_FS_GENERAL
, NULL
,
552 "dir entry for \"%s\" has null id", key
);
554 if (strcmp(ent
->name
, key
) != 0)
555 return svn_error_createf
556 (SVN_ERR_FS_GENERAL
, NULL
,
557 "dir entry for \"%s\" contains wrong name (\"%s\")", key
, ent
->name
);
564 list_directory(const char **msg
,
565 svn_boolean_t msg_only
,
566 svn_test_opts_t
*opts
,
571 svn_fs_root_t
*txn_root
;
574 *msg
= "fill a directory, then list it";
579 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-list-dir",
580 opts
->fs_type
, pool
));
581 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
582 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
584 /* We create this tree
594 * then list dir A. It should have 3 files: "x", "y", and "z", no
598 /* Create the tree. */
599 SVN_ERR(svn_fs_make_file(txn_root
, "q", pool
));
600 SVN_ERR(svn_fs_make_dir(txn_root
, "A", pool
));
601 SVN_ERR(svn_fs_make_file(txn_root
, "A/x", pool
));
602 SVN_ERR(svn_fs_make_file(txn_root
, "A/y", pool
));
603 SVN_ERR(svn_fs_make_file(txn_root
, "A/z", pool
));
604 SVN_ERR(svn_fs_make_dir(txn_root
, "B", pool
));
605 SVN_ERR(svn_fs_make_file(txn_root
, "B/m", pool
));
606 SVN_ERR(svn_fs_make_file(txn_root
, "B/n", pool
));
607 SVN_ERR(svn_fs_make_file(txn_root
, "B/o", pool
));
609 /* Get A's entries. */
610 SVN_ERR(svn_fs_dir_entries(&entries
, txn_root
, "A", pool
));
612 /* Make sure exactly the right set of entries is present. */
613 if (apr_hash_count(entries
) != 3)
615 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
616 "unexpected number of entries in dir");
620 SVN_ERR(verify_entry(entries
, "x"));
621 SVN_ERR(verify_entry(entries
, "y"));
622 SVN_ERR(verify_entry(entries
, "z"));
630 revision_props(const char **msg
,
631 svn_boolean_t msg_only
,
632 svn_test_opts_t
*opts
,
636 apr_hash_t
*proplist
;
641 const char *initial_props
[4][2] = {
644 { "favorite saturday morning cartoon", "looney tunes" },
645 { "auto", "Green 1997 Saturn SL1" }
648 const char *final_props
[4][2] = {
649 { "color", "violet" },
650 { "flower", "violet" },
651 { "favorite saturday morning cartoon", "looney tunes" },
652 { "auto", "Red 2000 Chevrolet Blazer" }
655 *msg
= "set and get some revision properties";
661 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-rev-props",
662 opts
->fs_type
, pool
));
664 /* Set some properties on the revision. */
665 for (i
= 0; i
< 4; i
++)
667 SET_STR(&s1
, initial_props
[i
][1]);
668 SVN_ERR(svn_fs_change_rev_prop(fs
, 0, initial_props
[i
][0], &s1
, pool
));
671 /* Change some of the above properties. */
672 SET_STR(&s1
, "violet");
673 SVN_ERR(svn_fs_change_rev_prop(fs
, 0, "color", &s1
, pool
));
675 SET_STR(&s1
, "Red 2000 Chevrolet Blazer");
676 SVN_ERR(svn_fs_change_rev_prop(fs
, 0, "auto", &s1
, pool
));
678 /* Remove a property altogether */
679 SVN_ERR(svn_fs_change_rev_prop(fs
, 0, "size", NULL
, pool
));
681 /* Copy a property's value into a new property. */
682 SVN_ERR(svn_fs_revision_prop(&value
, fs
, 0, "color", pool
));
684 s1
.data
= value
->data
;
686 SVN_ERR(svn_fs_change_rev_prop(fs
, 0, "flower", &s1
, pool
));
688 /* Obtain a list of all current properties, and make sure it matches
689 the expected values. */
690 SVN_ERR(svn_fs_revision_proplist(&proplist
, fs
, 0, pool
));
692 svn_string_t
*prop_value
;
694 if (apr_hash_count(proplist
) < 4 )
695 return svn_error_createf
696 (SVN_ERR_FS_GENERAL
, NULL
,
697 "too few revision properties found");
699 /* Loop through our list of expected revision property name/value
701 for (i
= 0; i
< 4; i
++)
703 /* For each expected property: */
705 /* Step 1. Find it by name in the hash of all rev. props
706 returned to us by svn_fs_revision_proplist. If it can't be
707 found, return an error. */
708 prop_value
= apr_hash_get(proplist
,
710 APR_HASH_KEY_STRING
);
712 return svn_error_createf
713 (SVN_ERR_FS_GENERAL
, NULL
,
714 "unable to find expected revision property");
716 /* Step 2. Make sure the value associated with it is the same
717 as what was expected, else return an error. */
718 if (strcmp(prop_value
->data
, final_props
[i
][1]))
719 return svn_error_createf
720 (SVN_ERR_FS_GENERAL
, NULL
,
721 "revision property had an unexpected value");
730 transaction_props(const char **msg
,
731 svn_boolean_t msg_only
,
732 svn_test_opts_t
*opts
,
737 apr_hash_t
*proplist
;
739 svn_revnum_t after_rev
;
743 const char *initial_props
[4][2] = {
746 { "favorite saturday morning cartoon", "looney tunes" },
747 { "auto", "Green 1997 Saturn SL1" }
750 const char *final_props
[5][2] = {
751 { "color", "violet" },
752 { "flower", "violet" },
753 { "favorite saturday morning cartoon", "looney tunes" },
754 { "auto", "Red 2000 Chevrolet Blazer" },
755 { SVN_PROP_REVISION_DATE
, "<some datestamp value>" }
758 *msg
= "set/get txn props, commit, validate new rev props";
764 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-txn-props",
765 opts
->fs_type
, pool
));
766 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
768 /* Set some properties on the revision. */
769 for (i
= 0; i
< 4; i
++)
771 SET_STR(&s1
, initial_props
[i
][1]);
772 SVN_ERR(svn_fs_change_txn_prop(txn
, initial_props
[i
][0], &s1
, pool
));
775 /* Change some of the above properties. */
776 SET_STR(&s1
, "violet");
777 SVN_ERR(svn_fs_change_txn_prop(txn
, "color", &s1
, pool
));
779 SET_STR(&s1
, "Red 2000 Chevrolet Blazer");
780 SVN_ERR(svn_fs_change_txn_prop(txn
, "auto", &s1
, pool
));
782 /* Remove a property altogether */
783 SVN_ERR(svn_fs_change_txn_prop(txn
, "size", NULL
, pool
));
785 /* Copy a property's value into a new property. */
786 SVN_ERR(svn_fs_txn_prop(&value
, txn
, "color", pool
));
788 s1
.data
= value
->data
;
790 SVN_ERR(svn_fs_change_txn_prop(txn
, "flower", &s1
, pool
));
792 /* Obtain a list of all current properties, and make sure it matches
793 the expected values. */
794 SVN_ERR(svn_fs_txn_proplist(&proplist
, txn
, pool
));
796 svn_string_t
*prop_value
;
798 /* All transactions get a datestamp property at their inception,
799 so we expect *5*, not 4 properties. */
800 if (apr_hash_count(proplist
) != 5 )
801 return svn_error_createf
802 (SVN_ERR_FS_GENERAL
, NULL
,
803 "unexpected number of transaction properties were found");
805 /* Loop through our list of expected revision property name/value
807 for (i
= 0; i
< 5; i
++)
809 /* For each expected property: */
811 /* Step 1. Find it by name in the hash of all rev. props
812 returned to us by svn_fs_revision_proplist. If it can't be
813 found, return an error. */
814 prop_value
= apr_hash_get(proplist
,
816 APR_HASH_KEY_STRING
);
818 return svn_error_createf
819 (SVN_ERR_FS_GENERAL
, NULL
,
820 "unable to find expected transaction property");
822 /* Step 2. Make sure the value associated with it is the same
823 as what was expected, else return an error. */
824 if (strcmp(final_props
[i
][0], SVN_PROP_REVISION_DATE
))
825 if (strcmp(prop_value
->data
, final_props
[i
][1]))
826 return svn_error_createf
827 (SVN_ERR_FS_GENERAL
, NULL
,
828 "transaction property had an unexpected value");
832 /* Commit the transaction. */
833 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
835 return svn_error_createf
836 (SVN_ERR_FS_GENERAL
, NULL
,
837 "committed transaction got wrong revision number");
839 /* Obtain a list of all properties on the new revision, and make
840 sure it matches the expected values. If you're wondering, the
841 expected values should be the exact same set of properties that
842 existed on the transaction just prior to its being committed. */
843 SVN_ERR(svn_fs_revision_proplist(&proplist
, fs
, after_rev
, pool
));
845 svn_string_t
*prop_value
;
847 if (apr_hash_count(proplist
) < 5 )
848 return svn_error_createf
849 (SVN_ERR_FS_GENERAL
, NULL
,
850 "unexpected number of revision properties were found");
852 /* Loop through our list of expected revision property name/value
854 for (i
= 0; i
< 5; i
++)
856 /* For each expected property: */
858 /* Step 1. Find it by name in the hash of all rev. props
859 returned to us by svn_fs_revision_proplist. If it can't be
860 found, return an error. */
861 prop_value
= apr_hash_get(proplist
,
863 APR_HASH_KEY_STRING
);
865 return svn_error_createf
866 (SVN_ERR_FS_GENERAL
, NULL
,
867 "unable to find expected revision property");
869 /* Step 2. Make sure the value associated with it is the same
870 as what was expected, else return an error. */
871 if (strcmp(final_props
[i
][0], SVN_PROP_REVISION_DATE
))
872 if (strcmp(prop_value
->data
, final_props
[i
][1]))
873 return svn_error_createf
874 (SVN_ERR_FS_GENERAL
, NULL
,
875 "revision property had an unexpected value");
884 node_props(const char **msg
,
885 svn_boolean_t msg_only
,
886 svn_test_opts_t
*opts
,
891 svn_fs_root_t
*txn_root
;
892 apr_hash_t
*proplist
;
897 const char *initial_props
[4][2] = {
898 { "Best Rock Artist", "Creed" },
899 { "Best Rap Artist", "Eminem" },
900 { "Best Country Artist", "(null)" },
901 { "Best Sound Designer", "Pluessman" }
904 const char *final_props
[4][2] = {
905 { "Best Rock Artist", "P.O.D." },
906 { "Best Rap Artist", "Busta Rhymes" },
907 { "Best Sound Designer", "Pluessman" },
908 { "Biggest Cakewalk Fanatic", "Pluessman" }
911 *msg
= "set and get some node properties";
916 /* Open the fs and transaction */
917 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-node-props",
918 opts
->fs_type
, pool
));
919 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
920 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
922 /* Make a node to put some properties into */
923 SVN_ERR(svn_fs_make_file(txn_root
, "music.txt", pool
));
925 /* Set some properties on the nodes. */
926 for (i
= 0; i
< 4; i
++)
928 SET_STR(&s1
, initial_props
[i
][1]);
929 SVN_ERR(svn_fs_change_node_prop
930 (txn_root
, "music.txt", initial_props
[i
][0], &s1
, pool
));
933 /* Change some of the above properties. */
934 SET_STR(&s1
, "P.O.D.");
935 SVN_ERR(svn_fs_change_node_prop(txn_root
, "music.txt", "Best Rock Artist",
938 SET_STR(&s1
, "Busta Rhymes");
939 SVN_ERR(svn_fs_change_node_prop(txn_root
, "music.txt", "Best Rap Artist",
942 /* Remove a property altogether */
943 SVN_ERR(svn_fs_change_node_prop(txn_root
, "music.txt",
944 "Best Country Artist", NULL
, pool
));
946 /* Copy a property's value into a new property. */
947 SVN_ERR(svn_fs_node_prop(&value
, txn_root
, "music.txt",
948 "Best Sound Designer", pool
));
950 s1
.data
= value
->data
;
952 SVN_ERR(svn_fs_change_node_prop(txn_root
, "music.txt",
953 "Biggest Cakewalk Fanatic", &s1
, pool
));
955 /* Obtain a list of all current properties, and make sure it matches
956 the expected values. */
957 SVN_ERR(svn_fs_node_proplist(&proplist
, txn_root
, "music.txt", pool
));
959 svn_string_t
*prop_value
;
961 if (apr_hash_count(proplist
) != 4 )
962 return svn_error_createf
963 (SVN_ERR_FS_GENERAL
, NULL
,
964 "unexpected number of node properties were found");
966 /* Loop through our list of expected node property name/value
968 for (i
= 0; i
< 4; i
++)
970 /* For each expected property: */
972 /* Step 1. Find it by name in the hash of all node props
973 returned to us by svn_fs_node_proplist. If it can't be
974 found, return an error. */
975 prop_value
= apr_hash_get(proplist
,
977 APR_HASH_KEY_STRING
);
979 return svn_error_createf
980 (SVN_ERR_FS_GENERAL
, NULL
,
981 "unable to find expected node property");
983 /* Step 2. Make sure the value associated with it is the same
984 as what was expected, else return an error. */
985 if (strcmp(prop_value
->data
, final_props
[i
][1]))
986 return svn_error_createf
987 (SVN_ERR_FS_GENERAL
, NULL
,
988 "node property had an unexpected value");
997 /* Set *PRESENT to true if entry NAME is present in directory PATH
998 under ROOT, else set *PRESENT to false. */
1000 check_entry(svn_fs_root_t
*root
,
1003 svn_boolean_t
*present
,
1006 apr_hash_t
*entries
;
1007 svn_fs_dirent_t
*ent
;
1009 SVN_ERR(svn_fs_dir_entries(&entries
, root
, path
, pool
));
1010 ent
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
1017 return SVN_NO_ERROR
;
1021 /* Return an error if entry NAME is absent in directory PATH under ROOT. */
1022 static svn_error_t
*
1023 check_entry_present(svn_fs_root_t
*root
, const char *path
,
1024 const char *name
, apr_pool_t
*pool
)
1026 svn_boolean_t present
;
1027 SVN_ERR(check_entry(root
, path
, name
, &present
, pool
));
1030 return svn_error_createf
1031 (SVN_ERR_FS_GENERAL
, NULL
,
1032 "entry \"%s\" absent when it should be present", name
);
1034 return SVN_NO_ERROR
;
1038 /* Return an error if entry NAME is present in directory PATH under ROOT. */
1039 static svn_error_t
*
1040 check_entry_absent(svn_fs_root_t
*root
, const char *path
,
1041 const char *name
, apr_pool_t
*pool
)
1043 svn_boolean_t present
;
1044 SVN_ERR(check_entry(root
, path
, name
, &present
, pool
));
1047 return svn_error_createf
1048 (SVN_ERR_FS_GENERAL
, NULL
,
1049 "entry \"%s\" present when it should be absent", name
);
1051 return SVN_NO_ERROR
;
1055 /* Fetch the youngest revision from a repos. */
1056 static svn_error_t
*
1057 fetch_youngest_rev(const char **msg
,
1058 svn_boolean_t msg_only
,
1059 svn_test_opts_t
*opts
,
1064 svn_fs_root_t
*txn_root
;
1065 svn_revnum_t new_rev
;
1066 svn_revnum_t youngest_rev
, new_youngest_rev
;
1068 *msg
= "fetch the youngest revision from a filesystem";
1071 return SVN_NO_ERROR
;
1073 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-youngest-rev",
1074 opts
->fs_type
, pool
));
1076 /* Get youngest revision of brand spankin' new filesystem. */
1077 SVN_ERR(svn_fs_youngest_rev(&youngest_rev
, fs
, pool
));
1079 /* Prepare a txn to receive the greek tree. */
1080 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
1081 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1083 /* Create the greek tree. */
1084 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
1087 SVN_ERR(test_commit_txn(&new_rev
, txn
, NULL
, pool
));
1089 /* Get the new youngest revision. */
1090 SVN_ERR(svn_fs_youngest_rev(&new_youngest_rev
, fs
, pool
));
1092 if (youngest_rev
== new_rev
)
1093 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
1094 "commit didn't bump up revision number");
1096 if (new_youngest_rev
!= new_rev
)
1097 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
1098 "couldn't fetch youngest revision");
1100 return SVN_NO_ERROR
;
1104 /* Test committing against an empty repository.
1105 todo: also test committing against youngest? */
1106 static svn_error_t
*
1107 basic_commit(const char **msg
,
1108 svn_boolean_t msg_only
,
1109 svn_test_opts_t
*opts
,
1114 svn_fs_root_t
*txn_root
, *revision_root
;
1115 svn_revnum_t before_rev
, after_rev
;
1116 const char *conflict
;
1118 *msg
= "basic commit";
1121 return SVN_NO_ERROR
;
1123 /* Prepare a filesystem. */
1124 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-basic-commit",
1125 opts
->fs_type
, pool
));
1127 /* Save the current youngest revision. */
1128 SVN_ERR(svn_fs_youngest_rev(&before_rev
, fs
, pool
));
1130 /* Prepare a txn to receive the greek tree. */
1131 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
1132 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1134 /* Paranoidly check that the current youngest rev is unchanged. */
1135 SVN_ERR(svn_fs_youngest_rev(&after_rev
, fs
, pool
));
1136 if (after_rev
!= before_rev
)
1137 return svn_error_create
1138 (SVN_ERR_FS_GENERAL
, NULL
,
1139 "youngest revision changed unexpectedly");
1141 /* Create the greek tree. */
1142 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
1145 SVN_ERR(svn_fs_commit_txn(&conflict
, &after_rev
, txn
, pool
));
1147 /* Make sure it's a different revision than before. */
1148 if (after_rev
== before_rev
)
1149 return svn_error_create
1150 (SVN_ERR_FS_GENERAL
, NULL
,
1151 "youngest revision failed to change");
1153 /* Get root of the revision */
1154 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1156 /* Check the tree. */
1157 SVN_ERR(svn_test__check_greek_tree(revision_root
, pool
));
1159 return SVN_NO_ERROR
;
1164 static svn_error_t
*
1165 test_tree_node_validation(const char **msg
,
1166 svn_boolean_t msg_only
,
1167 svn_test_opts_t
*opts
,
1172 svn_fs_root_t
*txn_root
, *revision_root
;
1173 svn_revnum_t after_rev
;
1174 const char *conflict
;
1175 apr_pool_t
*subpool
;
1177 *msg
= "testing tree validation helper";
1180 return SVN_NO_ERROR
;
1182 /* Prepare a filesystem. */
1183 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-validate-tree-entries",
1184 opts
->fs_type
, pool
));
1186 /* In a txn, create the greek tree. */
1187 subpool
= svn_pool_create(pool
);
1189 static svn_test__tree_entry_t expected_entries
[] = {
1190 /* path, contents (0 = dir) */
1191 { "iota", "This is the file 'iota'.\n" },
1193 { "A/mu", "This is the file 'mu'.\n" },
1195 { "A/B/lambda", "This is the file 'lambda'.\n" },
1197 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1198 { "A/B/E/beta", "This is the file 'beta'.\n" },
1202 { "A/D/gamma", "This is the file 'gamma'.\n" },
1204 { "A/D/G/pi", "This is the file 'pi'.\n" },
1205 { "A/D/G/rho", "This is the file 'rho'.\n" },
1206 { "A/D/G/tau", "This is the file 'tau'.\n" },
1208 { "A/D/H/chi", "This is the file 'chi'.\n" },
1209 { "A/D/H/psi", "This is the file 'psi'.\n" },
1210 { "A/D/H/omega", "This is the file 'omega'.\n" }
1212 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
1213 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
1214 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
1216 /* Carefully validate that tree in the transaction. */
1217 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 20,
1220 /* Go ahead and commit the tree, and destroy the txn object. */
1221 SVN_ERR(svn_fs_commit_txn(&conflict
, &after_rev
, txn
, subpool
));
1223 /* Carefully validate that tree in the new revision, now. */
1224 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, subpool
));
1225 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
, 20,
1228 svn_pool_destroy(subpool
);
1230 /* In a new txn, modify the greek tree. */
1231 subpool
= svn_pool_create(pool
);
1233 static svn_test__tree_entry_t expected_entries
[] = {
1234 /* path, contents (0 = dir) */
1235 { "iota", "This is a new version of 'iota'.\n" },
1238 { "A/B/lambda", "This is the file 'lambda'.\n" },
1240 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1241 { "A/B/E/beta", "This is the file 'beta'.\n" },
1244 { "A/C/kappa", "This is the file 'kappa'.\n" },
1246 { "A/D/gamma", "This is the file 'gamma'.\n" },
1248 { "A/D/H/chi", "This is the file 'chi'.\n" },
1249 { "A/D/H/psi", "This is the file 'psi'.\n" },
1250 { "A/D/H/omega", "This is the file 'omega'.\n" },
1252 { "A/D/I/delta", "This is the file 'delta'.\n" },
1253 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1256 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, subpool
));
1257 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
1258 SVN_ERR(svn_test__set_file_contents
1259 (txn_root
, "iota", "This is a new version of 'iota'.\n",
1261 SVN_ERR(svn_fs_delete(txn_root
, "A/mu", subpool
));
1262 SVN_ERR(svn_fs_delete(txn_root
, "A/D/G", subpool
));
1263 SVN_ERR(svn_fs_make_dir(txn_root
, "A/D/I", subpool
));
1264 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/I/delta", subpool
));
1265 SVN_ERR(svn_test__set_file_contents
1266 (txn_root
, "A/D/I/delta", "This is the file 'delta'.\n",
1268 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/I/epsilon", subpool
));
1269 SVN_ERR(svn_test__set_file_contents
1270 (txn_root
, "A/D/I/epsilon", "This is the file 'epsilon'.\n",
1272 SVN_ERR(svn_fs_make_file(txn_root
, "A/C/kappa", subpool
));
1273 SVN_ERR(svn_test__set_file_contents
1274 (txn_root
, "A/C/kappa", "This is the file 'kappa'.\n",
1277 /* Carefully validate that tree in the transaction. */
1278 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 19,
1281 /* Go ahead and commit the tree, and destroy the txn object. */
1282 SVN_ERR(svn_fs_commit_txn(&conflict
, &after_rev
, txn
, subpool
));
1284 /* Carefully validate that tree in the new revision, now. */
1285 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, subpool
));
1286 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1289 svn_pool_destroy(subpool
);
1291 return SVN_NO_ERROR
;
1295 /* Commit with merging (committing against non-youngest). */
1296 static svn_error_t
*
1297 merging_commit(const char **msg
,
1298 svn_boolean_t msg_only
,
1299 svn_test_opts_t
*opts
,
1304 svn_fs_root_t
*txn_root
, *revision_root
;
1305 svn_revnum_t after_rev
;
1306 svn_revnum_t revisions
[24];
1308 svn_revnum_t revision_count
;
1310 *msg
= "merging commit";
1313 return SVN_NO_ERROR
;
1315 /* Prepare a filesystem. */
1316 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-merging-commit",
1317 opts
->fs_type
, pool
));
1319 /* Initialize our revision number stuffs. */
1321 i
< ((sizeof(revisions
)) / (sizeof(svn_revnum_t
)));
1323 revisions
[i
] = SVN_INVALID_REVNUM
;
1325 revisions
[revision_count
++] = 0; /* the brand spankin' new revision */
1327 /***********************************************************************/
1329 /***********************************************************************/
1331 /* In one txn, create and commit the greek tree. */
1332 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
1333 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1334 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
1335 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1337 /***********************************************************************/
1339 /***********************************************************************/
1341 static svn_test__tree_entry_t expected_entries
[] = {
1342 /* path, contents (0 = dir) */
1343 { "iota", "This is the file 'iota'.\n" },
1345 { "A/mu", "This is the file 'mu'.\n" },
1347 { "A/B/lambda", "This is the file 'lambda'.\n" },
1349 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1350 { "A/B/E/beta", "This is the file 'beta'.\n" },
1354 { "A/D/gamma", "This is the file 'gamma'.\n" },
1356 { "A/D/G/pi", "This is the file 'pi'.\n" },
1357 { "A/D/G/rho", "This is the file 'rho'.\n" },
1358 { "A/D/G/tau", "This is the file 'tau'.\n" },
1360 { "A/D/H/chi", "This is the file 'chi'.\n" },
1361 { "A/D/H/psi", "This is the file 'psi'.\n" },
1362 { "A/D/H/omega", "This is the file 'omega'.\n" }
1364 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1365 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1368 revisions
[revision_count
++] = after_rev
;
1370 /* Let's add a directory and some files to the tree, and delete
1372 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[revision_count
-1], pool
));
1373 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1374 SVN_ERR(svn_fs_make_dir(txn_root
, "A/D/I", pool
));
1375 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/I/delta", pool
));
1376 SVN_ERR(svn_test__set_file_contents
1377 (txn_root
, "A/D/I/delta", "This is the file 'delta'.\n", pool
));
1378 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/I/epsilon", pool
));
1379 SVN_ERR(svn_test__set_file_contents
1380 (txn_root
, "A/D/I/epsilon", "This is the file 'epsilon'.\n", pool
));
1381 SVN_ERR(svn_fs_make_file(txn_root
, "A/C/kappa", pool
));
1382 SVN_ERR(svn_test__set_file_contents
1383 (txn_root
, "A/C/kappa", "This is the file 'kappa'.\n", pool
));
1384 SVN_ERR(svn_fs_delete(txn_root
, "iota", pool
));
1385 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1387 /***********************************************************************/
1389 /***********************************************************************/
1391 static svn_test__tree_entry_t expected_entries
[] = {
1392 /* path, contents (0 = dir) */
1394 { "A/mu", "This is the file 'mu'.\n" },
1396 { "A/B/lambda", "This is the file 'lambda'.\n" },
1398 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1399 { "A/B/E/beta", "This is the file 'beta'.\n" },
1402 { "A/C/kappa", "This is the file 'kappa'.\n" },
1404 { "A/D/gamma", "This is the file 'gamma'.\n" },
1406 { "A/D/G/pi", "This is the file 'pi'.\n" },
1407 { "A/D/G/rho", "This is the file 'rho'.\n" },
1408 { "A/D/G/tau", "This is the file 'tau'.\n" },
1410 { "A/D/H/chi", "This is the file 'chi'.\n" },
1411 { "A/D/H/psi", "This is the file 'psi'.\n" },
1412 { "A/D/H/omega", "This is the file 'omega'.\n" },
1414 { "A/D/I/delta", "This is the file 'delta'.\n" },
1415 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1417 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1418 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1421 revisions
[revision_count
++] = after_rev
;
1423 /* We don't think the A/D/H directory is pulling its weight...let's
1424 knock it off. Oh, and let's re-add iota, too. */
1425 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[revision_count
-1], pool
));
1426 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1427 SVN_ERR(svn_fs_delete(txn_root
, "A/D/H", pool
));
1428 SVN_ERR(svn_fs_make_file(txn_root
, "iota", pool
));
1429 SVN_ERR(svn_test__set_file_contents
1430 (txn_root
, "iota", "This is the new file 'iota'.\n", pool
));
1431 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1433 /***********************************************************************/
1435 /***********************************************************************/
1437 static svn_test__tree_entry_t expected_entries
[] = {
1438 /* path, contents (0 = dir) */
1439 { "iota", "This is the new file 'iota'.\n" },
1441 { "A/mu", "This is the file 'mu'.\n" },
1443 { "A/B/lambda", "This is the file 'lambda'.\n" },
1445 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1446 { "A/B/E/beta", "This is the file 'beta'.\n" },
1449 { "A/C/kappa", "This is the file 'kappa'.\n" },
1451 { "A/D/gamma", "This is the file 'gamma'.\n" },
1453 { "A/D/G/pi", "This is the file 'pi'.\n" },
1454 { "A/D/G/rho", "This is the file 'rho'.\n" },
1455 { "A/D/G/tau", "This is the file 'tau'.\n" },
1457 { "A/D/I/delta", "This is the file 'delta'.\n" },
1458 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1460 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1461 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1464 revisions
[revision_count
++] = after_rev
;
1466 /* Delete iota (yet again). */
1467 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[revision_count
-1], pool
));
1468 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1469 SVN_ERR(svn_fs_delete(txn_root
, "iota", pool
));
1470 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1472 /***********************************************************************/
1474 /***********************************************************************/
1476 static svn_test__tree_entry_t expected_entries
[] = {
1477 /* path, contents (0 = dir) */
1479 { "A/mu", "This is the file 'mu'.\n" },
1481 { "A/B/lambda", "This is the file 'lambda'.\n" },
1483 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1484 { "A/B/E/beta", "This is the file 'beta'.\n" },
1487 { "A/C/kappa", "This is the file 'kappa'.\n" },
1489 { "A/D/gamma", "This is the file 'gamma'.\n" },
1491 { "A/D/G/pi", "This is the file 'pi'.\n" },
1492 { "A/D/G/rho", "This is the file 'rho'.\n" },
1493 { "A/D/G/tau", "This is the file 'tau'.\n" },
1495 { "A/D/I/delta", "This is the file 'delta'.\n" },
1496 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1498 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1499 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1502 revisions
[revision_count
++] = after_rev
;
1504 /***********************************************************************/
1505 /* GIVEN: A and B, with common ancestor ANCESTOR, where A and B
1506 directories, and E, an entry in either A, B, or ANCESTOR.
1508 For every E, the following cases exist:
1509 - E exists in neither ANCESTOR nor A.
1510 - E doesn't exist in ANCESTOR, and has been added to A.
1511 - E exists in ANCESTOR, but has been deleted from A.
1512 - E exists in both ANCESTOR and A ...
1513 - but refers to different node revisions.
1514 - and refers to the same node revision.
1516 The same set of possible relationships with ANCESTOR holds for B,
1517 so there are thirty-six combinations. The matrix is symmetrical
1518 with A and B reversed, so we only have to describe one triangular
1519 half, including the diagonal --- 21 combinations.
1521 Our goal here is to test all the possible scenarios that can
1522 occur given the above boolean logic table, and to make sure that
1523 the results we get are as expected.
1525 The test cases below have the following features:
1527 - They run straight through the scenarios as described in the
1528 `structure' document at this time.
1530 - In each case, a txn is begun based on some revision (ANCESTOR),
1531 is modified into a new tree (B), and then is attempted to be
1532 committed (which happens against the head of the tree, A).
1534 - If the commit is successful (and is *expected* to be such),
1535 that new revision (which exists now as a result of the
1536 successful commit) is thoroughly tested for accuracy of tree
1537 entries, and in the case of files, for their contents. It is
1538 important to realize that these successful commits are
1539 advancing the head of the tree, and each one effective becomes
1540 the new `A' described in further test cases.
1542 /***********************************************************************/
1544 /* (6) E exists in neither ANCESTOR nor A. */
1546 /* (1) E exists in neither ANCESTOR nor B. Can't occur, by
1547 assumption that E exists in either A, B, or ancestor. */
1549 /* (1) E has been added to B. Add E in the merged result. */
1550 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[0], pool
));
1551 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1552 SVN_ERR(svn_fs_make_file(txn_root
, "theta", pool
));
1553 SVN_ERR(svn_test__set_file_contents
1554 (txn_root
, "theta", "This is the file 'theta'.\n", pool
));
1555 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1557 /*********************************************************************/
1559 /*********************************************************************/
1561 static svn_test__tree_entry_t expected_entries
[] = {
1562 /* path, contents (0 = dir) */
1563 { "theta", "This is the file 'theta'.\n" },
1565 { "A/mu", "This is the file 'mu'.\n" },
1567 { "A/B/lambda", "This is the file 'lambda'.\n" },
1569 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1570 { "A/B/E/beta", "This is the file 'beta'.\n" },
1573 { "A/C/kappa", "This is the file 'kappa'.\n" },
1575 { "A/D/gamma", "This is the file 'gamma'.\n" },
1577 { "A/D/G/pi", "This is the file 'pi'.\n" },
1578 { "A/D/G/rho", "This is the file 'rho'.\n" },
1579 { "A/D/G/tau", "This is the file 'tau'.\n" },
1581 { "A/D/I/delta", "This is the file 'delta'.\n" },
1582 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1584 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1585 SVN_ERR(svn_test__validate_tree(revision_root
,
1589 revisions
[revision_count
++] = after_rev
;
1591 /* (1) E has been deleted from B. Can't occur, by assumption that
1592 E doesn't exist in ANCESTOR. */
1594 /* (3) E exists in both ANCESTOR and B. Can't occur, by
1595 assumption that E doesn't exist in ancestor. */
1598 /* (5) E doesn't exist in ANCESTOR, and has been added to A. */
1600 /* (1) E doesn't exist in ANCESTOR, and has been added to B.
1602 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[4], pool
));
1603 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1604 SVN_ERR(svn_fs_make_file(txn_root
, "theta", pool
));
1605 SVN_ERR(svn_test__set_file_contents
1606 (txn_root
, "theta", "This is another file 'theta'.\n", pool
));
1607 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/theta", pool
));
1608 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1610 /* (1) E exists in ANCESTOR, but has been deleted from B. Can't
1611 occur, by assumption that E doesn't exist in ANCESTOR. */
1613 /* (3) E exists in both ANCESTOR and B. Can't occur, by assumption
1614 that E doesn't exist in ANCESTOR. */
1617 /* (4) E exists in ANCESTOR, but has been deleted from A */
1619 /* (1) E exists in ANCESTOR, but has been deleted from B. If
1620 neither delete was a result of a rename, then omit E from the
1621 merged tree. Otherwise, conflict. */
1622 /* ### cmpilato todo: the rename case isn't actually handled by
1623 merge yet, so we know we won't get a conflict here. */
1624 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1625 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1626 SVN_ERR(svn_fs_delete(txn_root
, "A/D/H", pool
));
1627 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1628 /*********************************************************************/
1630 /*********************************************************************/
1632 static svn_test__tree_entry_t expected_entries
[] = {
1633 /* path, contents (0 = dir) */
1634 { "theta", "This is the file 'theta'.\n" },
1636 { "A/mu", "This is the file 'mu'.\n" },
1638 { "A/B/lambda", "This is the file 'lambda'.\n" },
1640 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1641 { "A/B/E/beta", "This is the file 'beta'.\n" },
1644 { "A/C/kappa", "This is the file 'kappa'.\n" },
1646 { "A/D/gamma", "This is the file 'gamma'.\n" },
1648 { "A/D/G/pi", "This is the file 'pi'.\n" },
1649 { "A/D/G/rho", "This is the file 'rho'.\n" },
1650 { "A/D/G/tau", "This is the file 'tau'.\n" },
1652 { "A/D/I/delta", "This is the file 'delta'.\n" },
1653 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1655 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1656 SVN_ERR(svn_test__validate_tree(revision_root
,
1660 revisions
[revision_count
++] = after_rev
;
1662 /* Try deleting a file F inside a subtree S where S does not exist
1663 in the most recent revision, but does exist in the ancestor
1664 tree. This should conflict. */
1665 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1666 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1667 SVN_ERR(svn_fs_delete(txn_root
, "A/D/H/omega", pool
));
1668 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/D/H", pool
));
1669 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1671 /* E exists in both ANCESTOR and B ... */
1673 /* (1) but refers to different nodes. Conflict. */
1674 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1675 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1676 SVN_ERR(svn_fs_delete(txn_root
, "A/D/H", pool
));
1677 SVN_ERR(svn_fs_make_dir(txn_root
, "A/D/H", pool
));
1678 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1679 revisions
[revision_count
++] = after_rev
;
1681 /*********************************************************************/
1683 /*********************************************************************/
1685 /* Re-remove A/D/H because future tests expect it to be absent. */
1687 SVN_ERR(svn_fs_begin_txn
1688 (&txn
, fs
, revisions
[revision_count
- 1], pool
));
1689 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1690 SVN_ERR(svn_fs_delete(txn_root
, "A/D/H", pool
));
1691 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1692 revisions
[revision_count
++] = after_rev
;
1695 /*********************************************************************/
1696 /* REVISION 8 (looks exactly like revision 6, we hope) */
1697 /*********************************************************************/
1699 /* (1) but refers to different revisions of the same node.
1701 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1702 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1703 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/H/zeta", pool
));
1704 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/D/H", pool
));
1705 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1707 /* (1) and refers to the same node revision. Omit E from the
1708 merged tree. This is already tested in Merge-Test 3
1709 (A/D/H/chi, A/D/H/psi, e.g.), but we'll test it here again
1710 anyway. A little paranoia never hurt anyone. */
1711 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1712 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1713 SVN_ERR(svn_fs_delete(txn_root
, "A/mu", pool
)); /* unrelated change */
1714 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1716 /*********************************************************************/
1718 /*********************************************************************/
1720 static svn_test__tree_entry_t expected_entries
[] = {
1721 /* path, contents (0 = dir) */
1722 { "theta", "This is the file 'theta'.\n" },
1725 { "A/B/lambda", "This is the file 'lambda'.\n" },
1727 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1728 { "A/B/E/beta", "This is the file 'beta'.\n" },
1731 { "A/C/kappa", "This is the file 'kappa'.\n" },
1733 { "A/D/gamma", "This is the file 'gamma'.\n" },
1735 { "A/D/G/pi", "This is the file 'pi'.\n" },
1736 { "A/D/G/rho", "This is the file 'rho'.\n" },
1737 { "A/D/G/tau", "This is the file 'tau'.\n" },
1739 { "A/D/I/delta", "This is the file 'delta'.\n" },
1740 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1742 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1743 SVN_ERR(svn_test__validate_tree(revision_root
,
1747 revisions
[revision_count
++] = after_rev
;
1751 /* Preparation for upcoming tests.
1752 We make a new head revision, with A/mu restored, but containing
1753 slightly different contents than its first incarnation. */
1754 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[revision_count
-1], pool
));
1755 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1756 SVN_ERR(svn_fs_make_file(txn_root
, "A/mu", pool
));
1757 SVN_ERR(svn_test__set_file_contents
1758 (txn_root
, "A/mu", "A new file 'mu'.\n", pool
));
1759 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/G/xi", pool
));
1760 SVN_ERR(svn_test__set_file_contents
1761 (txn_root
, "A/D/G/xi", "This is the file 'xi'.\n", pool
));
1762 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1763 /*********************************************************************/
1765 /*********************************************************************/
1767 static svn_test__tree_entry_t expected_entries
[] = {
1768 /* path, contents (0 = dir) */
1769 { "theta", "This is the file 'theta'.\n" },
1771 { "A/mu", "A new file 'mu'.\n" },
1773 { "A/B/lambda", "This is the file 'lambda'.\n" },
1775 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1776 { "A/B/E/beta", "This is the file 'beta'.\n" },
1779 { "A/C/kappa", "This is the file 'kappa'.\n" },
1781 { "A/D/gamma", "This is the file 'gamma'.\n" },
1783 { "A/D/G/pi", "This is the file 'pi'.\n" },
1784 { "A/D/G/rho", "This is the file 'rho'.\n" },
1785 { "A/D/G/tau", "This is the file 'tau'.\n" },
1786 { "A/D/G/xi", "This is the file 'xi'.\n" },
1788 { "A/D/I/delta", "This is the file 'delta'.\n" },
1789 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1791 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1792 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1795 revisions
[revision_count
++] = after_rev
;
1797 /* (3) E exists in both ANCESTOR and A, but refers to different
1800 /* (1) E exists in both ANCESTOR and B, but refers to different
1801 nodes, and not all nodes are directories. Conflict. */
1803 /* ### kff todo: A/mu's contents will be exactly the same.
1804 If the fs ever starts optimizing this case, these tests may
1806 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1807 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1808 SVN_ERR(svn_fs_delete(txn_root
, "A/mu", pool
));
1809 SVN_ERR(svn_fs_make_file(txn_root
, "A/mu", pool
));
1810 SVN_ERR(svn_test__set_file_contents
1811 (txn_root
, "A/mu", "This is the file 'mu'.\n", pool
));
1812 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/mu", pool
));
1813 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1815 /* (1) E exists in both ANCESTOR and B, but refers to different
1816 revisions of the same node. Conflict. */
1817 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1818 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1819 SVN_ERR(svn_test__set_file_contents
1820 (txn_root
, "A/mu", "A change to file 'mu'.\n", pool
));
1821 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/mu", pool
));
1822 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1824 /* (1) E exists in both ANCESTOR and B, and refers to the same
1825 node revision. Replace E with A's node revision. */
1827 svn_stringbuf_t
*old_mu_contents
;
1828 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1829 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1830 SVN_ERR(svn_test__get_file_contents
1831 (txn_root
, "A/mu", &old_mu_contents
, pool
));
1832 if ((! old_mu_contents
) || (strcmp(old_mu_contents
->data
,
1833 "This is the file 'mu'.\n") != 0))
1835 return svn_error_create
1836 (SVN_ERR_FS_GENERAL
, NULL
,
1837 "got wrong contents from an old revision tree");
1839 SVN_ERR(svn_fs_make_file(txn_root
, "A/sigma", pool
));
1840 SVN_ERR(svn_test__set_file_contents
/* unrelated change */
1841 (txn_root
, "A/sigma", "This is the file 'sigma'.\n", pool
));
1842 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1843 /*********************************************************************/
1845 /*********************************************************************/
1847 static svn_test__tree_entry_t expected_entries
[] = {
1848 /* path, contents (0 = dir) */
1849 { "theta", "This is the file 'theta'.\n" },
1851 { "A/mu", "A new file 'mu'.\n" },
1852 { "A/sigma", "This is the file 'sigma'.\n" },
1854 { "A/B/lambda", "This is the file 'lambda'.\n" },
1856 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1857 { "A/B/E/beta", "This is the file 'beta'.\n" },
1860 { "A/C/kappa", "This is the file 'kappa'.\n" },
1862 { "A/D/gamma", "This is the file 'gamma'.\n" },
1864 { "A/D/G/pi", "This is the file 'pi'.\n" },
1865 { "A/D/G/rho", "This is the file 'rho'.\n" },
1866 { "A/D/G/tau", "This is the file 'tau'.\n" },
1867 { "A/D/G/xi", "This is the file 'xi'.\n" },
1869 { "A/D/I/delta", "This is the file 'delta'.\n" },
1870 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1872 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1873 SVN_ERR(svn_test__validate_tree(revision_root
,
1877 revisions
[revision_count
++] = after_rev
;
1881 /* Preparation for upcoming tests.
1882 We make a new head revision. There are two changes in the new
1883 revision: A/B/lambda has been modified. We will also use the
1884 recent addition of A/D/G/xi, treated as a modification to
1886 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[revision_count
-1], pool
));
1887 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1888 SVN_ERR(svn_test__set_file_contents
1889 (txn_root
, "A/B/lambda", "Change to file 'lambda'.\n", pool
));
1890 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1891 /*********************************************************************/
1893 /*********************************************************************/
1895 static svn_test__tree_entry_t expected_entries
[] = {
1896 /* path, contents (0 = dir) */
1897 { "theta", "This is the file 'theta'.\n" },
1899 { "A/mu", "A new file 'mu'.\n" },
1900 { "A/sigma", "This is the file 'sigma'.\n" },
1902 { "A/B/lambda", "Change to file 'lambda'.\n" },
1904 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1905 { "A/B/E/beta", "This is the file 'beta'.\n" },
1908 { "A/C/kappa", "This is the file 'kappa'.\n" },
1910 { "A/D/gamma", "This is the file 'gamma'.\n" },
1912 { "A/D/G/pi", "This is the file 'pi'.\n" },
1913 { "A/D/G/rho", "This is the file 'rho'.\n" },
1914 { "A/D/G/tau", "This is the file 'tau'.\n" },
1915 { "A/D/G/xi", "This is the file 'xi'.\n" },
1917 { "A/D/I/delta", "This is the file 'delta'.\n" },
1918 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1920 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1921 SVN_ERR(svn_test__validate_tree(revision_root
, expected_entries
,
1924 revisions
[revision_count
++] = after_rev
;
1926 /* (2) E exists in both ANCESTOR and A, but refers to different
1927 revisions of the same node. */
1929 /* (1a) E exists in both ANCESTOR and B, but refers to different
1930 revisions of the same file node. Conflict. */
1931 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1932 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1933 SVN_ERR(svn_test__set_file_contents
1934 (txn_root
, "A/B/lambda", "A different change to 'lambda'.\n",
1936 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/B/lambda", pool
));
1937 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1939 /* (1b) E exists in both ANCESTOR and B, but refers to different
1940 revisions of the same directory node. Merge A/E and B/E,
1941 recursively. Succeed, because no conflict beneath E. */
1942 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1943 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1944 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/G/nu", pool
));
1945 SVN_ERR(svn_test__set_file_contents
1946 (txn_root
, "A/D/G/nu", "This is the file 'nu'.\n", pool
));
1947 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
1948 /*********************************************************************/
1950 /*********************************************************************/
1952 static svn_test__tree_entry_t expected_entries
[] = {
1953 /* path, contents (0 = dir) */
1954 { "theta", "This is the file 'theta'.\n" },
1956 { "A/mu", "A new file 'mu'.\n" },
1957 { "A/sigma", "This is the file 'sigma'.\n" },
1959 { "A/B/lambda", "Change to file 'lambda'.\n" },
1961 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
1962 { "A/B/E/beta", "This is the file 'beta'.\n" },
1965 { "A/C/kappa", "This is the file 'kappa'.\n" },
1967 { "A/D/gamma", "This is the file 'gamma'.\n" },
1969 { "A/D/G/pi", "This is the file 'pi'.\n" },
1970 { "A/D/G/rho", "This is the file 'rho'.\n" },
1971 { "A/D/G/tau", "This is the file 'tau'.\n" },
1972 { "A/D/G/xi", "This is the file 'xi'.\n" },
1973 { "A/D/G/nu", "This is the file 'nu'.\n" },
1975 { "A/D/I/delta", "This is the file 'delta'.\n" },
1976 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
1978 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
1979 SVN_ERR(svn_test__validate_tree(revision_root
,
1983 revisions
[revision_count
++] = after_rev
;
1985 /* (1c) E exists in both ANCESTOR and B, but refers to different
1986 revisions of the same directory node. Merge A/E and B/E,
1987 recursively. Fail, because conflict beneath E. */
1988 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
1989 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
1990 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/G/xi", pool
));
1991 SVN_ERR(svn_test__set_file_contents
1992 (txn_root
, "A/D/G/xi", "This is a different file 'xi'.\n", pool
));
1993 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/A/D/G/xi", pool
));
1994 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
1996 /* (1) E exists in both ANCESTOR and B, and refers to the same node
1997 revision. Replace E with A's node revision. */
1999 svn_stringbuf_t
*old_lambda_ctnts
;
2000 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
2001 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2002 SVN_ERR(svn_test__get_file_contents
2003 (txn_root
, "A/B/lambda", &old_lambda_ctnts
, pool
));
2004 if ((! old_lambda_ctnts
)
2005 || (strcmp(old_lambda_ctnts
->data
,
2006 "This is the file 'lambda'.\n") != 0))
2008 return svn_error_create
2009 (SVN_ERR_FS_GENERAL
, NULL
,
2010 "got wrong contents from an old revision tree");
2012 SVN_ERR(svn_test__set_file_contents
2013 (txn_root
, "A/D/G/rho",
2014 "This is an irrelevant change to 'rho'.\n", pool
));
2015 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2016 /*********************************************************************/
2018 /*********************************************************************/
2020 static svn_test__tree_entry_t expected_entries
[] = {
2021 /* path, contents (0 = dir) */
2022 { "theta", "This is the file 'theta'.\n" },
2024 { "A/mu", "A new file 'mu'.\n" },
2025 { "A/sigma", "This is the file 'sigma'.\n" },
2027 { "A/B/lambda", "Change to file 'lambda'.\n" },
2029 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2030 { "A/B/E/beta", "This is the file 'beta'.\n" },
2033 { "A/C/kappa", "This is the file 'kappa'.\n" },
2035 { "A/D/gamma", "This is the file 'gamma'.\n" },
2037 { "A/D/G/pi", "This is the file 'pi'.\n" },
2038 { "A/D/G/rho", "This is an irrelevant change to 'rho'.\n" },
2039 { "A/D/G/tau", "This is the file 'tau'.\n" },
2040 { "A/D/G/xi", "This is the file 'xi'.\n" },
2041 { "A/D/G/nu", "This is the file 'nu'.\n"},
2043 { "A/D/I/delta", "This is the file 'delta'.\n" },
2044 { "A/D/I/epsilon", "This is the file 'epsilon'.\n" }
2046 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
, after_rev
, pool
));
2047 SVN_ERR(svn_test__validate_tree(revision_root
,
2051 revisions
[revision_count
++] = after_rev
;
2055 /* (1) E exists in both ANCESTOR and A, and refers to the same node
2058 /* (1) E exists in both ANCESTOR and B, and refers to the same
2059 node revision. Nothing has happened to ANCESTOR/E, so no
2060 change is necessary. */
2062 /* This has now been tested about fifty-four trillion times. We
2063 don't need to test it again here. */
2066 /* E exists in ANCESTOR, but has been deleted from A. E exists in
2067 both ANCESTOR and B but refers to different revisions of the same
2069 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, revisions
[1], pool
));
2070 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2071 SVN_ERR(svn_test__set_file_contents
2072 (txn_root
, "iota", "New contents for 'iota'.\n", pool
));
2073 SVN_ERR(test_commit_txn(&after_rev
, txn
, "/iota", pool
));
2074 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
2076 return SVN_NO_ERROR
;
2080 static svn_error_t
*
2081 copy_test(const char **msg
,
2082 svn_boolean_t msg_only
,
2083 svn_test_opts_t
*opts
,
2088 svn_fs_root_t
*txn_root
, *rev_root
;
2089 svn_revnum_t after_rev
;
2091 *msg
= "copying and tracking copy history";
2094 return SVN_NO_ERROR
;
2096 /* Prepare a filesystem. */
2097 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-copy-test",
2098 opts
->fs_type
, pool
));
2100 /* In first txn, create and commit the greek tree. */
2101 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2102 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2103 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2104 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2106 /* In second txn, copy the file A/D/G/pi into the subtree A/D/H as
2107 pi2. Change that file's contents to state its new name. Along
2108 the way, test that the copy history was preserved both during the
2109 transaction and after the commit. */
2111 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2112 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, pool
));
2113 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2114 SVN_ERR(svn_fs_copy(rev_root
, "A/D/G/pi",
2115 txn_root
, "A/D/H/pi2",
2117 { /* Check that copy history was preserved. */
2121 SVN_ERR(svn_fs_copied_from(&rev
, &path
, txn_root
,
2122 "A/D/H/pi2", pool
));
2124 if (rev
!= after_rev
)
2125 return svn_error_create
2126 (SVN_ERR_FS_GENERAL
, NULL
,
2127 "pre-commit copy history not preserved (rev lost) for A/D/H/pi2");
2129 if (strcmp(path
, "/A/D/G/pi") != 0)
2130 return svn_error_create
2131 (SVN_ERR_FS_GENERAL
, NULL
,
2132 "pre-commit copy history not preserved (path lost) for A/D/H/pi2");
2134 SVN_ERR(svn_test__set_file_contents
2135 (txn_root
, "A/D/H/pi2", "This is the file 'pi2'.\n", pool
));
2136 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2138 { /* Check that copy history is still preserved _after_ the commit. */
2139 svn_fs_root_t
*root
;
2143 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2144 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/D/H/pi2", pool
));
2146 if (rev
!= (after_rev
- 1))
2147 return svn_error_create
2148 (SVN_ERR_FS_GENERAL
, NULL
,
2149 "post-commit copy history wrong (rev) for A/D/H/pi2");
2151 if (strcmp(path
, "/A/D/G/pi") != 0)
2152 return svn_error_create
2153 (SVN_ERR_FS_GENERAL
, NULL
,
2154 "post-commit copy history wrong (path) for A/D/H/pi2");
2157 /* Let's copy the copy we just made, to make sure copy history gets
2158 chained correctly. */
2159 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2160 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, pool
));
2161 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2162 SVN_ERR(svn_fs_copy(rev_root
, "A/D/H/pi2", txn_root
, "A/D/H/pi3", pool
));
2163 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2164 { /* Check the copy history. */
2165 svn_fs_root_t
*root
;
2169 /* Check that the original copy still has its old history. */
2170 SVN_ERR(svn_fs_revision_root(&root
, fs
, (after_rev
- 1), pool
));
2171 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/D/H/pi2", pool
));
2173 if (rev
!= (after_rev
- 2))
2174 return svn_error_create
2175 (SVN_ERR_FS_GENERAL
, NULL
,
2176 "first copy history wrong (rev) for A/D/H/pi2");
2178 if (strcmp(path
, "/A/D/G/pi") != 0)
2179 return svn_error_create
2180 (SVN_ERR_FS_GENERAL
, NULL
,
2181 "first copy history wrong (path) for A/D/H/pi2");
2183 /* Check that the copy of the copy has the right history. */
2184 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2185 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/D/H/pi3", pool
));
2187 if (rev
!= (after_rev
- 1))
2188 return svn_error_create
2189 (SVN_ERR_FS_GENERAL
, NULL
,
2190 "second copy history wrong (rev) for A/D/H/pi3");
2192 if (strcmp(path
, "/A/D/H/pi2") != 0)
2193 return svn_error_create
2194 (SVN_ERR_FS_GENERAL
, NULL
,
2195 "second copy history wrong (path) for A/D/H/pi3");
2198 /* Commit a regular change to a copy, make sure the copy history
2200 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2201 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, pool
));
2202 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2203 SVN_ERR(svn_test__set_file_contents
2204 (txn_root
, "A/D/H/pi3", "This is the file 'pi3'.\n", pool
));
2205 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2206 { /* Check the copy history. */
2207 svn_fs_root_t
*root
;
2211 /* Check that the copy still has its history. */
2212 SVN_ERR(svn_fs_revision_root(&root
, fs
, (after_rev
- 1), pool
));
2213 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/D/H/pi3", pool
));
2215 if (rev
!= (after_rev
- 2))
2216 return svn_error_create
2217 (SVN_ERR_FS_GENERAL
, NULL
,
2218 "copy history wrong (rev) for A/D/H/pi3");
2220 if (strcmp(path
, "/A/D/H/pi2") != 0)
2221 return svn_error_create
2222 (SVN_ERR_FS_GENERAL
, NULL
,
2223 "copy history wrong (path) for A/D/H/pi3");
2225 /* Check that the next revision after the copy has no copy history. */
2226 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2227 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/D/H/pi3", pool
));
2229 if (rev
!= SVN_INVALID_REVNUM
)
2230 return svn_error_create
2231 (SVN_ERR_FS_GENERAL
, NULL
,
2232 "copy history wrong (rev) for A/D/H/pi3");
2235 return svn_error_create
2236 (SVN_ERR_FS_GENERAL
, NULL
,
2237 "copy history wrong (path) for A/D/H/pi3");
2240 /* Then, as if that wasn't fun enough, copy the whole subtree A/D/H
2241 into the root directory as H2! */
2242 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2243 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, pool
));
2244 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2245 SVN_ERR(svn_fs_copy(rev_root
, "A/D/H", txn_root
, "H2", pool
));
2246 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2247 { /* Check the copy history. */
2248 svn_fs_root_t
*root
;
2252 /* Check that the top of the copy has history. */
2253 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2254 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "H2", pool
));
2256 if (rev
!= (after_rev
- 1))
2257 return svn_error_create
2258 (SVN_ERR_FS_GENERAL
, NULL
,
2259 "copy history wrong (rev) for H2");
2261 if (strcmp(path
, "/A/D/H") != 0)
2262 return svn_error_create
2263 (SVN_ERR_FS_GENERAL
, NULL
,
2264 "copy history wrong (path) for H2");
2266 /* Check that a random file under H2 reports no copy history. */
2267 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "H2/omega", pool
));
2269 if (rev
!= SVN_INVALID_REVNUM
)
2270 return svn_error_create
2271 (SVN_ERR_FS_GENERAL
, NULL
,
2272 "copy history wrong (rev) for H2/omega");
2275 return svn_error_create
2276 (SVN_ERR_FS_GENERAL
, NULL
,
2277 "copy history wrong (path) for H2/omega");
2279 /* Note that H2/pi2 still has copy history, though. See the doc
2280 string for svn_fs_copied_from() for more on this. */
2283 /* Let's live dangerously. What happens if we copy a path into one
2284 of its own children. Looping filesystem? Cyclic ancestry?
2285 Another West Virginia family tree with no branches? We certainly
2286 hope that's not the case. */
2287 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2288 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, pool
));
2289 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2290 SVN_ERR(svn_fs_copy(rev_root
, "A/B", txn_root
, "A/B/E/B", pool
));
2291 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, pool
));
2292 { /* Check the copy history. */
2293 svn_fs_root_t
*root
;
2297 /* Check that the copy has history. */
2298 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2299 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/B/E/B", pool
));
2301 if (rev
!= (after_rev
- 1))
2302 return svn_error_create
2303 (SVN_ERR_FS_GENERAL
, NULL
,
2304 "copy history wrong (rev) for A/B/E/B");
2306 if (strcmp(path
, "/A/B") != 0)
2307 return svn_error_create
2308 (SVN_ERR_FS_GENERAL
, NULL
,
2309 "copy history wrong (path) for A/B/E/B");
2311 /* Check that the original does not have copy history. */
2312 SVN_ERR(svn_fs_revision_root(&root
, fs
, after_rev
, pool
));
2313 SVN_ERR(svn_fs_copied_from(&rev
, &path
, root
, "A/B", pool
));
2315 if (rev
!= SVN_INVALID_REVNUM
)
2316 return svn_error_create
2317 (SVN_ERR_FS_GENERAL
, NULL
,
2318 "copy history wrong (rev) for A/B");
2321 return svn_error_create
2322 (SVN_ERR_FS_GENERAL
, NULL
,
2323 "copy history wrong (path) for A/B");
2326 /* After all these changes, let's see if the filesystem looks as we
2327 would expect it to. */
2329 static svn_test__tree_entry_t expected_entries
[] = {
2330 /* path, contents (0 = dir) */
2331 { "iota", "This is the file 'iota'.\n" },
2333 { "H2/chi", "This is the file 'chi'.\n" },
2334 { "H2/pi2", "This is the file 'pi2'.\n" },
2335 { "H2/pi3", "This is the file 'pi3'.\n" },
2336 { "H2/psi", "This is the file 'psi'.\n" },
2337 { "H2/omega", "This is the file 'omega'.\n" },
2339 { "A/mu", "This is the file 'mu'.\n" },
2341 { "A/B/lambda", "This is the file 'lambda'.\n" },
2343 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2344 { "A/B/E/beta", "This is the file 'beta'.\n" },
2346 { "A/B/E/B/lambda", "This is the file 'lambda'.\n" },
2348 { "A/B/E/B/E/alpha", "This is the file 'alpha'.\n" },
2349 { "A/B/E/B/E/beta", "This is the file 'beta'.\n" },
2354 { "A/D/gamma", "This is the file 'gamma'.\n" },
2356 { "A/D/G/pi", "This is the file 'pi'.\n" },
2357 { "A/D/G/rho", "This is the file 'rho'.\n" },
2358 { "A/D/G/tau", "This is the file 'tau'.\n" },
2360 { "A/D/H/chi", "This is the file 'chi'.\n" },
2361 { "A/D/H/pi2", "This is the file 'pi2'.\n" },
2362 { "A/D/H/pi3", "This is the file 'pi3'.\n" },
2363 { "A/D/H/psi", "This is the file 'psi'.\n" },
2364 { "A/D/H/omega", "This is the file 'omega'.\n" }
2366 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, pool
));
2367 SVN_ERR(svn_test__validate_tree(rev_root
, expected_entries
,
2371 return SVN_NO_ERROR
;
2375 /* This tests deleting of mutable nodes. We build a tree in a
2376 * transaction, then try to delete various items in the tree. We
2377 * never commit the tree, so every entry being deleted points to a
2380 * ### todo: this test was written before commits worked. It might
2381 * now be worthwhile to combine it with delete().
2383 static svn_error_t
*
2384 delete_mutables(const char **msg
,
2385 svn_boolean_t msg_only
,
2386 svn_test_opts_t
*opts
,
2391 svn_fs_root_t
*txn_root
;
2394 *msg
= "delete mutable nodes from directories";
2397 return SVN_NO_ERROR
;
2399 /* Prepare a txn to receive the greek tree. */
2400 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-del-from-dir",
2401 opts
->fs_type
, pool
));
2402 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2403 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2405 /* Create the greek tree. */
2406 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2408 /* Baby, it's time to test like you've never tested before. We do
2409 * the following, in this order:
2411 * 1. Delete a single file somewhere, succeed.
2412 * 2. Delete two files of three, then make sure the third remains.
2413 * 3. Delete the third and last file.
2414 * 4. Try again to delete the dir, succeed.
2415 * 5. Delete one of the natively empty dirs, succeed.
2416 * 6. Try to delete root, fail.
2417 * 7. Try to delete a top-level file, succeed.
2419 * Specifically, that's:
2421 * 1. Delete A/D/gamma.
2422 * 2. Delete A/D/G/pi, A/D/G/rho.
2423 * 3. Delete A/D/G/tau.
2424 * 4. Try again to delete A/D/G, succeed.
2426 * 6. Try to delete /, fail.
2427 * 7. Try to delete iota, succeed.
2429 * Before and after each deletion or attempted deletion, we probe
2430 * the affected directory, to make sure everything is as it should
2436 const svn_fs_id_t
*gamma_id
;
2437 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "A/D/gamma", pool
));
2438 SVN_ERR(check_entry_present(txn_root
, "A/D", "gamma", pool
));
2439 SVN_ERR(svn_fs_delete(txn_root
, "A/D/gamma", pool
));
2440 SVN_ERR(check_entry_absent(txn_root
, "A/D", "gamma", pool
));
2445 const svn_fs_id_t
*pi_id
, *rho_id
, *tau_id
;
2446 SVN_ERR(svn_fs_node_id(&pi_id
, txn_root
, "A/D/G/pi", pool
));
2447 SVN_ERR(svn_fs_node_id(&rho_id
, txn_root
, "A/D/G/rho", pool
));
2448 SVN_ERR(svn_fs_node_id(&tau_id
, txn_root
, "A/D/G/tau", pool
));
2449 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "pi", pool
));
2450 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "rho", pool
));
2451 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2452 SVN_ERR(svn_fs_delete(txn_root
, "A/D/G/pi", pool
));
2453 SVN_ERR(check_entry_absent(txn_root
, "A/D/G", "pi", pool
));
2454 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "rho", pool
));
2455 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2456 SVN_ERR(svn_fs_delete(txn_root
, "A/D/G/rho", pool
));
2457 SVN_ERR(check_entry_absent(txn_root
, "A/D/G", "pi", pool
));
2458 SVN_ERR(check_entry_absent(txn_root
, "A/D/G", "rho", pool
));
2459 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2464 const svn_fs_id_t
*tau_id
;
2465 SVN_ERR(svn_fs_node_id(&tau_id
, txn_root
, "A/D/G/tau", pool
));
2466 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2467 SVN_ERR(svn_fs_delete(txn_root
, "A/D/G/tau", pool
));
2468 SVN_ERR(check_entry_absent(txn_root
, "A/D/G", "tau", pool
));
2473 const svn_fs_id_t
*G_id
;
2474 SVN_ERR(svn_fs_node_id(&G_id
, txn_root
, "A/D/G", pool
));
2475 SVN_ERR(check_entry_present(txn_root
, "A/D", "G", pool
));
2476 SVN_ERR(svn_fs_delete(txn_root
, "A/D/G", pool
)); /* succeed */
2477 SVN_ERR(check_entry_absent(txn_root
, "A/D", "G", pool
));
2482 const svn_fs_id_t
*C_id
;
2483 SVN_ERR(svn_fs_node_id(&C_id
, txn_root
, "A/C", pool
));
2484 SVN_ERR(check_entry_present(txn_root
, "A", "C", pool
));
2485 SVN_ERR(svn_fs_delete(txn_root
, "A/C", pool
));
2486 SVN_ERR(check_entry_absent(txn_root
, "A", "C", pool
));
2491 const svn_fs_id_t
*root_id
;
2492 SVN_ERR(svn_fs_node_id(&root_id
, txn_root
, "", pool
));
2494 err
= svn_fs_delete(txn_root
, "", pool
);
2496 if (err
&& (err
->apr_err
!= SVN_ERR_FS_ROOT_DIR
))
2498 return svn_error_createf
2499 (SVN_ERR_FS_GENERAL
, NULL
,
2500 "deleting root directory got wrong error");
2504 return svn_error_createf
2505 (SVN_ERR_FS_GENERAL
, NULL
,
2506 "deleting root directory failed to get error");
2508 svn_error_clear(err
);
2514 const svn_fs_id_t
*iota_id
;
2515 SVN_ERR(svn_fs_node_id(&iota_id
, txn_root
, "iota", pool
));
2516 SVN_ERR(check_entry_present(txn_root
, "", "iota", pool
));
2517 SVN_ERR(svn_fs_delete(txn_root
, "iota", pool
));
2518 SVN_ERR(check_entry_absent(txn_root
, "", "iota", pool
));
2521 return SVN_NO_ERROR
;
2525 /* This tests deleting in general.
2527 * ### todo: this test was written after (and independently of)
2528 * delete_mutables(). It might be worthwhile to combine them.
2530 static svn_error_t
*
2531 delete(const char **msg
,
2532 svn_boolean_t msg_only
,
2533 svn_test_opts_t
*opts
,
2538 svn_fs_root_t
*txn_root
;
2539 svn_revnum_t new_rev
;
2541 *msg
= "delete nodes tree";
2544 return SVN_NO_ERROR
;
2546 /* This function tests 5 cases:
2548 * 1. Delete mutable file.
2549 * 2. Delete mutable directory.
2550 * 3. Delete mutable directory with immutable nodes.
2551 * 4. Delete immutable file.
2552 * 5. Delete immutable directory.
2555 /* Prepare a txn to receive the greek tree. */
2556 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-del-tree",
2557 opts
->fs_type
, pool
));
2558 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2559 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2561 /* Create the greek tree. */
2562 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2564 /* 1. Delete mutable file. */
2566 const svn_fs_id_t
*iota_id
, *gamma_id
;
2567 static svn_test__tree_entry_t expected_entries
[] = {
2568 /* path, contents (0 = dir) */
2570 { "A/mu", "This is the file 'mu'.\n" },
2572 { "A/B/lambda", "This is the file 'lambda'.\n" },
2574 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2575 { "A/B/E/beta", "This is the file 'beta'.\n" },
2580 { "A/D/G/pi", "This is the file 'pi'.\n" },
2581 { "A/D/G/rho", "This is the file 'rho'.\n" },
2582 { "A/D/G/tau", "This is the file 'tau'.\n" },
2584 { "A/D/H/chi", "This is the file 'chi'.\n" },
2585 { "A/D/H/psi", "This is the file 'psi'.\n" },
2586 { "A/D/H/omega", "This is the file 'omega'.\n" }
2589 /* Check nodes revision ID is gone. */
2590 SVN_ERR(svn_fs_node_id(&iota_id
, txn_root
, "iota", pool
));
2591 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "A/D/gamma", pool
));
2593 SVN_ERR(check_entry_present(txn_root
, "", "iota", pool
));
2595 /* Try deleting mutable files. */
2596 SVN_ERR(svn_fs_delete(txn_root
, "iota", pool
));
2597 SVN_ERR(svn_fs_delete(txn_root
, "A/D/gamma", pool
));
2598 SVN_ERR(check_entry_absent(txn_root
, "", "iota", pool
));
2599 SVN_ERR(check_entry_absent(txn_root
, "A/D", "gamma", pool
));
2601 /* Validate the tree. */
2602 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 18, pool
));
2604 /* Abort transaction. */
2605 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
2607 /* 2. Delete mutable directory. */
2609 /* Prepare a txn to receive the greek tree. */
2610 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2611 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2613 /* Create the greek tree. */
2614 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2617 const svn_fs_id_t
*A_id
, *mu_id
, *B_id
, *lambda_id
, *E_id
, *alpha_id
,
2618 *beta_id
, *F_id
, *C_id
, *D_id
, *gamma_id
, *H_id
, *chi_id
,
2619 *psi_id
, *omega_id
, *G_id
, *pi_id
, *rho_id
, *tau_id
;
2621 /* Check nodes revision ID is gone. */
2622 SVN_ERR(svn_fs_node_id(&A_id
, txn_root
, "/A", pool
));
2623 SVN_ERR(check_entry_present(txn_root
, "", "A", pool
));
2624 SVN_ERR(svn_fs_node_id(&mu_id
, txn_root
, "/A/mu", pool
));
2625 SVN_ERR(check_entry_present(txn_root
, "A", "mu", pool
));
2626 SVN_ERR(svn_fs_node_id(&B_id
, txn_root
, "/A/B", pool
));
2627 SVN_ERR(check_entry_present(txn_root
, "A", "B", pool
));
2628 SVN_ERR(svn_fs_node_id(&lambda_id
, txn_root
, "/A/B/lambda", pool
));
2629 SVN_ERR(check_entry_present(txn_root
, "A/B", "lambda", pool
));
2630 SVN_ERR(svn_fs_node_id(&E_id
, txn_root
, "/A/B/E", pool
));
2631 SVN_ERR(check_entry_present(txn_root
, "A/B", "E", pool
));
2632 SVN_ERR(svn_fs_node_id(&alpha_id
, txn_root
, "/A/B/E/alpha", pool
));
2633 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "alpha", pool
));
2634 SVN_ERR(svn_fs_node_id(&beta_id
, txn_root
, "/A/B/E/beta", pool
));
2635 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "beta", pool
));
2636 SVN_ERR(svn_fs_node_id(&F_id
, txn_root
, "/A/B/F", pool
));
2637 SVN_ERR(check_entry_present(txn_root
, "A/B", "F", pool
));
2638 SVN_ERR(svn_fs_node_id(&C_id
, txn_root
, "/A/C", pool
));
2639 SVN_ERR(check_entry_present(txn_root
, "A", "C", pool
));
2640 SVN_ERR(svn_fs_node_id(&D_id
, txn_root
, "/A/D", pool
));
2641 SVN_ERR(check_entry_present(txn_root
, "A", "D", pool
));
2642 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "/A/D/gamma", pool
));
2643 SVN_ERR(check_entry_present(txn_root
, "A/D", "gamma", pool
));
2644 SVN_ERR(svn_fs_node_id(&H_id
, txn_root
, "/A/D/H", pool
));
2645 SVN_ERR(check_entry_present(txn_root
, "A/D", "H", pool
));
2646 SVN_ERR(svn_fs_node_id(&chi_id
, txn_root
, "/A/D/H/chi", pool
));
2647 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "chi", pool
));
2648 SVN_ERR(svn_fs_node_id(&psi_id
, txn_root
, "/A/D/H/psi", pool
));
2649 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "psi", pool
));
2650 SVN_ERR(svn_fs_node_id(&omega_id
, txn_root
, "/A/D/H/omega", pool
));
2651 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "omega", pool
));
2652 SVN_ERR(svn_fs_node_id(&G_id
, txn_root
, "/A/D/G", pool
));
2653 SVN_ERR(check_entry_present(txn_root
, "A/D", "G", pool
));
2654 SVN_ERR(svn_fs_node_id(&pi_id
, txn_root
, "/A/D/G/pi", pool
));
2655 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "pi", pool
));
2656 SVN_ERR(svn_fs_node_id(&rho_id
, txn_root
, "/A/D/G/rho", pool
));
2657 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "rho", pool
));
2658 SVN_ERR(svn_fs_node_id(&tau_id
, txn_root
, "/A/D/G/tau", pool
));
2659 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2661 /* Try deleting a mutable empty dir. */
2662 SVN_ERR(svn_fs_delete(txn_root
, "A/C", pool
));
2663 SVN_ERR(svn_fs_delete(txn_root
, "A/B/F", pool
));
2664 SVN_ERR(check_entry_absent(txn_root
, "A", "C", pool
));
2665 SVN_ERR(check_entry_absent(txn_root
, "A/B", "F", pool
));
2667 /* Now delete a mutable non-empty dir. */
2668 SVN_ERR(svn_fs_delete(txn_root
, "A", pool
));
2669 SVN_ERR(check_entry_absent(txn_root
, "", "A", pool
));
2671 /* Validate the tree. */
2673 static svn_test__tree_entry_t expected_entries
[] = {
2674 /* path, contents (0 = dir) */
2675 { "iota", "This is the file 'iota'.\n" } };
2676 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 1, pool
));
2680 /* Abort transaction. */
2681 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
2683 /* 3. Delete mutable directory with immutable nodes. */
2685 /* Prepare a txn to receive the greek tree. */
2686 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2687 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2689 /* Create the greek tree. */
2690 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2692 /* Commit the greek tree. */
2693 SVN_ERR(svn_fs_commit_txn(NULL
, &new_rev
, txn
, pool
));
2695 /* Create new transaction. */
2696 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, new_rev
, pool
));
2697 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2700 const svn_fs_id_t
*A_id
, *mu_id
, *B_id
, *lambda_id
, *E_id
, *alpha_id
,
2701 *beta_id
, *F_id
, *C_id
, *D_id
, *gamma_id
, *H_id
, *chi_id
,
2702 *psi_id
, *omega_id
, *G_id
, *pi_id
, *rho_id
, *tau_id
, *sigma_id
;
2704 /* Create A/D/G/sigma. This makes all components of A/D/G
2706 SVN_ERR(svn_fs_make_file(txn_root
, "A/D/G/sigma", pool
));
2707 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/G/sigma",
2708 "This is another file 'sigma'.\n", pool
));
2710 /* Check that mutable node-revision-IDs are removed and immutable
2711 ones still exist. */
2712 SVN_ERR(svn_fs_node_id(&A_id
, txn_root
, "/A", pool
));
2713 SVN_ERR(check_entry_present(txn_root
, "", "A", pool
));
2714 SVN_ERR(svn_fs_node_id(&mu_id
, txn_root
, "/A/mu", pool
));
2715 SVN_ERR(check_entry_present(txn_root
, "A", "mu", pool
));
2716 SVN_ERR(svn_fs_node_id(&B_id
, txn_root
, "/A/B", pool
));
2717 SVN_ERR(check_entry_present(txn_root
, "A", "B", pool
));
2718 SVN_ERR(svn_fs_node_id(&lambda_id
, txn_root
, "/A/B/lambda", pool
));
2719 SVN_ERR(check_entry_present(txn_root
, "A/B", "lambda", pool
));
2720 SVN_ERR(svn_fs_node_id(&E_id
, txn_root
, "/A/B/E", pool
));
2721 SVN_ERR(check_entry_present(txn_root
, "A/B", "E", pool
));
2722 SVN_ERR(svn_fs_node_id(&alpha_id
, txn_root
, "/A/B/E/alpha", pool
));
2723 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "alpha", pool
));
2724 SVN_ERR(svn_fs_node_id(&beta_id
, txn_root
, "/A/B/E/beta", pool
));
2725 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "beta", pool
));
2726 SVN_ERR(svn_fs_node_id(&F_id
, txn_root
, "/A/B/F", pool
));
2727 SVN_ERR(check_entry_present(txn_root
, "A/B", "F", pool
));
2728 SVN_ERR(svn_fs_node_id(&C_id
, txn_root
, "/A/C", pool
));
2729 SVN_ERR(check_entry_present(txn_root
, "A", "C", pool
));
2730 SVN_ERR(svn_fs_node_id(&D_id
, txn_root
, "/A/D", pool
));
2731 SVN_ERR(check_entry_present(txn_root
, "A", "D", pool
));
2732 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "/A/D/gamma", pool
));
2733 SVN_ERR(check_entry_present(txn_root
, "A/D", "gamma", pool
));
2734 SVN_ERR(svn_fs_node_id(&H_id
, txn_root
, "/A/D/H", pool
));
2735 SVN_ERR(check_entry_present(txn_root
, "A/D", "H", pool
));
2736 SVN_ERR(svn_fs_node_id(&chi_id
, txn_root
, "/A/D/H/chi", pool
));
2737 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "chi", pool
));
2738 SVN_ERR(svn_fs_node_id(&psi_id
, txn_root
, "/A/D/H/psi", pool
));
2739 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "psi", pool
));
2740 SVN_ERR(svn_fs_node_id(&omega_id
, txn_root
, "/A/D/H/omega", pool
));
2741 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "omega", pool
));
2742 SVN_ERR(svn_fs_node_id(&G_id
, txn_root
, "/A/D/G", pool
));
2743 SVN_ERR(check_entry_present(txn_root
, "A/D", "G", pool
));
2744 SVN_ERR(svn_fs_node_id(&pi_id
, txn_root
, "/A/D/G/pi", pool
));
2745 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "pi", pool
));
2746 SVN_ERR(svn_fs_node_id(&rho_id
, txn_root
, "/A/D/G/rho", pool
));
2747 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "rho", pool
));
2748 SVN_ERR(svn_fs_node_id(&tau_id
, txn_root
, "/A/D/G/tau", pool
));
2749 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2750 SVN_ERR(svn_fs_node_id(&sigma_id
, txn_root
, "/A/D/G/sigma", pool
));
2751 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "sigma", pool
));
2754 SVN_ERR(svn_fs_delete(txn_root
, "A", pool
));
2755 SVN_ERR(check_entry_absent(txn_root
, "", "A", pool
));
2757 /* Validate the tree. */
2759 static svn_test__tree_entry_t expected_entries
[] = {
2760 /* path, contents (0 = dir) */
2761 { "iota", "This is the file 'iota'.\n" }
2764 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 1, pool
));
2768 /* Abort transaction. */
2769 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
2771 /* 4. Delete immutable file. */
2773 /* Create new transaction. */
2774 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, new_rev
, pool
));
2775 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2778 const svn_fs_id_t
*iota_id
, *gamma_id
;
2780 /* Check nodes revision ID is present. */
2781 SVN_ERR(svn_fs_node_id(&iota_id
, txn_root
, "iota", pool
));
2782 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "A/D/gamma", pool
));
2783 SVN_ERR(check_entry_present(txn_root
, "", "iota", pool
));
2784 SVN_ERR(check_entry_present(txn_root
, "A/D", "gamma", pool
));
2786 /* Delete some files. */
2787 SVN_ERR(svn_fs_delete(txn_root
, "iota", pool
));
2788 SVN_ERR(svn_fs_delete(txn_root
, "A/D/gamma", pool
));
2789 SVN_ERR(check_entry_absent(txn_root
, "", "iota", pool
));
2790 SVN_ERR(check_entry_absent(txn_root
, "A/D", "iota", pool
));
2792 /* Validate the tree. */
2794 static svn_test__tree_entry_t expected_entries
[] = {
2795 /* path, contents (0 = dir) */
2797 { "A/mu", "This is the file 'mu'.\n" },
2799 { "A/B/lambda", "This is the file 'lambda'.\n" },
2801 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
2802 { "A/B/E/beta", "This is the file 'beta'.\n" },
2807 { "A/D/G/pi", "This is the file 'pi'.\n" },
2808 { "A/D/G/rho", "This is the file 'rho'.\n" },
2809 { "A/D/G/tau", "This is the file 'tau'.\n" },
2811 { "A/D/H/chi", "This is the file 'chi'.\n" },
2812 { "A/D/H/psi", "This is the file 'psi'.\n" },
2813 { "A/D/H/omega", "This is the file 'omega'.\n" }
2815 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 18, pool
));
2819 /* Abort transaction. */
2820 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
2822 /* 5. Delete immutable directory. */
2824 /* Create new transaction. */
2825 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, new_rev
, pool
));
2826 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2829 const svn_fs_id_t
*A_id
, *mu_id
, *B_id
, *lambda_id
, *E_id
, *alpha_id
,
2830 *beta_id
, *F_id
, *C_id
, *D_id
, *gamma_id
, *H_id
, *chi_id
,
2831 *psi_id
, *omega_id
, *G_id
, *pi_id
, *rho_id
, *tau_id
;
2833 /* Check nodes revision ID is present. */
2834 SVN_ERR(svn_fs_node_id(&A_id
, txn_root
, "/A", pool
));
2835 SVN_ERR(check_entry_present(txn_root
, "", "A", pool
));
2836 SVN_ERR(svn_fs_node_id(&mu_id
, txn_root
, "/A/mu", pool
));
2837 SVN_ERR(check_entry_present(txn_root
, "A", "mu", pool
));
2838 SVN_ERR(svn_fs_node_id(&B_id
, txn_root
, "/A/B", pool
));
2839 SVN_ERR(check_entry_present(txn_root
, "A", "B", pool
));
2840 SVN_ERR(svn_fs_node_id(&lambda_id
, txn_root
, "/A/B/lambda", pool
));
2841 SVN_ERR(check_entry_present(txn_root
, "A/B", "lambda", pool
));
2842 SVN_ERR(svn_fs_node_id(&E_id
, txn_root
, "/A/B/E", pool
));
2843 SVN_ERR(check_entry_present(txn_root
, "A/B", "E", pool
));
2844 SVN_ERR(svn_fs_node_id(&alpha_id
, txn_root
, "/A/B/E/alpha", pool
));
2845 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "alpha", pool
));
2846 SVN_ERR(svn_fs_node_id(&beta_id
, txn_root
, "/A/B/E/beta", pool
));
2847 SVN_ERR(check_entry_present(txn_root
, "A/B/E", "beta", pool
));
2848 SVN_ERR(svn_fs_node_id(&F_id
, txn_root
, "/A/B/F", pool
));
2849 SVN_ERR(check_entry_present(txn_root
, "A/B", "F", pool
));
2850 SVN_ERR(svn_fs_node_id(&C_id
, txn_root
, "/A/C", pool
));
2851 SVN_ERR(check_entry_present(txn_root
, "A", "C", pool
));
2852 SVN_ERR(svn_fs_node_id(&D_id
, txn_root
, "/A/D", pool
));
2853 SVN_ERR(check_entry_present(txn_root
, "A", "D", pool
));
2854 SVN_ERR(svn_fs_node_id(&gamma_id
, txn_root
, "/A/D/gamma", pool
));
2855 SVN_ERR(check_entry_present(txn_root
, "A/D", "gamma", pool
));
2856 SVN_ERR(svn_fs_node_id(&H_id
, txn_root
, "/A/D/H", pool
));
2857 SVN_ERR(check_entry_present(txn_root
, "A/D", "H", pool
));
2858 SVN_ERR(svn_fs_node_id(&chi_id
, txn_root
, "/A/D/H/chi", pool
));
2859 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "chi", pool
));
2860 SVN_ERR(svn_fs_node_id(&psi_id
, txn_root
, "/A/D/H/psi", pool
));
2861 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "psi", pool
));
2862 SVN_ERR(svn_fs_node_id(&omega_id
, txn_root
, "/A/D/H/omega", pool
));
2863 SVN_ERR(check_entry_present(txn_root
, "A/D/H", "omega", pool
));
2864 SVN_ERR(svn_fs_node_id(&G_id
, txn_root
, "/A/D/G", pool
));
2865 SVN_ERR(check_entry_present(txn_root
, "A/D", "G", pool
));
2866 SVN_ERR(svn_fs_node_id(&pi_id
, txn_root
, "/A/D/G/pi", pool
));
2867 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "pi", pool
));
2868 SVN_ERR(svn_fs_node_id(&rho_id
, txn_root
, "/A/D/G/rho", pool
));
2869 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "rho", pool
));
2870 SVN_ERR(svn_fs_node_id(&tau_id
, txn_root
, "/A/D/G/tau", pool
));
2871 SVN_ERR(check_entry_present(txn_root
, "A/D/G", "tau", pool
));
2874 SVN_ERR(svn_fs_delete(txn_root
, "A", pool
));
2875 SVN_ERR(check_entry_absent(txn_root
, "", "A", pool
));
2877 /* Validate the tree. */
2879 static svn_test__tree_entry_t expected_entries
[] = {
2880 /* path, contents (0 = dir) */
2881 { "iota", "This is the file 'iota'.\n" }
2883 SVN_ERR(svn_test__validate_tree(txn_root
, expected_entries
, 1, pool
));
2887 return SVN_NO_ERROR
;
2892 /* Test the datestamps on commits. */
2893 static svn_error_t
*
2894 commit_date(const char **msg
,
2895 svn_boolean_t msg_only
,
2896 svn_test_opts_t
*opts
,
2901 svn_fs_root_t
*txn_root
;
2903 svn_string_t
*datestamp
;
2904 apr_time_t before_commit
, at_commit
, after_commit
;
2906 *msg
= "commit datestamps";
2909 return SVN_NO_ERROR
;
2911 /* Prepare a filesystem. */
2912 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-commit-date",
2913 opts
->fs_type
, pool
));
2915 before_commit
= apr_time_now();
2917 /* Commit a greek tree. */
2918 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
2919 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
2920 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
2921 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, pool
));
2923 after_commit
= apr_time_now();
2925 /* Get the datestamp of the commit. */
2926 SVN_ERR(svn_fs_revision_prop(&datestamp
, fs
, rev
, SVN_PROP_REVISION_DATE
,
2929 if (datestamp
== NULL
)
2930 return svn_error_create
2931 (SVN_ERR_FS_GENERAL
, NULL
,
2932 "failed to get datestamp of committed revision");
2934 SVN_ERR(svn_time_from_cstring(&at_commit
, datestamp
->data
, pool
));
2936 if (at_commit
< before_commit
)
2937 return svn_error_create
2938 (SVN_ERR_FS_GENERAL
, NULL
,
2939 "datestamp too early");
2941 if (at_commit
> after_commit
)
2942 return svn_error_create
2943 (SVN_ERR_FS_GENERAL
, NULL
,
2944 "datestamp too late");
2946 return SVN_NO_ERROR
;
2950 static svn_error_t
*
2951 check_old_revisions(const char **msg
,
2952 svn_boolean_t msg_only
,
2953 svn_test_opts_t
*opts
,
2958 svn_fs_root_t
*txn_root
;
2960 apr_pool_t
*subpool
= svn_pool_create(pool
);
2962 *msg
= "check old revisions";
2965 return SVN_NO_ERROR
;
2967 /* Prepare a filesystem. */
2968 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-check-old-revisions",
2969 opts
->fs_type
, pool
));
2971 /* Commit a greek tree. */
2972 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
2973 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
2974 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
2975 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
2976 svn_pool_clear(subpool
);
2978 /* Modify and commit iota a few times, then test to see if we can
2979 retrieve all the committed revisions. */
2981 /* right-side numbers match revision numbers */
2982 #define iota_contents_1 "This is the file 'iota'.\n"
2984 /* Add a char to the front. */
2985 #define iota_contents_2 "XThis is the file 'iota'.\n"
2987 /* Add a char to the end. */
2988 #define iota_contents_3 "XThis is the file 'iota'.\nX"
2990 /* Add a couple of chars in the middle. */
2991 #define iota_contents_4 "XThis is the X file 'iota'.\nX"
2993 /* Randomly add and delete chars all over. */
2994 #define iota_contents_5 \
2995 "XTYhQis is ACK, PHHHT! no longer 'ioZZZZZta'.blarf\nbye"
2997 /* Reassure iota that it will live for quite some time. */
2998 #define iota_contents_6 "Matthew 5:18 (Revised Standard Version) --\n\
2999 For truly, I say to you, till heaven and earth pass away, not an iota,\n\
3000 not a dot, will pass from the law until all is accomplished."
3002 /* Revert to the original contents. */
3003 #define iota_contents_7 "This is the file 'iota'.\n"
3006 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3007 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3008 SVN_ERR(svn_test__set_file_contents
3009 (txn_root
, "iota", iota_contents_2
, subpool
));
3010 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3011 svn_pool_clear(subpool
);
3014 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3015 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3016 SVN_ERR(svn_test__set_file_contents
3017 (txn_root
, "iota", iota_contents_3
, subpool
));
3018 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3019 svn_pool_clear(subpool
);
3022 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3023 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3024 SVN_ERR(svn_test__set_file_contents
3025 (txn_root
, "iota", iota_contents_4
, subpool
));
3026 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3027 svn_pool_clear(subpool
);
3030 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3031 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3032 SVN_ERR(svn_test__set_file_contents
3033 (txn_root
, "iota", iota_contents_5
, subpool
));
3034 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3035 svn_pool_clear(subpool
);
3038 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3039 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3040 SVN_ERR(svn_test__set_file_contents
3041 (txn_root
, "iota", iota_contents_6
, subpool
));
3042 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3043 svn_pool_clear(subpool
);
3046 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, rev
, subpool
));
3047 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3048 SVN_ERR(svn_test__set_file_contents
3049 (txn_root
, "iota", iota_contents_7
, subpool
));
3050 SVN_ERR(svn_fs_commit_txn(NULL
, &rev
, txn
, subpool
));
3051 svn_pool_clear(subpool
);
3053 /** Now check the full Greek Tree in all of those revisions,
3054 adjusting `iota' for each one. ***/
3056 /* Validate revision 1. */
3058 svn_fs_root_t
*root
;
3059 static svn_test__tree_entry_t expected_entries
[] = {
3060 /* path, contents (0 = dir) */
3061 { "iota", iota_contents_1
},
3063 { "A/mu", "This is the file 'mu'.\n" },
3065 { "A/B/lambda", "This is the file 'lambda'.\n" },
3067 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3068 { "A/B/E/beta", "This is the file 'beta'.\n" },
3072 { "A/D/gamma", "This is the file 'gamma'.\n" },
3074 { "A/D/G/pi", "This is the file 'pi'.\n" },
3075 { "A/D/G/rho", "This is the file 'rho'.\n" },
3076 { "A/D/G/tau", "This is the file 'tau'.\n" },
3078 { "A/D/H/chi", "This is the file 'chi'.\n" },
3079 { "A/D/H/psi", "This is the file 'psi'.\n" },
3080 { "A/D/H/omega", "This is the file 'omega'.\n" }
3083 SVN_ERR(svn_fs_revision_root(&root
, fs
, 1, pool
));
3084 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3087 /* Validate revision 2. */
3089 svn_fs_root_t
*root
;
3090 static svn_test__tree_entry_t expected_entries
[] = {
3091 /* path, contents (0 = dir) */
3092 { "iota", iota_contents_2
},
3094 { "A/mu", "This is the file 'mu'.\n" },
3096 { "A/B/lambda", "This is the file 'lambda'.\n" },
3098 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3099 { "A/B/E/beta", "This is the file 'beta'.\n" },
3103 { "A/D/gamma", "This is the file 'gamma'.\n" },
3105 { "A/D/G/pi", "This is the file 'pi'.\n" },
3106 { "A/D/G/rho", "This is the file 'rho'.\n" },
3107 { "A/D/G/tau", "This is the file 'tau'.\n" },
3109 { "A/D/H/chi", "This is the file 'chi'.\n" },
3110 { "A/D/H/psi", "This is the file 'psi'.\n" },
3111 { "A/D/H/omega", "This is the file 'omega'.\n" }
3114 SVN_ERR(svn_fs_revision_root(&root
, fs
, 2, pool
));
3115 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3118 /* Validate revision 3. */
3120 svn_fs_root_t
*root
;
3121 static svn_test__tree_entry_t expected_entries
[] = {
3122 /* path, contents (0 = dir) */
3123 { "iota", iota_contents_3
},
3125 { "A/mu", "This is the file 'mu'.\n" },
3127 { "A/B/lambda", "This is the file 'lambda'.\n" },
3129 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3130 { "A/B/E/beta", "This is the file 'beta'.\n" },
3134 { "A/D/gamma", "This is the file 'gamma'.\n" },
3136 { "A/D/G/pi", "This is the file 'pi'.\n" },
3137 { "A/D/G/rho", "This is the file 'rho'.\n" },
3138 { "A/D/G/tau", "This is the file 'tau'.\n" },
3140 { "A/D/H/chi", "This is the file 'chi'.\n" },
3141 { "A/D/H/psi", "This is the file 'psi'.\n" },
3142 { "A/D/H/omega", "This is the file 'omega'.\n" }
3145 SVN_ERR(svn_fs_revision_root(&root
, fs
, 3, pool
));
3146 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3149 /* Validate revision 4. */
3151 svn_fs_root_t
*root
;
3152 static svn_test__tree_entry_t expected_entries
[] = {
3153 /* path, contents (0 = dir) */
3154 { "iota", iota_contents_4
},
3156 { "A/mu", "This is the file 'mu'.\n" },
3158 { "A/B/lambda", "This is the file 'lambda'.\n" },
3160 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3161 { "A/B/E/beta", "This is the file 'beta'.\n" },
3165 { "A/D/gamma", "This is the file 'gamma'.\n" },
3167 { "A/D/G/pi", "This is the file 'pi'.\n" },
3168 { "A/D/G/rho", "This is the file 'rho'.\n" },
3169 { "A/D/G/tau", "This is the file 'tau'.\n" },
3171 { "A/D/H/chi", "This is the file 'chi'.\n" },
3172 { "A/D/H/psi", "This is the file 'psi'.\n" },
3173 { "A/D/H/omega", "This is the file 'omega'.\n" }
3176 SVN_ERR(svn_fs_revision_root(&root
, fs
, 4, pool
));
3177 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3180 /* Validate revision 5. */
3182 svn_fs_root_t
*root
;
3183 static svn_test__tree_entry_t expected_entries
[] = {
3184 /* path, contents (0 = dir) */
3185 { "iota", iota_contents_5
},
3187 { "A/mu", "This is the file 'mu'.\n" },
3189 { "A/B/lambda", "This is the file 'lambda'.\n" },
3191 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3192 { "A/B/E/beta", "This is the file 'beta'.\n" },
3197 { "A/D/gamma", "This is the file 'gamma'.\n" },
3198 { "A/D/G/pi", "This is the file 'pi'.\n" },
3199 { "A/D/G/rho", "This is the file 'rho'.\n" },
3200 { "A/D/G/tau", "This is the file 'tau'.\n" },
3202 { "A/D/H/chi", "This is the file 'chi'.\n" },
3203 { "A/D/H/psi", "This is the file 'psi'.\n" },
3204 { "A/D/H/omega", "This is the file 'omega'.\n" }
3207 SVN_ERR(svn_fs_revision_root(&root
, fs
, 5, pool
));
3208 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3211 /* Validate revision 6. */
3213 svn_fs_root_t
*root
;
3214 static svn_test__tree_entry_t expected_entries
[] = {
3215 /* path, contents (0 = dir) */
3216 { "iota", iota_contents_6
},
3218 { "A/mu", "This is the file 'mu'.\n" },
3220 { "A/B/lambda", "This is the file 'lambda'.\n" },
3222 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3223 { "A/B/E/beta", "This is the file 'beta'.\n" },
3227 { "A/D/gamma", "This is the file 'gamma'.\n" },
3229 { "A/D/G/pi", "This is the file 'pi'.\n" },
3230 { "A/D/G/rho", "This is the file 'rho'.\n" },
3231 { "A/D/G/tau", "This is the file 'tau'.\n" },
3233 { "A/D/H/chi", "This is the file 'chi'.\n" },
3234 { "A/D/H/psi", "This is the file 'psi'.\n" },
3235 { "A/D/H/omega", "This is the file 'omega'.\n" }
3238 SVN_ERR(svn_fs_revision_root(&root
, fs
, 6, pool
));
3239 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3242 /* Validate revision 7. */
3244 svn_fs_root_t
*root
;
3245 static svn_test__tree_entry_t expected_entries
[] = {
3246 /* path, contents (0 = dir) */
3247 { "iota", iota_contents_7
},
3249 { "A/mu", "This is the file 'mu'.\n" },
3251 { "A/B/lambda", "This is the file 'lambda'.\n" },
3253 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3254 { "A/B/E/beta", "This is the file 'beta'.\n" },
3258 { "A/D/gamma", "This is the file 'gamma'.\n" },
3260 { "A/D/G/pi", "This is the file 'pi'.\n" },
3261 { "A/D/G/rho", "This is the file 'rho'.\n" },
3262 { "A/D/G/tau", "This is the file 'tau'.\n" },
3264 { "A/D/H/chi", "This is the file 'chi'.\n" },
3265 { "A/D/H/psi", "This is the file 'psi'.\n" },
3266 { "A/D/H/omega", "This is the file 'omega'.\n" }
3269 SVN_ERR(svn_fs_revision_root(&root
, fs
, 7, pool
));
3270 SVN_ERR(svn_test__validate_tree(root
, expected_entries
, 20, pool
));
3274 svn_pool_destroy(subpool
);
3275 return SVN_NO_ERROR
;
3279 /* For each revision R in FS, from 0 to MAX_REV, check that it
3280 matches the tree in EXPECTED_TREES[R]. Use POOL for any
3281 allocations. This is a helper function for check_all_revisions. */
3282 static svn_error_t
*
3283 validate_revisions(svn_fs_t
*fs
,
3284 svn_test__tree_t
*expected_trees
,
3285 svn_revnum_t max_rev
,
3288 svn_fs_root_t
*revision_root
;
3291 apr_pool_t
*subpool
= svn_pool_create(pool
);
3293 /* Validate all revisions up to the current one. */
3294 for (i
= 0; i
<= max_rev
; i
++)
3296 SVN_ERR(svn_fs_revision_root(&revision_root
, fs
,
3297 (svn_revnum_t
)i
, subpool
));
3298 err
= svn_test__validate_tree(revision_root
,
3299 expected_trees
[i
].entries
,
3300 expected_trees
[i
].num_entries
,
3303 return svn_error_createf
3304 (SVN_ERR_FS_GENERAL
, err
,
3305 "Error validating revision %ld (youngest is %ld)", i
, max_rev
);
3306 svn_pool_clear(subpool
);
3309 svn_pool_destroy(subpool
);
3310 return SVN_NO_ERROR
;
3314 static svn_error_t
*
3315 check_all_revisions(const char **msg
,
3316 svn_boolean_t msg_only
,
3317 svn_test_opts_t
*opts
,
3322 svn_fs_root_t
*txn_root
;
3323 svn_revnum_t youngest_rev
;
3324 svn_test__tree_t expected_trees
[5]; /* one tree per commit, please */
3325 svn_revnum_t revision_count
= 0;
3326 apr_pool_t
*subpool
= svn_pool_create(pool
);
3328 *msg
= "after each commit, check all revisions";
3331 return SVN_NO_ERROR
;
3333 /* Create a filesystem and repository. */
3334 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-check-all-revisions",
3335 opts
->fs_type
, pool
));
3337 /***********************************************************************/
3339 /***********************************************************************/
3341 expected_trees
[revision_count
].num_entries
= 0;
3342 expected_trees
[revision_count
].entries
= 0;
3343 SVN_ERR(validate_revisions(fs
, expected_trees
, revision_count
, subpool
));
3346 svn_pool_clear(subpool
);
3348 /* Create and commit the greek tree. */
3349 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
3350 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3351 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
3352 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3354 /***********************************************************************/
3356 /***********************************************************************/
3358 static svn_test__tree_entry_t expected_entries
[] = {
3359 /* path, contents (0 = dir) */
3360 { "iota", "This is the file 'iota'.\n" },
3362 { "A/mu", "This is the file 'mu'.\n" },
3364 { "A/B/lambda", "This is the file 'lambda'.\n" },
3366 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3367 { "A/B/E/beta", "This is the file 'beta'.\n" },
3371 { "A/D/gamma", "This is the file 'gamma'.\n" },
3373 { "A/D/G/pi", "This is the file 'pi'.\n" },
3374 { "A/D/G/rho", "This is the file 'rho'.\n" },
3375 { "A/D/G/tau", "This is the file 'tau'.\n" },
3377 { "A/D/H/chi", "This is the file 'chi'.\n" },
3378 { "A/D/H/psi", "This is the file 'psi'.\n" },
3379 { "A/D/H/omega", "This is the file 'omega'.\n" }
3381 expected_trees
[revision_count
].entries
= expected_entries
;
3382 expected_trees
[revision_count
].num_entries
= 20;
3383 SVN_ERR(validate_revisions(fs
, expected_trees
, revision_count
, subpool
));
3386 svn_pool_clear(subpool
);
3388 /* Make a new txn based on the youngest revision, make some changes,
3389 and commit those changes (which makes a new youngest
3391 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3392 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3394 static svn_test__txn_script_command_t script_entries
[] = {
3395 { 'a', "A/delta", "This is the file 'delta'.\n" },
3396 { 'a', "A/epsilon", "This is the file 'epsilon'.\n" },
3397 { 'a', "A/B/Z", 0 },
3398 { 'a', "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3400 { 'd', "A/mu", "" },
3401 { 'd', "A/D/G/tau", "" },
3402 { 'd', "A/D/H/omega", "" },
3403 { 'e', "iota", "Changed file 'iota'.\n" },
3404 { 'e', "A/D/G/rho", "Changed file 'rho'.\n" }
3406 SVN_ERR(svn_test__txn_script_exec(txn_root
, script_entries
, 10,
3409 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3411 /***********************************************************************/
3413 /***********************************************************************/
3415 static svn_test__tree_entry_t expected_entries
[] = {
3416 /* path, contents (0 = dir) */
3417 { "iota", "Changed file 'iota'.\n" },
3419 { "A/delta", "This is the file 'delta'.\n" },
3420 { "A/epsilon", "This is the file 'epsilon'.\n" },
3422 { "A/B/lambda", "This is the file 'lambda'.\n" },
3424 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3425 { "A/B/E/beta", "This is the file 'beta'.\n" },
3428 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3430 { "A/D/gamma", "This is the file 'gamma'.\n" },
3432 { "A/D/G/pi", "This is the file 'pi'.\n" },
3433 { "A/D/G/rho", "Changed file 'rho'.\n" },
3435 { "A/D/H/chi", "This is the file 'chi'.\n" },
3436 { "A/D/H/psi", "This is the file 'psi'.\n" }
3438 expected_trees
[revision_count
].entries
= expected_entries
;
3439 expected_trees
[revision_count
].num_entries
= 20;
3440 SVN_ERR(validate_revisions(fs
, expected_trees
, revision_count
, subpool
));
3443 svn_pool_clear(subpool
);
3445 /* Make a new txn based on the youngest revision, make some changes,
3446 and commit those changes (which makes a new youngest
3448 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3449 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3451 static svn_test__txn_script_command_t script_entries
[] = {
3452 { 'a', "A/mu", "Re-added file 'mu'.\n" },
3453 { 'a', "A/D/H/omega", 0 }, /* re-add omega as directory! */
3454 { 'd', "iota", "" },
3455 { 'e', "A/delta", "This is the file 'delta'.\nLine 2.\n" }
3457 SVN_ERR(svn_test__txn_script_exec(txn_root
, script_entries
, 4,
3460 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3462 /***********************************************************************/
3464 /***********************************************************************/
3466 static svn_test__tree_entry_t expected_entries
[] = {
3467 /* path, contents (0 = dir) */
3469 { "A/delta", "This is the file 'delta'.\nLine 2.\n" },
3470 { "A/epsilon", "This is the file 'epsilon'.\n" },
3471 { "A/mu", "Re-added file 'mu'.\n" },
3473 { "A/B/lambda", "This is the file 'lambda'.\n" },
3475 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3476 { "A/B/E/beta", "This is the file 'beta'.\n" },
3479 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3481 { "A/D/gamma", "This is the file 'gamma'.\n" },
3483 { "A/D/G/pi", "This is the file 'pi'.\n" },
3484 { "A/D/G/rho", "Changed file 'rho'.\n" },
3486 { "A/D/H/chi", "This is the file 'chi'.\n" },
3487 { "A/D/H/psi", "This is the file 'psi'.\n" },
3488 { "A/D/H/omega", 0 }
3490 expected_trees
[revision_count
].entries
= expected_entries
;
3491 expected_trees
[revision_count
].num_entries
= 21;
3492 SVN_ERR(validate_revisions(fs
, expected_trees
, revision_count
, subpool
));
3495 svn_pool_clear(subpool
);
3497 /* Make a new txn based on the youngest revision, make some changes,
3498 and commit those changes (which makes a new youngest
3500 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3501 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3503 static svn_test__txn_script_command_t script_entries
[] = {
3504 { 'c', "A/D/G", "A/D/G2" },
3505 { 'c', "A/epsilon", "A/B/epsilon" },
3507 SVN_ERR(svn_test__txn_script_exec(txn_root
, script_entries
, 2, subpool
));
3509 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3511 /***********************************************************************/
3513 /***********************************************************************/
3515 static svn_test__tree_entry_t expected_entries
[] = {
3516 /* path, contents (0 = dir) */
3518 { "A/delta", "This is the file 'delta'.\nLine 2.\n" },
3519 { "A/epsilon", "This is the file 'epsilon'.\n" },
3520 { "A/mu", "Re-added file 'mu'.\n" },
3522 { "A/B/epsilon", "This is the file 'epsilon'.\n" },
3523 { "A/B/lambda", "This is the file 'lambda'.\n" },
3525 { "A/B/E/alpha", "This is the file 'alpha'.\n" },
3526 { "A/B/E/beta", "This is the file 'beta'.\n" },
3529 { "A/B/Z/zeta", "This is the file 'zeta'.\n" },
3531 { "A/D/gamma", "This is the file 'gamma'.\n" },
3533 { "A/D/G/pi", "This is the file 'pi'.\n" },
3534 { "A/D/G/rho", "Changed file 'rho'.\n" },
3536 { "A/D/G2/pi", "This is the file 'pi'.\n" },
3537 { "A/D/G2/rho", "Changed file 'rho'.\n" },
3539 { "A/D/H/chi", "This is the file 'chi'.\n" },
3540 { "A/D/H/psi", "This is the file 'psi'.\n" },
3541 { "A/D/H/omega", 0 }
3543 expected_trees
[revision_count
].entries
= expected_entries
;
3544 expected_trees
[revision_count
].num_entries
= 25;
3545 SVN_ERR(validate_revisions(fs
, expected_trees
, revision_count
, subpool
));
3548 svn_pool_destroy(subpool
);
3550 return SVN_NO_ERROR
;
3554 /* Helper function for large_file_integrity(). Given a ROOT and PATH
3555 to a file, calculate and return the MD5 digest for the contents of
3557 static svn_error_t
*
3558 get_file_digest(unsigned char digest
[APR_MD5_DIGESTSIZE
],
3559 svn_fs_root_t
*root
,
3563 svn_stream_t
*stream
;
3565 const apr_size_t buf_size
= 100000;
3566 apr_md5_ctx_t context
;
3568 /* ### todo: Pool usage in svndiff is currently really, really
3569 crappy. We need to keep this buffer fairly large so we don't run
3570 out of memory doing undeltification of large files into tiny
3571 buffers. Issue #465. */
3572 char *buf
= apr_palloc(pool
, buf_size
);
3574 /* Get a stream for the file contents. */
3575 SVN_ERR(svn_fs_file_contents(&stream
, root
, path
, pool
));
3577 /* Initialize APR MD5 context. */
3578 apr_md5_init(&context
);
3582 /* "please fill the buf with bytes" */
3584 SVN_ERR(svn_stream_read(stream
, buf
, &len
));
3586 /* Update the MD5 calculation with the data we just read. */
3587 apr_md5_update(&context
, buf
, len
);
3589 } while (len
== buf_size
); /* Continue until a short read. */
3591 /* Finalize MD5 calculation. */
3592 apr_md5_final(digest
, &context
);
3594 return SVN_NO_ERROR
;
3598 /* Return a pseudo-random number in the range [0,SCALAR) i.e. return
3599 a number N such that 0 <= N < SCALAR */
3600 static int my_rand(int scalar
, apr_uint32_t
*seed
)
3602 static const apr_uint32_t TEST_RAND_MAX
= 0xffffffffUL
;
3603 /* Assumes TEST_RAND_MAX+1 can be exactly represented in a double */
3604 return (int)(((double)svn_test_rand(seed
)
3605 / ((double)TEST_RAND_MAX
+1.0))
3610 /* Put pseudo-random bytes in buffer BUF (which is LEN bytes long).
3611 If FULL is TRUE, simply replace every byte in BUF with a
3612 pseudo-random byte, else, replace a pseudo-random collection of
3613 bytes with pseudo-random data. */
3615 random_data_to_buffer(char *buf
,
3621 apr_size_t num_bytes
;
3625 const char *dataset
= "0123456789";
3626 int dataset_size
= strlen(dataset
);
3630 for (i
= 0; i
< buf_len
; i
++)
3632 ds_off
= my_rand(dataset_size
, seed
);
3633 buf
[i
] = dataset
[ds_off
];
3639 num_bytes
= my_rand(buf_len
/ 100, seed
) + 1;
3640 for (i
= 0; i
< num_bytes
; i
++)
3642 offset
= my_rand(buf_len
- 1, seed
);
3643 ds_off
= my_rand(dataset_size
, seed
);
3644 buf
[offset
] = dataset
[ds_off
];
3651 static svn_error_t
*
3652 file_integrity_helper(apr_size_t filesize
, apr_uint32_t
*seed
,
3653 const char *fs_type
, const char *fs_name
,
3658 svn_fs_root_t
*txn_root
, *rev_root
;
3659 svn_revnum_t youngest_rev
= 0;
3660 apr_pool_t
*subpool
= svn_pool_create(pool
);
3661 svn_string_t contents
;
3662 char *content_buffer
;
3663 unsigned char digest
[APR_MD5_DIGESTSIZE
];
3664 unsigned char digest_list
[100][APR_MD5_DIGESTSIZE
];
3665 svn_txdelta_window_handler_t wh_func
;
3669 /* Create a filesystem and repository. */
3670 SVN_ERR(svn_test__create_fs(&fs
, fs_name
, fs_type
, pool
));
3672 /* Set up our file contents string buffer. */
3673 content_buffer
= apr_palloc(pool
, filesize
);
3675 contents
.data
= content_buffer
;
3676 contents
.len
= filesize
;
3680 The plan here is simple. We have a very large file (FILESIZE
3681 bytes) that we initialize with pseudo-random data and commit.
3682 Then we make pseudo-random modifications to that file's contents,
3683 committing after each mod. Prior to each commit, we generate an
3684 MD5 checksum for the contents of the file, storing each of those
3685 checksums in an array. After we've made a whole bunch of edits
3686 and commits, we'll re-check that file's contents as of each
3687 revision in the repository, recalculate a checksum for those
3688 contents, and make sure the "before" and "after" checksums
3691 /* Create a big, ugly, pseudo-random-filled file and commit it. */
3692 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3693 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3694 SVN_ERR(svn_fs_make_file(txn_root
, "bigfile", subpool
));
3695 random_data_to_buffer(content_buffer
, filesize
, TRUE
, seed
);
3696 apr_md5(digest
, contents
.data
, contents
.len
);
3697 SVN_ERR(svn_fs_apply_textdelta
3698 (&wh_func
, &wh_baton
, txn_root
, "bigfile", NULL
, NULL
, subpool
));
3699 SVN_ERR(svn_txdelta_send_string(&contents
, wh_func
, wh_baton
, subpool
));
3700 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3701 SVN_ERR(svn_fs_deltify_revision(fs
, youngest_rev
, subpool
));
3702 memcpy(digest_list
[youngest_rev
], digest
, APR_MD5_DIGESTSIZE
);
3703 svn_pool_clear(subpool
);
3705 /* Now, let's make some edits to the beginning of our file, and
3707 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3708 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3709 random_data_to_buffer(content_buffer
, 20, TRUE
, seed
);
3710 apr_md5(digest
, contents
.data
, contents
.len
);
3711 SVN_ERR(svn_fs_apply_textdelta
3712 (&wh_func
, &wh_baton
, txn_root
, "bigfile", NULL
, NULL
, subpool
));
3713 SVN_ERR(svn_txdelta_send_string(&contents
, wh_func
, wh_baton
, subpool
));
3714 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3715 SVN_ERR(svn_fs_deltify_revision(fs
, youngest_rev
, subpool
));
3716 memcpy(digest_list
[youngest_rev
], digest
, APR_MD5_DIGESTSIZE
);
3717 svn_pool_clear(subpool
);
3719 /* Now, let's make some edits to the end of our file. */
3720 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3721 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3722 random_data_to_buffer(content_buffer
+ (filesize
- 20), 20, TRUE
, seed
);
3723 apr_md5(digest
, contents
.data
, contents
.len
);
3724 SVN_ERR(svn_fs_apply_textdelta
3725 (&wh_func
, &wh_baton
, txn_root
, "bigfile", NULL
, NULL
, subpool
));
3726 SVN_ERR(svn_txdelta_send_string(&contents
, wh_func
, wh_baton
, subpool
));
3727 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3728 SVN_ERR(svn_fs_deltify_revision(fs
, youngest_rev
, subpool
));
3729 memcpy(digest_list
[youngest_rev
], digest
, APR_MD5_DIGESTSIZE
);
3730 svn_pool_clear(subpool
);
3732 /* How about some edits to both the beginning and the end of the
3734 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3735 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3736 random_data_to_buffer(content_buffer
, 20, TRUE
, seed
);
3737 random_data_to_buffer(content_buffer
+ (filesize
- 20), 20, TRUE
, seed
);
3738 apr_md5(digest
, contents
.data
, contents
.len
);
3739 SVN_ERR(svn_fs_apply_textdelta
3740 (&wh_func
, &wh_baton
, txn_root
, "bigfile", NULL
, NULL
, subpool
));
3741 SVN_ERR(svn_txdelta_send_string(&contents
, wh_func
, wh_baton
, subpool
));
3742 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3743 SVN_ERR(svn_fs_deltify_revision(fs
, youngest_rev
, subpool
));
3744 memcpy(digest_list
[youngest_rev
], digest
, APR_MD5_DIGESTSIZE
);
3745 svn_pool_clear(subpool
);
3747 /* Alright, now we're just going to go crazy. Let's make many more
3748 edits -- pseudo-random numbers and offsets of bytes changed to
3749 more pseudo-random values. */
3750 for (j
= youngest_rev
; j
< 30; j
= youngest_rev
)
3752 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3753 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3754 random_data_to_buffer(content_buffer
, filesize
, FALSE
, seed
);
3755 apr_md5(digest
, contents
.data
, contents
.len
);
3756 SVN_ERR(svn_fs_apply_textdelta(&wh_func
, &wh_baton
, txn_root
,
3757 "bigfile", NULL
, NULL
, subpool
));
3758 SVN_ERR(svn_txdelta_send_string
3759 (&contents
, wh_func
, wh_baton
, subpool
));
3760 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3761 SVN_ERR(svn_fs_deltify_revision(fs
, youngest_rev
, subpool
));
3762 memcpy(digest_list
[youngest_rev
], digest
, APR_MD5_DIGESTSIZE
);
3763 svn_pool_clear(subpool
);
3766 /* Now, calculate an MD5 digest for the contents of our big ugly
3767 file in each revision currently in existence, and make the sure
3768 the checksum matches the checksum of the data prior to its
3770 for (j
= youngest_rev
; j
> 0; j
--)
3772 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, j
, subpool
));
3773 SVN_ERR(get_file_digest(digest
, rev_root
, "bigfile", subpool
));
3774 if (memcmp(digest
, digest_list
[j
], APR_MD5_DIGESTSIZE
))
3775 return svn_error_createf
3776 (SVN_ERR_FS_GENERAL
, NULL
,
3777 "MD5 checksum failure, revision %ld", j
);
3778 svn_pool_clear(subpool
);
3781 svn_pool_destroy(subpool
);
3782 return SVN_NO_ERROR
;
3786 static svn_error_t
*
3787 medium_file_integrity(const char **msg
,
3788 svn_boolean_t msg_only
,
3789 svn_test_opts_t
*opts
,
3792 apr_uint32_t seed
= (apr_uint32_t
) apr_time_now();
3793 *msg
= apr_psprintf(pool
,
3794 "create and modify medium file (seed=%lu)",
3795 (unsigned long) seed
);
3798 return SVN_NO_ERROR
;
3800 /* Being no larger than the standard delta window size affects
3801 deltification internally, so test that. */
3802 return file_integrity_helper(SVN_DELTA_WINDOW_SIZE
, &seed
, opts
->fs_type
,
3803 "test-repo-medium-file-integrity", pool
);
3807 static svn_error_t
*
3808 large_file_integrity(const char **msg
,
3809 svn_boolean_t msg_only
,
3810 svn_test_opts_t
*opts
,
3813 apr_uint32_t seed
= (apr_uint32_t
) apr_time_now();
3814 *msg
= apr_psprintf(pool
,
3815 "create and modify large file (seed=%lu)",
3816 (unsigned long) seed
);
3819 return SVN_NO_ERROR
;
3821 /* Being larger than the standard delta window size affects
3822 deltification internally, so test that. */
3823 return file_integrity_helper(SVN_DELTA_WINDOW_SIZE
+ 1, &seed
, opts
->fs_type
,
3824 "test-repo-large-file-integrity", pool
);
3828 static svn_error_t
*
3829 check_root_revision(const char **msg
,
3830 svn_boolean_t msg_only
,
3831 svn_test_opts_t
*opts
,
3836 svn_fs_root_t
*txn_root
, *rev_root
;
3837 svn_revnum_t youngest_rev
, test_rev
;
3838 apr_pool_t
*subpool
= svn_pool_create(pool
);
3841 *msg
= "ensure accurate storage of root node";
3844 return SVN_NO_ERROR
;
3846 /* Create a filesystem and repository. */
3847 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-check-root-revision",
3848 opts
->fs_type
, pool
));
3850 /* Create and commit the greek tree. */
3851 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
3852 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3853 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
3854 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3856 /* Root node's revision should be the same as YOUNGEST_REV. */
3857 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, subpool
));
3858 SVN_ERR(svn_fs_node_created_rev(&test_rev
, rev_root
, "", subpool
));
3859 if (test_rev
!= youngest_rev
)
3860 return svn_error_createf
3861 (SVN_ERR_FS_GENERAL
, NULL
,
3862 "Root node in revision %ld has unexpected stored revision %ld",
3863 youngest_rev
, test_rev
);
3864 svn_pool_clear(subpool
);
3866 for (i
= 0; i
< 10; i
++)
3868 /* Create and commit the greek tree. */
3869 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3870 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3871 SVN_ERR(svn_test__set_file_contents
3873 apr_psprintf(subpool
, "iota version %d", i
+ 2), subpool
));
3875 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3877 /* Root node's revision should be the same as YOUNGEST_REV. */
3878 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, subpool
));
3879 SVN_ERR(svn_fs_node_created_rev(&test_rev
, rev_root
, "", subpool
));
3880 if (test_rev
!= youngest_rev
)
3881 return svn_error_createf
3882 (SVN_ERR_FS_GENERAL
, NULL
,
3883 "Root node in revision %ld has unexpected stored revision %ld",
3884 youngest_rev
, test_rev
);
3885 svn_pool_clear(subpool
);
3888 svn_pool_destroy(subpool
);
3889 return SVN_NO_ERROR
;
3893 struct node_created_rev_args
{
3899 static svn_error_t
*
3900 verify_path_revs(svn_fs_root_t
*root
,
3901 struct node_created_rev_args
*args
,
3905 apr_pool_t
*subpool
= svn_pool_create(pool
);
3909 for (i
= 0; i
< num_path_revs
; i
++)
3911 svn_pool_clear(subpool
);
3912 SVN_ERR(svn_fs_node_created_rev(&rev
, root
, args
[i
].path
, subpool
));
3913 if (rev
!= args
[i
].rev
)
3914 return svn_error_createf
3915 (SVN_ERR_FS_GENERAL
, NULL
,
3916 "verify_path_revs: '%s' has created rev '%ld' "
3918 args
[i
].path
, rev
, args
[i
].rev
);
3921 svn_pool_destroy(subpool
);
3922 return SVN_NO_ERROR
;
3926 static svn_error_t
*
3927 test_node_created_rev(const char **msg
,
3928 svn_boolean_t msg_only
,
3929 svn_test_opts_t
*opts
,
3932 apr_pool_t
*subpool
= svn_pool_create(pool
);
3935 svn_fs_root_t
*txn_root
, *rev_root
;
3936 svn_revnum_t youngest_rev
= 0;
3938 struct node_created_rev_args path_revs
[21];
3939 const char *greek_paths
[21] = {
3945 /* 5 */ "A/B/lambda",
3947 /* 7 */ "A/B/E/alpha",
3948 /* 8 */ "A/B/E/beta",
3952 /* 12 */ "A/D/gamma",
3954 /* 14 */ "A/D/G/pi",
3955 /* 15 */ "A/D/G/rho",
3956 /* 16 */ "A/D/G/tau",
3958 /* 18 */ "A/D/H/chi",
3959 /* 19 */ "A/D/H/psi",
3960 /* 20 */ "A/D/H/omega",
3963 *msg
= "svn_fs_node_created_rev test";
3966 return SVN_NO_ERROR
;
3968 /* Initialize the paths in our args list. */
3969 for (i
= 0; i
< 20; i
++)
3970 path_revs
[i
].path
= greek_paths
[i
];
3972 /* Create a filesystem and repository. */
3973 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-node-created-rev",
3974 opts
->fs_type
, pool
));
3976 /* Created the greek tree in revision 1. */
3977 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
3978 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
3979 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
3981 /* Now, prior to committing, all these nodes should have an invalid
3982 created rev. After all, the rev has been created yet. Verify
3984 for (i
= 0; i
< 20; i
++)
3985 path_revs
[i
].rev
= SVN_INVALID_REVNUM
;
3986 SVN_ERR(verify_path_revs(txn_root
, path_revs
, 20, subpool
));
3988 /* Now commit the transaction. */
3989 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
3991 /* Now, we have a new revision, and all paths in it should have a
3992 created rev of 1. Verify this. */
3993 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, subpool
));
3994 for (i
= 0; i
< 20; i
++)
3995 path_revs
[i
].rev
= 1;
3996 SVN_ERR(verify_path_revs(rev_root
, path_revs
, 20, subpool
));
3998 /*** Let's make some changes/commits here and there, and make sure
3999 we can keep this whole created rev thing in good standing. The
4000 general rule here is that prior to commit, mutable things have
4001 an invalid created rev, immutable things have their original
4002 created rev. After the commit, those things which had invalid
4003 created revs in the transaction now have the youngest revision
4004 as their created rev.
4006 ### NOTE: Bubble-up currently affect the created revisions for
4007 directory nodes. I'm not sure if this is the behavior we've
4008 settled on as desired.
4011 /*** clear the per-commit pool */
4012 svn_pool_clear(subpool
);
4013 /* begin a new transaction */
4014 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4015 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4016 /* The created revs on a txn root should be the same as on the rev
4017 root it came from, if we haven't made changes yet. (See issue
4019 SVN_ERR(verify_path_revs(txn_root
, path_revs
, 20, subpool
));
4021 SVN_ERR(svn_test__set_file_contents
4022 (txn_root
, "iota", "pointless mod here", subpool
));
4023 /* verify created revs */
4024 path_revs
[0].rev
= SVN_INVALID_REVNUM
; /* (root) */
4025 path_revs
[1].rev
= SVN_INVALID_REVNUM
; /* iota */
4026 SVN_ERR(verify_path_revs(txn_root
, path_revs
, 20, subpool
));
4027 /* commit transaction */
4028 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4029 /* get a revision root for the new revision */
4030 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, subpool
));
4031 /* verify created revs */
4032 path_revs
[0].rev
= 2; /* (root) */
4033 path_revs
[1].rev
= 2; /* iota */
4034 SVN_ERR(verify_path_revs(rev_root
, path_revs
, 20, subpool
));
4036 /*** clear the per-commit pool */
4037 svn_pool_clear(subpool
);
4038 /* begin a new transaction */
4039 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4040 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4042 SVN_ERR(svn_test__set_file_contents
4043 (txn_root
, "A/D/H/omega", "pointless mod here", subpool
));
4044 /* verify created revs */
4045 path_revs
[0].rev
= SVN_INVALID_REVNUM
; /* (root) */
4046 path_revs
[2].rev
= SVN_INVALID_REVNUM
; /* A */
4047 path_revs
[11].rev
= SVN_INVALID_REVNUM
; /* D */
4048 path_revs
[17].rev
= SVN_INVALID_REVNUM
; /* H */
4049 path_revs
[20].rev
= SVN_INVALID_REVNUM
; /* omega */
4050 SVN_ERR(verify_path_revs(txn_root
, path_revs
, 20, subpool
));
4051 /* commit transaction */
4052 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4053 /* get a revision root for the new revision */
4054 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, subpool
));
4055 /* verify created revs */
4056 path_revs
[0].rev
= 3; /* (root) */
4057 path_revs
[2].rev
= 3; /* A */
4058 path_revs
[11].rev
= 3; /* D */
4059 path_revs
[17].rev
= 3; /* H */
4060 path_revs
[20].rev
= 3; /* omega */
4061 SVN_ERR(verify_path_revs(rev_root
, path_revs
, 20, subpool
));
4063 /* Destroy the per-commit subpool. */
4064 svn_pool_destroy(subpool
);
4066 return SVN_NO_ERROR
;
4070 static svn_error_t
*
4071 check_related(const char **msg
,
4072 svn_boolean_t msg_only
,
4073 svn_test_opts_t
*opts
,
4076 apr_pool_t
*subpool
= svn_pool_create(pool
);
4079 svn_fs_root_t
*txn_root
, *rev_root
;
4080 svn_revnum_t youngest_rev
= 0;
4082 *msg
= "test svn_fs_check_related";
4085 return SVN_NO_ERROR
;
4087 /* Create a filesystem and repository. */
4088 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-check-related",
4089 opts
->fs_type
, pool
));
4091 /*** Step I: Build up some state in our repository through a series
4094 /* Using files because bubble-up complicates the testing. However,
4095 the algorithm itself is ambivalent about what type of node is
4098 - New files show up in this order (through time): A,B,C,D,E,F
4099 - Number following filename is the revision.
4100 - Vertical motion shows revision history
4101 - Horizontal motion show copy history.
4114 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4115 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4116 SVN_ERR(svn_fs_make_file(txn_root
, "A", subpool
));
4117 SVN_ERR(svn_test__set_file_contents(txn_root
, "A", "1", subpool
));
4118 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4119 svn_pool_clear(subpool
);
4121 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4122 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4123 SVN_ERR(svn_test__set_file_contents(txn_root
, "A", "2", subpool
));
4124 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4125 svn_pool_clear(subpool
);
4127 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4128 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4129 SVN_ERR(svn_test__set_file_contents(txn_root
, "A", "3", subpool
));
4130 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4131 svn_pool_clear(subpool
);
4133 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4134 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4135 SVN_ERR(svn_test__set_file_contents(txn_root
, "A", "4", subpool
));
4136 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, 3, subpool
));
4137 SVN_ERR(svn_fs_copy(rev_root
, "A", txn_root
, "B", subpool
));
4138 SVN_ERR(svn_test__set_file_contents(txn_root
, "B", "4", subpool
));
4139 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, 1, subpool
));
4140 SVN_ERR(svn_fs_copy(rev_root
, "A", txn_root
, "C", subpool
));
4141 SVN_ERR(svn_test__set_file_contents(txn_root
, "C", "4", subpool
));
4142 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4143 svn_pool_clear(subpool
);
4145 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4146 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4147 SVN_ERR(svn_test__set_file_contents(txn_root
, "B", "5", subpool
));
4148 SVN_ERR(svn_test__set_file_contents(txn_root
, "C", "5", subpool
));
4149 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4150 svn_pool_clear(subpool
);
4152 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4153 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4154 SVN_ERR(svn_test__set_file_contents(txn_root
, "B", "6", subpool
));
4155 SVN_ERR(svn_test__set_file_contents(txn_root
, "C", "6", subpool
));
4156 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, 5, subpool
));
4157 SVN_ERR(svn_fs_copy(rev_root
, "B", txn_root
, "D", subpool
));
4158 SVN_ERR(svn_test__set_file_contents(txn_root
, "D", "5", subpool
));
4159 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4160 svn_pool_clear(subpool
);
4162 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4163 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4164 SVN_ERR(svn_test__set_file_contents(txn_root
, "D", "7", subpool
));
4165 SVN_ERR(svn_fs_make_file(txn_root
, "E", subpool
));
4166 SVN_ERR(svn_test__set_file_contents(txn_root
, "E", "7", subpool
));
4167 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4168 svn_pool_clear(subpool
);
4170 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4171 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4172 SVN_ERR(svn_test__set_file_contents(txn_root
, "E", "8", subpool
));
4173 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4174 svn_pool_clear(subpool
);
4176 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4177 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4178 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, 8, subpool
));
4179 SVN_ERR(svn_fs_copy(rev_root
, "E", txn_root
, "F", subpool
));
4180 SVN_ERR(svn_test__set_file_contents(txn_root
, "F", "9", subpool
));
4181 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4182 svn_pool_clear(subpool
);
4184 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4185 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4186 SVN_ERR(svn_test__set_file_contents(txn_root
, "F", "10", subpool
));
4187 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4188 svn_pool_clear(subpool
);
4190 /*** Step II: Exhaustively verify relationship between all nodes in
4201 /* Our 16 existing files/revisions. */
4202 struct path_rev_t path_revs
[16] = {
4203 { "A", 1 }, { "A", 2 }, { "A", 3 }, { "A", 4 },
4204 { "B", 4 }, { "B", 5 }, { "B", 6 }, { "C", 4 },
4205 { "C", 5 }, { "C", 6 }, { "D", 6 }, { "D", 7 },
4206 { "E", 7 }, { "E", 8 }, { "F", 9 }, { "F", 10 }
4209 int related_matrix
[16][16] = {
4210 /* A1 ... F10 across the top here*/
4211 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A1 */
4212 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A2 */
4213 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A3 */
4214 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* A4 */
4215 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B4 */
4216 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B5 */
4217 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* B6 */
4218 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C4 */
4219 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C5 */
4220 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* C6 */
4221 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* D6 */
4222 { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* D7 */
4223 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* E7 */
4224 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* E8 */
4225 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, /* F9 */
4226 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 } /* F10 */
4229 /* Here's the fun part. Running the tests. */
4230 for (i
= 0; i
< 16; i
++)
4232 for (j
= 0; j
< 16; j
++)
4234 struct path_rev_t pr1
= path_revs
[i
];
4235 struct path_rev_t pr2
= path_revs
[j
];
4236 const svn_fs_id_t
*id1
, *id2
;
4239 /* Get the ID for the first path/revision combination. */
4240 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, pr1
.rev
, pool
));
4241 SVN_ERR(svn_fs_node_id(&id1
, rev_root
, pr1
.path
, pool
));
4243 /* Get the ID for the second path/revision combination. */
4244 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, pr2
.rev
, pool
));
4245 SVN_ERR(svn_fs_node_id(&id2
, rev_root
, pr2
.path
, pool
));
4247 /* <exciting> Now, run the relationship check! </exciting> */
4248 related
= svn_fs_check_related(id1
, id2
) ? 1 : 0;
4249 if (related
== related_matrix
[i
][j
])
4253 else if (related
&& (! related_matrix
[i
][j
]))
4255 return svn_error_createf
4256 (SVN_ERR_TEST_FAILED
, NULL
,
4257 "expected '%s:%d' to be related to '%s:%d'; it was not",
4258 pr1
.path
, (int)pr1
.rev
, pr2
.path
, (int)pr2
.rev
);
4260 else if ((! related
) && related_matrix
[i
][j
])
4262 return svn_error_createf
4263 (SVN_ERR_TEST_FAILED
, NULL
,
4264 "expected '%s:%d' to not be related to '%s:%d'; it was",
4265 pr1
.path
, (int)pr1
.rev
, pr2
.path
, (int)pr2
.rev
);
4268 svn_pool_clear(subpool
);
4273 /* Destroy the subpool. */
4274 svn_pool_destroy(subpool
);
4276 return SVN_NO_ERROR
;
4280 static svn_error_t
*
4281 branch_test(const char **msg
,
4282 svn_boolean_t msg_only
,
4283 svn_test_opts_t
*opts
,
4286 apr_pool_t
*spool
= svn_pool_create(pool
);
4289 svn_fs_root_t
*txn_root
, *rev_root
;
4290 svn_revnum_t youngest_rev
= 0;
4292 *msg
= "test complex copies (branches)";
4295 return SVN_NO_ERROR
;
4297 /* Create a filesystem and repository. */
4298 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-branch-test",
4299 opts
->fs_type
, pool
));
4301 /*** Revision 1: Create the greek tree in revision. ***/
4302 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, spool
));
4303 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4304 SVN_ERR(svn_test__create_greek_tree(txn_root
, spool
));
4305 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, spool
));
4306 svn_pool_clear(spool
);
4308 /*** Revision 2: Copy A/D/G/rho to A/D/G/rho2. ***/
4309 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, spool
));
4310 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4311 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, spool
));
4312 SVN_ERR(svn_fs_copy(rev_root
, "A/D/G/rho", txn_root
, "A/D/G/rho2", spool
));
4313 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, spool
));
4314 svn_pool_clear(spool
);
4316 /*** Revision 3: Copy A/D/G to A/D/G2. ***/
4317 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, spool
));
4318 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4319 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, spool
));
4320 SVN_ERR(svn_fs_copy(rev_root
, "A/D/G", txn_root
, "A/D/G2", spool
));
4321 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, spool
));
4322 svn_pool_clear(spool
);
4324 /*** Revision 4: Copy A/D to A/D2. ***/
4325 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, spool
));
4326 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4327 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, spool
));
4328 SVN_ERR(svn_fs_copy(rev_root
, "A/D", txn_root
, "A/D2", spool
));
4329 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, spool
));
4330 svn_pool_clear(spool
);
4332 /*** Revision 5: Edit all the rho's! ***/
4333 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, spool
));
4334 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4335 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, youngest_rev
, spool
));
4336 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/G/rho",
4337 "Edited text.", spool
));
4338 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/G/rho2",
4339 "Edited text.", spool
));
4340 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/G2/rho",
4341 "Edited text.", spool
));
4342 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/G2/rho2",
4343 "Edited text.", spool
));
4344 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D2/G/rho",
4345 "Edited text.", spool
));
4346 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D2/G/rho2",
4347 "Edited text.", spool
));
4348 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D2/G2/rho",
4349 "Edited text.", spool
));
4350 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D2/G2/rho2",
4351 "Edited text.", spool
));
4352 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, spool
));
4354 svn_pool_destroy(spool
);
4356 return SVN_NO_ERROR
;
4360 static svn_error_t
*
4361 verify_checksum(const char **msg
,
4362 svn_boolean_t msg_only
,
4363 svn_test_opts_t
*opts
,
4368 svn_fs_root_t
*txn_root
;
4369 svn_stringbuf_t
*str
;
4370 unsigned char expected_digest
[APR_MD5_DIGESTSIZE
];
4371 unsigned char actual_digest
[APR_MD5_DIGESTSIZE
];
4373 /* Write a file, compare the repository's idea of its checksum
4374 against our idea of its checksum. They should be the same. */
4376 *msg
= "test checksums";
4379 return SVN_NO_ERROR
;
4381 str
= svn_stringbuf_create("My text editor charges me rent.", pool
);
4382 apr_md5(expected_digest
, str
->data
, str
->len
);
4384 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-verify-checksum",
4385 opts
->fs_type
, pool
));
4386 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
4387 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
4388 SVN_ERR(svn_fs_make_file(txn_root
, "fact", pool
));
4389 SVN_ERR(svn_test__set_file_contents(txn_root
, "fact", str
->data
, pool
));
4390 SVN_ERR(svn_fs_file_md5_checksum(actual_digest
, txn_root
, "fact", pool
));
4392 if (memcmp(expected_digest
, actual_digest
, APR_MD5_DIGESTSIZE
) != 0)
4393 return svn_error_createf
4394 (SVN_ERR_FS_GENERAL
, NULL
,
4395 "verify-checksum: checksum mismatch:\n"
4398 svn_md5_digest_to_cstring(expected_digest
, pool
),
4399 svn_md5_digest_to_cstring(actual_digest
, pool
));
4401 return SVN_NO_ERROR
;
4405 /* Helper for closest_copy_test(). Verify that CLOSEST_PATH and the
4406 revision associated with CLOSEST_ROOT match the EXPECTED_PATH and
4407 EXPECTED_REVISION, respectively. */
4408 static svn_error_t
*
4409 test_closest_copy_pair(svn_fs_root_t
*closest_root
,
4410 const char *closest_path
,
4411 svn_revnum_t expected_revision
,
4412 const char *expected_path
)
4414 svn_revnum_t closest_rev
= SVN_INVALID_REVNUM
;
4416 /* Callers must pass valid -- EXPECTED_PATH and EXPECTED_REVISION
4417 come as a both-or-nothing pair. */
4418 assert(((! expected_path
) && (! SVN_IS_VALID_REVNUM(expected_revision
)))
4419 || (expected_path
&& SVN_IS_VALID_REVNUM(expected_revision
)));
4421 /* CLOSEST_PATH and CLOSEST_ROOT come as a both-or-nothing pair, too. */
4422 if (closest_path
&& (! closest_root
))
4423 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
4424 "got closest path but no closest root");
4425 if ((! closest_path
) && closest_root
)
4426 return svn_error_create(SVN_ERR_FS_GENERAL
, NULL
,
4427 "got closest root but no closest path");
4429 /* Now that our pairs are known sane, we can compare them. */
4430 if (closest_path
&& (! expected_path
))
4431 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
4432 "got closest path ('%s') when none expected",
4434 if ((! closest_path
) && expected_path
)
4435 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
4436 "got no closest path; expected '%s'",
4438 if (closest_path
&& (strcmp(closest_path
, expected_path
) != 0))
4439 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
4440 "got a different closest path than expected:\n"
4443 expected_path
, closest_path
);
4445 closest_rev
= svn_fs_revision_root_revision(closest_root
);
4446 if (closest_rev
!= expected_revision
)
4447 return svn_error_createf(SVN_ERR_FS_GENERAL
, NULL
,
4448 "got a different closest rev than expected:\n"
4451 expected_revision
, closest_rev
);
4453 return SVN_NO_ERROR
;
4457 static svn_error_t
*
4458 closest_copy_test(const char **msg
,
4459 svn_boolean_t msg_only
,
4460 svn_test_opts_t
*opts
,
4465 svn_fs_root_t
*txn_root
, *rev_root
, *croot
;
4466 svn_revnum_t after_rev
;
4468 apr_pool_t
*spool
= svn_pool_create(pool
);
4470 *msg
= "calculating closest history-affecting copies";
4473 return SVN_NO_ERROR
;
4475 /* Prepare a filesystem. */
4476 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-closest-copy",
4477 opts
->fs_type
, pool
));
4479 /* In first txn, create and commit the greek tree. */
4480 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, spool
));
4481 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4482 SVN_ERR(svn_test__create_greek_tree(txn_root
, spool
));
4483 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, spool
));
4484 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, spool
));
4486 /* Copy A to Z, and commit. */
4487 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, spool
));
4488 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4489 SVN_ERR(svn_fs_copy(rev_root
, "A", txn_root
, "Z", spool
));
4490 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, spool
));
4491 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, spool
));
4493 /* Anything under Z should have a closest copy pair of ("/Z", 2), so
4494 we'll pick some spots to test. Stuff under A should have no
4495 relevant closest copy. */
4496 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z", spool
));
4497 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 2, "/Z"));
4498 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z/D/G", spool
));
4499 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 2, "/Z"));
4500 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z/mu", spool
));
4501 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 2, "/Z"));
4502 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z/B/E/beta", spool
));
4503 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 2, "/Z"));
4504 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "A", spool
));
4505 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4506 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "A/D/G", spool
));
4507 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4508 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "A/mu", spool
));
4509 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4510 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "A/B/E/beta", spool
));
4511 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4513 /* Okay, so let's do some more stuff. We'll edit Z/mu, copy A to
4514 Z2, copy A/D/H to Z2/D/H2, and edit Z2/D/H2/chi. We'll also make
4515 new Z/t and Z2/D/H2/t files. */
4516 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, spool
));
4517 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4518 SVN_ERR(svn_test__set_file_contents(txn_root
, "Z/mu",
4519 "Edited text.", spool
));
4520 SVN_ERR(svn_fs_copy(rev_root
, "A", txn_root
, "Z2", spool
));
4521 SVN_ERR(svn_fs_copy(rev_root
, "A/D/H", txn_root
, "Z2/D/H2", spool
));
4522 SVN_ERR(svn_test__set_file_contents(txn_root
, "Z2/D/H2/chi",
4523 "Edited text.", spool
));
4524 SVN_ERR(svn_fs_make_file(txn_root
, "Z/t", pool
));
4525 SVN_ERR(svn_fs_make_file(txn_root
, "Z2/D/H2/t", pool
));
4526 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, spool
));
4527 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, spool
));
4529 /* Okay, just for kicks, let's modify Z2/D/H2/t. Shouldn't affect
4530 its closest-copy-ness, right? */
4531 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, spool
));
4532 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4533 SVN_ERR(svn_test__set_file_contents(txn_root
, "Z2/D/H2/t",
4534 "Edited text.", spool
));
4535 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, spool
));
4536 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, spool
));
4538 /* Now, we expect Z2/D/H2 to have a closest copy of ("/Z2/D/H2", 3)
4539 because of the deepest path rule. We expected Z2/D to have a
4540 closest copy of ("/Z2", 3). Z/mu should still have a closest
4541 copy of ("/Z", 2). As for the two new files (Z/t and Z2/D/H2/t),
4542 neither should have a closest copy. */
4543 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "A/mu", spool
));
4544 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4545 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z/mu", spool
));
4546 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 2, "/Z"));
4547 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z2/D/H2", spool
));
4548 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 3, "/Z2/D/H2"));
4549 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z2/D", spool
));
4550 SVN_ERR(test_closest_copy_pair(croot
, cpath
, 3, "/Z2"));
4551 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z/t", spool
));
4552 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4553 SVN_ERR(svn_fs_closest_copy(&croot
, &cpath
, rev_root
, "Z2/D/H2/t", spool
));
4554 SVN_ERR(test_closest_copy_pair(croot
, cpath
, SVN_INVALID_REVNUM
, NULL
));
4556 return SVN_NO_ERROR
;
4559 static svn_error_t
*
4560 root_revisions(const char **msg
,
4561 svn_boolean_t msg_only
,
4562 svn_test_opts_t
*opts
,
4567 svn_fs_root_t
*txn_root
, *rev_root
;
4568 svn_revnum_t after_rev
, fetched_rev
;
4569 apr_pool_t
*spool
= svn_pool_create(pool
);
4571 *msg
= "svn_fs_root_t (base) revisions";
4574 return SVN_NO_ERROR
;
4576 /* Prepare a filesystem. */
4577 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-root-revisions",
4578 opts
->fs_type
, pool
));
4580 /* In first txn, create and commit the greek tree. */
4581 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, spool
));
4582 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4583 SVN_ERR(svn_test__create_greek_tree(txn_root
, spool
));
4584 SVN_ERR(test_commit_txn(&after_rev
, txn
, NULL
, spool
));
4586 /* First, verify that a revision root based on our new revision
4587 reports the correct associated revision. */
4588 SVN_ERR(svn_fs_revision_root(&rev_root
, fs
, after_rev
, spool
));
4589 fetched_rev
= svn_fs_revision_root_revision(rev_root
);
4590 if (after_rev
!= fetched_rev
)
4591 return svn_error_createf
4592 (SVN_ERR_TEST_FAILED
, NULL
,
4593 "expected revision '%d'; "
4594 "got '%d' from svn_fs_revision_root_revision(rev_root)",
4595 (int)after_rev
, (int)fetched_rev
);
4597 /* Then verify that we can't ask about the txn-base-rev from a
4599 fetched_rev
= svn_fs_txn_root_base_revision(rev_root
);
4600 if (fetched_rev
!= SVN_INVALID_REVNUM
)
4601 return svn_error_createf
4602 (SVN_ERR_TEST_FAILED
, NULL
,
4603 "expected SVN_INVALID_REVNUM; "
4604 "got '%d' from svn_fs_txn_root_base_revision(rev_root)",
4607 /* Now, create a second txn based on AFTER_REV. */
4608 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, after_rev
, spool
));
4609 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, spool
));
4611 /* Verify that it reports the right base revision. */
4612 fetched_rev
= svn_fs_txn_root_base_revision(txn_root
);
4613 if (after_rev
!= fetched_rev
)
4614 return svn_error_createf
4615 (SVN_ERR_TEST_FAILED
, NULL
,
4617 "got '%d' from svn_fs_txn_root_base_revision(txn_root)",
4618 (int)after_rev
, (int)fetched_rev
);
4620 /* Then verify that we can't ask about the rev-root-rev from a
4622 fetched_rev
= svn_fs_revision_root_revision(txn_root
);
4623 if (fetched_rev
!= SVN_INVALID_REVNUM
)
4624 return svn_error_createf
4625 (SVN_ERR_TEST_FAILED
, NULL
,
4626 "expected SVN_INVALID_REVNUM; "
4627 "got '%d' from svn_fs_revision_root_revision(txn_root)",
4630 return SVN_NO_ERROR
;
4634 static svn_error_t
*
4635 unordered_txn_dirprops(const char **msg
,
4636 svn_boolean_t msg_only
,
4637 svn_test_opts_t
*opts
,
4641 svn_fs_txn_t
*txn
, *txn2
;
4642 svn_fs_root_t
*txn_root
, *txn_root2
;
4644 svn_revnum_t new_rev
, not_rev
;
4646 /* This is a regression test for issue #2751. */
4647 *msg
= "test dir prop preservation in unordered txns";
4650 return SVN_NO_ERROR
;
4652 /* Prepare a filesystem. */
4653 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-unordered-txn-dirprops",
4654 opts
->fs_type
, pool
));
4656 /* Create and commit the greek tree. */
4657 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, pool
));
4658 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
4659 SVN_ERR(svn_test__create_greek_tree(txn_root
, pool
));
4660 SVN_ERR(test_commit_txn(&new_rev
, txn
, NULL
, pool
));
4662 /* Open two transactions */
4663 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, new_rev
, pool
));
4664 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
4665 SVN_ERR(svn_fs_begin_txn(&txn2
, fs
, new_rev
, pool
));
4666 SVN_ERR(svn_fs_txn_root(&txn_root2
, txn2
, pool
));
4668 /* Change a child file in one. */
4669 SVN_ERR(svn_test__set_file_contents(txn_root
, "/A/B/E/alpha",
4670 "New contents", pool
));
4672 /* Change dir props in the other. (We're using svn:mergeinfo
4673 property just to make sure special handling logic for that
4674 property doesn't croak.) */
4675 SET_STR(&pval
, "/A/C:1");
4676 SVN_ERR(svn_fs_change_node_prop(txn_root2
, "/A/B", "svn:mergeinfo",
4679 /* Commit the second one first. */
4680 SVN_ERR(test_commit_txn(&new_rev
, txn2
, NULL
, pool
));
4682 /* Then commit the first -- but expect a conflict due to the
4683 propchanges made by the other txn. */
4684 SVN_ERR(test_commit_txn(¬_rev
, txn
, "/A/B", pool
));
4685 SVN_ERR(svn_fs_abort_txn(txn
, pool
));
4687 /* Now, let's try those in reverse. Open two transactions */
4688 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, new_rev
, pool
));
4689 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, pool
));
4690 SVN_ERR(svn_fs_begin_txn(&txn2
, fs
, new_rev
, pool
));
4691 SVN_ERR(svn_fs_txn_root(&txn_root2
, txn2
, pool
));
4693 /* Change a child file in one. */
4694 SVN_ERR(svn_test__set_file_contents(txn_root
, "/A/B/E/alpha",
4695 "New contents", pool
));
4697 /* Change dir props in the other. */
4698 SET_STR(&pval
, "/A/C:1");
4699 SVN_ERR(svn_fs_change_node_prop(txn_root2
, "/A/B", "svn:mergeinfo",
4702 /* Commit the first one first. */
4703 SVN_ERR(test_commit_txn(&new_rev
, txn
, NULL
, pool
));
4705 /* Then commit the second -- but expect an conflict because the
4706 directory wasn't up-to-date, which is required for propchanges. */
4707 SVN_ERR(test_commit_txn(¬_rev
, txn2
, "/A/B", pool
));
4708 SVN_ERR(svn_fs_abort_txn(txn2
, pool
));
4710 return SVN_NO_ERROR
;
4713 static svn_error_t
*
4714 set_uuid(const char **msg
,
4715 svn_boolean_t msg_only
,
4716 svn_test_opts_t
*opts
,
4720 const char *fixed_uuid
= svn_uuid_generate(pool
);
4721 const char *fetched_uuid
;
4723 *msg
= "test svn_fs_set_uuid";
4726 return SVN_NO_ERROR
;
4728 /* Prepare a filesystem. */
4729 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-set-uuid",
4730 opts
->fs_type
, pool
));
4732 /* Set the repository UUID to something fixed. */
4733 SVN_ERR(svn_fs_set_uuid(fs
, fixed_uuid
, pool
));
4735 /* Make sure we get back what we set. */
4736 SVN_ERR(svn_fs_get_uuid(fs
, &fetched_uuid
, pool
));
4737 if (strcmp(fixed_uuid
, fetched_uuid
) != 0)
4738 return svn_error_createf
4739 (SVN_ERR_TEST_FAILED
, NULL
, "expected UUID '%s'; got '%s'",
4740 fixed_uuid
, fetched_uuid
);
4742 /* Set the repository UUID to something new (and unknown). */
4743 SVN_ERR(svn_fs_set_uuid(fs
, NULL
, pool
));
4745 /* Make sure we *don't* get back what we previously set (after all,
4746 this stuff is supposed to be universally unique!). */
4747 SVN_ERR(svn_fs_get_uuid(fs
, &fetched_uuid
, pool
));
4748 if (strcmp(fixed_uuid
, fetched_uuid
) == 0)
4749 return svn_error_createf
4750 (SVN_ERR_TEST_FAILED
, NULL
,
4751 "expected something other than UUID '%s', but got that one",
4754 return SVN_NO_ERROR
;
4757 static svn_error_t
*
4758 node_origin_rev(const char **msg
,
4759 svn_boolean_t msg_only
,
4760 svn_test_opts_t
*opts
,
4763 apr_pool_t
*subpool
= svn_pool_create(pool
);
4766 svn_fs_root_t
*txn_root
, *root
;
4767 svn_revnum_t youngest_rev
= 0;
4775 *msg
= "test svn_fs_node_origin_rev";
4777 return SVN_NO_ERROR
;
4779 /* Create the repository. */
4780 SVN_ERR(svn_test__create_fs(&fs
, "test-repo-node-origin-rev",
4781 opts
->fs_type
, pool
));
4783 /* Revision 1: Create the Greek tree. */
4784 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, 0, subpool
));
4785 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4786 SVN_ERR(svn_test__create_greek_tree(txn_root
, subpool
));
4787 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4788 svn_pool_clear(subpool
);
4790 /* Revision 2: Modify A/D/H/chi and A/B/E/alpha. */
4791 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4792 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4793 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/H/chi", "2", subpool
));
4794 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/B/E/alpha", "2", subpool
));
4795 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4796 svn_pool_clear(subpool
);
4798 /* Revision 3: Copy A/D to A/D2, and create A/D2/floop new. */
4799 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4800 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4801 SVN_ERR(svn_fs_revision_root(&root
, fs
, youngest_rev
, subpool
));
4802 SVN_ERR(svn_fs_copy(root
, "A/D", txn_root
, "A/D2", subpool
));
4803 SVN_ERR(svn_fs_make_file(txn_root
, "A/D2/floop", subpool
));
4804 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4805 svn_pool_clear(subpool
);
4807 /* Revision 4: Modify A/D/H/chi and A/D2/H/chi. */
4808 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4809 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4810 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/H/chi", "4", subpool
));
4811 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D2/H/chi", "4", subpool
));
4812 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4813 svn_pool_clear(subpool
);
4815 /* Revision 5: Delete A/D2/G, add A/B/E/alfalfa. */
4816 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4817 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4818 SVN_ERR(svn_fs_delete(txn_root
, "A/D2/G", subpool
));
4819 SVN_ERR(svn_fs_make_file(txn_root
, "A/B/E/alfalfa", subpool
));
4820 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4821 svn_pool_clear(subpool
);
4823 /* Revision 6: Restore A/D2/G (from version 4). */
4824 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4825 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4826 SVN_ERR(svn_fs_revision_root(&root
, fs
, 4, subpool
));
4827 SVN_ERR(svn_fs_copy(root
, "A/D2/G", txn_root
, "A/D2/G", subpool
));
4828 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4829 svn_pool_clear(subpool
);
4831 /* Revision 7: Move A/D2 to A/D (replacing it), and tweak A/D/floop. */
4832 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4833 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4834 SVN_ERR(svn_fs_revision_root(&root
, fs
, youngest_rev
, subpool
));
4835 SVN_ERR(svn_fs_delete(txn_root
, "A/D", subpool
));
4836 SVN_ERR(svn_fs_copy(root
, "A/D2", txn_root
, "A/D", subpool
));
4837 SVN_ERR(svn_fs_delete(txn_root
, "A/D2", subpool
));
4838 SVN_ERR(svn_test__set_file_contents(txn_root
, "A/D/floop", "7", subpool
));
4839 SVN_ERR(svn_fs_commit_txn(NULL
, &youngest_rev
, txn
, subpool
));
4840 svn_pool_clear(subpool
);
4842 /* Now test some origin revisions. */
4844 struct path_rev_t pathrevs
[4] = { { "A/D", 1 },
4847 { "A/B/E/alfalfa", 5 } };
4849 SVN_ERR(svn_fs_revision_root(&root
, fs
, youngest_rev
, pool
));
4850 for (i
= 0; i
< (sizeof(pathrevs
) / sizeof(struct path_rev_t
)); i
++)
4852 struct path_rev_t path_rev
= pathrevs
[i
];
4853 svn_revnum_t revision
;
4854 SVN_ERR(svn_fs_node_origin_rev(&revision
, root
, path_rev
.path
, pool
));
4855 if (path_rev
.rev
!= revision
)
4856 return svn_error_createf
4857 (SVN_ERR_TEST_FAILED
, NULL
,
4858 "expected origin revision of '%ld' for '%s'; got '%ld'",
4859 path_rev
.rev
, path_rev
.path
, revision
);
4863 /* Also, we'll check a couple of queries into a transaction root. */
4864 SVN_ERR(svn_fs_begin_txn(&txn
, fs
, youngest_rev
, subpool
));
4865 SVN_ERR(svn_fs_txn_root(&txn_root
, txn
, subpool
));
4866 SVN_ERR(svn_fs_make_file(txn_root
, "bloop", subpool
));
4867 SVN_ERR(svn_fs_make_dir(txn_root
, "A/D/blarp", subpool
));
4870 struct path_rev_t pathrevs
[6] = { { "A/D", 1 },
4873 { "A/D/blarp", -1 },
4875 { "A/B/E/alfalfa", 5 } };
4878 for (i
= 0; i
< (sizeof(pathrevs
) / sizeof(struct path_rev_t
)); i
++)
4880 struct path_rev_t path_rev
= pathrevs
[i
];
4881 svn_revnum_t revision
;
4882 SVN_ERR(svn_fs_node_origin_rev(&revision
, root
, path_rev
.path
, pool
));
4883 if (! SVN_IS_VALID_REVNUM(revision
))
4885 if (path_rev
.rev
!= revision
)
4886 return svn_error_createf
4887 (SVN_ERR_TEST_FAILED
, NULL
,
4888 "expected origin revision of '%ld' for '%s'; got '%ld'",
4889 path_rev
.rev
, path_rev
.path
, revision
);
4893 return SVN_NO_ERROR
;
4896 /* ------------------------------------------------------------------------ */
4898 /* The test table. */
4900 struct svn_test_descriptor_t test_funcs
[] =
4903 SVN_TEST_PASS(trivial_transaction
),
4904 SVN_TEST_PASS(reopen_trivial_transaction
),
4905 SVN_TEST_PASS(create_file_transaction
),
4906 SVN_TEST_PASS(verify_txn_list
),
4907 SVN_TEST_PASS(txn_names_are_not_reused
),
4908 SVN_TEST_PASS(write_and_read_file
),
4909 SVN_TEST_PASS(create_mini_tree_transaction
),
4910 SVN_TEST_PASS(create_greek_tree_transaction
),
4911 SVN_TEST_PASS(list_directory
),
4912 SVN_TEST_PASS(revision_props
),
4913 SVN_TEST_PASS(transaction_props
),
4914 SVN_TEST_PASS(node_props
),
4915 SVN_TEST_PASS(delete_mutables
),
4916 SVN_TEST_PASS(delete),
4917 SVN_TEST_PASS(fetch_youngest_rev
),
4918 SVN_TEST_PASS(basic_commit
),
4919 SVN_TEST_PASS(test_tree_node_validation
),
4920 SVN_TEST_XFAIL(merging_commit
), /* Needs to be written to match new
4921 merge() algorithm expectations */
4922 SVN_TEST_PASS(copy_test
),
4923 SVN_TEST_PASS(commit_date
),
4924 SVN_TEST_PASS(check_old_revisions
),
4925 SVN_TEST_PASS(check_all_revisions
),
4926 SVN_TEST_PASS(medium_file_integrity
),
4927 SVN_TEST_PASS(large_file_integrity
),
4928 SVN_TEST_PASS(check_root_revision
),
4929 SVN_TEST_PASS(test_node_created_rev
),
4930 SVN_TEST_PASS(check_related
),
4931 SVN_TEST_PASS(branch_test
),
4932 SVN_TEST_PASS(verify_checksum
),
4933 SVN_TEST_PASS(closest_copy_test
),
4934 SVN_TEST_PASS(root_revisions
),
4935 SVN_TEST_PASS(unordered_txn_dirprops
),
4936 SVN_TEST_PASS(set_uuid
),
4937 SVN_TEST_PASS(node_origin_rev
),