diff options
Diffstat (limited to 'subversion/tests/libsvn_wc/conflict-data-test.c')
-rw-r--r-- | subversion/tests/libsvn_wc/conflict-data-test.c | 831 |
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 + }; + |