/* * wc-test.c : test WC APIs * * ==================================================================== * 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 #define SVN_DEPRECATED #include "svn_types.h" #include "svn_io.h" #include "svn_dirent_uri.h" #include "svn_pools.h" #include "svn_repos.h" #include "svn_wc.h" #include "svn_client.h" #include "svn_hash.h" #include "utils.h" #include "private/svn_wc_private.h" #include "private/svn_sqlite.h" #include "private/svn_dep_compat.h" #include "../../libsvn_wc/wc.h" #include "../../libsvn_wc/wc_db.h" #define SVN_WC__I_AM_WC_DB #include "../../libsvn_wc/wc_db_private.h" #include "../svn_test.h" #ifdef _MSC_VER #pragma warning(disable: 4221) /* nonstandard extension used */ #endif /* ---------------------------------------------------------------------- */ /* The test functions */ /* Structure for testing node_get_base and node_get_origin. */ struct base_origin_t { /* Path to create and test, WC-relative */ const char *path; /* Expected base rev. "-1" means no base. (Expected base path * == base_rev valid ? path : NULL) */ svn_revnum_t base_rev; /* Path to copy from, WC-relative */ const char *src_path; /* Expected "origin" */ struct { const char *path; svn_revnum_t rev; } origin; }; /* Data for testing node_get_base and node_get_origin. */ static struct base_origin_t base_origin_subtests[] = { /* file copied onto nothing */ { "A/C/copy1", -1, "iota", {"iota", 1} }, /* dir copied onto nothing */ { "A/C/copy2", -1, "A/B/E", {"A/B/E", 1} }, /* replacement: file copied over a schedule-delete file */ { "A/B/lambda", 1, "iota", {"iota", 1} }, /* replacement: dir copied over a schedule-delete dir */ { "A/D/G", 1, "A/B/E", {"A/B/E", 1} }, /* replacement: dir copied over a schedule-delete file */ { "A/D/gamma", 1, "A/B/E", {"A/B/E", 1} }, /* replacement: file copied over a schedule-delete dir */ { "A/D/H", 1, "iota", {"iota", 1} }, { 0 } }; /* Create a WC containing lots of different node states, in the sandbox B. */ static svn_error_t * create_wc_for_base_and_origin_tests(svn_test__sandbox_t *b) { struct base_origin_t *copy; SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* Copy various things */ for (copy = base_origin_subtests; copy->src_path; copy++) { if (SVN_IS_VALID_REVNUM(copy->base_rev)) SVN_ERR(sbox_wc_delete(b, copy->path)); SVN_ERR(sbox_wc_copy(b, copy->src_path, copy->path)); } return SVN_NO_ERROR; } /* Test svn_wc__node_get_base(). */ static svn_error_t * test_node_get_base(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); SVN_ERR(svn_test__sandbox_create(b, "node_get_base", opts, pool)); SVN_ERR(create_wc_for_base_and_origin_tests(b)); { struct base_origin_t *subtest; for (subtest = base_origin_subtests; subtest->path; subtest++) { const char *local_abspath = svn_dirent_join(b->wc_abspath, subtest->path, b->pool); svn_revnum_t revision; const char *repos_relpath, *repos_root_url, *repos_uuid; SVN_ERR(svn_wc__node_get_base(NULL, &revision, &repos_relpath, &repos_root_url, &repos_uuid, NULL, b->wc_ctx, local_abspath, TRUE /* ignore_enoent */, b->pool, b->pool)); SVN_TEST_ASSERT(revision == subtest->base_rev); if (SVN_IS_VALID_REVNUM(subtest->base_rev)) { SVN_TEST_STRING_ASSERT(repos_relpath, subtest->path); SVN_TEST_STRING_ASSERT(repos_root_url, b->repos_url); SVN_TEST_ASSERT(repos_uuid != NULL); } else { SVN_TEST_STRING_ASSERT(repos_relpath, NULL); SVN_TEST_STRING_ASSERT(repos_root_url, NULL); SVN_TEST_STRING_ASSERT(repos_uuid, NULL); } } } return SVN_NO_ERROR; } /* Test svn_wc__node_get_origin(). */ static svn_error_t * test_node_get_origin(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b)); SVN_ERR(svn_test__sandbox_create(b, "node_get_origin", opts, pool)); SVN_ERR(create_wc_for_base_and_origin_tests(b)); { struct base_origin_t *subtest; for (subtest = base_origin_subtests; subtest->path; subtest++) { const char *local_abspath = svn_dirent_join(b->wc_abspath, subtest->path, b->pool); svn_revnum_t revision; const char *repos_relpath, *repos_root_url, *repos_uuid; SVN_ERR(svn_wc__node_get_origin(NULL, &revision, &repos_relpath, &repos_root_url, &repos_uuid, NULL, NULL, b->wc_ctx, local_abspath, FALSE, b->pool, b->pool)); SVN_TEST_ASSERT(revision == subtest->origin.rev); if (SVN_IS_VALID_REVNUM(subtest->origin.rev)) { SVN_TEST_STRING_ASSERT(repos_relpath, subtest->origin.path); SVN_TEST_STRING_ASSERT(repos_root_url, b->repos_url); SVN_TEST_ASSERT(repos_uuid != NULL); } else { SVN_TEST_STRING_ASSERT(repos_relpath, NULL); SVN_TEST_STRING_ASSERT(repos_root_url, NULL); SVN_TEST_STRING_ASSERT(repos_uuid, NULL); } } } return SVN_NO_ERROR; } static svn_error_t * test_externals_parse(const svn_test_opts_t *opts, apr_pool_t *pool) { int i; struct external_info { const char *line; const char *url; const char *local_path; svn_revnum_t peg_rev; svn_revnum_t rev; } items[] = { { "dir http://server/svn/a", "http://server/svn/a", "dir" }, { "/svn/home dir", "u://svr/svn/home", "dir" }, { "//server/home dir", "u://server/home", "dir" }, { "../../../../home dir", "u://svr/svn/home", "dir", }, { "^/../repB/tools/scripts scripts", "u://svr/svn/cur/repB/tools/scripts", "scripts" }, { "^/../repB/tools/README.txt scripts/README.txt", "u://svr/svn/cur/repB/tools/README.txt", "scripts/README.txt" }, }; for (i = 0; i < sizeof(items) / sizeof(items[0]); i++) { apr_array_header_t *results; svn_wc_external_item2_t *external_item; const char *resolved_url; SVN_ERR(svn_wc_parse_externals_description3(&results, "/my/current/dir", items[i].line, FALSE, pool)); SVN_TEST_ASSERT(results && results->nelts == 1); external_item = APR_ARRAY_IDX(results, 0, svn_wc_external_item2_t *); SVN_ERR(svn_wc__resolve_relative_external_url(&resolved_url, external_item, "u://svr/svn/cur/dir", "u://svr/svn/cur/dir/sd/fl", pool, pool)); SVN_TEST_STRING_ASSERT(resolved_url, items[i].url); SVN_TEST_STRING_ASSERT(external_item->target_dir, items[i].local_path); if (items[i].peg_rev != 0) SVN_TEST_ASSERT(external_item->peg_revision.value.number == items[i].peg_rev); if (items[i].rev != 0) SVN_TEST_ASSERT(external_item->revision.value.number == items[i].rev); SVN_TEST_ASSERT(svn_uri_is_canonical(resolved_url, pool)); } return SVN_NO_ERROR; } static svn_error_t * test_externals_parse_erratic(apr_pool_t *pool) { svn_error_t *err; apr_array_header_t *list = NULL; err = svn_wc_parse_externals_description3( &list, "parent_dir", "^/valid/but/should/not/be/on/record wc_target\n" "because_this_is_an_error", FALSE, pool); /* DESC above has an error, so expect one. */ SVN_TEST_ASSERT(err != NULL); svn_error_clear(err); /* svn_wc_parse_externals_description3() should not touch LIST when DESC had an error.*/ SVN_TEST_ASSERT(list == NULL); return SVN_NO_ERROR; } static svn_error_t * test_legacy_commit1(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_test__sandbox_t b; svn_wc_adm_access_t *adm_access; const char *lambda; SVN_ERR(svn_test__sandbox_create(&b, "legacy_commit1", opts, pool)); SVN_ERR(sbox_add_and_commit_greek_tree(&b)); SVN_ERR(sbox_wc_copy(&b, "A", "A_copied")); lambda = sbox_wc_path(&b, "A_copied/B/lambda"); SVN_ERR(svn_io_remove_file2(lambda, FALSE, pool)); SVN_ERR(svn_io_copy_file(sbox_wc_path(&b, "iota"), lambda, FALSE, pool)); SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, b.wc_abspath, TRUE, -1, NULL, NULL, pool)); { svn_wc_status2_t *status; SVN_ERR(svn_wc_status2(&status, lambda, adm_access, pool)); SVN_TEST_ASSERT(status != NULL); SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); SVN_TEST_ASSERT(status->copied == TRUE); } /* Simulate a very old style svn ci . -m "QQQ" on the WC root */ SVN_ERR(svn_wc_process_committed4(sbox_wc_path(&b, "A_copied"), adm_access, TRUE, 12, "2014-10-01T19:00:50.966679Z", "me", NULL, TRUE, TRUE, NULL, pool)); { unsigned char digest[APR_MD5_DIGESTSIZE]; /* Use the fact that iota has the same checksum to ease committing */ SVN_ERR(svn_io_file_checksum (digest, lambda, pool)); SVN_ERR(svn_wc_process_committed4(lambda, adm_access, TRUE, 12, "2014-10-01T19:00:50.966679Z", "me", NULL, TRUE, TRUE, digest, pool)); } { svn_wc_status2_t *status; SVN_ERR(svn_wc_status2(&status, lambda, adm_access, pool)); /* Node is still modified, as we didn't change the text base! */ SVN_TEST_ASSERT(status != NULL); SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); SVN_TEST_ASSERT(status->copied == FALSE); } return SVN_NO_ERROR; } static svn_error_t * test_legacy_commit2(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_test__sandbox_t b; svn_wc_adm_access_t *adm_access; const char *lambda; svn_wc_committed_queue_t *queue; SVN_ERR(svn_test__sandbox_create(&b, "legacy_commit2", opts, pool)); SVN_ERR(sbox_add_and_commit_greek_tree(&b)); SVN_ERR(sbox_wc_copy(&b, "A", "A_copied")); lambda = sbox_wc_path(&b, "A_copied/B/lambda"); SVN_ERR(svn_io_remove_file2(lambda, FALSE, pool)); SVN_ERR(svn_io_copy_file(sbox_wc_path(&b, "iota"), lambda, FALSE, pool)); SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, b.wc_abspath, TRUE, -1, NULL, NULL, pool)); { svn_wc_status2_t *status; SVN_ERR(svn_wc_status2(&status, lambda, adm_access, pool)); SVN_TEST_ASSERT(status != NULL); SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified); SVN_TEST_ASSERT(status->copied == TRUE); } /* Simulate an old style svn ci . -m "QQQ" on the WC root */ queue = svn_wc_committed_queue_create(pool); SVN_ERR(svn_wc_queue_committed(&queue, sbox_wc_path(&b, "A_copied"), adm_access, TRUE, NULL, FALSE, FALSE, NULL, pool)); { unsigned char digest[APR_MD5_DIGESTSIZE]; /* Use the fact that iota has the same checksum to ease committing */ SVN_ERR(svn_io_file_checksum(digest, lambda, pool)); SVN_ERR(svn_wc_queue_committed(&queue, lambda, adm_access, FALSE, NULL, FALSE, FALSE, digest, pool)); } SVN_ERR(svn_wc_process_committed_queue(queue, adm_access, 12, "2014-10-01T19:00:50.966679Z", "me", pool)); { svn_wc_status2_t *status; SVN_ERR(svn_wc_status2(&status, lambda, adm_access, pool)); /* Node is still modified, as we didn't change the text base! */ SVN_TEST_ASSERT(status != NULL); SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal); SVN_TEST_ASSERT(status->copied == FALSE); } return SVN_NO_ERROR; } static svn_error_t * test_internal_file_modified(const svn_test_opts_t *opts, apr_pool_t *pool) { svn_test__sandbox_t b; svn_boolean_t modified; const char *iota_path; apr_time_t time; SVN_ERR(svn_test__sandbox_create(&b, "internal_file_modified_p", opts, pool)); SVN_ERR(sbox_add_and_commit_greek_tree(&b)); iota_path = sbox_wc_path(&b, "iota"); /* No modification, timestamps match.*/ SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, FALSE, pool)); SVN_TEST_ASSERT(!modified); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, TRUE, pool)); SVN_TEST_ASSERT(!modified); /* Change timestamp on 'iota' and check. */ SVN_ERR(svn_io_file_affected_time(&time, iota_path, pool)); SVN_ERR(svn_io_set_file_affected_time(time + apr_time_from_sec(1), iota_path, pool)); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, FALSE, pool)); SVN_TEST_ASSERT(!modified); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, TRUE, pool)); SVN_TEST_ASSERT(!modified); /* Modify 'iota' to be different size. */ SVN_ERR(sbox_file_write(&b, iota_path, "new iota")); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, FALSE, pool)); SVN_TEST_ASSERT(modified); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, TRUE, pool)); SVN_TEST_ASSERT(modified); /* Working copy is smart and able to detect changes in files of different * size even if timestamp didn't change. */ SVN_ERR(svn_io_set_file_affected_time(time, iota_path, pool)); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, FALSE, pool)); SVN_TEST_ASSERT(modified); SVN_ERR(svn_wc__internal_file_modified_p(&modified, b.wc_ctx->db, iota_path, TRUE, pool)); SVN_TEST_ASSERT(modified); return SVN_NO_ERROR; } /* ---------------------------------------------------------------------- */ /* The list of test functions */ static int max_threads = 2; static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_OPTS_PASS(test_node_get_base, "test_node_get_base"), SVN_TEST_OPTS_PASS(test_node_get_origin, "test_node_get_origin"), SVN_TEST_OPTS_PASS(test_externals_parse, "test svn_wc_parse_externals_description3"), SVN_TEST_PASS2(test_externals_parse_erratic, "parse erratic externals definition"), SVN_TEST_OPTS_PASS(test_legacy_commit1, "test legacy commit1"), SVN_TEST_OPTS_PASS(test_legacy_commit2, "test legacy commit2"), SVN_TEST_OPTS_PASS(test_internal_file_modified, "test internal_file_modified"), SVN_TEST_NULL }; SVN_TEST_MAIN