| /* fs-test.c --- tests for the filesystem |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <apr_pools.h> |
| |
| #include "../svn_test.h" |
| |
| #include "svn_pools.h" |
| #include "svn_time.h" |
| #include "svn_string.h" |
| #include "svn_fs.h" |
| |
| #include "../svn_test_fs.h" |
| |
| #include "../../libsvn_fs_base/id.h" |
| #include "../../libsvn_fs_base/trail.h" |
| #include "../../libsvn_fs_base/bdb/txn-table.h" |
| #include "../../libsvn_fs_base/bdb/nodes-table.h" |
| #include "../../libsvn_fs_base/key-gen.h" |
| |
| #include "private/svn_fs_util.h" |
| #include "../../libsvn_delta/delta.h" |
| |
| #define SET_STR(ps, s) ((ps)->data = (s), (ps)->len = strlen(s)) |
| |
| |
| /*-----------------------------------------------------------------*/ |
| |
| /** The actual fs-tests called by `make check` **/ |
| |
| /* Create a filesystem. */ |
| static svn_error_t * |
| create_berkeley_filesystem(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_fs_t *fs; |
| |
| /* Create and close a repository. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-berkeley", opts, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Generic Berkeley DB error handler function. */ |
| static void |
| berkeley_error_handler(const char *errpfx, char *msg) |
| { |
| fprintf(stderr, "%s%s\n", errpfx ? errpfx : "", msg); |
| } |
| |
| |
| /* Open an existing filesystem. */ |
| static svn_error_t * |
| open_berkeley_filesystem(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_fs_t *fs, *fs2; |
| |
| /* Create and close a repository (using fs). */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-open-berkeley", opts, |
| pool)); |
| |
| /* Create a different fs object, and use it to re-open the |
| repository again. */ |
| SVN_ERR(svn_test__fs_new(&fs2, pool)); |
| SVN_ERR(svn_fs_open_berkeley(fs2, "test-repo-open-berkeley")); |
| |
| /* Provide a handler for Berkeley DB error messages. */ |
| SVN_ERR(svn_fs_set_berkeley_errcall(fs2, berkeley_error_handler)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Set *PRESENT to true if entry NAME is present in directory PATH |
| under ROOT, else set *PRESENT to false. */ |
| static svn_error_t * |
| check_entry(svn_fs_root_t *root, |
| const char *path, |
| const char *name, |
| svn_boolean_t *present, |
| apr_pool_t *pool) |
| { |
| apr_hash_t *entries; |
| svn_fs_dirent_t *ent; |
| |
| SVN_ERR(svn_fs_dir_entries(&entries, root, path, pool)); |
| ent = apr_hash_get(entries, name, APR_HASH_KEY_STRING); |
| |
| if (ent) |
| *present = TRUE; |
| else |
| *present = FALSE; |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Return an error if entry NAME is absent in directory PATH under ROOT. */ |
| static svn_error_t * |
| check_entry_present(svn_fs_root_t *root, const char *path, |
| const char *name, apr_pool_t *pool) |
| { |
| svn_boolean_t present = FALSE; |
| SVN_ERR(check_entry(root, path, name, &present, pool)); |
| |
| if (! present) |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "entry \"%s\" absent when it should be present", name); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Return an error if entry NAME is present in directory PATH under ROOT. */ |
| static svn_error_t * |
| check_entry_absent(svn_fs_root_t *root, const char *path, |
| const char *name, apr_pool_t *pool) |
| { |
| svn_boolean_t present = TRUE; |
| SVN_ERR(check_entry(root, path, name, &present, pool)); |
| |
| if (present) |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "entry \"%s\" present when it should be absent", name); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| struct check_id_args |
| { |
| svn_fs_t *fs; |
| const svn_fs_id_t *id; |
| svn_boolean_t present; |
| }; |
| |
| |
| static svn_error_t * |
| txn_body_check_id(void *baton, trail_t *trail) |
| { |
| struct check_id_args *args = baton; |
| node_revision_t *noderev; |
| svn_error_t *err; |
| |
| err = svn_fs_bdb__get_node_revision(&noderev, args->fs, args->id, |
| trail, trail->pool); |
| |
| if (err && (err->apr_err == SVN_ERR_FS_ID_NOT_FOUND)) |
| args->present = FALSE; |
| else if (! err) |
| args->present = TRUE; |
| else |
| { |
| svn_string_t *id_str = svn_fs_unparse_id(args->id, trail->pool); |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, err, |
| "error looking for node revision id \"%s\"", id_str->data); |
| } |
| svn_error_clear(err); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Set *PRESENT to true if node revision ID is present in filesystem |
| FS, else set *PRESENT to false. */ |
| static svn_error_t * |
| check_id(svn_fs_t *fs, const svn_fs_id_t *id, svn_boolean_t *present, |
| apr_pool_t *pool) |
| { |
| struct check_id_args args; |
| |
| args.id = id; |
| args.fs = fs; |
| SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_check_id, &args, TRUE, pool)); |
| |
| if (args.present) |
| *present = TRUE; |
| else |
| *present = FALSE; |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Return error if node revision ID is not present in FS. */ |
| static svn_error_t * |
| check_id_present(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool) |
| { |
| svn_boolean_t present = FALSE; |
| SVN_ERR(check_id(fs, id, &present, pool)); |
| |
| if (! present) |
| { |
| svn_string_t *id_str = svn_fs_unparse_id(id, pool); |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "node revision id \"%s\" absent when should be present", |
| id_str->data); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Return error if node revision ID is present in FS. */ |
| static svn_error_t * |
| check_id_absent(svn_fs_t *fs, const svn_fs_id_t *id, apr_pool_t *pool) |
| { |
| svn_boolean_t present = TRUE; |
| SVN_ERR(check_id(fs, id, &present, pool)); |
| |
| if (present) |
| { |
| svn_string_t *id_str = svn_fs_unparse_id(id, pool); |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "node revision id \"%s\" present when should be absent", |
| id_str->data); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Test that aborting a Subversion transaction works. |
| |
| NOTE: This function tests internal filesystem interfaces, not just |
| the public filesystem interface. */ |
| static svn_error_t * |
| abort_txn(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_fs_t *fs; |
| svn_fs_txn_t *txn1, *txn2; |
| svn_fs_root_t *txn1_root, *txn2_root; |
| const char *txn1_name, *txn2_name; |
| |
| /* Prepare two txns to receive the Greek tree. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-abort-txn", opts, |
| pool)); |
| SVN_ERR(svn_fs_begin_txn(&txn1, fs, 0, pool)); |
| SVN_ERR(svn_fs_begin_txn(&txn2, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn1_root, txn1, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn2_root, txn2, pool)); |
| |
| /* Save their names for later. */ |
| SVN_ERR(svn_fs_txn_name(&txn1_name, txn1, pool)); |
| SVN_ERR(svn_fs_txn_name(&txn2_name, txn2, pool)); |
| |
| /* Create greek trees in them. */ |
| SVN_ERR(svn_test__create_greek_tree(txn1_root, pool)); |
| SVN_ERR(svn_test__create_greek_tree(txn2_root, pool)); |
| |
| /* The test is to abort txn2, while leaving txn1. |
| * |
| * After we abort txn2, we make sure that a) all of its nodes |
| * disappeared from the database, and b) none of txn1's nodes |
| * disappeared. |
| * |
| * Finally, we create a third txn, and check that the name it got is |
| * different from the names of txn1 and txn2. |
| */ |
| |
| { |
| /* Yes, I really am this paranoid. */ |
| |
| /* IDs for every file in the standard Greek Tree. */ |
| const svn_fs_id_t |
| *t1_root_id, *t2_root_id, |
| *t1_iota_id, *t2_iota_id, |
| *t1_A_id, *t2_A_id, |
| *t1_mu_id, *t2_mu_id, |
| *t1_B_id, *t2_B_id, |
| *t1_lambda_id, *t2_lambda_id, |
| *t1_E_id, *t2_E_id, |
| *t1_alpha_id, *t2_alpha_id, |
| *t1_beta_id, *t2_beta_id, |
| *t1_F_id, *t2_F_id, |
| *t1_C_id, *t2_C_id, |
| *t1_D_id, *t2_D_id, |
| *t1_gamma_id, *t2_gamma_id, |
| *t1_H_id, *t2_H_id, |
| *t1_chi_id, *t2_chi_id, |
| *t1_psi_id, *t2_psi_id, |
| *t1_omega_id, *t2_omega_id, |
| *t1_G_id, *t2_G_id, |
| *t1_pi_id, *t2_pi_id, |
| *t1_rho_id, *t2_rho_id, |
| *t1_tau_id, *t2_tau_id; |
| |
| SVN_ERR(svn_fs_node_id(&t1_root_id, txn1_root, "", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_root_id, txn2_root, "", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_iota_id, txn1_root, "iota", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_iota_id, txn2_root, "iota", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_A_id, txn1_root, "/A", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_A_id, txn2_root, "/A", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_mu_id, txn1_root, "/A/mu", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_mu_id, txn2_root, "/A/mu", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_B_id, txn1_root, "/A/B", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_B_id, txn2_root, "/A/B", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_lambda_id, txn1_root, "/A/B/lambda", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_lambda_id, txn2_root, "/A/B/lambda", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_E_id, txn1_root, "/A/B/E", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_E_id, txn2_root, "/A/B/E", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_alpha_id, txn1_root, "/A/B/E/alpha", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_alpha_id, txn2_root, "/A/B/E/alpha", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_beta_id, txn1_root, "/A/B/E/beta", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_beta_id, txn2_root, "/A/B/E/beta", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_F_id, txn1_root, "/A/B/F", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_F_id, txn2_root, "/A/B/F", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_C_id, txn1_root, "/A/C", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_C_id, txn2_root, "/A/C", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_D_id, txn1_root, "/A/D", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_D_id, txn2_root, "/A/D", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_gamma_id, txn1_root, "/A/D/gamma", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_gamma_id, txn2_root, "/A/D/gamma", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_H_id, txn1_root, "/A/D/H", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_H_id, txn2_root, "/A/D/H", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_chi_id, txn1_root, "/A/D/H/chi", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_chi_id, txn2_root, "/A/D/H/chi", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_psi_id, txn1_root, "/A/D/H/psi", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_psi_id, txn2_root, "/A/D/H/psi", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_omega_id, txn1_root, "/A/D/H/omega", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_omega_id, txn2_root, "/A/D/H/omega", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_G_id, txn1_root, "/A/D/G", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_G_id, txn2_root, "/A/D/G", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_pi_id, txn1_root, "/A/D/G/pi", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_pi_id, txn2_root, "/A/D/G/pi", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_rho_id, txn1_root, "/A/D/G/rho", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_rho_id, txn2_root, "/A/D/G/rho", pool)); |
| SVN_ERR(svn_fs_node_id(&t1_tau_id, txn1_root, "/A/D/G/tau", pool)); |
| SVN_ERR(svn_fs_node_id(&t2_tau_id, txn2_root, "/A/D/G/tau", pool)); |
| |
| /* Abort just txn2. */ |
| SVN_ERR(svn_fs_abort_txn(txn2, pool)); |
| |
| /* Now test that all the nodes in txn2 at the time of the abort |
| * are gone, but all of the ones in txn1 are still there. |
| */ |
| |
| /* Check that every node rev in t2 has vanished from the fs. */ |
| SVN_ERR(check_id_absent(fs, t2_root_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_iota_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_A_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_mu_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_B_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_lambda_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_E_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_alpha_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_beta_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_F_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_C_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_D_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_gamma_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_H_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_chi_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_psi_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_omega_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_G_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_pi_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_rho_id, pool)); |
| SVN_ERR(check_id_absent(fs, t2_tau_id, pool)); |
| |
| /* Check that every node rev in t1 is still in the fs. */ |
| SVN_ERR(check_id_present(fs, t1_root_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_iota_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_A_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_mu_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_B_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_lambda_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_E_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_alpha_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_beta_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_F_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_C_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_D_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_gamma_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_H_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_chi_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_psi_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_omega_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_G_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_pi_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_rho_id, pool)); |
| SVN_ERR(check_id_present(fs, t1_tau_id, pool)); |
| } |
| |
| /* Test that txn2 itself is gone, by trying to open it. */ |
| { |
| svn_fs_txn_t *txn2_again; |
| svn_error_t *err; |
| |
| err = svn_fs_open_txn(&txn2_again, fs, txn2_name, pool); |
| if (err && (err->apr_err != SVN_ERR_FS_NO_SUCH_TRANSACTION)) |
| { |
| return svn_error_create |
| (SVN_ERR_FS_GENERAL, err, |
| "opening non-existent txn got wrong error"); |
| } |
| else if (! err) |
| { |
| return svn_error_create |
| (SVN_ERR_FS_GENERAL, NULL, |
| "opening non-existent txn failed to get error"); |
| } |
| svn_error_clear(err); |
| } |
| |
| /* Test that txn names are not recycled, by opening a new txn. */ |
| { |
| svn_fs_txn_t *txn3; |
| const char *txn3_name; |
| |
| SVN_ERR(svn_fs_begin_txn(&txn3, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_name(&txn3_name, txn3, pool)); |
| |
| if ((strcmp(txn3_name, txn2_name) == 0) |
| || (strcmp(txn3_name, txn1_name) == 0)) |
| { |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "txn name \"%s\" was recycled", txn3_name); |
| } |
| } |
| |
| /* Test that aborting a txn that's already committed fails. */ |
| { |
| svn_fs_txn_t *txn4; |
| const char *txn4_name; |
| svn_revnum_t new_rev; |
| const char *conflict; |
| svn_error_t *err; |
| |
| SVN_ERR(svn_fs_begin_txn(&txn4, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_name(&txn4_name, txn4, pool)); |
| SVN_ERR(svn_fs_commit_txn(&conflict, &new_rev, txn4, pool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(new_rev)); |
| err = svn_fs_abort_txn(txn4, pool); |
| if (! err) |
| return svn_error_create |
| (SVN_ERR_FS_GENERAL, NULL, |
| "expected error trying to abort a committed txn; got none"); |
| else if (err->apr_err != SVN_ERR_FS_TRANSACTION_NOT_MUTABLE) |
| return svn_error_create |
| (SVN_ERR_FS_GENERAL, err, |
| "got an unexpected error trying to abort a committed txn"); |
| else |
| svn_error_clear(err); |
| } |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* This tests deleting of mutable nodes. We build a tree in a |
| * transaction, then try to delete various items in the tree. We |
| * never commit the tree, so every entry being deleted points to a |
| * mutable node. |
| * |
| * ### todo: this test was written before commits worked. It might |
| * now be worthwhile to combine it with delete(). |
| */ |
| static svn_error_t * |
| delete_mutables(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_error_t *err; |
| |
| /* Prepare a txn to receive the greek tree. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-from-dir", opts, |
| pool)); |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| /* Create the greek tree. */ |
| SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); |
| |
| /* Baby, it's time to test like you've never tested before. We do |
| * the following, in this order: |
| * |
| * 1. Delete a single file somewhere, succeed. |
| * 2. Delete two files of three, then make sure the third remains. |
| * 3. Delete the third and last file. |
| * 4. Try again to delete the dir, succeed. |
| * 5. Delete one of the natively empty dirs, succeed. |
| * 6. Try to delete root, fail. |
| * 7. Try to delete a top-level file, succeed. |
| * |
| * Specifically, that's: |
| * |
| * 1. Delete A/D/gamma. |
| * 2. Delete A/D/G/pi, A/D/G/rho. |
| * 3. Delete A/D/G/tau. |
| * 4. Try again to delete A/D/G, succeed. |
| * 5. Delete A/C. |
| * 6. Try to delete /, fail. |
| * 7. Try to delete iota, succeed. |
| * |
| * Before and after each deletion or attempted deletion, we probe |
| * the affected directory, to make sure everything is as it should |
| * be. |
| */ |
| |
| /* 1 */ |
| { |
| const svn_fs_id_t *gamma_id; |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(check_id_absent(fs, gamma_id, pool)); |
| } |
| |
| /* 2 */ |
| { |
| const svn_fs_id_t *pi_id, *rho_id, *tau_id; |
| SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "A/D/G/pi", pool)); |
| SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "A/D/G/rho", pool)); |
| SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(check_id_present(fs, pi_id, pool)); |
| SVN_ERR(check_id_present(fs, rho_id, pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/G/pi", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(check_id_absent(fs, pi_id, pool)); |
| SVN_ERR(check_id_present(fs, rho_id, pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/G/rho", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(check_id_absent(fs, pi_id, pool)); |
| SVN_ERR(check_id_absent(fs, rho_id, pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| } |
| |
| /* 3 */ |
| { |
| const svn_fs_id_t *tau_id; |
| SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "A/D/G/tau", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/G/tau", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(check_id_absent(fs, tau_id, pool)); |
| } |
| |
| /* 4 */ |
| { |
| const svn_fs_id_t *G_id; |
| SVN_ERR(svn_fs_node_id(&G_id, txn_root, "A/D/G", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool)); |
| SVN_ERR(check_id_present(fs, G_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/G", pool)); /* succeed */ |
| |
| SVN_ERR(check_entry_absent(txn_root, "A/D", "G", pool)); |
| SVN_ERR(check_id_absent(fs, G_id, pool)); |
| } |
| |
| /* 5 */ |
| { |
| const svn_fs_id_t *C_id; |
| SVN_ERR(svn_fs_node_id(&C_id, txn_root, "A/C", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "A", "C", pool)); |
| SVN_ERR(check_id_present(fs, C_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "A/C", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "A", "C", pool)); |
| SVN_ERR(check_id_absent(fs, C_id, pool)); |
| } |
| |
| /* 6 */ |
| { |
| const svn_fs_id_t *root_id; |
| SVN_ERR(svn_fs_node_id(&root_id, txn_root, "", pool)); |
| |
| err = svn_fs_delete(txn_root, "", pool); |
| |
| if (err && (err->apr_err != SVN_ERR_FS_ROOT_DIR)) |
| { |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, err, |
| "deleting root directory got wrong error"); |
| } |
| else if (! err) |
| { |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "deleting root directory failed to get error"); |
| } |
| svn_error_clear(err); |
| |
| SVN_ERR(check_id_present(fs, root_id, pool)); |
| } |
| |
| /* 7 */ |
| { |
| const svn_fs_id_t *iota_id; |
| SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "", "iota", pool)); |
| SVN_ERR(check_id_present(fs, iota_id, pool)); |
| |
| SVN_ERR(svn_fs_delete(txn_root, "iota", pool)); |
| |
| SVN_ERR(check_entry_absent(txn_root, "", "iota", pool)); |
| SVN_ERR(check_id_absent(fs, iota_id, pool)); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* This tests deleting in general. |
| * |
| * ### todo: this test was written after (and independently of) |
| * delete_mutables(). It might be worthwhile to combine them. |
| */ |
| static svn_error_t * |
| delete(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 new_rev; |
| |
| /* This function tests 5 cases: |
| * |
| * 1. Delete mutable file. |
| * 2. Delete mutable directory. |
| * 3. Delete mutable directory with immutable nodes. |
| * 4. Delete immutable file. |
| * 5. Delete immutable directory. |
| */ |
| |
| /* Prepare a txn to receive the greek tree. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-del-tree", opts, |
| pool)); |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| /* Create the greek tree. */ |
| SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); |
| |
| /* 1. Delete mutable file. */ |
| { |
| const svn_fs_id_t *iota_id, *gamma_id; |
| static svn_test__tree_entry_t expected_entries[] = { |
| /* path, contents (0 = dir) */ |
| { "A", 0 }, |
| { "A/mu", "This is the file 'mu'.\n" }, |
| { "A/B", 0 }, |
| { "A/B/lambda", "This is the file 'lambda'.\n" }, |
| { "A/B/E", 0 }, |
| { "A/B/E/alpha", "This is the file 'alpha'.\n" }, |
| { "A/B/E/beta", "This is the file 'beta'.\n" }, |
| { "A/C", 0 }, |
| { "A/B/F", 0 }, |
| { "A/D", 0 }, |
| { "A/D/G", 0 }, |
| { "A/D/G/pi", "This is the file 'pi'.\n" }, |
| { "A/D/G/rho", "This is the file 'rho'.\n" }, |
| { "A/D/G/tau", "This is the file 'tau'.\n" }, |
| { "A/D/H", 0 }, |
| { "A/D/H/chi", "This is the file 'chi'.\n" }, |
| { "A/D/H/psi", "This is the file 'psi'.\n" }, |
| { "A/D/H/omega", "This is the file 'omega'.\n" } |
| }; |
| |
| /* Check nodes revision ID is gone. */ |
| SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool)); |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool)); |
| |
| SVN_ERR(check_entry_present(txn_root, "", "iota", pool)); |
| SVN_ERR(check_id_present(fs, iota_id, pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| |
| /* Try deleting mutable files. */ |
| SVN_ERR(svn_fs_delete(txn_root, "iota", pool)); |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "", "iota", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(check_id_absent(fs, iota_id, pool)); |
| SVN_ERR(check_id_absent(fs, gamma_id, pool)); |
| |
| /* Validate the tree. */ |
| SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool)); |
| } |
| /* Abort transaction. */ |
| SVN_ERR(svn_fs_abort_txn(txn, pool)); |
| |
| /* 2. Delete mutable directory. */ |
| |
| /* Prepare a txn to receive the greek tree. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| /* Create the greek tree. */ |
| SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); |
| |
| { |
| const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id, |
| *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id, |
| *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id; |
| |
| /* Check nodes revision ID is gone. */ |
| SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool)); |
| SVN_ERR(check_entry_present(txn_root, "", "A", pool)); |
| SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "mu", pool)); |
| SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "B", pool)); |
| SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool)); |
| SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool)); |
| SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool)); |
| SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool)); |
| SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool)); |
| SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "C", pool)); |
| SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "D", pool)); |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool)); |
| SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool)); |
| SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool)); |
| SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool)); |
| SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool)); |
| SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| |
| /* Try deleting a mutable empty dir. */ |
| SVN_ERR(svn_fs_delete(txn_root, "A/C", pool)); |
| SVN_ERR(svn_fs_delete(txn_root, "A/B/F", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "A", "C", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "A/B", "F", pool)); |
| SVN_ERR(check_id_absent(fs, C_id, pool)); |
| SVN_ERR(check_id_absent(fs, F_id, pool)); |
| |
| /* Now delete a mutable non-empty dir. */ |
| SVN_ERR(svn_fs_delete(txn_root, "A", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "", "A", pool)); |
| SVN_ERR(check_id_absent(fs, A_id, pool)); |
| SVN_ERR(check_id_absent(fs, mu_id, pool)); |
| SVN_ERR(check_id_absent(fs, B_id, pool)); |
| SVN_ERR(check_id_absent(fs, lambda_id, pool)); |
| SVN_ERR(check_id_absent(fs, E_id, pool)); |
| SVN_ERR(check_id_absent(fs, alpha_id, pool)); |
| SVN_ERR(check_id_absent(fs, beta_id, pool)); |
| SVN_ERR(check_id_absent(fs, D_id, pool)); |
| SVN_ERR(check_id_absent(fs, gamma_id, pool)); |
| SVN_ERR(check_id_absent(fs, H_id, pool)); |
| SVN_ERR(check_id_absent(fs, chi_id, pool)); |
| SVN_ERR(check_id_absent(fs, psi_id, pool)); |
| SVN_ERR(check_id_absent(fs, omega_id, pool)); |
| SVN_ERR(check_id_absent(fs, G_id, pool)); |
| SVN_ERR(check_id_absent(fs, pi_id, pool)); |
| SVN_ERR(check_id_absent(fs, rho_id, pool)); |
| SVN_ERR(check_id_absent(fs, tau_id, pool)); |
| |
| /* Validate the tree. */ |
| { |
| static svn_test__tree_entry_t expected_entries[] = { |
| /* path, contents (0 = dir) */ |
| { "iota", "This is the file 'iota'.\n" } }; |
| SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool)); |
| } |
| } |
| |
| /* Abort transaction. */ |
| SVN_ERR(svn_fs_abort_txn(txn, pool)); |
| |
| /* 3. Delete mutable directory with immutable nodes. */ |
| |
| /* Prepare a txn to receive the greek tree. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| /* Create the greek tree. */ |
| SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); |
| |
| /* Commit the greek tree. */ |
| SVN_ERR(svn_fs_commit_txn(NULL, &new_rev, txn, pool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(new_rev)); |
| |
| /* Create new transaction. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| { |
| const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id, |
| *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id, |
| *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id, *sigma_id; |
| |
| /* Create A/D/G/sigma. This makes all components of A/D/G |
| mutable. */ |
| SVN_ERR(svn_fs_make_file(txn_root, "A/D/G/sigma", pool)); |
| SVN_ERR(svn_test__set_file_contents(txn_root, "A/D/G/sigma", |
| "This is another file 'sigma'.\n", pool)); |
| |
| /* Check that mutable node-revision-IDs are removed and immutable |
| ones still exist. */ |
| SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool)); |
| SVN_ERR(check_entry_present(txn_root, "", "A", pool)); |
| SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "mu", pool)); |
| SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "B", pool)); |
| SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool)); |
| SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool)); |
| SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool)); |
| SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool)); |
| SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool)); |
| SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "C", pool)); |
| SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "D", pool)); |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool)); |
| SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool)); |
| SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool)); |
| SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool)); |
| SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool)); |
| SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| SVN_ERR(svn_fs_node_id(&sigma_id, txn_root, "/A/D/G/sigma", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "sigma", pool)); |
| |
| /* Delete "A" */ |
| SVN_ERR(svn_fs_delete(txn_root, "A", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "", "A", pool)); |
| SVN_ERR(check_id_absent(fs, A_id, pool)); |
| SVN_ERR(check_id_present(fs, mu_id, pool)); |
| SVN_ERR(check_id_present(fs, B_id, pool)); |
| SVN_ERR(check_id_present(fs, lambda_id, pool)); |
| SVN_ERR(check_id_present(fs, E_id, pool)); |
| SVN_ERR(check_id_present(fs, alpha_id, pool)); |
| SVN_ERR(check_id_present(fs, beta_id, pool)); |
| SVN_ERR(check_id_present(fs, F_id, pool)); |
| SVN_ERR(check_id_present(fs, C_id, pool)); |
| SVN_ERR(check_id_absent(fs, D_id, pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| SVN_ERR(check_id_present(fs, H_id, pool)); |
| SVN_ERR(check_id_present(fs, chi_id, pool)); |
| SVN_ERR(check_id_present(fs, psi_id, pool)); |
| SVN_ERR(check_id_present(fs, omega_id, pool)); |
| SVN_ERR(check_id_absent(fs, G_id, pool)); |
| SVN_ERR(check_id_present(fs, pi_id, pool)); |
| SVN_ERR(check_id_present(fs, rho_id, pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| SVN_ERR(check_id_absent(fs, sigma_id, pool)); |
| |
| /* Validate the tree. */ |
| { |
| static svn_test__tree_entry_t expected_entries[] = { |
| /* path, contents (0 = dir) */ |
| { "iota", "This is the file 'iota'.\n" } |
| }; |
| |
| SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool)); |
| } |
| } |
| |
| /* Abort transaction. */ |
| SVN_ERR(svn_fs_abort_txn(txn, pool)); |
| |
| /* 4. Delete immutable file. */ |
| |
| /* Create new transaction. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| { |
| const svn_fs_id_t *iota_id, *gamma_id; |
| |
| /* Check nodes revision ID is present. */ |
| SVN_ERR(svn_fs_node_id(&iota_id, txn_root, "iota", pool)); |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "A/D/gamma", pool)); |
| SVN_ERR(check_entry_present(txn_root, "", "iota", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(check_id_present(fs, iota_id, pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| |
| /* Delete some files. */ |
| SVN_ERR(svn_fs_delete(txn_root, "iota", pool)); |
| SVN_ERR(svn_fs_delete(txn_root, "A/D/gamma", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "", "iota", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "A/D", "iota", pool)); |
| SVN_ERR(check_id_present(fs, iota_id, pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| |
| /* Validate the tree. */ |
| { |
| static svn_test__tree_entry_t expected_entries[] = { |
| /* path, contents (0 = dir) */ |
| { "A", 0 }, |
| { "A/mu", "This is the file 'mu'.\n" }, |
| { "A/B", 0 }, |
| { "A/B/lambda", "This is the file 'lambda'.\n" }, |
| { "A/B/E", 0 }, |
| { "A/B/E/alpha", "This is the file 'alpha'.\n" }, |
| { "A/B/E/beta", "This is the file 'beta'.\n" }, |
| { "A/B/F", 0 }, |
| { "A/C", 0 }, |
| { "A/D", 0 }, |
| { "A/D/G", 0 }, |
| { "A/D/G/pi", "This is the file 'pi'.\n" }, |
| { "A/D/G/rho", "This is the file 'rho'.\n" }, |
| { "A/D/G/tau", "This is the file 'tau'.\n" }, |
| { "A/D/H", 0 }, |
| { "A/D/H/chi", "This is the file 'chi'.\n" }, |
| { "A/D/H/psi", "This is the file 'psi'.\n" }, |
| { "A/D/H/omega", "This is the file 'omega'.\n" } |
| }; |
| SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 18, pool)); |
| } |
| } |
| |
| /* Abort transaction. */ |
| SVN_ERR(svn_fs_abort_txn(txn, pool)); |
| |
| /* 5. Delete immutable directory. */ |
| |
| /* Create new transaction. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, new_rev, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| |
| { |
| const svn_fs_id_t *A_id, *mu_id, *B_id, *lambda_id, *E_id, *alpha_id, |
| *beta_id, *F_id, *C_id, *D_id, *gamma_id, *H_id, *chi_id, |
| *psi_id, *omega_id, *G_id, *pi_id, *rho_id, *tau_id; |
| |
| /* Check nodes revision ID is present. */ |
| SVN_ERR(svn_fs_node_id(&A_id, txn_root, "/A", pool)); |
| SVN_ERR(check_entry_present(txn_root, "", "A", pool)); |
| SVN_ERR(svn_fs_node_id(&mu_id, txn_root, "/A/mu", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "mu", pool)); |
| SVN_ERR(svn_fs_node_id(&B_id, txn_root, "/A/B", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "B", pool)); |
| SVN_ERR(svn_fs_node_id(&lambda_id, txn_root, "/A/B/lambda", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "lambda", pool)); |
| SVN_ERR(svn_fs_node_id(&E_id, txn_root, "/A/B/E", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "E", pool)); |
| SVN_ERR(svn_fs_node_id(&alpha_id, txn_root, "/A/B/E/alpha", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "alpha", pool)); |
| SVN_ERR(svn_fs_node_id(&beta_id, txn_root, "/A/B/E/beta", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B/E", "beta", pool)); |
| SVN_ERR(svn_fs_node_id(&F_id, txn_root, "/A/B/F", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/B", "F", pool)); |
| SVN_ERR(svn_fs_node_id(&C_id, txn_root, "/A/C", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "C", pool)); |
| SVN_ERR(svn_fs_node_id(&D_id, txn_root, "/A/D", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A", "D", pool)); |
| SVN_ERR(svn_fs_node_id(&gamma_id, txn_root, "/A/D/gamma", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "gamma", pool)); |
| SVN_ERR(svn_fs_node_id(&H_id, txn_root, "/A/D/H", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "H", pool)); |
| SVN_ERR(svn_fs_node_id(&chi_id, txn_root, "/A/D/H/chi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "chi", pool)); |
| SVN_ERR(svn_fs_node_id(&psi_id, txn_root, "/A/D/H/psi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "psi", pool)); |
| SVN_ERR(svn_fs_node_id(&omega_id, txn_root, "/A/D/H/omega", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/H", "omega", pool)); |
| SVN_ERR(svn_fs_node_id(&G_id, txn_root, "/A/D/G", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D", "G", pool)); |
| SVN_ERR(svn_fs_node_id(&pi_id, txn_root, "/A/D/G/pi", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "pi", pool)); |
| SVN_ERR(svn_fs_node_id(&rho_id, txn_root, "/A/D/G/rho", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "rho", pool)); |
| SVN_ERR(svn_fs_node_id(&tau_id, txn_root, "/A/D/G/tau", pool)); |
| SVN_ERR(check_entry_present(txn_root, "A/D/G", "tau", pool)); |
| |
| /* Delete "A" */ |
| SVN_ERR(svn_fs_delete(txn_root, "A", pool)); |
| SVN_ERR(check_entry_absent(txn_root, "", "A", pool)); |
| SVN_ERR(check_id_present(fs, A_id, pool)); |
| SVN_ERR(check_id_present(fs, mu_id, pool)); |
| SVN_ERR(check_id_present(fs, B_id, pool)); |
| SVN_ERR(check_id_present(fs, lambda_id, pool)); |
| SVN_ERR(check_id_present(fs, E_id, pool)); |
| SVN_ERR(check_id_present(fs, alpha_id, pool)); |
| SVN_ERR(check_id_present(fs, beta_id, pool)); |
| SVN_ERR(check_id_present(fs, F_id, pool)); |
| SVN_ERR(check_id_present(fs, C_id, pool)); |
| SVN_ERR(check_id_present(fs, D_id, pool)); |
| SVN_ERR(check_id_present(fs, gamma_id, pool)); |
| SVN_ERR(check_id_present(fs, H_id, pool)); |
| SVN_ERR(check_id_present(fs, chi_id, pool)); |
| SVN_ERR(check_id_present(fs, psi_id, pool)); |
| SVN_ERR(check_id_present(fs, omega_id, pool)); |
| SVN_ERR(check_id_present(fs, G_id, pool)); |
| SVN_ERR(check_id_present(fs, pi_id, pool)); |
| SVN_ERR(check_id_present(fs, rho_id, pool)); |
| SVN_ERR(check_id_present(fs, tau_id, pool)); |
| |
| /* Validate the tree. */ |
| { |
| static svn_test__tree_entry_t expected_entries[] = { |
| /* path, contents (0 = dir) */ |
| { "iota", "This is the file 'iota'.\n" } |
| }; |
| SVN_ERR(svn_test__validate_tree(txn_root, expected_entries, 1, pool)); |
| } |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| canonicalize_abspath(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| apr_size_t i; |
| const char *paths[21][2] = |
| /* in out */ |
| { { NULL, NULL }, |
| { "", "/" }, |
| { "/", "/" }, |
| { "//", "/" }, |
| { "///", "/" }, |
| { "foo", "/foo" }, |
| { "foo/", "/foo" }, |
| { "foo//", "/foo" }, |
| { "/foo", "/foo" }, |
| { "/foo/", "/foo" }, |
| { "/foo//", "/foo" }, |
| { "//foo//", "/foo" }, |
| { "foo/bar", "/foo/bar" }, |
| { "foo/bar/", "/foo/bar" }, |
| { "foo/bar//", "/foo/bar" }, |
| { "foo//bar", "/foo/bar" }, |
| { "foo//bar/", "/foo/bar" }, |
| { "foo//bar//", "/foo/bar" }, |
| { "/foo//bar//", "/foo/bar" }, |
| { "//foo//bar//", "/foo/bar" }, |
| { "///foo///bar///baz///", "/foo/bar/baz" }, |
| }; |
| |
| for (i = 0; i < (sizeof(paths) / 2 / sizeof(const char *)); i++) |
| { |
| const char *input = paths[i][0]; |
| const char *output = paths[i][1]; |
| const char *actual = svn_fs__canonicalize_abspath(input, pool); |
| |
| if ((! output) && (! actual)) |
| continue; |
| if ((! output) && actual) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "expected NULL path; got '%s'", actual); |
| if (output && (! actual)) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "expected '%s' path; got NULL", output); |
| if (strcmp(output, actual)) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "expected '%s' path; got '%s'", |
| output, actual); |
| } |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| create_within_copy(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| apr_pool_t *spool = svn_pool_create(pool); |
| svn_fs_t *fs; |
| svn_fs_txn_t *txn; |
| svn_fs_root_t *txn_root, *rev_root; |
| svn_revnum_t youngest_rev = 0; |
| |
| /* Create a filesystem and repository. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-create-within-copy", opts, |
| pool)); |
| |
| /*** Revision 1: Create the greek tree in revision. ***/ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool)); |
| SVN_ERR(svn_test__create_greek_tree(txn_root, spool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(spool); |
| |
| /*** Revision 2: Copy A/D to A/D3 ***/ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool)); |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_copy(rev_root, "A/D", txn_root, "A/D3", spool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(spool); |
| |
| /*** Revision 3: Copy A/D/G to A/D/G2 ***/ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool)); |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_copy(rev_root, "A/D/G", txn_root, "A/D/G2", spool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(spool); |
| |
| /*** Revision 4: Copy A/D to A/D2 and create up and I in the existing |
| A/D/G2, in the new A/D2, and in the nested, new A/D2/G2 ***/ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool)); |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_copy(rev_root, "A/D", txn_root, "A/D2", spool)); |
| SVN_ERR(svn_fs_make_dir(txn_root, "A/D/G2/I", spool)); |
| SVN_ERR(svn_fs_make_file(txn_root, "A/D/G2/up", spool)); |
| SVN_ERR(svn_fs_make_dir(txn_root, "A/D2/I", spool)); |
| SVN_ERR(svn_fs_make_file(txn_root, "A/D2/up", spool)); |
| SVN_ERR(svn_fs_make_dir(txn_root, "A/D2/G2/I", spool)); |
| SVN_ERR(svn_fs_make_file(txn_root, "A/D2/G2/up", spool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(spool); |
| |
| /*** Revision 5: Create A/D3/down and A/D3/J ***/ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, spool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, spool)); |
| SVN_ERR(svn_fs_make_file(txn_root, "A/D3/down", spool)); |
| SVN_ERR(svn_fs_make_dir(txn_root, "A/D3/J", spool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, spool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(spool); |
| |
| { |
| /* New items should have same CopyID as their parent */ |
| const char *pathgroup[4][3] = |
| { |
| { "A/D/G2", |
| "A/D/G2/I", |
| "A/D/G2/up" }, |
| { "A/D2", |
| "A/D2/I", |
| "A/D2/up" }, |
| { "A/D2/G2", |
| "A/D2/G2/I", |
| "A/D2/G2/up" }, |
| { "A/D3", |
| "A/D3/down", |
| "A/D3/J" } |
| }; |
| int i; |
| |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, spool)); |
| |
| for (i = 0; i < 4; i++) |
| { |
| const svn_fs_id_t *lead_id; |
| const char *lead_copy_id; |
| int j; |
| |
| /* Get the FSIdentifier for the first path in each group... */ |
| SVN_ERR(svn_fs_node_id(&lead_id, rev_root, pathgroup[i][0], spool)); |
| lead_copy_id = svn_fs_base__id_copy_id(lead_id); |
| |
| for (j = 1; j < 3; j++) |
| { |
| const svn_fs_id_t *id; |
| const char *copy_id; |
| |
| /* ... and make sure the other members of the group have |
| the same copy_id component as the 'lead' member. */ |
| |
| SVN_ERR(svn_fs_node_id(&id, rev_root, pathgroup[i][j], spool)); |
| copy_id = svn_fs_base__id_copy_id(id); |
| |
| if (strcmp(copy_id, lead_copy_id) != 0) |
| return svn_error_createf |
| (SVN_ERR_TEST_FAILED, NULL, |
| "'%s' id: expected copy_id '%s'; got copy_id '%s'", |
| pathgroup[i][j], lead_copy_id, copy_id); |
| } |
| } |
| svn_pool_clear(spool); |
| } |
| |
| svn_pool_destroy(spool); |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Test the skip delta support by commiting so many changes to a file |
| * that some of its older revisions become reachable by skip deltas, |
| * then try retrieving those revisions. |
| */ |
| static svn_error_t * |
| skip_deltas(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, *rev_root; |
| apr_pool_t *subpool = svn_pool_create(pool); |
| svn_revnum_t youngest_rev = 0; |
| const char *one_line = "This is a line in file 'f'.\n"; |
| svn_stringbuf_t *f = svn_stringbuf_create(one_line, pool); |
| |
| /* Create a filesystem and repository. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-skip-deltas", opts, |
| pool)); |
| |
| /* Create the file. */ |
| 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_file(txn_root, "f", subpool)); |
| SVN_ERR(svn_test__set_file_contents(txn_root, "f", f->data, subpool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool)); |
| svn_pool_clear(subpool); |
| |
| /* Now, commit changes to the file 128 times. */ |
| while (youngest_rev <= 128) |
| { |
| /* Append another line to the ever-growing file contents. */ |
| svn_stringbuf_appendcstr(f, one_line); |
| |
| /* Commit the new contents. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); |
| SVN_ERR(svn_test__set_file_contents(txn_root, "f", f->data, subpool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| SVN_ERR(svn_fs_deltify_revision(fs, youngest_rev, subpool)); |
| svn_pool_clear(subpool); |
| } |
| |
| /* Now go back and check revision 1. */ |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, pool)); |
| SVN_ERR(svn_test__get_file_contents(rev_root, "f", &f, pool)); |
| if (strcmp(one_line, f->data) != 0) |
| return svn_error_createf |
| (SVN_ERR_TEST_FAILED, NULL, |
| "Wrong contents. Expected:\n '%s'\nGot:\n '%s'\n", |
| one_line, f->data); |
| |
| svn_pool_destroy(subpool); |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Trail-ish helpers for redundant_copy(). */ |
| struct get_txn_args |
| { |
| transaction_t **txn; |
| const char *txn_name; |
| svn_fs_t *fs; |
| }; |
| |
| static svn_error_t * |
| txn_body_get_txn(void *baton, trail_t *trail) |
| { |
| struct get_txn_args *args = baton; |
| return svn_fs_bdb__get_txn(args->txn, args->fs, args->txn_name, |
| trail, trail->pool); |
| } |
| |
| |
| static svn_error_t * |
| redundant_copy(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_fs_t *fs; |
| svn_fs_txn_t *txn; |
| const char *txn_name; |
| transaction_t *transaction; |
| svn_fs_root_t *txn_root, *rev_root; |
| const svn_fs_id_t *old_D_id, *new_D_id; |
| svn_revnum_t youngest_rev = 0; |
| struct get_txn_args args; |
| |
| /* Create a filesystem and repository. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-redundant-copy", opts, |
| pool)); |
| |
| /* Create the greek tree in revision 1. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| SVN_ERR(svn_test__create_greek_tree(txn_root, pool)); |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, pool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| |
| /* In a transaction, copy A to Z. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, pool)); |
| SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool)); |
| SVN_ERR(svn_fs_revision_root(&rev_root, fs, youngest_rev, pool)); |
| SVN_ERR(svn_fs_copy(rev_root, "A", txn_root, "Z", pool)); |
| |
| /* Now, examine the transaction. There should have been only one |
| copy there. */ |
| args.fs = fs; |
| args.txn_name = txn_name; |
| args.txn = &transaction; |
| SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_txn, &args, FALSE, pool)); |
| if (transaction->copies->nelts != 1) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "Expected 1 copy; got %d", |
| transaction->copies->nelts); |
| |
| /* Get the node-rev-id for A/D (the reason will be clear a little later). */ |
| SVN_ERR(svn_fs_node_id(&old_D_id, txn_root, "A/D", pool)); |
| |
| /* Now copy A/D/G Z/D/G. */ |
| SVN_ERR(svn_fs_copy(rev_root, "A/D/G", txn_root, "Z/D/G", pool)); |
| |
| /* Now, examine the transaction. There should still only have been |
| one copy operation that "took". */ |
| SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_get_txn, &args, FALSE, pool)); |
| if (transaction->copies->nelts != 1) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "Expected only 1 copy; got %d", |
| transaction->copies->nelts); |
| |
| /* Finally, check the node-rev-id for "Z/D" -- it should never have |
| been made mutable (since the second copy should not have taken |
| place). */ |
| SVN_ERR(svn_fs_node_id(&new_D_id, txn_root, "A/D", pool)); |
| if (! svn_string_compare(svn_fs_unparse_id(old_D_id, pool), |
| svn_fs_unparse_id(new_D_id, pool))) |
| return svn_error_create |
| (SVN_ERR_TEST_FAILED, NULL, |
| "Expected equivalent node-rev-ids; got differing ones"); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| orphaned_textmod_change(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_txdelta_window_handler_t wh_func; |
| void *wh_baton; |
| apr_hash_t *changed_paths; |
| |
| /* Create a filesystem and repository. */ |
| SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-orphaned-changes", opts, |
| pool)); |
| |
| /* Revision 1: Create and commit the greek tree. */ |
| SVN_ERR(svn_fs_begin_txn(&txn, fs, 0, subpool)); |
| SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool)); |
| SVN_ERR(svn_test__create_greek_tree(txn_root, 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); |
| |
| /* Revision 2: Start to change "iota", but don't complete the work. */ |
| 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_apply_textdelta |
| (&wh_func, &wh_baton, txn_root, "iota", NULL, NULL, subpool)); |
| |
| /* Don't send any delta windows, but do commit the transaction. |
| According to the FS API docs, this is not a legal codepath. But |
| this requirement on the API was added *after* its BDB |
| implementation, and the BDB backend can't enforce compliance with |
| the additional API rules in this case. So we are really just |
| testing that misbehaving callers don't introduce more damage to |
| the repository than they have to. */ |
| SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool)); |
| SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); |
| svn_pool_clear(subpool); |
| |
| /* Fetch changed paths for the youngest revision. We should find none. */ |
| SVN_ERR(svn_fs_revision_root(&root, fs, youngest_rev, subpool)); |
| SVN_ERR(svn_fs_paths_changed(&changed_paths, root, subpool)); |
| if (apr_hash_count(changed_paths) != 0) |
| { |
| svn_fs_path_change_t *change = apr_hash_get(changed_paths, "/iota", |
| APR_HASH_KEY_STRING); |
| if (change && change->text_mod) |
| return svn_error_create(SVN_ERR_TEST_FAILED, NULL, |
| "Got unexpected textmods changed path " |
| "for 'iota'"); |
| else |
| return svn_error_create(SVN_ERR_TEST_FAILED, NULL, |
| "Got non-empty changed paths hash where empty " |
| "one expected"); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| key_test(apr_pool_t *pool) |
| { |
| int i; |
| const char *keys[9][2] = { |
| { "0", "1" }, |
| { "9", "a" }, |
| { "zzzzz", "100000" }, |
| { "z000000zzzzzz", "z000001000000" }, |
| { "97hnq33jx2a", "97hnq33jx2b" }, |
| { "97hnq33jx2z", "97hnq33jx30" }, |
| { "999", "99a" }, |
| { "a9z", "aa0" }, |
| { "z", "10" } |
| }; |
| |
| for (i = 0; i < 9; i++) |
| { |
| char gen_key[MAX_KEY_SIZE]; |
| const char *orig_key = keys[i][0]; |
| const char *next_key = keys[i][1]; |
| apr_size_t len, olen; |
| |
| len = strlen(orig_key); |
| olen = len; |
| |
| svn_fs_base__next_key(orig_key, &len, gen_key); |
| if (! (((len == olen) || (len == (olen + 1))) |
| && (strlen(next_key) == len) |
| && (strcmp(next_key, gen_key) == 0))) |
| { |
| return svn_error_createf |
| (SVN_ERR_FS_GENERAL, NULL, |
| "failed to increment key \"%s\" correctly\n" |
| " expected: %s\n" |
| " actual: %s", |
| orig_key, next_key, gen_key); |
| } |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* ------------------------------------------------------------------------ */ |
| |
| /* The test table. */ |
| |
| static int max_threads = 2; |
| |
| static struct svn_test_descriptor_t test_funcs[] = |
| { |
| SVN_TEST_NULL, |
| SVN_TEST_OPTS_PASS(create_berkeley_filesystem, |
| "svn_fs_create_berkeley"), |
| SVN_TEST_OPTS_PASS(open_berkeley_filesystem, |
| "open an existing Berkeley DB filesystem"), |
| SVN_TEST_OPTS_PASS(delete_mutables, |
| "delete mutable nodes from directories"), |
| SVN_TEST_OPTS_PASS(delete, |
| "delete nodes tree"), |
| SVN_TEST_OPTS_PASS(abort_txn, |
| "abort a transaction"), |
| SVN_TEST_OPTS_PASS(create_within_copy, |
| "create new items within a copied directory"), |
| SVN_TEST_OPTS_PASS(canonicalize_abspath, |
| "test svn_fs__canonicalize_abspath"), |
| SVN_TEST_OPTS_PASS(skip_deltas, |
| "test skip deltas"), |
| SVN_TEST_OPTS_PASS(redundant_copy, |
| "ensure no-op for redundant copies"), |
| SVN_TEST_OPTS_PASS(orphaned_textmod_change, |
| "test for orphaned textmod changed paths"), |
| SVN_TEST_PASS2(key_test, |
| "testing sequential alphanumeric key generation"), |
| SVN_TEST_NULL |
| }; |
| |
| SVN_TEST_MAIN |