#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <apr.h>
#include "../svn_test.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "private/svn_skel.h"
#include "../svn_test_fs.h"
#include "../../libsvn_fs_base/util/fs_skels.h"
#include "../../libsvn_fs_base/bdb/changes-table.h"
static const char *standard_txns[]
= { "0", "1", "2", "3", "4", "5", "6" };
static const char *standard_changes[19][6]
= { { "0", "/foo", "1.0.0", "add", 0, 0 },
{ "0", "/foo", "1.0.0", "modify", 0, "1" },
{ "0", "/bar", "2.0.0", "add", 0, 0 },
{ "0", "/bar", "2.0.0", "modify", "1", 0 },
{ "0", "/bar", "2.0.0", "modify", 0, "1" },
{ "0", "/baz", "3.0.0", "add", 0, 0 },
{ "0", "/baz", "3.0.0", "modify", "1", 0 },
{ "1", "/foo", "1.0.1", "modify", "1", 0 },
{ "2", "/foo", "1.0.2", "modify", 0, "1" },
{ "2", "/bar", "2.0.2", "modify", "1", 0 },
{ "3", "/baz", "3.0.3", "modify", "1", 0 },
{ "4", "/fob", "4.0.4", "add", 0, 0 },
{ "4", "/fob", "4.0.4", "modify", "1", 0 },
{ "5", "/baz", "3.0.3", "delete", 0, 0 },
{ "5", "/baz", "5.0.5", "add", 0, "1" },
{ "5", "/baz", "5.0.5", "modify", "1", 0 },
{ "6", "/fob", "4.0.6", "modify", "1", 0 },
{ "6", "/fob", "4.0.6", "reset", 0, 0 },
{ "6", "/fob", "4.0.6", "modify", 0, "1" } };
static svn_fs_path_change_kind_t string_to_kind(const char *str)
{
if (strcmp(str, "add") == 0)
return svn_fs_path_change_add;
if (strcmp(str, "delete") == 0)
return svn_fs_path_change_delete;
if (strcmp(str, "replace") == 0)
return svn_fs_path_change_replace;
if (strcmp(str, "modify") == 0)
return svn_fs_path_change_modify;
if (strcmp(str, "reset") == 0)
return svn_fs_path_change_reset;
return 0;
}
struct changes_args
{
svn_fs_t *fs;
const char *key;
change_t *change;
apr_array_header_t *raw_changes;
apr_hash_t *changes;
};
static svn_error_t *
txn_body_changes_add(void *baton, trail_t *trail)
{
struct changes_args *b = baton;
return svn_fs_bdb__changes_add(b->fs, b->key, b->change,
trail, trail->pool);
}
static svn_error_t *
add_standard_changes(svn_fs_t *fs,
apr_pool_t *pool)
{
int i;
struct changes_args args;
int num_changes = sizeof(standard_changes) / sizeof(const char *) / 6;
for (i = 0; i < num_changes; i++)
{
change_t change;
change.path = standard_changes[i][1];
change.noderev_id = svn_fs_parse_id(standard_changes[i][2],
strlen(standard_changes[i][2]),
pool);
change.kind = string_to_kind(standard_changes[i][3]);
change.text_mod = standard_changes[i][4] ? 1 : 0;
change.prop_mod = standard_changes[i][5] ? 1 : 0;
args.fs = fs;
args.key = standard_changes[i][0];
args.change = &change;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_add, &args,
TRUE, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
txn_body_changes_fetch_raw(void *baton, trail_t *trail)
{
struct changes_args *b = baton;
return svn_fs_bdb__changes_fetch_raw(&(b->raw_changes), b->fs, b->key,
trail, trail->pool);
}
static svn_error_t *
txn_body_changes_fetch(void *baton, trail_t *trail)
{
struct changes_args *b = baton;
return svn_fs_bdb__changes_fetch(&(b->changes), b->fs, b->key,
trail, trail->pool);
}
static svn_error_t *
txn_body_changes_delete(void *baton, trail_t *trail)
{
struct changes_args *b = baton;
return svn_fs_bdb__changes_delete(b->fs, b->key, trail, trail->pool);
}
static svn_error_t *
changes_add(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-add", opts,
pool));
SVN_ERR(add_standard_changes(fs, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
changes_fetch_raw(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
int i;
int num_txns = sizeof(standard_txns) / sizeof(const char *);
int cur_change_index = 0;
struct changes_args args;
SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch", opts,
pool));
args.fs = fs;
args.key = "blahbliggityblah";
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch_raw, &args,
FALSE, pool));
if ((! args.raw_changes) || (args.raw_changes->nelts))
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"expected empty changes array");
SVN_ERR(add_standard_changes(fs, pool));
for (i = 0; i < num_txns; i++)
{
const char *txn_id = standard_txns[i];
int j;
args.fs = fs;
args.key = txn_id;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch_raw,
&args, FALSE, pool));
if (! args.raw_changes)
return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
"got no changes for key '%s'", txn_id);
for (j = 0; j < args.raw_changes->nelts; j++)
{
svn_string_t *noderev_id;
svn_fs_path_change_kind_t kind;
change_t *change = APR_ARRAY_IDX(args.raw_changes, j, change_t *);
int mod_bit = 0;
if (strcmp(standard_changes[cur_change_index][0], txn_id))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"missing some changes for key '%s'", txn_id);
if (strcmp(standard_changes[cur_change_index][1], change->path))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"paths differ in change for key '%s'", txn_id);
noderev_id = svn_fs_unparse_id(change->noderev_id, pool);
if (strcmp(standard_changes[cur_change_index][2], noderev_id->data))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"node revision ids differ in change for key '%s'", txn_id);
kind = string_to_kind(standard_changes[cur_change_index][3]);
if (kind != change->kind)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change kinds differ in change for key '%s'", txn_id);
mod_bit = standard_changes[cur_change_index][4] ? 1 : 0;
if (mod_bit != change->text_mod)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change text-mod bits differ in change for key '%s'", txn_id);
mod_bit = standard_changes[cur_change_index][5] ? 1 : 0;
if (mod_bit != change->prop_mod)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change prop-mod bits differ in change for key '%s'", txn_id);
cur_change_index++;
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
changes_delete(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
int i;
int num_txns = sizeof(standard_txns) / sizeof(const char *);
struct changes_args args;
SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-delete", opts,
pool));
SVN_ERR(add_standard_changes(fs, pool));
for (i = 0; i < num_txns; i++)
{
args.fs = fs;
args.key = standard_txns[i];
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_delete,
&args, FALSE, pool));
args.changes = 0;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch_raw,
&args, FALSE, pool));
if ((! args.raw_changes) || (args.raw_changes->nelts))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"expected empty changes array for txn '%s'", args.key);
}
return SVN_NO_ERROR;
}
static apr_hash_t *
get_ideal_changes(const char *txn_id,
apr_pool_t *pool)
{
apr_hash_t *ideal = apr_hash_make(pool);
svn_fs_path_change_t *change;
if (strcmp(txn_id, "0") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("1.0.0", 5, pool);
change->change_kind = svn_fs_path_change_add;
change->text_mod = 0;
change->prop_mod = 1;
apr_hash_set(ideal, "/foo", APR_HASH_KEY_STRING, change);
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("2.0.0", 5, pool);
change->change_kind = svn_fs_path_change_add;
change->text_mod = 1;
change->prop_mod = 1;
apr_hash_set(ideal, "/bar", APR_HASH_KEY_STRING, change);
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("3.0.0", 5, pool);
change->change_kind = svn_fs_path_change_add;
change->text_mod = 1;
change->prop_mod = 0;
apr_hash_set(ideal, "/baz", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "1") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("1.0.1", 5, pool);
change->change_kind = svn_fs_path_change_modify;
change->text_mod = 1;
change->prop_mod = 0;
apr_hash_set(ideal, "/foo", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "2") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("1.0.2", 5, pool);
change->change_kind = svn_fs_path_change_modify;
change->text_mod = 0;
change->prop_mod = 1;
apr_hash_set(ideal, "/foo", APR_HASH_KEY_STRING, change);
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("2.0.2", 5, pool);
change->change_kind = svn_fs_path_change_modify;
change->text_mod = 1;
change->prop_mod = 0;
apr_hash_set(ideal, "/bar", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "3") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("3.0.3", 5, pool);
change->change_kind = svn_fs_path_change_modify;
change->text_mod = 1;
change->prop_mod = 0;
apr_hash_set(ideal, "/baz", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "4") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("4.0.4", 5, pool);
change->change_kind = svn_fs_path_change_add;
change->text_mod = 1;
change->prop_mod = 0;
apr_hash_set(ideal, "/fob", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "5") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("5.0.5", 5, pool);
change->change_kind = svn_fs_path_change_replace;
change->text_mod = 1;
change->prop_mod = 1;
apr_hash_set(ideal, "/baz", APR_HASH_KEY_STRING, change);
}
if (strcmp(txn_id, "6") == 0)
{
change = apr_palloc(pool, sizeof(*change));
change->node_rev_id = svn_fs_parse_id("4.0.6", 5, pool);
change->change_kind = svn_fs_path_change_modify;
change->text_mod = 0;
change->prop_mod = 1;
apr_hash_set(ideal, "/fob", APR_HASH_KEY_STRING, change);
}
return ideal;
}
static svn_error_t *
compare_changes(apr_hash_t *ideals,
apr_hash_t *changes,
const svn_test_opts_t *opts,
const char *txn_id,
apr_pool_t *pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(pool, ideals); hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change_t *ideal_change, *change;
const char *path;
apr_hash_this(hi, &key, NULL, &val);
path = (const char *) key;
ideal_change = val;
change = apr_hash_get(changes, path, APR_HASH_KEY_STRING);
if (! change)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"missing expected change for path '%s' in txn_id '%s'",
path, txn_id);
if (svn_fs_compare_ids(change->node_rev_id,
ideal_change->node_rev_id))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"node revision ids differ in change for key '%s'", txn_id);
if (change->change_kind != ideal_change->change_kind)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change kinds differ in change for key '%s'", txn_id);
if (change->text_mod != ideal_change->text_mod)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change text-mod bits differ in change for key '%s'", txn_id);
if (change->prop_mod != ideal_change->prop_mod)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"change prop-mod bits differ in change for key '%s'", txn_id);
}
return SVN_NO_ERROR;
}
static svn_error_t *
changes_fetch(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
int i;
int num_txns = sizeof(standard_txns) / sizeof(const char *);
struct changes_args args;
SVN_ERR(svn_test__create_bdb_fs(&fs, "test-repo-changes-fetch", opts,
pool));
args.fs = fs;
args.key = "blahbliggityblah";
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args,
FALSE, pool));
if ((! args.changes) || (apr_hash_count(args.changes)))
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"expected empty changes hash");
SVN_ERR(add_standard_changes(fs, pool));
for (i = 0; i < num_txns; i++)
{
const char *txn_id = standard_txns[i];
apr_hash_t *ideals;
ideals = get_ideal_changes(txn_id, pool);
args.fs = fs;
args.key = txn_id;
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args,
FALSE, pool));
if (! args.changes)
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"got no changes for key '%s'", txn_id);
if (apr_hash_count(ideals) != apr_hash_count(args.changes))
return svn_error_createf
(SVN_ERR_TEST_FAILED, NULL,
"unexpected number of changes for key '%s'", txn_id);
SVN_ERR(compare_changes(ideals, args.changes, opts, txn_id, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
changes_fetch_ordering(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
svn_revnum_t youngest_rev = 0;
const char *txn_name;
svn_fs_txn_t *txn;
svn_fs_root_t *txn_root, *rev_root;
struct changes_args args;
apr_pool_t *subpool = svn_pool_create(pool);
apr_hash_index_t *hi;
SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-changes-fetch-ordering", opts,
pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
{
static svn_test__txn_script_command_t script_entries[] = {
{ 'a', "dir1", 0 },
{ 'a', "file1", "This is the file 'file1'.\n" },
{ 'a', "dir1/file2", "This is the file 'file2'.\n" },
{ 'a', "dir1/file3", "This is the file 'file3'.\n" },
{ 'a', "dir1/file4", "This is the file 'file4'.\n" },
};
SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool));
}
SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
svn_pool_clear(subpool);
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
{
static svn_test__txn_script_command_t script_entries[] = {
{ 'd', "file1", "This is the file 'file1'.\n" },
{ 'd', "dir1/file2", "This is the file 'file2'.\n" },
{ 'd', "dir1/file3", "This is the file 'file3'.\n" },
{ 'a', "dir1/file5", "This is the file 'file4'.\n" },
{ 'a', "dir1/dir2", 0 },
{ 'd', "dir1", 0 },
{ 'a', "dir3", 0 },
};
SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 7, subpool));
}
SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
svn_pool_clear(subpool);
args.fs = fs;
args.key = txn_name;
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args,
FALSE, subpool));
if ((! args.changes) || (apr_hash_count(args.changes) != 3))
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"expected changes");
for (hi = apr_hash_first(subpool, args.changes);
hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change_t *change;
apr_hash_this(hi, &key, NULL, &val);
change = val;
if ((change->change_kind == svn_fs_path_change_add)
&& (strcmp(key, "/dir3") == 0))
;
else if ((change->change_kind == svn_fs_path_change_delete)
&& ((strcmp(key, "/dir1") == 0)
|| (strcmp(key, "/file1") == 0)))
;
else
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"got wrong changes");
}
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
{
static svn_test__txn_script_command_t script_entries[] = {
{ 'a', "dir1", 0 },
{ 'a', "file1", "This is the file 'file1'.\n" },
{ 'a', "dir1/file2", "This is the file 'file2'.\n" },
{ 'a', "dir1/file3", "This is the file 'file3'.\n" },
{ 'a', "dir1/file4", "This is the file 'file4'.\n" },
};
SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool));
}
SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
svn_pool_clear(subpool);
SVN_ERR(svn_fs_begin_txn(&txn, fs, youngest_rev, subpool));
SVN_ERR(svn_fs_txn_name(&txn_name, txn, pool));
SVN_ERR(svn_fs_txn_root(&txn_root, txn, subpool));
SVN_ERR(svn_fs_revision_root(&rev_root, fs, 1, subpool));
{
static svn_test__txn_script_command_t script_entries[] = {
{ 'd', "file1", "This is the file 'file1'.\n" },
{ 'd', "dir1/file2", "This is the file 'file2'.\n" },
{ 'd', "dir1/file3", "This is the file 'file3'.\n" },
{ 'a', "dir1/file5", "This is the file 'file4'.\n" },
{ 'a', "dir1/dir2", 0 },
};
SVN_ERR(svn_test__txn_script_exec(txn_root, script_entries, 5, subpool));
SVN_ERR(svn_fs_copy(rev_root, "dir1", txn_root, "dir1", subpool));
SVN_ERR(svn_fs_make_dir(txn_root, "dir4", subpool));
}
SVN_ERR(svn_fs_commit_txn(NULL, &youngest_rev, txn, subpool));
SVN_TEST_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
svn_pool_clear(subpool);
args.fs = fs;
args.key = txn_name;
SVN_ERR(svn_fs_base__retry_txn(fs, txn_body_changes_fetch, &args,
FALSE, subpool));
if ((! args.changes) || (apr_hash_count(args.changes) != 3))
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"expected changes");
for (hi = apr_hash_first(subpool, args.changes);
hi; hi = apr_hash_next(hi))
{
const void *key;
void *val;
svn_fs_path_change_t *change;
apr_hash_this(hi, &key, NULL, &val);
change = val;
if ((change->change_kind == svn_fs_path_change_add)
&& (strcmp(key, "/dir4") == 0))
;
else if ((change->change_kind == svn_fs_path_change_replace)
&& (strcmp(key, "/dir1") == 0))
;
else if ((change->change_kind == svn_fs_path_change_delete)
&& (strcmp(key, "/file1") == 0))
;
else
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"got wrong changes");
}
return SVN_NO_ERROR;
}
static svn_error_t *
changes_bad_sequences(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_fs_t *fs;
apr_pool_t *subpool = svn_pool_create(pool);
svn_error_t *err;
SVN_ERR(svn_test__create_bdb_fs
(&fs, "test-repo-changes-bad-sequences", opts,
pool));
svn_pool_clear(subpool);
{
static const char *bogus_changes[][6]
= { { "x", "/foo", "1.0.0", "add", 0 , 0 },
{ "x", "/foo", "1.0.0", "modify", 0 , "1" },
{ "x", "/foo", "2.0.0", "modify", "1", "1" } };
int num_changes = sizeof(bogus_changes) / sizeof(const char *) / 6;
struct changes_args args;
int i;
for (i = 0; i < num_changes; i++)
{
change_t change;
change.path = bogus_changes[i][1];
change.noderev_id = svn_fs_parse_id(bogus_changes[i][2],
strlen(bogus_changes[i][2]),
subpool);
change.kind = string_to_kind(bogus_changes[i][3]);
change.text_mod = bogus_changes[i][4] ? 1 : 0;
change.prop_mod = bogus_changes[i][5] ? 1 : 0;
args.fs = fs;
args.key = "x";
args.change = &change;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_add, &args,
TRUE, subpool));
}
args.fs = fs;
args.key = "x";
err = svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch, &args,
TRUE, subpool);
if (!err)
{
return svn_error_create(SVN_ERR_TEST_FAILED, 0,
"Expected SVN_ERR_FS_CORRUPT, got no error.");
}
else if (err->apr_err != SVN_ERR_FS_CORRUPT)
{
return svn_error_create(SVN_ERR_TEST_FAILED, err,
"Expected SVN_ERR_FS_CORRUPT, got a different error.");
}
else
{
svn_error_clear(err);
}
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_delete, &args,
TRUE, subpool));
}
svn_pool_clear(subpool);
{
static const char *bogus_changes[][6]
= { { "x", "/foo", "1.0.0", "delete", 0 , 0 },
{ "x", "/foo", "1.0.0", "modify", "1", 0 } };
int num_changes = sizeof(bogus_changes) / sizeof(const char *) / 6;
struct changes_args args;
int i;
for (i = 0; i < num_changes; i++)
{
change_t change;
change.path = bogus_changes[i][1];
change.noderev_id = svn_fs_parse_id(bogus_changes[i][2],
strlen(bogus_changes[i][2]),
subpool);
change.kind = string_to_kind(bogus_changes[i][3]);
change.text_mod = bogus_changes[i][4] ? 1 : 0;
change.prop_mod = bogus_changes[i][5] ? 1 : 0;
args.fs = fs;
args.key = "x";
args.change = &change;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_add, &args,
TRUE, subpool));
}
args.fs = fs;
args.key = "x";
err = svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch, &args,
TRUE, subpool);
if (!err)
{
return svn_error_create(SVN_ERR_TEST_FAILED, 0,
"Expected SVN_ERR_FS_CORRUPT, got no error.");
}
else if (err->apr_err != SVN_ERR_FS_CORRUPT)
{
return svn_error_create(SVN_ERR_TEST_FAILED, err,
"Expected SVN_ERR_FS_CORRUPT, got a different error.");
}
else
{
svn_error_clear(err);
}
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_delete, &args,
TRUE, subpool));
}
svn_pool_clear(subpool);
{
static const char *bogus_changes[][6]
= { { "x", "/foo", "1.0.0", "modify", "1", 0 },
{ "x", "/foo", "1.0.0", "add", "1", 0 } };
int num_changes = sizeof(bogus_changes) / sizeof(const char *) / 6;
struct changes_args args;
int i;
for (i = 0; i < num_changes; i++)
{
change_t change;
change.path = bogus_changes[i][1];
change.noderev_id = svn_fs_parse_id(bogus_changes[i][2],
strlen(bogus_changes[i][2]),
subpool);
change.kind = string_to_kind(bogus_changes[i][3]);
change.text_mod = bogus_changes[i][4] ? 1 : 0;
change.prop_mod = bogus_changes[i][5] ? 1 : 0;
args.fs = fs;
args.key = "x";
args.change = &change;
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_add, &args,
TRUE, subpool));
}
args.fs = fs;
args.key = "x";
err = svn_fs_base__retry_txn(args.fs, txn_body_changes_fetch, &args,
TRUE, subpool);
if (!err)
{
return svn_error_create(SVN_ERR_TEST_FAILED, 0,
"Expected SVN_ERR_FS_CORRUPT, got no error.");
}
else if (err->apr_err != SVN_ERR_FS_CORRUPT)
{
return svn_error_create(SVN_ERR_TEST_FAILED, err,
"Expected SVN_ERR_FS_CORRUPT, got a different error.");
}
else
{
svn_error_clear(err);
}
SVN_ERR(svn_fs_base__retry_txn(args.fs, txn_body_changes_delete, &args,
TRUE, subpool));
}
return SVN_NO_ERROR;
}
struct svn_test_descriptor_t test_funcs[] =
{
SVN_TEST_NULL,
SVN_TEST_OPTS_PASS(changes_add,
"add changes to the changes table"),
SVN_TEST_OPTS_PASS(changes_fetch_raw,
"fetch raw changes from the changes table"),
SVN_TEST_OPTS_PASS(changes_delete,
"delete changes from the changes table"),
SVN_TEST_OPTS_PASS(changes_fetch,
"fetch compressed changes from the changes table"),
SVN_TEST_OPTS_PASS(changes_fetch_ordering,
"verify ordered-ness of fetched compressed changes"),
SVN_TEST_OPTS_PASS(changes_bad_sequences,
"verify that bad change sequences raise errors"),
SVN_TEST_NULL
};