blob: 7d898251c6e0e10ccd71b461c60c2b03fdd53e31 [file] [log] [blame]
/*
*
* 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.
*
*/
/*
* conflict-data-test.c -- test the storage of tree conflict data
*/
#include <stdio.h>
#include <string.h>
#include <apr_hash.h>
#include <apr_tables.h>
#include "svn_props.h"
#include "svn_pools.h"
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_wc.h"
#include "private/svn_wc_private.h"
#include "utils.h"
#include "../svn_test.h"
#include "../../libsvn_wc/tree_conflicts.h"
#include "../../libsvn_wc/wc.h"
#include "../../libsvn_wc/wc_db.h"
#include "../../libsvn_wc/conflicts.h"
/* A quick way to create error messages. */
static svn_error_t *
fail(apr_pool_t *pool, const char *fmt, ...)
{
va_list ap;
char *msg;
va_start(ap, fmt);
msg = apr_pvsprintf(pool, fmt, ap);
va_end(ap);
return svn_error_create(SVN_ERR_TEST_FAILED, 0, msg);
}
/* Assert that two integers are equal. Return an error if not. */
#define ASSERT_INT_EQ(a, b) \
do { \
if ((a) != (b)) \
return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, \
"failed: ASSERT_INT_EQ(" #a ", " #b ") " \
"-> (%d == %d)", a, b); \
} while (0)
/* Assert that two strings are equal or both null. Return an error if not. */
#define ASSERT_STR_EQ(a, b) \
SVN_TEST_STRING_ASSERT(a, b)
/* Assert that two version_t's are equal or both null. Return an error if not. */
static svn_error_t *
compare_version(const svn_wc_conflict_version_t *actual,
const svn_wc_conflict_version_t *expected)
{
if (actual == NULL && expected == NULL)
return SVN_NO_ERROR;
SVN_TEST_ASSERT(actual && expected);
ASSERT_STR_EQ(actual->repos_url, expected->repos_url);
ASSERT_INT_EQ((int)actual->peg_rev, (int)expected->peg_rev);
ASSERT_STR_EQ(actual->path_in_repos, expected->path_in_repos);
ASSERT_INT_EQ(actual->node_kind, expected->node_kind);
return SVN_NO_ERROR;
}
/* Assert that two conflict descriptions contain exactly the same data
* (including names of temporary files), or are both NULL. Return an
* error if not. */
static svn_error_t *
compare_conflict(const svn_wc_conflict_description2_t *actual,
const svn_wc_conflict_description2_t *expected)
{
if (actual == NULL && expected == NULL)
return SVN_NO_ERROR;
SVN_TEST_ASSERT(actual && expected);
ASSERT_INT_EQ(actual->kind, expected->kind);
ASSERT_STR_EQ(actual->local_abspath, expected->local_abspath);
ASSERT_INT_EQ(actual->node_kind, expected->node_kind);
ASSERT_STR_EQ(actual->property_name, expected->property_name);
ASSERT_INT_EQ(actual->is_binary, expected->is_binary);
ASSERT_STR_EQ(actual->mime_type, expected->mime_type);
ASSERT_INT_EQ(actual->action, expected->action);
ASSERT_INT_EQ(actual->reason, expected->reason);
ASSERT_STR_EQ(actual->base_abspath, expected->base_abspath);
ASSERT_STR_EQ(actual->their_abspath, expected->their_abspath);
ASSERT_STR_EQ(actual->my_abspath, expected->my_abspath);
ASSERT_STR_EQ(actual->merged_file, expected->merged_file);
ASSERT_INT_EQ(actual->operation, expected->operation);
SVN_ERR(compare_version(actual->src_left_version,
expected->src_left_version));
SVN_ERR(compare_version(actual->src_right_version,
expected->src_right_version));
return SVN_NO_ERROR;
}
/* Assert that a file contains the expected data. Return an
* error if not. */
static svn_error_t *
compare_file_content(const char *file_abspath,
const char *expected_val,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *actual_val;
SVN_ERR(svn_stringbuf_from_file2(&actual_val, file_abspath, scratch_pool));
ASSERT_STR_EQ(actual_val->data, expected_val);
return SVN_NO_ERROR;
}
/* Assert that ACTUAL and EXPECTED both represent the same property
* conflict, or are both NULL. Return an error if not.
*
* Compare the property values found in files named by
* ACTUAL->base_abspath, ACTUAL->my_abspath, ACTUAL->merged_file
* with EXPECTED_BASE_VAL, EXPECTED_MY_VAL, EXPECTED_THEIR_VAL
* respectively, ignoring the corresponding fields in EXPECTED. */
static svn_error_t *
compare_prop_conflict(const svn_wc_conflict_description2_t *actual,
const svn_wc_conflict_description2_t *expected,
const char *expected_base_val,
const char *expected_my_val,
const char *expected_their_val,
apr_pool_t *scratch_pool)
{
if (actual == NULL && expected == NULL)
return SVN_NO_ERROR;
SVN_TEST_ASSERT(actual && expected);
ASSERT_INT_EQ(actual->kind, svn_wc_conflict_kind_property);
ASSERT_INT_EQ(expected->kind, svn_wc_conflict_kind_property);
ASSERT_STR_EQ(actual->local_abspath, expected->local_abspath);
ASSERT_INT_EQ(actual->node_kind, expected->node_kind);
ASSERT_STR_EQ(actual->property_name, expected->property_name);
ASSERT_INT_EQ(actual->action, expected->action);
ASSERT_INT_EQ(actual->reason, expected->reason);
ASSERT_INT_EQ(actual->operation, expected->operation);
SVN_ERR(compare_version(actual->src_left_version,
expected->src_left_version));
SVN_ERR(compare_version(actual->src_right_version,
expected->src_right_version));
SVN_ERR(compare_file_content(actual->base_abspath, expected_base_val,
scratch_pool));
SVN_ERR(compare_file_content(actual->my_abspath, expected_my_val,
scratch_pool));
/* Historical wart: for a prop conflict, 'theirs' is in the 'merged_file'
* field, and the conflict artifact file is in the 'theirs_abspath' field. */
SVN_ERR(compare_file_content(actual->merged_file, expected_their_val,
scratch_pool));
/*ASSERT_STR_EQ(actual->theirs_abspath, conflict_artifact_file));*/
/* These are 'undefined' for a prop conflict */
/*ASSERT_INT_EQ(actual->is_binary, expected->is_binary);*/
/*ASSERT_STR_EQ(actual->mime_type, expected->mime_type);*/
return SVN_NO_ERROR;
}
/* Create and return a tree conflict description */
static svn_wc_conflict_description2_t *
tree_conflict_create(const char *local_abspath,
svn_node_kind_t node_kind,
svn_wc_operation_t operation,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason,
const char *left_repo,
const char *left_path,
svn_revnum_t left_revnum,
svn_node_kind_t left_kind,
const char *right_repo,
const char *right_path,
svn_revnum_t right_revnum,
svn_node_kind_t right_kind,
apr_pool_t *result_pool)
{
svn_wc_conflict_version_t *left, *right;
svn_wc_conflict_description2_t *conflict;
left = svn_wc_conflict_version_create2(left_repo, NULL, left_path,
left_revnum, left_kind, result_pool);
right = svn_wc_conflict_version_create2(right_repo, NULL, right_path,
right_revnum, right_kind,
result_pool);
conflict = svn_wc_conflict_description_create_tree2(
local_abspath, node_kind, operation,
left, right, result_pool);
conflict->action = action;
conflict->reason = reason;
return conflict;
}
static svn_error_t *
test_deserialize_tree_conflict(apr_pool_t *pool)
{
const svn_wc_conflict_description2_t *conflict;
svn_wc_conflict_description2_t *exp_conflict;
const char *tree_conflict_data;
const char *local_abspath;
const svn_skel_t *skel;
tree_conflict_data = "(conflict Foo.c file update deleted edited "
"(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))";
SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool));
exp_conflict = svn_wc_conflict_description_create_tree2(
local_abspath, svn_node_file, svn_wc_operation_update,
NULL, NULL, pool);
exp_conflict->action = svn_wc_conflict_action_delete;
exp_conflict->reason = svn_wc_conflict_reason_edited;
skel = svn_skel__parse(tree_conflict_data, strlen(tree_conflict_data), pool);
SVN_ERR(svn_wc__deserialize_conflict(&conflict, skel, "", pool, pool));
if ((conflict->node_kind != exp_conflict->node_kind) ||
(conflict->action != exp_conflict->action) ||
(conflict->reason != exp_conflict->reason) ||
(conflict->operation != exp_conflict->operation) ||
(strcmp(conflict->local_abspath, exp_conflict->local_abspath) != 0))
return fail(pool, "Unexpected tree conflict");
return SVN_NO_ERROR;
}
static svn_error_t *
test_serialize_tree_conflict_data(apr_pool_t *pool)
{
svn_wc_conflict_description2_t *conflict;
const char *tree_conflict_data;
const char *expected;
const char *local_abspath;
svn_skel_t *skel;
SVN_ERR(svn_dirent_get_absolute(&local_abspath, "Foo.c", pool));
conflict = svn_wc_conflict_description_create_tree2(
local_abspath, svn_node_file, svn_wc_operation_update,
NULL, NULL, pool);
conflict->action = svn_wc_conflict_action_delete;
conflict->reason = svn_wc_conflict_reason_edited;
SVN_ERR(svn_wc__serialize_conflict(&skel, conflict, pool, pool));
tree_conflict_data = svn_skel__unparse(skel, pool)->data;
expected = "(conflict Foo.c file update deleted edited "
"(version 0 2 -1 0 0 ) (version 0 2 -1 0 0 ))";
if (strcmp(expected, tree_conflict_data) != 0)
return fail(pool, "Unexpected text from tree conflict\n"
" Expected: %s\n"
" Actual: %s\n", expected, tree_conflict_data);
return SVN_NO_ERROR;
}
/* Test WC-DB-level conflict APIs. Especially tree conflicts. */
static svn_error_t *
test_read_write_tree_conflicts(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
const char *parent_abspath;
const char *child1_abspath, *child2_abspath;
svn_wc_conflict_description2_t *conflict1, *conflict2;
SVN_ERR(svn_test__sandbox_create(&sbox, "read_write_tree_conflicts", opts, pool));
parent_abspath = svn_dirent_join(sbox.wc_abspath, "A", pool);
child1_abspath = svn_dirent_join(parent_abspath, "foo", pool);
child2_abspath = svn_dirent_join(parent_abspath, "bar", pool);
SVN_ERR(sbox_wc_mkdir(&sbox, "A"));
SVN_ERR(sbox_wc_mkdir(&sbox, "A/bar"));
SVN_ERR(sbox_file_write(&sbox, "A/foo", ""));
SVN_ERR(sbox_wc_add(&sbox, "A/foo"));
conflict1 = tree_conflict_create(child1_abspath, svn_node_file,
svn_wc_operation_merge,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
"dummy://localhost", "path/to/foo",
51, svn_node_file,
"dummy://localhost", "path/to/foo",
52, svn_node_none,
pool);
conflict2 = tree_conflict_create(child2_abspath, svn_node_dir,
svn_wc_operation_merge,
svn_wc_conflict_action_replace,
svn_wc_conflict_reason_edited,
"dummy://localhost", "path/to/bar",
51, svn_node_dir,
"dummy://localhost", "path/to/bar",
52, svn_node_file,
pool);
/* Write */
SVN_ERR(svn_wc__add_tree_conflict(sbox.wc_ctx, /*child1_abspath,*/
conflict1, pool));
SVN_ERR(svn_wc__add_tree_conflict(sbox.wc_ctx, /*child2_abspath,*/
conflict2, pool));
/* Query (conflict1 through WC-DB API, conflict2 through WC API) */
{
svn_boolean_t text_c, prop_c, tree_c;
SVN_ERR(svn_wc__internal_conflicted_p(&text_c, &prop_c, &tree_c,
sbox.wc_ctx->db, child1_abspath, pool));
SVN_TEST_ASSERT(tree_c);
SVN_TEST_ASSERT(! text_c && ! prop_c);
SVN_ERR(svn_wc_conflicted_p3(&text_c, &prop_c, &tree_c,
sbox.wc_ctx, child2_abspath, pool));
SVN_TEST_ASSERT(tree_c);
SVN_TEST_ASSERT(! text_c && ! prop_c);
}
/* Read conflicts back */
{
const svn_wc_conflict_description2_t *read_conflict;
SVN_ERR(svn_wc__get_tree_conflict(&read_conflict, sbox.wc_ctx,
child1_abspath, pool, pool));
SVN_ERR(compare_conflict(read_conflict, conflict1));
SVN_ERR(svn_wc__get_tree_conflict(&read_conflict, sbox.wc_ctx,
child2_abspath, pool, pool));
SVN_ERR(compare_conflict(read_conflict, conflict2));
}
/* Read many */
{
const apr_array_header_t *victims;
SVN_ERR(svn_wc__db_read_conflict_victims(&victims,
sbox.wc_ctx->db, parent_abspath,
pool, pool));
SVN_TEST_ASSERT(victims->nelts == 2);
}
/* ### TODO: to test...
* svn_wc__db_read_conflicts
* svn_wc__node_get_conflict_info
* svn_wc__del_tree_conflict
*/
return SVN_NO_ERROR;
}
static svn_error_t *
test_serialize_prop_conflict(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
svn_skel_t *conflict_skel;
svn_boolean_t complete;
SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_prop_conflict", opts, pool));
conflict_skel = svn_wc__conflict_skel_create(pool);
SVN_TEST_ASSERT(conflict_skel != NULL);
SVN_TEST_ASSERT(svn_skel__list_length(conflict_skel) == 2);
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(!complete); /* Nothing set */
{
apr_hash_t *mine = apr_hash_make(pool);
apr_hash_t *their_old = apr_hash_make(pool);
apr_hash_t *theirs = apr_hash_make(pool);
apr_hash_t *conflicts = apr_hash_make(pool);
const char *marker_abspath;
apr_hash_set(mine, "prop", APR_HASH_KEY_STRING,
svn_string_create("Mine", pool));
apr_hash_set(their_old, "prop", APR_HASH_KEY_STRING,
svn_string_create("Their-Old", pool));
apr_hash_set(theirs, "prop", APR_HASH_KEY_STRING,
svn_string_create("Theirs", pool));
apr_hash_set(conflicts, "prop", APR_HASH_KEY_STRING, "");
SVN_ERR(svn_io_open_unique_file3(NULL, &marker_abspath, sbox.wc_abspath,
svn_io_file_del_on_pool_cleanup, pool,
pool));
SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_skel,
sbox.wc_ctx->db,
sbox.wc_abspath,
marker_abspath,
mine, their_old,
theirs, conflicts,
pool, pool));
}
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(!complete); /* Misses operation */
SVN_ERR(svn_wc__conflict_skel_set_op_update(
conflict_skel,
svn_wc_conflict_version_create2("http://my-repos/svn",
"uuid", "trunk", 12,
svn_node_dir, pool),
NULL /* wc_only */,
pool, pool));
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(complete); /* Everything available */
{
apr_hash_t *mine;
apr_hash_t *their_old;
apr_hash_t *theirs;
apr_hash_t *conflicts;
const char *marker_abspath;
svn_string_t *v;
SVN_ERR(svn_wc__conflict_read_prop_conflict(&marker_abspath,
&mine,
&their_old,
&theirs,
&conflicts,
sbox.wc_ctx->db,
sbox.wc_abspath,
conflict_skel,
pool, pool));
SVN_TEST_ASSERT(svn_dirent_is_ancestor(sbox.wc_abspath, marker_abspath));
v = apr_hash_get(mine, "prop", APR_HASH_KEY_STRING);
SVN_TEST_STRING_ASSERT(v->data, "Mine");
v = apr_hash_get(their_old, "prop", APR_HASH_KEY_STRING);
SVN_TEST_STRING_ASSERT(v->data, "Their-Old");
v = apr_hash_get(theirs, "prop", APR_HASH_KEY_STRING);
SVN_TEST_STRING_ASSERT(v->data, "Theirs");
SVN_TEST_ASSERT(apr_hash_count(conflicts) == 1);
}
return SVN_NO_ERROR;
}
static svn_error_t *
test_serialize_text_conflict(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
svn_skel_t *conflict_skel;
svn_boolean_t complete;
SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_text_conflict", opts, pool));
conflict_skel = svn_wc__conflict_skel_create(pool);
SVN_ERR(svn_wc__conflict_skel_add_text_conflict(
conflict_skel,
sbox.wc_ctx->db, sbox.wc_abspath,
svn_dirent_join(sbox.wc_abspath, "mine", pool),
svn_dirent_join(sbox.wc_abspath, "old-theirs", pool),
svn_dirent_join(sbox.wc_abspath, "theirs", pool),
pool, pool));
SVN_ERR(svn_wc__conflict_skel_set_op_merge(
conflict_skel,
svn_wc_conflict_version_create2("http://my-repos/svn",
"uuid", "trunk", 12,
svn_node_dir, pool),
svn_wc_conflict_version_create2("http://my-repos/svn",
"uuid", "branch/my", 8,
svn_node_dir, pool),
pool, pool));
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(complete); /* Everything available */
{
const char *mine_abspath;
const char *old_their_abspath;
const char *their_abspath;
SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
&old_their_abspath,
&their_abspath,
sbox.wc_ctx->db,
sbox.wc_abspath,
conflict_skel,
pool, pool));
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, mine_abspath),
"mine");
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, old_their_abspath),
"old-theirs");
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, their_abspath),
"theirs");
}
{
svn_wc_operation_t operation;
svn_boolean_t text_conflicted;
const apr_array_header_t *locs;
SVN_ERR(svn_wc__conflict_read_info(&operation, &locs,
&text_conflicted, NULL, NULL,
sbox.wc_ctx->db, sbox.wc_abspath,
conflict_skel, pool, pool));
SVN_TEST_ASSERT(text_conflicted);
SVN_TEST_ASSERT(operation == svn_wc_operation_merge);
SVN_TEST_ASSERT(locs != NULL && locs->nelts == 2);
SVN_TEST_ASSERT(APR_ARRAY_IDX(locs, 0, svn_wc_conflict_version_t*) != NULL);
SVN_TEST_ASSERT(APR_ARRAY_IDX(locs, 1, svn_wc_conflict_version_t*) != NULL);
}
{
const apr_array_header_t *markers;
const char *old_their_abspath;
const char *their_abspath;
const char *mine_abspath;
SVN_ERR(svn_wc__conflict_read_markers(&markers,
sbox.wc_ctx->db, sbox.wc_abspath,
conflict_skel, pool, pool));
SVN_TEST_ASSERT(markers != NULL);
SVN_TEST_ASSERT(markers->nelts == 3);
old_their_abspath = APR_ARRAY_IDX(markers, 0, const char *);
mine_abspath = APR_ARRAY_IDX(markers, 1, const char *);
their_abspath = APR_ARRAY_IDX(markers, 2, const char *);
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, mine_abspath),
"mine");
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, old_their_abspath),
"old-theirs");
SVN_TEST_STRING_ASSERT(
svn_dirent_skip_ancestor(sbox.wc_abspath, their_abspath),
"theirs");
}
return SVN_NO_ERROR;
}
static svn_error_t *
test_serialize_tree_conflict(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
svn_skel_t *conflict_skel;
svn_boolean_t complete;
SVN_ERR(svn_test__sandbox_create(&sbox, "test_serialize_tree_conflict", opts, pool));
conflict_skel = svn_wc__conflict_skel_create(pool);
SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
conflict_skel,
sbox.wc_ctx->db, sbox_wc_path(&sbox, "A/B"),
svn_wc_conflict_reason_moved_away,
svn_wc_conflict_action_delete,
sbox_wc_path(&sbox, "A/B"),
pool, pool));
SVN_ERR(svn_wc__conflict_skel_set_op_switch(
conflict_skel,
svn_wc_conflict_version_create2("http://my-repos/svn",
"uuid", "trunk", 12,
svn_node_dir, pool),
NULL /* wc_only */,
pool, pool));
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(complete); /* Everything available */
{
svn_wc_conflict_reason_t reason;
svn_wc_conflict_action_t action;
const char *moved_away_op_root_abspath;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
&action,
&moved_away_op_root_abspath,
sbox.wc_ctx->db,
sbox.wc_abspath,
conflict_skel,
pool, pool));
SVN_TEST_ASSERT(reason == svn_wc_conflict_reason_moved_away);
SVN_TEST_ASSERT(action == svn_wc_conflict_action_delete);
SVN_TEST_STRING_ASSERT(moved_away_op_root_abspath,
sbox_wc_path(&sbox, "A/B"));
}
return SVN_NO_ERROR;
}
/* A conflict resolver callback baton for test_prop_conflicts(). */
typedef struct test_prop_conflict_baton_t
{
/* Sets of properties. */
apr_hash_t *mine;
apr_hash_t *their_old;
apr_hash_t *theirs;
/* The set of prop names in conflict. */
apr_hash_t *conflicts;
/* We use all the fields of DESC except the base/theirs/mine/merged paths. */
svn_wc_conflict_description2_t *desc;
int conflicts_seen;
} test_prop_conflict_baton_t;
/* Set *CONFLICT_SKEL_P to a new property conflict skel reflecting the
* conflict details given in B. */
static svn_error_t *
create_prop_conflict_skel(svn_skel_t **conflict_skel_p,
svn_wc_context_t *wc_ctx,
const test_prop_conflict_baton_t *b,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *conflict_skel = svn_wc__conflict_skel_create(result_pool);
const char *marker_abspath;
svn_boolean_t complete;
SVN_ERR(svn_io_write_unique(&marker_abspath,
b->desc->local_abspath,
"conflict-artifact-file-content\n", 6,
svn_io_file_del_none, scratch_pool));
SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflict_skel,
wc_ctx->db,
b->desc->local_abspath,
marker_abspath,
b->mine, b->their_old,
b->theirs, b->conflicts,
result_pool, scratch_pool));
switch (b->desc->operation)
{
case svn_wc_operation_update:
SVN_ERR(svn_wc__conflict_skel_set_op_update(
conflict_skel,
b->desc->src_left_version, b->desc->src_right_version,
result_pool, scratch_pool));
break;
case svn_wc_operation_switch:
SVN_ERR(svn_wc__conflict_skel_set_op_switch(
conflict_skel,
b->desc->src_left_version, b->desc->src_right_version,
result_pool, scratch_pool));
break;
case svn_wc_operation_merge:
SVN_ERR(svn_wc__conflict_skel_set_op_merge(
conflict_skel,
b->desc->src_left_version, b->desc->src_right_version,
result_pool, scratch_pool));
break;
default:
SVN_ERR_MALFUNCTION();
}
SVN_ERR(svn_wc__conflict_skel_is_complete(&complete, conflict_skel));
SVN_TEST_ASSERT(complete);
*conflict_skel_p = conflict_skel;
return SVN_NO_ERROR;
}
/* A conflict resolver callback for test_prop_conflicts(), that checks
* that the conflict described to it matches the one described in BATON,
* and also counts the number of times it is called. */
static svn_error_t *
prop_conflict_cb(svn_wc_conflict_result_t **result_p,
const svn_wc_conflict_description2_t *desc,
void *baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
test_prop_conflict_baton_t *b = baton;
SVN_ERR(compare_prop_conflict(
desc, b->desc,
svn_prop_get_value(b->their_old, desc->property_name),
svn_prop_get_value(b->mine, desc->property_name),
svn_prop_get_value(b->theirs, desc->property_name),
scratch_pool));
b->conflicts_seen++;
*result_p = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
NULL /*merged_file*/, result_pool);
return SVN_NO_ERROR;
}
/* Test for correct retrieval of property conflict descriptions from
* the WC DB.
*
* Presently it tests just one prop conflict, and only during the
* 'resolve' operation. We should also test during the 'update'/
* 'switch'/'merge' operations.
*/
static svn_error_t *
test_prop_conflicts(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
svn_skel_t *conflict_skel;
svn_error_t *err;
const char *lock_abspath;
test_prop_conflict_baton_t *b = apr_pcalloc(pool, sizeof(*b));
svn_wc_conflict_description2_t *desc = apr_pcalloc(pool, sizeof(*desc));
SVN_ERR(svn_test__sandbox_create(&sbox, "test_prop_conflicts", opts, pool));
/* Describe a property conflict */
b->mine = apr_hash_make(pool);
b->their_old = apr_hash_make(pool);
b->theirs = apr_hash_make(pool);
b->conflicts = apr_hash_make(pool);
svn_hash_sets(b->mine, "prop", svn_string_create("Mine", pool));
svn_hash_sets(b->their_old, "prop", svn_string_create("Their-Old", pool));
svn_hash_sets(b->theirs, "prop", svn_string_create("Theirs", pool));
svn_hash_sets(b->conflicts, "prop", "");
b->desc = desc;
desc->local_abspath = sbox.wc_abspath;
desc->kind = svn_wc_conflict_kind_property;
desc->node_kind = svn_node_dir;
desc->operation = svn_wc_operation_update;
desc->action = svn_wc_conflict_action_edit;
desc->reason = svn_wc_conflict_reason_edited;
desc->mime_type = NULL;
desc->is_binary = FALSE;
desc->property_name = "prop";
desc->src_left_version
= svn_wc_conflict_version_create2(sbox.repos_url, "uuid",
"trunk", 12, svn_node_dir, pool);
desc->src_right_version = NULL; /* WC only */
b->conflicts_seen = 0;
/* Record a conflict */
{
apr_pool_t *subpool = svn_pool_create(pool);
SVN_ERR(create_prop_conflict_skel(&conflict_skel, sbox.wc_ctx, b,
pool, subpool));
svn_pool_clear(subpool);
SVN_ERR(svn_wc__db_op_mark_conflict(sbox.wc_ctx->db,
sbox.wc_abspath,
conflict_skel, NULL, subpool));
svn_pool_destroy(subpool);
}
/* Test the API for resolving the conflict: check that correct details
* of the conflict are returned. */
SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, sbox.wc_ctx,
sbox.wc_abspath, pool, pool));
err = svn_wc__resolve_conflicts(sbox.wc_ctx, sbox.wc_abspath,
svn_depth_empty,
FALSE /* resolve_text */,
"" /* resolve_prop (ALL props) */,
FALSE /* resolve_tree */,
svn_wc_conflict_choose_unspecified,
prop_conflict_cb, b,
NULL, NULL, /* cancellation */
NULL, NULL, /* notification */
pool);
SVN_ERR(svn_error_compose_create(err,
svn_wc__release_write_lock(sbox.wc_ctx,
lock_abspath,
pool)));
ASSERT_INT_EQ(b->conflicts_seen, 1);
return SVN_NO_ERROR;
}
static svn_error_t *
test_prop_conflict_resolving(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t b;
svn_skel_t *conflict;
const char *A_abspath;
const char *marker_abspath;
apr_hash_t *conflicted_props;
apr_hash_t *props;
const char *value;
SVN_ERR(svn_test__sandbox_create(&b, "test_prop_resolving", opts, pool));
SVN_ERR(sbox_wc_mkdir(&b, "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-1", "r1", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-2", "r1", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-3", "r1", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-4", "r1", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-5", "r1", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-6", "r1", "A"));
SVN_ERR(sbox_wc_commit(&b, ""));
SVN_ERR(sbox_wc_propset(&b, "prop-1", "r2", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-2", "r2", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-3", "r2", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-4", NULL, "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-5", NULL, "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-7", "r2", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-8", "r2", "A"));
SVN_ERR(sbox_wc_commit(&b, ""));
SVN_ERR(sbox_wc_propset(&b, "prop-1", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-2", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-3", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-4", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-5", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-6", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-7", "mod", "A"));
SVN_ERR(sbox_wc_propset(&b, "prop-8", "mod", "A"));
SVN_ERR(sbox_wc_update(&b, "", 1));
A_abspath = sbox_wc_path(&b, "A");
SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
b.wc_ctx->db, A_abspath,
pool, pool));
/* We have tree conflicts... */
SVN_TEST_ASSERT(conflict != NULL);
SVN_ERR(svn_wc__conflict_read_prop_conflict(&marker_abspath,
NULL, NULL, NULL,
&conflicted_props,
b.wc_ctx->db, A_abspath,
conflict,
pool, pool));
SVN_TEST_ASSERT(conflicted_props != NULL);
/* All properties but r6 are conflicted */
SVN_TEST_ASSERT(apr_hash_count(conflicted_props) == 7);
SVN_TEST_ASSERT(! svn_hash_gets(conflicted_props, "prop-6"));
/* Let's resolve a few conflicts */
SVN_ERR(sbox_wc_resolve_prop(&b, "A", "prop-1",
svn_wc_conflict_choose_mine_conflict));
SVN_ERR(sbox_wc_resolve_prop(&b, "A", "prop-2",
svn_wc_conflict_choose_theirs_conflict));
SVN_ERR(sbox_wc_resolve_prop(&b, "A", "prop-3",
svn_wc_conflict_choose_merged));
SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
b.wc_ctx->db, A_abspath,
pool, pool));
/* We have tree conflicts... */
SVN_TEST_ASSERT(conflict != NULL);
SVN_ERR(svn_wc__conflict_read_prop_conflict(&marker_abspath,
NULL, NULL, NULL,
&conflicted_props,
b.wc_ctx->db, A_abspath,
conflict,
pool, pool));
SVN_TEST_ASSERT(conflicted_props != NULL);
SVN_TEST_ASSERT(apr_hash_count(conflicted_props) == 4);
SVN_ERR(svn_wc__db_read_props(&props, b.wc_ctx->db, A_abspath,
pool, pool));
value = svn_prop_get_value(props, "prop-1");
SVN_TEST_STRING_ASSERT(value, "mod");
value = svn_prop_get_value(props, "prop-2");
SVN_TEST_STRING_ASSERT(value, "r1");
value = svn_prop_get_value(props, "prop-3");
SVN_TEST_STRING_ASSERT(value, "mod");
return SVN_NO_ERROR;
}
static svn_error_t *
test_binary_file_conflict(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t sbox;
const apr_array_header_t *conflicts;
svn_wc_conflict_description2_t *desc;
SVN_ERR(svn_test__sandbox_create(&sbox, "test_binary_file_conflict", opts, pool));
/* Create and add a binary file. */
SVN_ERR(sbox_file_write(&sbox, "binary-file", "\xff\xff"));
SVN_ERR(sbox_wc_add(&sbox, "binary-file"));
SVN_ERR(sbox_wc_propset(&sbox, SVN_PROP_MIME_TYPE,
"application/octet-stream", "binary-file"));
SVN_ERR(sbox_wc_commit(&sbox, "binary-file")); /* r1 */
/* Make a change to the binary file. */
SVN_ERR(sbox_file_write(&sbox, "binary-file", "\xfc\xfc\xfc\xfc\xfc\xfc"));
SVN_ERR(sbox_wc_commit(&sbox, "binary-file")); /* r2 */
/* Update back to r1, make a conflicting change to binary file. */
SVN_ERR(sbox_wc_update(&sbox, "binary-file", 1));
SVN_ERR(sbox_file_write(&sbox, "binary-file", "\xfd\xfd\xfd\xfd"));
/* Update to HEAD and ensure the conflict is marked as binary. */
SVN_ERR(sbox_wc_update(&sbox, "binary-file", 2));
SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL, sbox.wc_ctx->db,
sbox_wc_path(&sbox, "binary-file"),
FALSE /* create_tempfiles */,
FALSE /* only_tree_conflict */,
pool, pool));
SVN_TEST_ASSERT(conflicts->nelts == 1);
desc = APR_ARRAY_IDX(conflicts, 0, svn_wc_conflict_description2_t *);
SVN_TEST_ASSERT(desc->is_binary);
return SVN_NO_ERROR;
}
/* The test table. */
static int max_threads = 1;
static struct svn_test_descriptor_t test_funcs[] =
{
SVN_TEST_NULL,
SVN_TEST_PASS2(test_deserialize_tree_conflict,
"deserialize tree conflict"),
SVN_TEST_PASS2(test_serialize_tree_conflict_data,
"serialize tree conflict data"),
SVN_TEST_OPTS_PASS(test_read_write_tree_conflicts,
"read and write tree conflict data"),
SVN_TEST_OPTS_PASS(test_serialize_prop_conflict,
"read and write a property conflict"),
SVN_TEST_OPTS_PASS(test_serialize_text_conflict,
"read and write a text conflict"),
SVN_TEST_OPTS_PASS(test_serialize_tree_conflict,
"read and write a tree conflict"),
SVN_TEST_OPTS_PASS(test_prop_conflicts,
"test prop conflicts"),
SVN_TEST_OPTS_PASS(test_prop_conflict_resolving,
"test property conflict resolving"),
SVN_TEST_OPTS_PASS(test_binary_file_conflict,
"test binary file conflict"),
SVN_TEST_NULL
};
SVN_TEST_MAIN