summaryrefslogtreecommitdiff
path: root/subversion/tests/libsvn_wc/conflict-data-test.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests/libsvn_wc/conflict-data-test.c')
-rw-r--r--subversion/tests/libsvn_wc/conflict-data-test.c831
1 files changed, 831 insertions, 0 deletions
diff --git a/subversion/tests/libsvn_wc/conflict-data-test.c b/subversion/tests/libsvn_wc/conflict-data-test.c
new file mode 100644
index 0000000..97d22ea
--- /dev/null
+++ b/subversion/tests/libsvn_wc/conflict-data-test.c
@@ -0,0 +1,831 @@
+/*
+ *
+ * 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_abspath
+ * 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);
+ SVN_ERR(svn_wc__db_op_add_directory(sbox.wc_ctx->db, parent_abspath,
+ NULL /*props*/, NULL, pool));
+ child1_abspath = svn_dirent_join(parent_abspath, "foo", pool);
+ child2_abspath = svn_dirent_join(parent_abspath, "bar", pool);
+
+ 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 local_change;
+ svn_wc_conflict_action_t incoming_change;
+ const char *moved_away_op_root_abspath;
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
+ &incoming_change,
+ &moved_away_op_root_abspath,
+ sbox.wc_ctx->db,
+ sbox.wc_abspath,
+ conflict_skel,
+ pool, pool));
+
+ SVN_TEST_ASSERT(local_change == svn_wc_conflict_reason_moved_away);
+ SVN_TEST_ASSERT(incoming_change == svn_wc_conflict_action_delete);
+ SVN_TEST_ASSERT(!strcmp(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;
+}
+
+/* The test table. */
+
+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_NULL
+ };
+