summaryrefslogtreecommitdiff
path: root/subversion/tests/libsvn_fs_x/fs-x-pack-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/libsvn_fs_x/fs-x-pack-test.c')
-rw-r--r--subversion/tests/libsvn_fs_x/fs-x-pack-test.c882
1 files changed, 882 insertions, 0 deletions
diff --git a/subversion/tests/libsvn_fs_x/fs-x-pack-test.c b/subversion/tests/libsvn_fs_x/fs-x-pack-test.c
new file mode 100644
index 0000000..f85a357
--- /dev/null
+++ b/subversion/tests/libsvn_fs_x/fs-x-pack-test.c
@@ -0,0 +1,882 @@
+/* fs-x-pack-test.c --- tests for the FSX 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 "../../libsvn_fs_x/fs.h"
+#include "../../libsvn_fs_x/reps.h"
+
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_fs.h"
+#include "private/svn_string_private.h"
+
+#include "../svn_test_fs.h"
+
+
+
+/*** Helper Functions ***/
+
+/* Write the format number and maximum number of files per directory
+ to a new format file in PATH, overwriting a previously existing
+ file. Use POOL for temporary allocation.
+
+ (This implementation is largely stolen from libsvn_fs_fs/fs_fs.c.) */
+static svn_error_t *
+write_format(const char *path,
+ int format,
+ int max_files_per_dir,
+ apr_pool_t *pool)
+{
+ const char *contents;
+
+ path = svn_dirent_join(path, "format", pool);
+ SVN_TEST_ASSERT(max_files_per_dir > 0);
+
+ contents = apr_psprintf(pool,
+ "%d\n"
+ "layout sharded %d\n",
+ format, max_files_per_dir);
+
+ SVN_ERR(svn_io_write_atomic(path, contents, strlen(contents),
+ NULL /* copy perms */, pool));
+
+ /* And set the perms to make it read only */
+ return svn_io_set_file_read_only(path, FALSE, pool);
+}
+
+/* Return the expected contents of "iota" in revision REV. */
+static const char *
+get_rev_contents(svn_revnum_t rev, apr_pool_t *pool)
+{
+ /* Toss in a bunch of magic numbers for spice. */
+ apr_int64_t num = ((rev * 1234353 + 4358) * 4583 + ((rev % 4) << 1)) / 42;
+ return apr_psprintf(pool, "%" APR_INT64_T_FMT "\n", num);
+}
+
+struct pack_notify_baton
+{
+ apr_int64_t expected_shard;
+ svn_fs_pack_notify_action_t expected_action;
+};
+
+static svn_error_t *
+pack_notify(void *baton,
+ apr_int64_t shard,
+ svn_fs_pack_notify_action_t action,
+ apr_pool_t *pool)
+{
+ struct pack_notify_baton *pnb = baton;
+
+ SVN_TEST_ASSERT(shard == pnb->expected_shard);
+ SVN_TEST_ASSERT(action == pnb->expected_action);
+
+ /* Update expectations. */
+ switch (action)
+ {
+ case svn_fs_pack_notify_start:
+ pnb->expected_action = svn_fs_pack_notify_end;
+ break;
+
+ case svn_fs_pack_notify_end:
+ pnb->expected_action = svn_fs_pack_notify_start;
+ pnb->expected_shard++;
+ break;
+
+ default:
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "Unknown notification action when packing");
+ }
+
+ return SVN_NO_ERROR;
+}
+
+#define R1_LOG_MSG "Let's serf"
+
+/* Create a packed filesystem in DIR. Set the shard size to
+ SHARD_SIZE and create NUM_REVS number of revisions (in addition to
+ r0). Use POOL for allocations. After this function successfully
+ completes, the filesystem's youngest revision number will be the
+ same as NUM_REVS. */
+static svn_error_t *
+create_packed_filesystem(const char *dir,
+ const svn_test_opts_t *opts,
+ int num_revs,
+ int shard_size,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ const char *conflict;
+ svn_revnum_t after_rev;
+ apr_pool_t *subpool = svn_pool_create(pool);
+ struct pack_notify_baton pnb;
+ apr_pool_t *iterpool;
+ int version;
+
+ /* Bail (with success) on known-untestable scenarios */
+ if (strcmp(opts->fs_type, "fsx") != 0)
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "this will test FSX repositories only");
+
+ if (opts->server_minor_version && (opts->server_minor_version < 9))
+ return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
+ "pre-1.9 SVN doesn't support FSX");
+
+ /* Create a filesystem, then close it */
+ SVN_ERR(svn_test__create_fs(&fs, dir, opts, subpool));
+ svn_pool_destroy(subpool);
+
+ subpool = svn_pool_create(pool);
+
+ /* Rewrite the format file */
+ SVN_ERR(svn_io_read_version_file(&version,
+ svn_dirent_join(dir, "format", subpool),
+ subpool));
+ SVN_ERR(write_format(dir, version, shard_size, subpool));
+
+ /* Reopen the filesystem */
+ SVN_ERR(svn_fs_open2(&fs, dir, NULL, subpool, subpool));
+
+ /* Revision 1: 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_change_txn_prop(txn, SVN_PROP_REVISION_LOG,
+ svn_string_create(R1_LOG_MSG, pool),
+ pool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
+
+ /* Revisions 2 thru NUM_REVS-1: content tweaks to "iota". */
+ iterpool = svn_pool_create(subpool);
+ while (after_rev < num_revs)
+ {
+ svn_pool_clear(iterpool);
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, after_rev, iterpool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, iterpool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "iota",
+ get_rev_contents(after_rev + 1,
+ iterpool),
+ iterpool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, iterpool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
+ }
+ svn_pool_destroy(iterpool);
+ svn_pool_destroy(subpool);
+
+ /* Now pack the FS */
+ pnb.expected_shard = 0;
+ pnb.expected_action = svn_fs_pack_notify_start;
+ return svn_fs_pack(dir, pack_notify, &pnb, NULL, NULL, pool);
+}
+
+/* Create a packed FSFS filesystem for revprop tests at REPO_NAME with
+ * MAX_REV revisions and the given SHARD_SIZE and OPTS. Return it in *FS.
+ * Use POOL for allocations.
+ */
+static svn_error_t *
+prepare_revprop_repo(svn_fs_t **fs,
+ const char *repo_name,
+ int max_rev,
+ int shard_size,
+ const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ const char *conflict;
+ svn_revnum_t after_rev;
+ apr_pool_t *subpool;
+
+ /* Create the packed FS and open it. */
+ SVN_ERR(create_packed_filesystem(repo_name, opts, max_rev, shard_size, pool));
+ SVN_ERR(svn_fs_open2(fs, repo_name, NULL, pool, pool));
+
+ subpool = svn_pool_create(pool);
+ /* Do a commit to trigger packing. */
+ SVN_ERR(svn_fs_begin_txn(&txn, *fs, max_rev, subpool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "iota", "new-iota", subpool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
+ svn_pool_destroy(subpool);
+
+ /* Pack the repository. */
+ SVN_ERR(svn_fs_pack(repo_name, NULL, NULL, NULL, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* For revision REV, return a short log message allocated in POOL.
+ */
+static svn_string_t *
+default_log(svn_revnum_t rev, apr_pool_t *pool)
+{
+ return svn_string_createf(pool, "Default message for rev %ld", rev);
+}
+
+/* For revision REV, return a long log message allocated in POOL.
+ */
+static svn_string_t *
+large_log(svn_revnum_t rev, apr_size_t length, apr_pool_t *pool)
+{
+ svn_stringbuf_t *temp = svn_stringbuf_create_ensure(100000, pool);
+ int i, count = (int)(length - 50) / 6;
+
+ svn_stringbuf_appendcstr(temp, "A ");
+ for (i = 0; i < count; ++i)
+ svn_stringbuf_appendcstr(temp, "very, ");
+
+ svn_stringbuf_appendcstr(temp,
+ apr_psprintf(pool, "very long message for rev %ld, indeed", rev));
+
+ return svn_stringbuf__morph_into_string(temp);
+}
+
+/* For revision REV, return a long log message allocated in POOL.
+ */
+static svn_string_t *
+huge_log(svn_revnum_t rev, apr_pool_t *pool)
+{
+ return large_log(rev, 90000, pool);
+}
+
+
+/*** Tests ***/
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-fsx-pack"
+#define SHARD_SIZE 7
+#define MAX_REV 53
+static svn_error_t *
+pack_filesystem(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ int i;
+ svn_node_kind_t kind;
+ const char *path;
+ char buf[80];
+ apr_file_t *file;
+ apr_size_t len;
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
+ pool));
+
+ /* Check to see that the pack files exist, and that the rev directories
+ don't. */
+ for (i = 0; i < (MAX_REV + 1) / SHARD_SIZE; i++)
+ {
+ path = svn_dirent_join_many(pool, REPO_NAME, "revs",
+ apr_psprintf(pool, "%d.pack", i / SHARD_SIZE),
+ "pack", SVN_VA_NULL);
+
+ /* This file should exist. */
+ SVN_ERR(svn_io_check_path(path, &kind, pool));
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Expected pack file '%s' not found", path);
+
+ /* This directory should not exist. */
+ path = svn_dirent_join_many(pool, REPO_NAME, "revs",
+ apr_psprintf(pool, "%d", i / SHARD_SIZE),
+ SVN_VA_NULL);
+ SVN_ERR(svn_io_check_path(path, &kind, pool));
+ if (kind != svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Unexpected directory '%s' found", path);
+ }
+
+ /* Ensure the min-unpacked-rev jives with the above operations. */
+ SVN_ERR(svn_io_file_open(&file,
+ svn_dirent_join(REPO_NAME, PATH_MIN_UNPACKED_REV,
+ pool),
+ APR_READ | APR_BUFFERED, APR_OS_DEFAULT, pool));
+ len = sizeof(buf);
+ SVN_ERR(svn_io_read_length_line(file, buf, &len, pool));
+ SVN_ERR(svn_io_file_close(file, pool));
+ if (SVN_STR_TO_REV(buf) != (MAX_REV / SHARD_SIZE) * SHARD_SIZE)
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Bad '%s' contents", PATH_MIN_UNPACKED_REV);
+
+ /* Finally, make sure the final revision directory does exist. */
+ path = svn_dirent_join_many(pool, REPO_NAME, "revs",
+ apr_psprintf(pool, "%d", (i / SHARD_SIZE) + 1),
+ SVN_VA_NULL);
+ SVN_ERR(svn_io_check_path(path, &kind, pool));
+ if (kind != svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Expected directory '%s' not found", path);
+
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-fsx-pack-even"
+#define SHARD_SIZE 4
+#define MAX_REV 11
+static svn_error_t *
+pack_even_filesystem(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_node_kind_t kind;
+ const char *path;
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
+ pool));
+
+ path = svn_dirent_join_many(pool, REPO_NAME, "revs", "2.pack", SVN_VA_NULL);
+ SVN_ERR(svn_io_check_path(path, &kind, pool));
+ if (kind != svn_node_dir)
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Packing did not complete as expected");
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-read-packed-fs"
+#define SHARD_SIZE 5
+#define MAX_REV 11
+static svn_error_t *
+read_packed_fs(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_stream_t *rstream;
+ svn_stringbuf_t *rstring;
+ svn_revnum_t i;
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
+
+ for (i = 1; i < (MAX_REV + 1); i++)
+ {
+ svn_fs_root_t *rev_root;
+ svn_stringbuf_t *sb;
+
+ SVN_ERR(svn_fs_revision_root(&rev_root, fs, i, pool));
+ SVN_ERR(svn_fs_file_contents(&rstream, rev_root, "iota", pool));
+ SVN_ERR(svn_test__stream_to_string(&rstring, rstream, pool));
+
+ if (i == 1)
+ sb = svn_stringbuf_create("This is the file 'iota'.\n", pool);
+ else
+ sb = svn_stringbuf_create(get_rev_contents(i, pool), pool);
+
+ if (! svn_stringbuf_compare(rstring, sb))
+ return svn_error_createf(SVN_ERR_FS_GENERAL, NULL,
+ "Bad data in revision %ld.", i);
+ }
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-commit-packed-fs"
+#define SHARD_SIZE 5
+#define MAX_REV 10
+static svn_error_t *
+commit_packed_fs(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;
+ const char *conflict;
+ svn_revnum_t after_rev;
+
+ /* Create the packed FS and open it. */
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, 5, pool));
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
+
+ /* Now do a commit. */
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, pool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, pool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "iota",
+ "How much better is it to get wisdom than gold! and to get "
+ "understanding rather to be chosen than silver!", pool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, pool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-get-set-revprop-packed-fs"
+#define SHARD_SIZE 4
+#define MAX_REV 10
+static svn_error_t *
+get_set_revprop_packed_fs(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_string_t *prop_value;
+
+ /* Create the packed FS and open it. */
+ SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
+ pool));
+
+ /* Try to get revprop for revision 0
+ * (non-packed due to special handling). */
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR,
+ pool));
+
+ /* Try to change revprop for revision 0
+ * (non-packed due to special handling). */
+ SVN_ERR(svn_fs_change_rev_prop(fs, 0, SVN_PROP_REVISION_AUTHOR,
+ svn_string_create("tweaked-author", pool),
+ pool));
+
+ /* verify */
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 0, SVN_PROP_REVISION_AUTHOR,
+ pool));
+ SVN_TEST_STRING_ASSERT(prop_value->data, "tweaked-author");
+
+ /* Try to get packed revprop for revision 5. */
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 5, SVN_PROP_REVISION_AUTHOR,
+ pool));
+
+ /* Try to change packed revprop for revision 5. */
+ SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_AUTHOR,
+ svn_string_create("tweaked-author2", pool),
+ pool));
+
+ /* verify */
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, 5, SVN_PROP_REVISION_AUTHOR,
+ pool));
+ SVN_TEST_STRING_ASSERT(prop_value->data, "tweaked-author2");
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-get-set-large-revprop-packed-fs"
+#define SHARD_SIZE 4
+#define MAX_REV 11
+static svn_error_t *
+get_set_large_revprop_packed_fs(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_string_t *prop_value;
+ svn_revnum_t rev;
+
+ /* Create the packed FS and open it. */
+ SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
+ pool));
+
+ /* Set commit messages to different, large values that fill the pack
+ * files but do not exceed the pack size limit. */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ SVN_ERR(svn_fs_change_rev_prop(fs, rev, SVN_PROP_REVISION_LOG,
+ large_log(rev, 15000, pool),
+ pool));
+
+ /* verify */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ {
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
+ SVN_PROP_REVISION_LOG, pool));
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ large_log(rev, 15000, pool)->data);
+ }
+
+ /* Put a larger revprop into the last, some middle and the first revision
+ * of a pack. This should cause the packs to split in the middle. */
+ SVN_ERR(svn_fs_change_rev_prop(fs, 3, SVN_PROP_REVISION_LOG,
+ /* rev 0 is not packed */
+ large_log(3, 37000, pool),
+ pool));
+ SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_LOG,
+ large_log(5, 25000, pool),
+ pool));
+ SVN_ERR(svn_fs_change_rev_prop(fs, 8, SVN_PROP_REVISION_LOG,
+ large_log(8, 25000, pool),
+ pool));
+
+ /* verify */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ {
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
+ SVN_PROP_REVISION_LOG, pool));
+
+ if (rev == 3)
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ large_log(rev, 37000, pool)->data);
+ else if (rev == 5 || rev == 8)
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ large_log(rev, 25000, pool)->data);
+ else
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ large_log(rev, 15000, pool)->data);
+ }
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-get-set-huge-revprop-packed-fs"
+#define SHARD_SIZE 4
+#define MAX_REV 10
+static svn_error_t *
+get_set_huge_revprop_packed_fs(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ svn_string_t *prop_value;
+ svn_revnum_t rev;
+
+ /* Create the packed FS and open it. */
+ SVN_ERR(prepare_revprop_repo(&fs, REPO_NAME, MAX_REV, SHARD_SIZE, opts,
+ pool));
+
+ /* Set commit messages to different values */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ SVN_ERR(svn_fs_change_rev_prop(fs, rev, SVN_PROP_REVISION_LOG,
+ default_log(rev, pool),
+ pool));
+
+ /* verify */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ {
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
+ SVN_PROP_REVISION_LOG, pool));
+ SVN_TEST_STRING_ASSERT(prop_value->data, default_log(rev, pool)->data);
+ }
+
+ /* Put a huge revprop into the last, some middle and the first revision
+ * of a pack. They will cause the pack files to split accordingly. */
+ SVN_ERR(svn_fs_change_rev_prop(fs, 3, SVN_PROP_REVISION_LOG,
+ huge_log(3, pool),
+ pool));
+ SVN_ERR(svn_fs_change_rev_prop(fs, 5, SVN_PROP_REVISION_LOG,
+ huge_log(5, pool),
+ pool));
+ SVN_ERR(svn_fs_change_rev_prop(fs, 8, SVN_PROP_REVISION_LOG,
+ huge_log(8, pool),
+ pool));
+
+ /* verify */
+ for (rev = 0; rev <= MAX_REV; ++rev)
+ {
+ SVN_ERR(svn_fs_revision_prop(&prop_value, fs, rev,
+ SVN_PROP_REVISION_LOG, pool));
+
+ if (rev == 3 || rev == 5 || rev == 8)
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ huge_log(rev, pool)->data);
+ else
+ SVN_TEST_STRING_ASSERT(prop_value->data,
+ default_log(rev, pool)->data);
+ }
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+/* Regression test for issue #3571 (fsfs 'svnadmin recover' expects
+ youngest revprop to be outside revprops.db). */
+#define REPO_NAME "test-repo-recover-fully-packed"
+#define SHARD_SIZE 4
+#define MAX_REV 7
+static svn_error_t *
+recover_fully_packed(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool;
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ const char *conflict;
+ svn_revnum_t after_rev;
+ svn_error_t *err;
+
+ /* Create a packed FS for which every revision will live in a pack
+ digest file, and then recover it. */
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
+ SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool));
+
+ /* Add another revision, re-pack, re-recover. */
+ subpool = svn_pool_create(pool);
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, subpool, subpool));
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "A/mu", "new-mu", subpool));
+ SVN_ERR(svn_fs_commit_txn(&conflict, &after_rev, txn, subpool));
+ SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(after_rev));
+ svn_pool_destroy(subpool);
+ SVN_ERR(svn_fs_pack(REPO_NAME, NULL, NULL, NULL, NULL, pool));
+ SVN_ERR(svn_fs_recover(REPO_NAME, NULL, NULL, pool));
+
+ /* Now, delete the youngest revprop file, and recover again. This
+ time we want to see an error! */
+ SVN_ERR(svn_io_remove_file2(
+ svn_dirent_join_many(pool, REPO_NAME, PATH_REVPROPS_DIR,
+ apr_psprintf(pool, "%ld/%ld",
+ after_rev / SHARD_SIZE,
+ after_rev),
+ SVN_VA_NULL),
+ FALSE, pool));
+ err = svn_fs_recover(REPO_NAME, NULL, NULL, pool);
+ if (! err)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "Expected SVN_ERR_FS_CORRUPT error; got none");
+ if (err->apr_err != SVN_ERR_FS_CORRUPT)
+ return svn_error_create(SVN_ERR_TEST_FAILED, err,
+ "Expected SVN_ERR_FS_CORRUPT error; got:");
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+/* Regression test for issue #4320 (fsfs file-hinting fails when reading a rep
+ from the transaction that is commiting rev = SHARD_SIZE). */
+#define REPO_NAME "test-repo-file-hint-at-shard-boundary"
+#define SHARD_SIZE 4
+#define MAX_REV (SHARD_SIZE - 1)
+static svn_error_t *
+file_hint_at_shard_boundary(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool;
+ svn_fs_t *fs;
+ svn_fs_txn_t *txn;
+ svn_fs_root_t *txn_root;
+ const char *file_contents;
+ svn_stringbuf_t *retrieved_contents;
+ svn_error_t *err = SVN_NO_ERROR;
+
+ /* Create a packed FS and MAX_REV revisions */
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE, pool));
+
+ /* Reopen the filesystem */
+ subpool = svn_pool_create(pool);
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, subpool, subpool));
+
+ /* Revision = SHARD_SIZE */
+ file_contents = get_rev_contents(SHARD_SIZE, subpool);
+ SVN_ERR(svn_fs_begin_txn(&txn, fs, MAX_REV, subpool));
+ SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
+ SVN_ERR(svn_test__set_file_contents(txn_root, "iota", file_contents,
+ subpool));
+
+ /* Retrieve the file. */
+ SVN_ERR(svn_test__get_file_contents(txn_root, "iota", &retrieved_contents,
+ subpool));
+ if (strcmp(retrieved_contents->data, file_contents))
+ {
+ err = svn_error_create(SVN_ERR_TEST_FAILED, err,
+ "Retrieved incorrect contents from iota.");
+ }
+
+ /* Close the repo. */
+ svn_pool_destroy(subpool);
+
+ return err;
+}
+#undef REPO_NAME
+#undef MAX_REV
+#undef SHARD_SIZE
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-fsx-info"
+#define SHARD_SIZE 3
+#define MAX_REV 5
+static svn_error_t *
+test_info(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs;
+ const svn_fs_fsfs_info_t *fsfs_info;
+ const svn_fs_info_placeholder_t *info;
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
+ pool));
+
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
+ SVN_ERR(svn_fs_info(&info, fs, pool, pool));
+ info = svn_fs_info_dup(info, pool, pool);
+
+ SVN_TEST_STRING_ASSERT(opts->fs_type, info->fs_type);
+
+ /* Bail (with success) on known-untestable scenarios */
+ if (strcmp(opts->fs_type, "fsx") != 0)
+ return SVN_NO_ERROR;
+
+ fsfs_info = (const void *)info;
+ SVN_TEST_ASSERT(fsfs_info->shard_size == SHARD_SIZE);
+ SVN_TEST_ASSERT(fsfs_info->min_unpacked_rev
+ == (MAX_REV + 1) / SHARD_SIZE * SHARD_SIZE);
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-fsx-rev-container"
+#define SHARD_SIZE 3
+#define MAX_REV 5
+static svn_error_t *
+test_reps(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_fs_t *fs = NULL;
+ svn_fs_x__reps_builder_t *builder;
+ svn_fs_x__reps_t *container;
+ svn_stringbuf_t *serialized;
+ svn_stream_t *stream;
+ svn_stringbuf_t *contents = svn_stringbuf_create_ensure(10000, pool);
+ int i;
+
+ for (i = 0; i < 10000; ++i)
+ {
+ int v, s = 0;
+ for (v = i; v > 0; v /= 10)
+ s += v % 10;
+
+ svn_stringbuf_appendbyte(contents, (char)(s + ' '));
+ }
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
+ pool));
+
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
+
+ builder = svn_fs_x__reps_builder_create(fs, pool);
+ for (i = 10000; i > 10; --i)
+ {
+ apr_size_t idx;
+ svn_string_t string;
+ string.data = contents->data;
+ string.len = i;
+
+ SVN_ERR(svn_fs_x__reps_add(&idx, builder, &string));
+ }
+
+ serialized = svn_stringbuf_create_empty(pool);
+ stream = svn_stream_from_stringbuf(serialized, pool);
+ SVN_ERR(svn_fs_x__write_reps_container(stream, builder, pool));
+
+ SVN_ERR(svn_stream_reset(stream));
+ SVN_ERR(svn_fs_x__read_reps_container(&container, stream, pool, pool));
+ SVN_ERR(svn_stream_close(stream));
+
+ return SVN_NO_ERROR;
+}
+
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+
+/* ------------------------------------------------------------------------ */
+#define REPO_NAME "test-repo-fsx-pack-shard-size-one"
+#define SHARD_SIZE 1
+#define MAX_REV 4
+static svn_error_t *
+pack_shard_size_one(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_string_t *propval;
+ svn_fs_t *fs;
+
+ SVN_ERR(create_packed_filesystem(REPO_NAME, opts, MAX_REV, SHARD_SIZE,
+ pool));
+ SVN_ERR(svn_fs_open2(&fs, REPO_NAME, NULL, pool, pool));
+ /* whitebox: revprop packing special-cases r0, which causes
+ (start_rev==1, end_rev==0) in pack_revprops_shard(). So test that. */
+ SVN_ERR(svn_fs_revision_prop(&propval, fs, 1, SVN_PROP_REVISION_LOG, pool));
+ SVN_TEST_STRING_ASSERT(propval->data, R1_LOG_MSG);
+
+ return SVN_NO_ERROR;
+}
+#undef REPO_NAME
+#undef SHARD_SIZE
+#undef MAX_REV
+/* ------------------------------------------------------------------------ */
+
+/* The test table. */
+
+static int max_threads = 4;
+
+static struct svn_test_descriptor_t test_funcs[] =
+ {
+ SVN_TEST_NULL,
+ SVN_TEST_OPTS_PASS(pack_filesystem,
+ "pack a FSX filesystem"),
+ SVN_TEST_OPTS_PASS(pack_even_filesystem,
+ "pack FSX where revs % shard = 0"),
+ SVN_TEST_OPTS_PASS(read_packed_fs,
+ "read from a packed FSX filesystem"),
+ SVN_TEST_OPTS_PASS(commit_packed_fs,
+ "commit to a packed FSX filesystem"),
+ SVN_TEST_OPTS_PASS(get_set_revprop_packed_fs,
+ "get/set revprop while packing FSX filesystem"),
+ SVN_TEST_OPTS_PASS(get_set_large_revprop_packed_fs,
+ "get/set large packed revprops in FSX"),
+ SVN_TEST_OPTS_PASS(get_set_huge_revprop_packed_fs,
+ "get/set huge packed revprops in FSX"),
+ SVN_TEST_OPTS_PASS(recover_fully_packed,
+ "recover a fully packed filesystem"),
+ SVN_TEST_OPTS_PASS(file_hint_at_shard_boundary,
+ "test file hint at shard boundary"),
+ SVN_TEST_OPTS_PASS(test_info,
+ "test svn_fs_info"),
+ SVN_TEST_OPTS_PASS(test_reps,
+ "test representations container"),
+ SVN_TEST_OPTS_PASS(pack_shard_size_one,
+ "test packing with shard size = 1"),
+ SVN_TEST_NULL
+ };
+
+SVN_TEST_MAIN