| /* |
| * pristine-store-test.c : test the pristine-store 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 <apr_pools.h> |
| #include <apr_general.h> |
| |
| #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 "svn_repos.h" |
| #include "svn_wc.h" |
| #include "svn_client.h" |
| |
| #include "utils.h" |
| |
| #include "../../libsvn_wc/wc.h" |
| #include "../../libsvn_wc/wc_db.h" |
| #include "../../libsvn_wc/wc-queries.h" |
| #include "../../libsvn_wc/workqueue.h" |
| |
| #include "private/svn_wc_private.h" |
| |
| #include "../svn_test.h" |
| |
| |
| /* Create repos and WC, set *WC_ABSPATH to the WC path, and set *DB to a new |
| * DB context. */ |
| static svn_error_t * |
| create_repos_and_wc(const char **wc_abspath, |
| svn_wc__db_t **db, |
| const char *test_name, |
| const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_test__sandbox_t sandbox; |
| |
| SVN_ERR(svn_test__sandbox_create(&sandbox, test_name, opts, pool)); |
| *wc_abspath = sandbox.wc_abspath; |
| *db = sandbox.wc_ctx->db; |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Exercise the pristine text API with a simple write and read. */ |
| static svn_error_t * |
| pristine_write_read(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_wc__db_t *db; |
| const char *wc_abspath; |
| |
| svn_wc__db_install_data_t *install_data; |
| svn_stream_t *pristine_stream; |
| apr_size_t sz; |
| |
| const char data[] = "Blah"; |
| svn_string_t *data_string = svn_string_create(data, pool); |
| svn_checksum_t *data_sha1, *data_md5; |
| |
| SVN_ERR(create_repos_and_wc(&wc_abspath, &db, |
| "pristine_write_read", opts, pool)); |
| |
| /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH |
| * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */ |
| SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, |
| &install_data, |
| &data_sha1, &data_md5, |
| db, wc_abspath, |
| pool, pool)); |
| |
| sz = strlen(data); |
| SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); |
| SVN_ERR(svn_stream_close(pristine_stream)); |
| |
| /* Ensure it's not already in the store. */ |
| { |
| svn_boolean_t present; |
| |
| SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, |
| pool)); |
| SVN_TEST_ASSERT(! present); |
| } |
| |
| /* Install the new pristine file, referenced by its checksum. */ |
| SVN_ERR(svn_wc__db_pristine_install(install_data, |
| data_sha1, data_md5, pool)); |
| |
| /* Ensure it is now found in the store. */ |
| { |
| svn_boolean_t present; |
| |
| SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, |
| pool)); |
| SVN_TEST_ASSERT(present); |
| } |
| |
| /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */ |
| { |
| const svn_checksum_t *looked_up_md5; |
| |
| SVN_ERR(svn_wc__db_pristine_get_md5(&looked_up_md5, db, wc_abspath, |
| data_sha1, pool, pool)); |
| SVN_TEST_ASSERT(looked_up_md5->kind == svn_checksum_md5); |
| SVN_TEST_ASSERT(svn_checksum_match(data_md5, looked_up_md5)); |
| } |
| |
| /* Read the pristine text back and verify it's the same content. */ |
| { |
| svn_stream_t *data_stream = svn_stream_from_string(data_string, pool); |
| svn_stream_t *data_read_back; |
| svn_boolean_t same; |
| |
| SVN_ERR(svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, |
| data_sha1, pool, pool)); |
| SVN_ERR(svn_stream_contents_same2(&same, data_read_back, data_stream, |
| pool)); |
| SVN_TEST_ASSERT(same); |
| } |
| |
| /* Trivially test the "remove if unreferenced" API: it's not referenced |
| so we should be able to remove it. */ |
| { |
| svn_error_t *err; |
| svn_stream_t *data_read_back; |
| |
| SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); |
| err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath, |
| data_sha1, pool, pool); |
| SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND); |
| } |
| |
| /* Ensure it's no longer found in the store. */ |
| { |
| svn_boolean_t present; |
| |
| SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, |
| pool)); |
| SVN_TEST_ASSERT(! present); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Test deleting a pristine text while it is open for reading. */ |
| static svn_error_t * |
| pristine_delete_while_open(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| svn_wc__db_t *db; |
| const char *wc_abspath; |
| svn_wc__db_install_data_t *install_data; |
| svn_stream_t *pristine_stream; |
| svn_stream_t *contents; |
| apr_size_t sz; |
| |
| const char data[] = "Blah"; |
| svn_checksum_t *data_sha1, *data_md5; |
| |
| SVN_ERR(create_repos_and_wc(&wc_abspath, &db, |
| "pristine_delete_while_open", opts, pool)); |
| |
| SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, |
| &install_data, |
| &data_sha1, &data_md5, |
| db, wc_abspath, |
| pool, pool)); |
| |
| sz = strlen(data); |
| SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); |
| SVN_ERR(svn_stream_close(pristine_stream)); |
| SVN_ERR(svn_wc__db_pristine_install(install_data, |
| data_sha1, data_md5, pool)); |
| |
| /* Open it for reading */ |
| SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, wc_abspath, data_sha1, |
| pool, pool)); |
| |
| /* Delete it */ |
| SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool)); |
| |
| /* Continue to read from it */ |
| { |
| char buffer[4]; |
| apr_size_t len = 4; |
| |
| SVN_ERR(svn_stream_read_full(contents, buffer, &len)); |
| SVN_TEST_ASSERT(len == 4); |
| SVN_TEST_ASSERT(memcmp(buffer, data, len) == 0); |
| } |
| |
| /* Ensure it's no longer found in the store. (The file may still exist as |
| * an orphan, depending on the implementation.) */ |
| { |
| svn_boolean_t present; |
| |
| SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1, |
| pool)); |
| SVN_TEST_ASSERT(! present); |
| } |
| |
| /* Close the read stream */ |
| SVN_ERR(svn_stream_close(contents)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Check that the store rejects an attempt to replace an existing pristine |
| * text with different text. |
| * |
| * White-box knowledge: The implementation compares the file sizes but |
| * doesn't compare the text itself, so in this test we ensure the second |
| * text is a different size. */ |
| static svn_error_t * |
| reject_mismatching_text(const svn_test_opts_t *opts, |
| apr_pool_t *pool) |
| { |
| #ifdef SVN_DEBUG /* The pristine store only checks this in debug mode. */ |
| svn_wc__db_t *db; |
| const char *wc_abspath; |
| |
| const char data[] = "Blah"; |
| svn_checksum_t *data_sha1, *data_md5; |
| |
| const char data2[] = "Baz"; |
| |
| SVN_ERR(create_repos_and_wc(&wc_abspath, &db, |
| "reject_mismatching_text", opts, pool)); |
| |
| /* Install a pristine text. */ |
| { |
| svn_wc__db_install_data_t *install_data; |
| svn_stream_t *pristine_stream; |
| apr_size_t sz; |
| |
| SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, |
| &install_data, |
| &data_sha1, &data_md5, |
| db, wc_abspath, |
| pool, pool)); |
| |
| sz = strlen(data); |
| SVN_ERR(svn_stream_write(pristine_stream, data, &sz)); |
| SVN_ERR(svn_stream_close(pristine_stream)); |
| |
| SVN_ERR(svn_wc__db_pristine_install(install_data, |
| data_sha1, data_md5, |
| pool)); |
| } |
| |
| /* Try to install the wrong pristine text against the same checksum. |
| * Should fail. */ |
| { |
| svn_wc__db_install_data_t *install_data; |
| svn_stream_t *pristine_stream; |
| apr_size_t sz; |
| |
| SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream, |
| &install_data, |
| &data_sha1, &data_md5, |
| db, wc_abspath, |
| pool, pool)); |
| |
| sz = strlen(data2); |
| SVN_ERR(svn_stream_write(pristine_stream, data2, &sz)); |
| SVN_ERR(svn_stream_close(pristine_stream)); |
| |
| SVN_ERR(svn_wc__db_pristine_install(install_data, |
| data_sha1, data_md5, |
| pool)); |
| } |
| |
| return SVN_NO_ERROR; |
| #else |
| return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, |
| "The consistency check to be tested is only " |
| "active in debug-mode builds"); |
| #endif |
| } |
| |
| |
| static int max_threads = -1; |
| |
| static struct svn_test_descriptor_t test_funcs[] = |
| { |
| SVN_TEST_NULL, |
| SVN_TEST_OPTS_PASS(pristine_write_read, |
| "pristine_write_read"), |
| SVN_TEST_OPTS_PASS(pristine_delete_while_open, |
| "pristine_delete_while_open"), |
| SVN_TEST_OPTS_PASS(reject_mismatching_text, |
| "reject_mismatching_text"), |
| SVN_TEST_NULL |
| }; |
| |
| SVN_TEST_MAIN |