diff options
Diffstat (limited to 'subversion/tests/libsvn_fs/fs-test.c')
-rw-r--r-- | subversion/tests/libsvn_fs/fs-test.c | 287 |
1 files changed, 260 insertions, 27 deletions
diff --git a/subversion/tests/libsvn_fs/fs-test.c b/subversion/tests/libsvn_fs/fs-test.c index 6f4d5db..104a692 100644 --- a/subversion/tests/libsvn_fs/fs-test.c +++ b/subversion/tests/libsvn_fs/fs-test.c @@ -27,6 +27,7 @@ #include "../svn_test.h" +#include "svn_hash.h" #include "svn_pools.h" #include "svn_time.h" #include "svn_string.h" @@ -34,6 +35,7 @@ #include "svn_checksum.h" #include "svn_mergeinfo.h" #include "svn_props.h" +#include "svn_version.h" #include "private/svn_fs_private.h" @@ -658,6 +660,7 @@ revision_props(const svn_test_opts_t *opts, /* Copy a property's value into a new property. */ SVN_ERR(svn_fs_revision_prop(&value, fs, 0, "color", pool)); + SVN_TEST_ASSERT(value); s1.data = value->data; s1.len = value->len; @@ -694,6 +697,7 @@ revision_props(const svn_test_opts_t *opts, /* Obtain a list of all current properties, and make sure it matches the expected values. */ SVN_ERR(svn_fs_revision_proplist(&proplist, fs, 0, pool)); + SVN_TEST_ASSERT(proplist); { svn_string_t *prop_value; @@ -1564,6 +1568,7 @@ merging_commit(const svn_test_opts_t *opts, /* (5) E doesn't exist in ANCESTOR, and has been added to A. */ { + svn_revnum_t failed_rev; /* (1) E doesn't exist in ANCESTOR, and has been added to B. Conflict. */ SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[4], pool)); @@ -1571,7 +1576,7 @@ merging_commit(const svn_test_opts_t *opts, SVN_ERR(svn_fs_make_file(txn_root, "theta", pool)); SVN_ERR(svn_test__set_file_contents (txn_root, "theta", "This is another file 'theta'.\n", pool)); - SVN_ERR(test_commit_txn(&after_rev, txn, "/theta", pool)); + SVN_ERR(test_commit_txn(&failed_rev, txn, "/theta", pool)); SVN_ERR(svn_fs_abort_txn(txn, pool)); /* (1) E exists in ANCESTOR, but has been deleted from B. Can't @@ -1579,6 +1584,8 @@ merging_commit(const svn_test_opts_t *opts, /* (3) E exists in both ANCESTOR and B. Can't occur, by assumption that E doesn't exist in ANCESTOR. */ + + SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM); } /* (4) E exists in ANCESTOR, but has been deleted from A */ @@ -1592,15 +1599,16 @@ merging_commit(const svn_test_opts_t *opts, SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool)); - /* ### FIXME: It is at this point that our test stops being valid, - ### hence its expected failure. The following call will now - ### conflict on /A/D/H, causing revision 6 *not* to be created, - ### and the remainer of this test (which was written long ago) - ### to suffer from a shift in the expected state and behavior - ### of the filesystem as a result of this commit not happening. - */ + /* We used to create the revision like this before fixing issue + #2751 -- Directory prop mods reverted in overlapping commits scenario. - SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool)); + But we now expect that to fail as out of date */ + { + svn_revnum_t failed_rev; + SVN_ERR(test_commit_txn(&failed_rev, txn, "/A/D/H", pool)); + + SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM); + } /*********************************************************************/ /* REVISION 6 */ /*********************************************************************/ @@ -1638,18 +1646,22 @@ merging_commit(const svn_test_opts_t *opts, /* Try deleting a file F inside a subtree S where S does not exist in the most recent revision, but does exist in the ancestor tree. This should conflict. */ - SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool)); - SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); - SVN_ERR(svn_fs_delete(txn_root, "A/D/H/omega", pool)); - SVN_ERR(test_commit_txn(&after_rev, txn, "/A/D/H", pool)); - SVN_ERR(svn_fs_abort_txn(txn, pool)); + { + svn_revnum_t failed_rev; + SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_delete(txn_root, "A/D/H/omega", pool)); + SVN_ERR(test_commit_txn(&failed_rev, txn, "/A/D/H", pool)); + SVN_ERR(svn_fs_abort_txn(txn, pool)); + + SVN_TEST_ASSERT(failed_rev == SVN_INVALID_REVNUM); + } /* E exists in both ANCESTOR and B ... */ { /* (1) but refers to different nodes. Conflict. */ - SVN_ERR(svn_fs_begin_txn(&txn, fs, revisions[1], pool)); + SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool)); SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); - SVN_ERR(svn_fs_delete(txn_root, "A/D/H", pool)); SVN_ERR(svn_fs_make_dir(txn_root, "A/D/H", pool)); SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool)); revisions[revision_count++] = after_rev; @@ -3527,11 +3539,12 @@ get_file_checksum(svn_checksum_t **checksum, /* Return a pseudo-random number in the range [0,SCALAR) i.e. return a number N such that 0 <= N < SCALAR */ -static int my_rand(int scalar, apr_uint32_t *seed) +static int my_rand(apr_uint64_t scalar, apr_uint32_t *seed) { static const apr_uint32_t TEST_RAND_MAX = 0xffffffffUL; /* Assumes TEST_RAND_MAX+1 can be exactly represented in a double */ - return (int)(((double)svn_test_rand(seed) + apr_uint32_t r = svn_test_rand(seed); + return (int)(((double)r / ((double)TEST_RAND_MAX+1.0)) * (double)scalar); } @@ -3553,7 +3566,7 @@ random_data_to_buffer(char *buf, int ds_off = 0; const char *dataset = "0123456789"; - int dataset_size = strlen(dataset); + apr_size_t dataset_size = strlen(dataset); if (full) { @@ -4174,12 +4187,12 @@ check_related(const svn_test_opts_t *opts, int related = 0; /* Get the ID for the first path/revision combination. */ - SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr1.rev, pool)); - SVN_ERR(svn_fs_node_id(&id1, rev_root, pr1.path, pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr1.rev, subpool)); + SVN_ERR(svn_fs_node_id(&id1, rev_root, pr1.path, subpool)); /* Get the ID for the second path/revision combination. */ - SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr2.rev, pool)); - SVN_ERR(svn_fs_node_id(&id2, rev_root, pr2.path, pool)); + SVN_ERR(svn_fs_revision_root(&rev_root, fs, pr2.rev, subpool)); + SVN_ERR(svn_fs_node_id(&id2, rev_root, pr2.path, subpool)); /* <exciting> Now, run the relationship check! </exciting> */ related = svn_fs_check_related(id1, id2) ? 1 : 0; @@ -4799,6 +4812,223 @@ node_origin_rev(const svn_test_opts_t *opts, return SVN_NO_ERROR; } + +/* Helper: call svn_fs_history_location() and check the results. */ +static svn_error_t * +check_history_location(const char *expected_path, + svn_revnum_t expected_revision, + svn_fs_history_t *history, + apr_pool_t *pool) +{ + const char *actual_path; + svn_revnum_t actual_revision; + + SVN_ERR(svn_fs_history_location(&actual_path, &actual_revision, + history, pool)); + + /* Validate the location against our expectations. */ + if (actual_revision != expected_revision + || (actual_path && expected_path && strcmp(actual_path, expected_path)) + || (actual_path != NULL) != (expected_path != NULL)) + { + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "svn_fs_history_location() failed:\n" + " expected '%s@%ld'\n" + " found '%s@%ld", + expected_path ? expected_path : "(null)", + expected_revision, + actual_path ? actual_path : "(null)", + actual_revision); + } + + return SVN_NO_ERROR; +} + +/* Test svn_fs_history_*(). */ +static svn_error_t * +node_history(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + svn_fs_t *fs; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root; + svn_revnum_t after_rev; + + /* Prepare a txn to receive the greek tree. */ + SVN_ERR(svn_test__create_fs(&fs, "test-repo-node-history", + opts, pool)); + SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + + /* Create and verify the greek tree. */ + SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); + SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool)); + + /* Make some changes, following copy_test() above. */ + + /* r2: copy pi to pi2, with textmods. */ + { + svn_fs_root_t *rev_root; + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool)); + SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, pool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); + SVN_ERR(svn_fs_copy(rev_root, "A/D/G/pi", + txn_root, "A/D/H/pi2", + pool)); + SVN_ERR(svn_test__set_file_contents + (txn_root, "A/D/H/pi2", "This is the file 'pi2'.\n", pool)); + SVN_ERR(test_commit_txn(&after_rev, txn, NULL, pool)); + } + + /* Go back in history: pi2@r2 -> pi@r1 */ + { + svn_fs_history_t *history; + svn_fs_root_t *rev_root; + + SVN_ERR(svn_fs_revision_root(&rev_root, fs, after_rev, pool)); + + /* Fetch a history object, and walk it until its start. */ + + SVN_ERR(svn_fs_node_history(&history, rev_root, "A/D/H/pi2", pool)); + SVN_ERR(check_history_location("/A/D/H/pi2", 2, history, pool)); + + SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool)); + SVN_ERR(check_history_location("/A/D/H/pi2", 2, history, pool)); + + SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool)); + SVN_ERR(check_history_location("/A/D/G/pi", 1, history, pool)); + + SVN_ERR(svn_fs_history_prev(&history, history, TRUE, pool)); + SVN_TEST_ASSERT(history == NULL); + } + + return SVN_NO_ERROR; +} + +/* Test svn_fs_delete_fs(). */ +static svn_error_t * +delete_fs(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + const char *path; + svn_node_kind_t kind; + + /* We have to use a subpool to close the svn_fs_t before calling + svn_fs_delete_fs. See issue 4264. */ + { + svn_fs_t *fs; + apr_pool_t *subpool = svn_pool_create(pool); + SVN_ERR(svn_test__create_fs(&fs, "test-repo-delete-fs", opts, subpool)); + path = svn_fs_path(fs, pool); + svn_pool_destroy(subpool); + } + + SVN_ERR(svn_io_check_path(path, &kind, pool)); + SVN_TEST_ASSERT(kind != svn_node_none); + SVN_ERR(svn_fs_delete_fs(path, pool)); + SVN_ERR(svn_io_check_path(path, &kind, pool)); + SVN_TEST_ASSERT(kind == svn_node_none); + + /* Recreate dir so that test cleanup doesn't fail. */ + SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool)); + + return SVN_NO_ERROR; +} + +/* Issue 4340, "fs layer should reject filenames with trailing \n" */ +static svn_error_t * +filename_trailing_newline(const svn_test_opts_t *opts, + apr_pool_t *pool) +{ + apr_pool_t *subpool = svn_pool_create(pool); + svn_fs_t *fs; + svn_fs_txn_t *txn; + svn_fs_root_t *txn_root, *root; + svn_revnum_t youngest_rev = 0; + svn_error_t *err; + svn_boolean_t legacy_backend; + static const char contents[] = "foo\003bar"; + + /* The FS API wants \n to be permitted, but FSFS never implemented that, + * so for FSFS we expect errors rather than successes in some of the commits. + * Use a blacklist approach so that new FSes default to implementing the API + * as originally defined. */ + legacy_backend = (!strcmp(opts->fs_type, SVN_FS_TYPE_FSFS)); + + SVN_ERR(svn_test__create_fs(&fs, "test-repo-filename-trailing-newline", + opts, pool)); + + /* Revision 1: Add a directory /foo */ + SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); + SVN_ERR(svn_fs_make_dir(txn_root, "/foo", subpool)); + SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); + SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); + svn_pool_clear(subpool); + + /* Attempt to copy /foo to "/bar\n". This should fail on FSFS. */ + SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); + SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); + SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool)); + err = svn_fs_copy(root, "/foo", txn_root, "/bar\n", subpool); + if (!legacy_backend) + SVN_TEST_ASSERT(err == SVN_NO_ERROR); + else + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX); + + /* Attempt to create a file /foo/baz\n. This should fail on FSFS. */ + err = svn_fs_make_file(txn_root, "/foo/baz\n", subpool); + if (!legacy_backend) + SVN_TEST_ASSERT(err == SVN_NO_ERROR); + else + SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_PATH_SYNTAX); + + + /* Create another file, with contents. */ + if (!legacy_backend) + { + SVN_ERR(svn_fs_make_file(txn_root, "/bar\n/baz\n", subpool)); + SVN_ERR(svn_test__set_file_contents(txn_root, "bar\n/baz\n", + contents, pool)); + } + + if (!legacy_backend) + { + svn_revnum_t after_rev; + static svn_test__tree_entry_t expected_entries[] = { + { "foo", NULL }, + { "bar\n", NULL }, + { "foo/baz\n", "" }, + { "bar\n/baz\n", contents }, + { NULL, NULL } + }; + const char *expected_changed_paths[] = { + "/bar\n", + "/foo/baz\n", + "/bar\n/baz\n", + NULL + }; + apr_hash_t *expected_changes = apr_hash_make(pool); + int i; + + SVN_ERR(svn_fs_commit_txn(NULL, &after_rev, txn, subpool)); + SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev)); + + /* Validate the DAG. */ + SVN_ERR(svn_fs_revision_root(&root, fs, after_rev, pool)); + SVN_ERR(svn_test__validate_tree(root, expected_entries, 4, pool)); + + /* Validate changed-paths, where the problem originally occurred. */ + for (i = 0; expected_changed_paths[i]; i++) + svn_hash_sets(expected_changes, expected_changed_paths[i], + "undefined value"); + SVN_ERR(svn_test__validate_changes(root, expected_changes, pool)); + } + + return SVN_NO_ERROR; +} + /* ------------------------------------------------------------------------ */ /* The test table. */ @@ -4840,10 +5070,7 @@ struct svn_test_descriptor_t test_funcs[] = "basic commit"), SVN_TEST_OPTS_PASS(test_tree_node_validation, "testing tree validation helper"), - SVN_TEST_OPTS_WIMP(merging_commit, - "merging commit", - "needs to be written to match new" - " merge() algorithm expectations"), + SVN_TEST_OPTS_PASS(merging_commit, "merging commit"), SVN_TEST_OPTS_PASS(copy_test, "copying and tracking copy history"), SVN_TEST_OPTS_PASS(commit_date, @@ -4878,5 +5105,11 @@ struct svn_test_descriptor_t test_funcs[] = "test svn_fs_node_origin_rev"), SVN_TEST_OPTS_PASS(small_file_integrity, "create and modify small file"), + SVN_TEST_OPTS_PASS(node_history, + "test svn_fs_node_history"), + SVN_TEST_OPTS_PASS(delete_fs, + "test svn_fs_delete_fs"), + SVN_TEST_OPTS_PASS(filename_trailing_newline, + "filenames with trailing \\n might be rejected"), SVN_TEST_NULL }; |