/* * db-test.c : test the wc_db subsystem * * ==================================================================== * 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 #include #include "svn_types.h" /* Make sure SVN_DEPRECATED is defined as empty before including svn_io.h. We don't want to trigger deprecation warnings. */ #ifdef SVN_DEPRECATED #undef SVN_DEPRECATED #endif #define SVN_DEPRECATED #include "svn_io.h" #include "svn_dirent_uri.h" #include "svn_pools.h" #include "private/svn_sqlite.h" #include "../../libsvn_wc/wc_db.h" #include "private/svn_wc_private.h" #include "../svn_test.h" #include "utils.h" #define ROOT_ONE "http://example.com/one" #define ROOT_TWO "http://example.com/two" #define ROOT_THREE "http://example.com/three" #define UUID_ONE "uuid1" #define UUID_TWO "uuid2" #define UUID_THREE "uuid3" #define TIME_1 1235142208 #define TIME_2 1235142268 #define TIME_3 1235142328 #define TIME_1s APR_STRINGIFY(TIME_1) "000000" #define TIME_2s APR_STRINGIFY(TIME_2) "000000" #define TIME_3s APR_STRINGIFY(TIME_3) "000000" #define TIME_1a apr_time_from_sec(TIME_1) #define TIME_2a apr_time_from_sec(TIME_2) #define TIME_3a apr_time_from_sec(TIME_3) #define AUTHOR_1 "johndoe" #define AUTHOR_2 "janedoe" /* Some arbitrary checksum values */ #define MD5_1 "2d18c5e57e84c5b8a5e9a6e13fa394dc" #define SHA1_1 "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" #define F_TC_DATA "(conflict F file update edited deleted (version 22 " ROOT_ONE " 1 2 branch1/ft/F none) (version 22 " ROOT_ONE " 1 3 branch1/ft/F file))" #define G_TC_DATA "(conflict G file update edited deleted (version 22 " ROOT_ONE " 1 2 branch1/ft/F none) (version 22 " ROOT_ONE " 1 3 branch1/ft/F file))" static const char * const TESTING_DATA = ( /* Load our test data. Note: do not use named-column insertions. This allows us to test the column count in the schema matches our expectation here. */ "insert into repository values (1, '" ROOT_ONE "', '" UUID_ONE "'); " "insert into repository values (2, '" ROOT_TWO "', '" UUID_TWO "'); " "insert into wcroot values (1, null); " "insert into pristine values ('$sha1$" SHA1_1 "', NULL, 15, 1, '$md5 $" MD5_1 "'); " ); #define NOT_MOVED FALSE, NULL #define NO_COPY_FROM 0, NULL, SVN_INVALID_REVNUM static const svn_test__nodes_data_t nodes_init_data[] = { /* load the base nodes into the nodes table */ { 0, "", "normal", 1, "", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "A", "normal", 1, "A", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 1, TIME_1a, AUTHOR_1, FALSE, NULL, 10, 10 }, { 0, "B", "excluded", 1, "B", SVN_INVALID_REVNUM, NOT_MOVED, svn_node_symlink}, { 0, "C", "server-excluded", 1, "C", 0, NOT_MOVED, svn_node_unknown}, { 0, "D", "not-present", 1, "D", 0, NOT_MOVED, svn_node_unknown}, { 0, "E", "incomplete", 1, "E", 1, NOT_MOVED, svn_node_unknown}, { 0, "F", "normal", 1, "F", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2}, { 0, "G", "normal", 2, "G-alt", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 0, "H", "normal", 1, "H", 1, NOT_MOVED, svn_node_symlink, "()", NULL, NULL, "H-target", 1, TIME_1a, AUTHOR_1 }, { 0, "I", "normal", 1, "I", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J", "normal", 1, "J", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-e", "normal", 1, "J/J-e", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-e/J-e-a", "normal", 1, "J/J-e/J-e-a", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-e/J-e-b", "normal", 1, "J/J-e/J-e-b", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-e/J-e-b/Jeba", "normal", 1, "J/J-e/J-e-b/Jeba", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-f", "normal", 1, "J/J-f", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J/J-f/J-f-a", "normal", 1, "J/J-f/J-f-a", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "J", "normal", 1, "J", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "K", "normal", 1, "K", 1, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "K/K-a", "normal", 2, "K/K-a", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2, FALSE, NULL, 15, 14}, { 0, "K/K-b", "normal", 2, "K/K-b", 1, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2, FALSE, NULL, 15, 14}, /* Load data into the working layers of NODES */ { 1, "I", "normal", 2, "some/dir", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, /* J was originally a local addition, but its descendants are replaced, so let's turn J in a copy */ { 1, "J", "normal", 2, "q", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-a", "normal", 2, "q/J-a", 2, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-b", "normal", 2, "q/J-b", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 3, "J/J-b/J-b-a", "normal", 2, "another/dir", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-b/J-b-b", "normal", 2, "q/J-b/J-b-b", 2, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-c", "normal", 2, "q/J-c", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-c/J-c-a", "normal", 2, "q/J-c/J-c-a", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "J/J-c", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 2, "J/J-c/J-c-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 2, "J/J-d", "normal", 2, "moved/file", 2, TRUE, NULL, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 0, "moved", "normal", 2, "moved", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 1, TIME_1a, AUTHOR_1 }, { 0, "moved/file", "normal", 2, "moved/file", 2, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "moved/file", "base-deleted", NO_COPY_FROM, FALSE, "J/J-d", svn_node_file}, { 1, "J/J-e", "normal", 2, "q/J-e", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-e/J-e-a", "normal", 2, "q/J-e/J-e-a", 2, NOT_MOVED, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-e/J-e-b", "normal", 2, "q/J-e/J-e-b", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "J/J-e", "base-deleted", NO_COPY_FROM, FALSE, "other/place", svn_node_dir}, { 2, "J/J-e/J-e-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_file}, { 2, "J/J-e/J-e-b", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 1, "J/J-e/J-e-b/Jeba", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_file}, { 1, "J/J-f", "normal", 2, "q/J-f", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "J/J-f/J-f-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 1, "K", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 1, "K/K-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_file}, { 1, "K/K-b", "base-deleted", NO_COPY_FROM, FALSE, "moved/away", svn_node_file}, { 1, "L", "normal", 2, "from", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "L/L-a", "normal", 2, "from/L-a", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 1, "L/L-a/L-a-a", "normal", 2, "from/L-a/L-a-a", 2, NOT_MOVED, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "L/L-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 2, "L/L-a/L-a-a", "base-deleted", NO_COPY_FROM, NOT_MOVED, svn_node_dir}, { 0, "other", "normal", 2, "other", 2, NOT_MOVED, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "other/place", "normal", 2, "q/J-e", 2, TRUE, NULL, svn_node_dir, "()", "immediates", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "other/place/J-e-a", "normal", 2, "q/J-e/J-e-a", 2, TRUE, NULL, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "other/place/J-e-b", "normal", 2, "q/J-e/J-e-b", 2, TRUE, NULL, svn_node_dir, "()", "infinity", NULL, NULL, 2, TIME_2a, AUTHOR_2 }, { 2, "other/place/J-e-b/Jeba", "normal", 2, "q/J-e/J-e-b/Jeba", 2, TRUE, NULL, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 1, TIME_1a, AUTHOR_1 }, /*** NEW ****/ { 2, "moved/away", "normal", 2, "K/K-b", 1, TRUE, NULL, svn_node_file, "()", NULL, "$sha1$" SHA1_1, NULL, 2, TIME_2a, AUTHOR_2, FALSE, NULL, 15, 14}, { 0 } }; static const svn_test__actual_data_t actual_init_data[] = { { "A", NULL, "changelist", NULL }, { "F", NULL, NULL, F_TC_DATA }, { "G", NULL, NULL, F_TC_DATA }, { 0 } }; static svn_error_t * create_open(svn_wc__db_t **db, const char **local_abspath, const char *subdir, apr_pool_t *pool) { SVN_ERR(svn_dirent_get_absolute(local_abspath, svn_dirent_join( svn_test_data_path("db-test", pool), subdir, pool), pool)); SVN_ERR(svn_io_remove_dir2(*local_abspath, TRUE, NULL, NULL, pool)); SVN_ERR(svn_wc__db_open(db, NULL, FALSE, TRUE, pool, pool)); SVN_ERR(svn_test__create_fake_wc(*local_abspath, TESTING_DATA, nodes_init_data, actual_init_data, pool)); svn_test_add_dir_cleanup(*local_abspath); return SVN_NO_ERROR; } /* Convert VALUE to a const svn_string_t *, and create a mapping from NAME to the converted data type in PROPS. */ static void set_prop(apr_hash_t *props, const char *name, const char *value, apr_pool_t *result_pool) { const svn_string_t *propval = svn_string_create(value, result_pool); apr_hash_set(props, name, APR_HASH_KEY_STRING, propval); } static svn_error_t * validate_abspath(const char *wcroot_abspath, const char *expected_relpath, const char *actual_abspath, apr_pool_t *scratch_pool) { SVN_TEST_STRING_ASSERT(actual_abspath, svn_dirent_join(wcroot_abspath, expected_relpath, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * test_getting_info(apr_pool_t *pool) { const char *local_abspath; svn_node_kind_t kind; svn_wc__db_status_t status; svn_revnum_t revision; const char *repos_relpath; const char *repos_root_url; const char *repos_uuid; svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; svn_depth_t depth; const svn_checksum_t *checksum; const char *target; svn_boolean_t had_props; apr_hash_t *props; svn_boolean_t update_root; svn_wc__db_lock_t *lock; svn_wc__db_t *db; svn_error_t *err; SVN_ERR(create_open(&db, &local_abspath, "test_getting_info", pool)); /* Test: basic fetching of data. */ SVN_ERR(svn_wc__db_base_get_info( &status, &kind, &revision, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author,&depth, &checksum, &target, &lock, &had_props, &props, &update_root, db, local_abspath, pool, pool)); SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); SVN_TEST_ASSERT(revision == 1); SVN_TEST_STRING_ASSERT(repos_relpath, ""); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_ASSERT(changed_rev == 1); SVN_TEST_ASSERT(changed_date == TIME_1a); SVN_TEST_STRING_ASSERT(changed_author, AUTHOR_1); SVN_TEST_ASSERT(depth == svn_depth_infinity); SVN_TEST_ASSERT(checksum == NULL); SVN_TEST_ASSERT(target == NULL); SVN_TEST_ASSERT(lock == NULL); SVN_TEST_ASSERT(!had_props); SVN_TEST_ASSERT(apr_hash_count(props) == 0); /* SVN_TEST_ASSERT(update_root == ???); */ /* Test: file-specific values. */ SVN_ERR(svn_wc__db_base_get_info( NULL, &kind, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "A", pool), pool, pool)); SVN_TEST_ASSERT(kind == svn_node_file); SVN_TEST_STRING_ASSERT(SHA1_1, svn_checksum_to_cstring(checksum, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "A"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); /* Test: symlink kind, excluded presence, default values for columns. */ SVN_ERR(svn_wc__db_base_get_info( &status, &kind, &revision, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, &depth, &checksum, &target, &lock, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "B", pool), pool, pool)); SVN_TEST_ASSERT(kind == svn_node_symlink); SVN_TEST_ASSERT(status == svn_wc__db_status_excluded); SVN_TEST_ASSERT(!SVN_IS_VALID_REVNUM(revision)); SVN_TEST_STRING_ASSERT(repos_relpath, "B"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_ASSERT(!SVN_IS_VALID_REVNUM(changed_rev)); SVN_TEST_ASSERT(changed_date == 0); SVN_TEST_ASSERT(changed_author == NULL); SVN_TEST_ASSERT(depth == svn_depth_unknown); SVN_TEST_ASSERT(checksum == NULL); SVN_TEST_ASSERT(target == NULL); SVN_TEST_ASSERT(lock == NULL); /* Test: unknown kind, server-excluded presence. */ SVN_ERR(svn_wc__db_base_get_info( &status, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "C", pool), pool, pool)); SVN_TEST_ASSERT(kind == svn_node_unknown); SVN_TEST_ASSERT(status == svn_wc__db_status_server_excluded); /* Test: not-present presence. */ SVN_ERR(svn_wc__db_base_get_info( &status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "D", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_not_present); /* Test: incomplete presence. */ SVN_ERR(svn_wc__db_base_get_info( &status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "E", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_incomplete); /* Test: SHA1 checksum. */ SVN_ERR(svn_wc__db_base_get_info( NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "F", pool), pool, pool)); SVN_TEST_STRING_ASSERT(SHA1_1, svn_checksum_to_cstring(checksum, pool)); /* Test: alternate repository (switched file). */ SVN_ERR(svn_wc__db_base_get_info( NULL, NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "G", pool), pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "G-alt"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_TWO); SVN_TEST_ASSERT(changed_rev == 2); SVN_TEST_ASSERT(changed_date == TIME_2a); SVN_TEST_STRING_ASSERT(changed_author, AUTHOR_2); /* Test: symlink target. */ SVN_ERR(svn_wc__db_base_get_info( NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &checksum, &target, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "H", pool), pool, pool)); SVN_TEST_ASSERT(checksum == NULL); SVN_TEST_STRING_ASSERT(target, "H-target"); /* Test: missing node. */ err = svn_wc__db_base_get_info( NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "missing-file", pool), pool, pool); SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); return SVN_NO_ERROR; } static svn_error_t * validate_node(svn_wc__db_t *db, const char *local_abspath, const char *relpath, svn_node_kind_t expected_kind, svn_wc__db_status_t expected_status, apr_pool_t *scratch_pool) { const char *path = svn_dirent_join(local_abspath, relpath, scratch_pool); svn_node_kind_t kind; svn_wc__db_status_t status; apr_hash_t *props; const svn_string_t *value; SVN_ERR(svn_wc__db_base_get_info( &status, &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(kind == expected_kind); SVN_TEST_ASSERT(status == expected_status); switch (status) { case svn_wc__db_status_server_excluded: case svn_wc__db_status_excluded: case svn_wc__db_status_incomplete: case svn_wc__db_status_not_present: /* Our tests aren't setting properties on these node types, so short-circuit examination of name/value pairs, to avoid having to handle the error from svn_wc__db_base_get_props(). */ return SVN_NO_ERROR; default: break; /* Fall through */ } SVN_ERR(svn_wc__db_base_get_props(&props, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(props != NULL); value = apr_hash_get(props, "p1", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, "v1"); value = apr_hash_get(props, "for-file", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, relpath); SVN_ERR(svn_wc__db_read_props(&props, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(props != NULL); value = apr_hash_get(props, "p1", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, "v1"); SVN_ERR(svn_wc__db_read_pristine_props(&props, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(props != NULL); value = apr_hash_get(props, "p1", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, "v1"); /* Now add a property value and read it back (all on actual) */ { apr_hash_t *actual_props = apr_hash_copy(scratch_pool, props); apr_hash_set(actual_props, "p999", APR_HASH_KEY_STRING, value); SVN_ERR(svn_wc__db_op_set_props(db, path, actual_props, FALSE, NULL, NULL, scratch_pool)); SVN_ERR(svn_wc__db_read_props(&props, db, path, scratch_pool, scratch_pool)); SVN_TEST_ASSERT(props != NULL); value = apr_hash_get(props, "p999", APR_HASH_KEY_STRING); SVN_TEST_STRING_ASSERT(value->data, "v1"); } return SVN_NO_ERROR; } static svn_error_t * test_inserting_nodes(apr_pool_t *pool) { const char *local_abspath; svn_checksum_t *checksum; svn_wc__db_t *db; apr_hash_t *props; const apr_array_header_t *children; SVN_ERR(create_open(&db, &local_abspath, "test_insert_nodes", pool)); props = apr_hash_make(pool); set_prop(props, "p1", "v1", pool); children = svn_cstring_split("N-a N-b N-c", " ", FALSE, pool); SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, SHA1_1, pool)); /* Create a new directory and several child nodes. */ set_prop(props, "for-file", "N", pool); SVN_ERR(svn_wc__db_base_add_directory( db, svn_dirent_join(local_abspath, "N", pool), local_abspath, "N", ROOT_ONE, UUID_ONE, 3, props, 1, TIME_1a, AUTHOR_1, children, svn_depth_infinity, NULL, FALSE, NULL, NULL, NULL, NULL, pool)); /* Replace an incomplete node with a file node. */ set_prop(props, "for-file", "N/N-a", pool); SVN_ERR(svn_wc__db_base_add_file( db, svn_dirent_join(local_abspath, "N/N-a", pool), local_abspath, "N/N-a", ROOT_ONE, UUID_ONE, 3, props, 1, TIME_1a, AUTHOR_1, checksum, NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE, NULL, NULL, pool)); /* Create a new symlink node. */ set_prop(props, "for-file", "O", pool); SVN_ERR(svn_wc__db_base_add_symlink( db, svn_dirent_join(local_abspath, "O", pool), local_abspath, "O", ROOT_ONE, UUID_ONE, 3, props, 1, TIME_1a, AUTHOR_1, "O-target", NULL, FALSE, FALSE, NULL, NULL, FALSE, FALSE, NULL, NULL, pool)); /* Replace an incomplete node with an server-excluded file node. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "N/N-b", pool), "N/N-b", ROOT_ONE, UUID_ONE, 3, svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); /* Create a new excluded directory node. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "P", pool), "P", ROOT_ONE, UUID_ONE, 3, svn_node_dir, svn_wc__db_status_excluded, NULL, NULL, pool)); /* Create a new not-present symlink node. */ SVN_ERR(svn_wc__db_base_add_not_present_node( db, svn_dirent_join(local_abspath, "Q", pool), "Q", ROOT_ONE, UUID_ONE, 3, svn_node_symlink, NULL, NULL, pool)); /* Create a new server-excluded unknown-kind node. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "R", pool), "R", ROOT_ONE, UUID_ONE, 3, svn_node_unknown, svn_wc__db_status_server_excluded, NULL, NULL, pool)); /* Are all the nodes where we expect them to be? */ SVN_ERR(validate_node(db, local_abspath, "N", svn_node_dir, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-a", svn_node_file, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-b", svn_node_file, svn_wc__db_status_server_excluded, pool)); SVN_ERR(validate_node(db, local_abspath, "N/N-c", svn_node_unknown, svn_wc__db_status_incomplete, pool)); SVN_ERR(validate_node(db, local_abspath, "O", svn_node_symlink, svn_wc__db_status_normal, pool)); SVN_ERR(validate_node(db, local_abspath, "P", svn_node_dir, svn_wc__db_status_excluded, pool)); SVN_ERR(validate_node(db, local_abspath, "Q", svn_node_symlink, svn_wc__db_status_not_present, pool)); SVN_ERR(validate_node(db, local_abspath, "R", svn_node_unknown, svn_wc__db_status_server_excluded, pool)); /* ### do we need to test any attributes of the node? */ /* ### yes: test the repos inheritance stuff (at least) */ return SVN_NO_ERROR; } static svn_error_t * test_children(apr_pool_t *pool) { const char *local_abspath; svn_wc__db_t *db; const apr_array_header_t *children; int i; SVN_ERR(create_open(&db, &local_abspath, "test_children", pool)); SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath, pool, pool)); SVN_TEST_ASSERT(children->nelts == 13); for (i = children->nelts; i--; ) { const char *name = APR_ARRAY_IDX(children, i, const char *); if (strcmp(name, "moved") == 0 || strcmp(name, "other") == 0) { continue; } SVN_TEST_ASSERT(strlen(name) == 1); /* ### check the actual values */ } SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath, pool, pool)); SVN_TEST_ASSERT(children->nelts == 14); for (i = children->nelts; i--; ) { const char *name = APR_ARRAY_IDX(children, i, const char *); if (strcmp(name, "moved") == 0 || strcmp(name, "other") == 0) { continue; } SVN_TEST_ASSERT(strlen(name) == 1); /* ### check the actual values */ } /* ### insert some more children. replace some nodes. check values. */ return SVN_NO_ERROR; } static svn_error_t * test_working_info(apr_pool_t *pool) { const char *local_abspath; svn_node_kind_t kind; svn_wc__db_status_t status; svn_revnum_t revision; const char *repos_relpath; const char *repos_root_url; const char *repos_uuid; svn_revnum_t changed_rev; apr_time_t changed_date; const char *changed_author; apr_time_t recorded_time; svn_depth_t depth; const svn_checksum_t *checksum; svn_filesize_t recorded_size; const char *target; const char *changelist; const char *original_repos_relpath; const char *original_root_url; const char *original_uuid; svn_revnum_t original_revnum; svn_boolean_t op_root; svn_boolean_t had_props; svn_boolean_t props_mod; svn_boolean_t have_base; svn_boolean_t have_more_work; svn_boolean_t have_work; svn_boolean_t conflicted; svn_wc__db_lock_t *lock; svn_wc__db_t *db; SVN_ERR(create_open(&db, &local_abspath, "test_working_info", pool)); /* Test: basic fetching of data. */ SVN_ERR(svn_wc__db_read_info( &status, &kind, &revision, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, &depth, &checksum, &target, &original_repos_relpath, &original_root_url, &original_uuid, &original_revnum, &lock, &recorded_size, &recorded_time, &changelist, &conflicted, &op_root, &had_props, &props_mod, &have_base, &have_more_work, &have_work, db, svn_dirent_join(local_abspath, "I", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_added); SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_ASSERT(revision == SVN_INVALID_REVNUM); SVN_TEST_ASSERT(repos_relpath == NULL); SVN_TEST_ASSERT(repos_root_url == NULL); SVN_TEST_ASSERT(repos_uuid == NULL); SVN_TEST_ASSERT(changed_rev == 2); SVN_TEST_ASSERT(changed_date == TIME_2a); SVN_TEST_STRING_ASSERT(changed_author, AUTHOR_2); SVN_TEST_ASSERT(depth == svn_depth_immediates); SVN_TEST_ASSERT(checksum == NULL); SVN_TEST_ASSERT(recorded_size == SVN_INVALID_FILESIZE); SVN_TEST_ASSERT(target == NULL); SVN_TEST_STRING_ASSERT(changelist, NULL); SVN_TEST_STRING_ASSERT(original_repos_relpath, "some/dir"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revnum == 2); SVN_TEST_ASSERT(!had_props); SVN_TEST_ASSERT(!props_mod); SVN_TEST_ASSERT(have_base); /* SVN_TEST_ASSERT(have_more_work...); */ SVN_TEST_ASSERT(have_work); SVN_TEST_ASSERT(!conflicted); SVN_TEST_ASSERT(lock == NULL); /* SVN_TEST_ASSERT(last_mod_time...); */ /* SVN_TEST_ASSERT(op_root...); */ /* ### we need a hojillion more tests in here. I just want to get this ### round checked in, so I'm skipping more tests at this point. */ SVN_ERR(svn_wc__db_read_info( &status, &kind, &revision, &repos_relpath, &repos_root_url, &repos_uuid, &changed_rev, &changed_date, &changed_author, &depth, &checksum, &target, &original_repos_relpath, &original_root_url, &original_uuid, &original_revnum, &lock, &recorded_size, &recorded_time, &changelist, &conflicted, &op_root, &had_props, &props_mod, &have_base, &have_more_work, &have_work, db, svn_dirent_join(local_abspath, "A", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); SVN_TEST_ASSERT(kind == svn_node_file); SVN_TEST_STRING_ASSERT(changelist, "changelist"); SVN_TEST_ASSERT(revision == 1); SVN_TEST_STRING_ASSERT(repos_relpath, "A"); SVN_TEST_STRING_ASSERT(repos_root_url, "http://example.com/one"); SVN_TEST_STRING_ASSERT(repos_uuid, "uuid1"); SVN_TEST_ASSERT(changed_rev == 1); SVN_TEST_ASSERT(changed_date == TIME_1a); SVN_TEST_STRING_ASSERT(changed_author, AUTHOR_1); SVN_TEST_ASSERT(depth == svn_depth_unknown); SVN_TEST_ASSERT(checksum != NULL); SVN_TEST_ASSERT(recorded_size == 10); SVN_TEST_ASSERT(target == NULL); return SVN_NO_ERROR; } static svn_error_t * test_pdh(apr_pool_t *pool) { const char *local_abspath; svn_wc__db_t *db; SVN_ERR(create_open(&db, &local_abspath, "test_pdh", pool)); /* NOTE: this test doesn't do anything apparent -- it simply exercises some internal functionality of wc_db. This is a handy driver for debugging wc_db to ensure it manages per-directory handles properly. */ SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "sub", pool), "sub", ROOT_ONE, UUID_ONE, 1, svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); SVN_ERR(svn_wc__db_base_add_directory( db, svn_dirent_join(local_abspath, "sub2", pool), local_abspath, "sub2", ROOT_ONE, UUID_ONE, 1, apr_hash_make(pool), 1, 1, "me", NULL, svn_depth_infinity, NULL, FALSE, NULL, NULL, NULL, NULL, pool)); SVN_ERR(svn_wc__db_base_add_excluded_node( db, svn_dirent_join(local_abspath, "sub2/A", pool), "sub2/A", ROOT_ONE, UUID_ONE, 1, svn_node_file, svn_wc__db_status_server_excluded, NULL, NULL, pool)); return SVN_NO_ERROR; } static svn_error_t * test_scan_addition(apr_pool_t *pool) { const char *local_abspath; svn_wc__db_t *db; svn_wc__db_status_t status; const char *op_root_abspath; const char *repos_relpath; const char *repos_root_url; const char *repos_uuid; const char *original_repos_relpath; const char *original_root_url; const char *original_uuid; svn_revnum_t original_revision; const char *moved_from_abspath; const char *move_op_root_abspath; const char *move_op_root_src; const char *delete_op_root_abspath; SVN_ERR(create_open(&db, &local_abspath, "test_scan_addition", pool)); /* Simple addition of a directory. */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_copied); SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "q"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); /* Simple copy (affects how scan-up is started). */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J/J-a", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_copied); SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-a"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "q"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); /* Node was moved here. */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J/J-d", pool), pool, pool)); SVN_ERR(svn_wc__db_scan_moved( &moved_from_abspath, &move_op_root_abspath, &move_op_root_src, &delete_op_root_abspath, db, svn_dirent_join(local_abspath, "J/J-d", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_moved_here); SVN_ERR(validate_abspath(local_abspath, "J/J-d", op_root_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "moved/file", moved_from_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "J/J-d", move_op_root_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "moved/file", move_op_root_src, pool)); SVN_ERR(validate_abspath(local_abspath, "moved/file", delete_op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-d"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "moved/file"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); /* Check root of a copy. */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J/J-b", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_copied); SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "q"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); /* Ignore parent copy. Use copy closest to target. */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J/J-b/J-b-a", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_copied); SVN_ERR(validate_abspath(local_abspath, "J/J-b/J-b-a", op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b/J-b-a"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "another/dir"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); /* Inherit parent copy. */ SVN_ERR(svn_wc__db_scan_addition( &status, &op_root_abspath, &repos_relpath, &repos_root_url, &repos_uuid, &original_repos_relpath, &original_root_url, &original_uuid, &original_revision, db, svn_dirent_join(local_abspath, "J/J-b/J-b-b", pool), pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_copied); SVN_ERR(validate_abspath(local_abspath, "J", op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "J/J-b/J-b-b"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); SVN_TEST_STRING_ASSERT(original_repos_relpath, "q"); SVN_TEST_STRING_ASSERT(original_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(original_uuid, UUID_TWO); SVN_TEST_ASSERT(original_revision == 2); return SVN_NO_ERROR; } static svn_error_t * test_scan_deletion(apr_pool_t *pool) { const char *local_abspath; svn_wc__db_t *db; const char *base_del_abspath; const char *work_del_abspath; const char *moved_to_abspath; const char *copy_op_root_abspath; SVN_ERR(create_open(&db, &local_abspath, "test_scan_deletion", pool)); /* Node was moved elsewhere. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, ©_op_root_abspath, db, svn_dirent_join(local_abspath, "J/J-e", pool), pool, pool)); SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "other/place", moved_to_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "J/J-e", work_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "other/place", copy_op_root_abspath, pool)); /* Node was moved elsewhere (child of operation root). */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, ©_op_root_abspath, db, svn_dirent_join(local_abspath, "J/J-e/J-e-a", pool), pool, pool)); SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "other/place/J-e-a", moved_to_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "J/J-e", work_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "other/place", copy_op_root_abspath, pool)); /* Root of delete. Parent is a WORKING node. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "J/J-c", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_TEST_ASSERT(moved_to_abspath == NULL); SVN_ERR(validate_abspath(local_abspath, "J/J-c", work_del_abspath, pool)); /* Child of a deleted root. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "J/J-c/J-c-a", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_TEST_ASSERT(moved_to_abspath == NULL); SVN_ERR(validate_abspath(local_abspath, "J/J-c", work_del_abspath, pool)); /* Base-deleted tree extending past deleted WORKING subtree. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "J/J-e/J-e-b/Jeba", pool), pool, pool)); /* ### I don't understand this. "J/J-e/J-e-b/Jeba" is a deleted base node that is not overlayed by the replacement rooted at "J". Why does base_del_abspath refer to "J-e"? */ SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "other/place/J-e-b/Jeba", moved_to_abspath, pool)); SVN_TEST_STRING_ASSERT(work_del_abspath, NULL); /* Base-deleted tree extending past added WORKING tree. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "J/J-f/J-f-a", pool), pool, pool)); /* Implicit delete of "J" (via replacement). */ SVN_ERR(validate_abspath(local_abspath, "J", base_del_abspath, pool)); SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL); SVN_TEST_STRING_ASSERT(work_del_abspath, NULL); /* Root of delete. Parent is a BASE node. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "K", pool), pool, pool)); SVN_ERR(validate_abspath(local_abspath, "K", base_del_abspath, pool)); SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL); SVN_TEST_STRING_ASSERT(work_del_abspath, NULL); /* Base-deleted tree. Start below root. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "K/K-a", pool), pool, pool)); SVN_ERR(validate_abspath(local_abspath, "K", base_del_abspath, pool)); SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL); SVN_TEST_STRING_ASSERT(work_del_abspath, NULL); /* Base-deleted tree via move. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, ©_op_root_abspath, db, svn_dirent_join(local_abspath, "K/K-b", pool), pool, pool)); SVN_ERR(validate_abspath(local_abspath, "K", base_del_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "moved/away", moved_to_abspath, pool)); SVN_ERR(validate_abspath(local_abspath, "moved/away", copy_op_root_abspath, pool)); SVN_TEST_STRING_ASSERT(work_del_abspath, NULL); /* Subtree deletion of added tree. Start at child. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "L/L-a/L-a-a", pool), pool, pool)); SVN_TEST_STRING_ASSERT(base_del_abspath, NULL); SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL); SVN_ERR(validate_abspath(local_abspath, "L/L-a", work_del_abspath, pool)); /* Subtree deletion of added tree. Start at root. */ SVN_ERR(svn_wc__db_scan_deletion( &base_del_abspath, &moved_to_abspath, &work_del_abspath, NULL, db, svn_dirent_join(local_abspath, "L/L-a", pool), pool, pool)); SVN_TEST_STRING_ASSERT(base_del_abspath, NULL); SVN_TEST_STRING_ASSERT(moved_to_abspath, NULL); SVN_ERR(validate_abspath(local_abspath, "L/L-a", work_del_abspath, pool)); return SVN_NO_ERROR; } static svn_error_t * test_global_relocate(apr_pool_t *pool) { const char *local_abspath; svn_wc__db_t *db; const char *repos_relpath; const char *repos_root_url; const char *repos_uuid; SVN_ERR(create_open(&db, &local_abspath, "test_global_relocate", pool)); /* Initial sanity check. */ SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, local_abspath, pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, ""); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_ONE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); /* Test relocating to a repos not existent in the db */ SVN_ERR(svn_wc__db_global_relocate(db, local_abspath, ROOT_THREE, pool)); SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, local_abspath, pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, ""); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_THREE); /* The UUID should still be the same. */ SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); /* While we're at it, let's see if the children have been relocated, too. */ SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "F", pool), pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "F"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_THREE); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_ONE); /* Alternate repository is not relocated. */ SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, db, svn_dirent_join(local_abspath, "G", pool), pool, pool)); SVN_TEST_STRING_ASSERT(repos_relpath, "G-alt"); SVN_TEST_STRING_ASSERT(repos_root_url, ROOT_TWO); SVN_TEST_STRING_ASSERT(repos_uuid, UUID_TWO); return SVN_NO_ERROR; } static int detect_work_item(const svn_skel_t *work_item) { /* Test work items are a list with one integer atom as operation */ if (!work_item->children) return -1; work_item = work_item->children; if (!work_item->is_atom || work_item->len != 1) return -1; return work_item->data[0] - '0'; } static svn_error_t * test_work_queue(apr_pool_t *pool) { svn_wc__db_t *db; const char *local_abspath; svn_skel_t *work_item; int run_count[3] = { 4, 7, 2 }; /* run the work 13 times, total. */ int fetches = 0; apr_int64_t last_id = 0; SVN_ERR(create_open(&db, &local_abspath, "test_work_queue", pool)); /* Create three work items. */ work_item = svn_skel__make_empty_list(pool); svn_skel__prepend_int(0, work_item, pool); SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item, pool)); work_item = svn_skel__make_empty_list(pool); svn_skel__prepend_int(1, work_item, pool); SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item, pool)); work_item = svn_skel__make_empty_list(pool); svn_skel__prepend_int(2, work_item, pool); SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item, pool)); while (TRUE) { apr_uint64_t id; int which; /* Fetch the next work item, or break when the work queue is empty. */ SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, local_abspath, last_id, pool, pool)); if (work_item == NULL) break; /* Got one. We should never fetch work more than 13 times. */ ++fetches; SVN_TEST_ASSERT(fetches <= 13); /* Parse the work item to see which of the three we found. */ which = detect_work_item(work_item); SVN_TEST_ASSERT(which >= 0 && which <= 2); /* We should not see an item after we've run it enough times. Note: strictly speaking, "in the wild" a work item could remain after a call to wq_completed (ie. crash while that function was processing), but we don't really have a way to test that here. */ SVN_TEST_ASSERT(run_count[which] > 0); /* If we have run this particular item enough times, then go ahead and remove it from the work queue. */ if (--run_count[which] == 0) last_id = id; else last_id = 0; } /* Should have run precisely 13 work items. */ SVN_TEST_ASSERT(fetches == 13); return SVN_NO_ERROR; } static svn_error_t * test_externals_store(apr_pool_t *pool) { svn_wc__db_t *db; const char *local_abspath; svn_checksum_t *orig_checksum; const char *file_external_path; const char *dir_external_path; const char *subdir; apr_hash_t *props = apr_hash_make(pool); svn_string_t *value = svn_string_create("value-data", pool); apr_hash_set(props, "key", APR_HASH_KEY_STRING, value); SVN_ERR(create_open(&db, &local_abspath, "test_externals_store", pool)); /* Directory I exists in the standard test db */ subdir = svn_dirent_join(local_abspath, "I", pool); SVN_ERR(svn_checksum_parse_hex(&orig_checksum, svn_checksum_sha1, SHA1_1, pool)); file_external_path = svn_dirent_join(subdir, "file-external", pool); dir_external_path = svn_dirent_join(subdir, "dir-external", pool); SVN_ERR(svn_wc__db_external_add_file(db, file_external_path, local_abspath /* wri_abspath */, "some/location", "svn://some-repos/svn", "not-a-uuid", 12, props, NULL, 10, 987654, "somebody", orig_checksum, NULL, subdir, "some/new-location", 90, 12, FALSE, NULL, FALSE, NULL, NULL, pool)); SVN_ERR(svn_wc__db_external_add_dir(db, dir_external_path, local_abspath /* wri_abspath */, "svn://other-repos/nsv", "no-uuid-either", subdir, "some/other-location", 70, 32, NULL, pool)); { svn_wc__db_status_t status; svn_node_kind_t kind; const char *repos_root_url; const char *repos_uuid; const char *defining_abspath; const char *recorded_repos_relpath; svn_revnum_t recorded_peg_revision; svn_revnum_t recorded_revision; SVN_ERR(svn_wc__db_external_read(&status, &kind, &defining_abspath, &repos_root_url, &repos_uuid, &recorded_repos_relpath, &recorded_peg_revision, &recorded_revision, db, file_external_path, local_abspath, pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); SVN_TEST_ASSERT(kind == svn_node_file); SVN_TEST_STRING_ASSERT(repos_root_url, "svn://some-repos/svn"); SVN_TEST_STRING_ASSERT(repos_uuid, "not-a-uuid"); SVN_TEST_STRING_ASSERT(defining_abspath, subdir); SVN_TEST_STRING_ASSERT(recorded_repos_relpath, "some/new-location"); SVN_TEST_ASSERT(recorded_peg_revision == 90); SVN_TEST_ASSERT(recorded_revision == 12); { apr_hash_t *new_props; svn_string_t *v; SVN_ERR(svn_wc__db_base_get_props(&new_props, db, file_external_path, pool, pool)); SVN_TEST_ASSERT(new_props != NULL); v = apr_hash_get(new_props, "key", APR_HASH_KEY_STRING); SVN_TEST_ASSERT(v != NULL); SVN_TEST_STRING_ASSERT(v->data, "value-data"); } SVN_ERR(svn_wc__db_external_read(&status, &kind, &defining_abspath, &repos_root_url, &repos_uuid, &recorded_repos_relpath, &recorded_peg_revision, &recorded_revision, db, dir_external_path, local_abspath, pool, pool)); SVN_TEST_ASSERT(status == svn_wc__db_status_normal); SVN_TEST_ASSERT(kind == svn_node_dir); SVN_TEST_STRING_ASSERT(repos_root_url, "svn://other-repos/nsv"); SVN_TEST_STRING_ASSERT(repos_uuid, "no-uuid-either"); SVN_TEST_STRING_ASSERT(defining_abspath, subdir); SVN_TEST_STRING_ASSERT(recorded_repos_relpath, "some/other-location"); SVN_TEST_ASSERT(recorded_peg_revision == 70); SVN_TEST_ASSERT(recorded_revision == 32); } return SVN_NO_ERROR; } static int max_threads = 2; static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_PASS2(test_getting_info, "get information from wc.db"), SVN_TEST_PASS2(test_inserting_nodes, "insert different nodes into wc.db"), SVN_TEST_PASS2(test_children, "getting the list of BASE or WORKING children"), SVN_TEST_PASS2(test_working_info, "reading information about the WORKING tree"), SVN_TEST_PASS2(test_pdh, "creation of per-directory handles"), SVN_TEST_PASS2(test_scan_addition, "scanning added working nodes"), SVN_TEST_PASS2(test_scan_deletion, "deletion introspection functions"), SVN_TEST_PASS2(test_global_relocate, "relocating a node"), SVN_TEST_PASS2(test_work_queue, "work queue processing"), SVN_TEST_PASS2(test_externals_store, "externals store"), SVN_TEST_NULL }; SVN_TEST_MAIN