Index: build.conf =================================================================== --- build.conf (revision 1349693) +++ build.conf (working copy) @@ -757,6 +757,14 @@ install = test libs = libsvn_test libsvn_subr apriconv apr +[io-test] +description = Test I/O Operations +type = exe +path = subversion/tests/libsvn_subr +sources = io-test.c +install = test +libs = libsvn_test libsvn_subr apriconv apr + [opt-test] description = Test options library type = exe Index: subversion/include/svn_io.h =================================================================== --- subversion/include/svn_io.h (revision 1349990) +++ subversion/include/svn_io.h (working copy) @@ -607,6 +607,25 @@ const char *file2, apr_pool_t *pool); +/** Set @a *different_p12 to non-zero if @a file1 and @a file2 have different + * sizes, else set to zero. Do the similar for @a *different_p23 with + * @a file2 and @a file3, and @a *different_p13 for @a file1 and @a file3. + * All three of @a file1, @a file2 and @a file3 are utf8-encoded. + * + * Setting @a *different_p12 to zero does not mean the files definitely + * have the same size, it merely means that the sizes are not + * definitely different. That is, if the size of one or both files + * cannot be determined, then the sizes are not known to be different, + * so @a *different_p12 is set to 0. + */ +svn_error_t * +svn_io_filesizes_three_different_p(svn_boolean_t *different_p12, + svn_boolean_t *different_p23, + svn_boolean_t *different_p13, + const char *file1, + const char *file2, + const char *file3, + apr_pool_t *scratch_pool); /** Return in @a *checksum the checksum of type @a kind of @a file * Use @a pool for temporary allocations and to allocate @a *checksum. @@ -642,6 +661,20 @@ const char *file2, apr_pool_t *pool); +/** Set @a *same12 to TRUE if @a file1 and @a file2 have the same + * contents, else set it to FALSE. Do the similar for @a *same23 + * with @a file2 and @a file3, and @a *same13 for @a file1 and @a + * file3. Use @a pool for temporary allocations. + */ +svn_error_t * +svn_io_files_contents_three_same_p(svn_boolean_t *same12, + svn_boolean_t *same23, + svn_boolean_t *same13, + const char *file1, + const char *file2, + const char *file3, + apr_pool_t *scratch_pool); + /** Create file at utf8-encoded @a file with contents @a contents. * @a file must not already exist. * Use @a pool for memory allocations. Index: subversion/libsvn_subr/io.c =================================================================== --- subversion/libsvn_subr/io.c (revision 1349990) +++ subversion/libsvn_subr/io.c (working copy) @@ -1311,6 +1311,43 @@ svn_error_t * +svn_io_filesizes_three_different_p(svn_boolean_t *different_p12, + svn_boolean_t *different_p23, + svn_boolean_t *different_p13, + const char *file1, + const char *file2, + const char *file3, + apr_pool_t *scratch_pool) +{ + apr_finfo_t finfo1, finfo2, finfo3; + apr_status_t status1, status2, status3; + const char *file1_apr, *file2_apr, *file3_apr; + + /* Not using svn_io_stat() because don't want to generate + svn_error_t objects for non-error conditions. */ + + SVN_ERR(cstring_from_utf8(&file1_apr, file1, scratch_pool)); + SVN_ERR(cstring_from_utf8(&file2_apr, file2, scratch_pool)); + SVN_ERR(cstring_from_utf8(&file3_apr, file3, scratch_pool)); + + /* Stat all three files */ + status1 = apr_stat(&finfo1, file1_apr, APR_FINFO_MIN, scratch_pool); + status2 = apr_stat(&finfo2, file2_apr, APR_FINFO_MIN, scratch_pool); + status3 = apr_stat(&finfo3, file3_apr, APR_FINFO_MIN, scratch_pool); + + /* If we got an error stat'ing a file, it could be because the + file was removed... or who knows. Whatever the case, we + don't know if the filesizes are definitely different, so + assume that they're not. */ + *different_p12 = !status1 && !status2 && finfo1.size != finfo2.size; + *different_p23 = !status2 && !status3 && finfo2.size != finfo3.size; + *different_p13 = !status1 && !status3 && finfo1.size != finfo3.size; + + return SVN_NO_ERROR; +} + + +svn_error_t * svn_io_file_checksum2(svn_checksum_t **checksum, const char *file, svn_checksum_kind_t kind, @@ -4072,6 +4109,138 @@ +/* Do a byte-for-byte comparison of FILE1, FILE2 and FILE3. */ +static svn_error_t * +contents_three_identical_p(svn_boolean_t *identical_p12, + svn_boolean_t *identical_p23, + svn_boolean_t *identical_p13, + const char *file1, + const char *file2, + const char *file3, + apr_pool_t *scratch_pool) +{ + svn_error_t *err; + apr_size_t bytes_read1, bytes_read2, bytes_read3; + char *buf1 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); + char *buf2 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); + char *buf3 = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); + apr_file_t *file1_h; + apr_file_t *file2_h; + apr_file_t *file3_h; + svn_boolean_t eof1 = FALSE; + svn_boolean_t eof2 = FALSE; + svn_boolean_t eof3 = FALSE; + svn_boolean_t read_1, read_2, read_3; + + SVN_ERR(svn_io_file_open(&file1_h, file1, APR_READ, APR_OS_DEFAULT, + scratch_pool)); + + err = svn_io_file_open(&file2_h, file2, APR_READ, APR_OS_DEFAULT, + scratch_pool); + + if (err) + return svn_error_trace( + svn_error_compose_create(err, + svn_io_file_close(file1_h, scratch_pool))); + + err = svn_io_file_open(&file3_h, file3, APR_READ, APR_OS_DEFAULT, + scratch_pool); + + if (err) + return svn_error_trace( + svn_error_compose_create( + err, + svn_error_compose_create(svn_io_file_close(file1_h, + scratch_pool), + svn_io_file_close(file2_h, + scratch_pool)))); + + /* assume TRUE, until disproved below */ + *identical_p12 = *identical_p23 = *identical_p13 = TRUE; + /* We need to read as long as no error occurs, and as long as one of the + * flags could still change due to a read operation */ + while (!err + && ((*identical_p12 && !eof1 && !eof2) + || (*identical_p23 && !eof2 && !eof3) + || (*identical_p13 && !eof1 && !eof3))) + { + read_1 = read_2 = read_3 = FALSE; + + /* As long as a file is not at the end yet, and it is still + * potentially identical to another file, we read the next chunk.*/ + if (!eof1 && (identical_p12 || identical_p13)) + { + err = svn_io_file_read_full2(file1_h, buf1, + SVN__STREAM_CHUNK_SIZE, &bytes_read1, + &eof1, scratch_pool); + if (err) + break; + read_1 = TRUE; + } + + if (!eof2 && (identical_p12 || identical_p23)) + { + err = svn_io_file_read_full2(file2_h, buf2, + SVN__STREAM_CHUNK_SIZE, &bytes_read2, + &eof2, scratch_pool); + if (err) + break; + read_2 = TRUE; + } + + if (!eof3 && (identical_p13 || identical_p23)) + { + err = svn_io_file_read_full2(file3_h, buf3, + SVN__STREAM_CHUNK_SIZE, &bytes_read3, + &eof3, scratch_pool); + if (err) + break; + read_3 = TRUE; + } + + /* If the files are still marked identical, and at least one of them + * is not at the end of file, we check whether they differ, and set + * their flag to false then. */ + if (*identical_p12 + && (read_1 || read_2) + && ((eof1 != eof2) + || (bytes_read1 != bytes_read2) + || memcmp(buf1, buf2, bytes_read1))) + { + *identical_p12 = FALSE; + } + + if (*identical_p23 + && (read_2 || read_3) + && ((eof2 != eof3) + || (bytes_read2 != bytes_read3) + || memcmp(buf2, buf3, bytes_read2))) + { + *identical_p23 = FALSE; + } + + if (*identical_p13 + && (read_1 || read_3) + && ((eof1 != eof3) + || (bytes_read1 != bytes_read3) + || memcmp(buf1, buf3, bytes_read3))) + { + *identical_p13 = FALSE; + } + } + + return svn_error_trace( + svn_error_compose_create( + err, + svn_error_compose_create( + svn_io_file_close(file1_h, scratch_pool), + svn_error_compose_create( + svn_io_file_close(file2_h, scratch_pool), + svn_io_file_close(file3_h, scratch_pool))))); +} + + + svn_error_t * svn_io_files_contents_same_p(svn_boolean_t *same, const char *file1, @@ -4098,6 +4267,55 @@ return SVN_NO_ERROR; } +svn_error_t * +svn_io_files_contents_three_same_p(svn_boolean_t *same12, + svn_boolean_t *same23, + svn_boolean_t *same13, + const char *file1, + const char *file2, + const char *file3, + apr_pool_t *scratch_pool) +{ + svn_boolean_t diff_size12, diff_size23, diff_size13; + + SVN_ERR(svn_io_filesizes_three_different_p(&diff_size12, + &diff_size23, + &diff_size13, + file1, + file2, + file3, + scratch_pool)); + + if (diff_size12 && diff_size23 && diff_size13) + { + *same12 = *same23 = *same13 = FALSE; + } + else if (diff_size12 && diff_size23) + { + *same12 = *same23 = FALSE; + SVN_ERR(contents_identical_p(same13, file1, file3, scratch_pool)); + } + else if (diff_size23 && diff_size13) + { + *same23 = *same13 = FALSE; + SVN_ERR(contents_identical_p(same12, file1, file2, scratch_pool)); + } + else if (diff_size12 && diff_size13) + { + *same12 = *same13 = FALSE; + SVN_ERR(contents_identical_p(same23, file2, file3, scratch_pool)); + } + else + { + SVN_ERR_ASSERT(!diff_size12 && !diff_size23 && !diff_size13); + SVN_ERR(contents_three_identical_p(same12, same23, same13, + file1, file2, file3, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + #ifdef WIN32 /* Counter value of file_mktemp request (used in a threadsafe way), to make sure that a single process normally never generates the same tempname Index: subversion/libsvn_wc/merge.c =================================================================== --- subversion/libsvn_wc/merge.c (revision 1349990) +++ subversion/libsvn_wc/merge.c (working copy) @@ -948,7 +948,9 @@ apr_pool_t *scratch_pool) { svn_skel_t *work_item; - svn_boolean_t same_contents = FALSE; + svn_boolean_t same_left_right; + svn_boolean_t same_right_target; + svn_boolean_t same_left_target; svn_node_kind_t kind; svn_boolean_t is_special; @@ -961,19 +963,23 @@ return SVN_NO_ERROR; } + /* Check the files */ + SVN_ERR(svn_io_files_contents_three_same_p(&same_left_right, + &same_right_target, + &same_left_target, + left_abspath, + right_abspath, + detranslated_target_abspath, + scratch_pool)); + /* If the LEFT side of the merge is equal to WORKING, then we can * copy RIGHT directly. */ - SVN_ERR(svn_io_files_contents_same_p(&same_contents, left_abspath, - detranslated_target_abspath, - scratch_pool)); - if (same_contents) + if (same_left_target) { /* Check whether the left side equals the right side. * If it does, there is no change to merge so we leave the target * unchanged. */ - SVN_ERR(svn_io_files_contents_same_p(&same_contents, left_abspath, - right_abspath, scratch_pool)); - if (same_contents) + if (same_left_right) { *merge_outcome = svn_wc_merge_unchanged; } @@ -1002,10 +1008,7 @@ * conflicted them needlessly, while merge_text_file figured it out * eventually and returned svn_wc_merge_unchanged for them, which * is what we do here. */ - SVN_ERR(svn_io_files_contents_same_p(&same_contents, - detranslated_target_abspath, - right_abspath, scratch_pool)); - if (same_contents) + if (same_right_target) { *merge_outcome = svn_wc_merge_unchanged; return SVN_NO_ERROR; Index: subversion/tests =================================================================== --- subversion/tests (revision 1349693) +++ subversion/tests (working copy) Property changes on: subversion/tests ___________________________________________________________________ Modified: svn:ignore ## -7,3 +7,4 ## *~ .*~ svnserveautocheck.pid +svn-test-work Index: subversion/tests/libsvn_subr/io-test.c =================================================================== --- subversion/tests/libsvn_subr/io-test.c (revision 0) +++ subversion/tests/libsvn_subr/io-test.c (working copy) @@ -0,0 +1,470 @@ +/* io-test.c --- tests for some i/o functions + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + +#include +#include +#include +#include + +#include + +#include "svn_pools.h" +#include "svn_string.h" +#include "private/svn_skel.h" + +#include "../svn_test.h" +#include "../svn_test_fs.h" + + +/* Helpers to create the test data directory. */ + +#define TEST_DIR "io-test-temp" + +/* The definition for the test data files. */ +struct test_file_definition_t + { + /* The name of the test data file. */ + const char* const name; + + /* The string needs to contain up to 5 bytes, they + * are interpreded as: + * - first byte + * - filler between first and medium byte + * - medium byte (the byte in the middle of the file) + * - filler between medium and last byte + * - last byte. + * If the string is shorter than the file length, + * the test will fail. */ + const char* const data; + + /* The size of the file actually to create. */ + const apr_off_t size; + + /* The created path of the file. Will be filled in + * by create_test_file() */ + char* created_path; + }; + +struct test_file_definition_t test_file_definitions[] = + { + {"empty", "", 0}, + {"single_a", "a", 1}, + {"single_b", "b", 1}, + {"hundred_a", "aaaaa", 100}, + {"hundred_b", "bbbbb", 100}, + {"hundred_b1", "baaaa", 100}, + {"hundred_b2", "abaaa", 100}, + {"hundred_b3", "aabaa", 100}, + {"hundred_b4", "aaaba", 100}, + {"hundred_b5", "aaaab", 100}, + {"chunk_minus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_minus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE - 1}, + {"chunk_a", "aaaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b1", "baaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b2", "abaaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b3", "aabaa", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b4", "aaaba", SVN__STREAM_CHUNK_SIZE}, + {"chunk_b5", "aaaab", SVN__STREAM_CHUNK_SIZE}, + {"chunk_plus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE + 1}, + {"chunk_plus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE + 1}, + {"twochunk_minus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_minus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2 - 1}, + {"twochunk_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2}, + {"twochunk_plus_one_a", "aaaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b1", "baaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b2", "abaaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b3", "aabaa", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b4", "aaaba", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {"twochunk_plus_one_b5", "aaaab", SVN__STREAM_CHUNK_SIZE*2 + 1}, + {0}, + }; + +/* Function to prepare a single test file */ + +static svn_error_t * +create_test_file(struct test_file_definition_t* definition, + apr_pool_t *pool, + apr_pool_t *scratch_pool) +{ + apr_status_t status = 0; + apr_file_t *file_h; + apr_off_t midpos = definition->size / 2; + svn_error_t *err = NULL; + int i; + + if (definition->size < 5) + SVN_ERR_ASSERT(strlen(definition->data) >= definition->size); + else + SVN_ERR_ASSERT(strlen(definition->data) >= 5); + + + definition->created_path = svn_dirent_join(TEST_DIR, + definition->name, + pool); + + SVN_ERR(svn_io_file_open(&file_h, + definition->created_path, + APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL, + APR_OS_DEFAULT, + scratch_pool)); + + for (i=1; i <= definition->size; i += 1) + { + char c; + if (i == 1) + c = definition->data[0]; + else if (i < midpos) + c = definition->data[1]; + else if (i == midpos) + c = definition->data[2]; + else if (i < definition->size) + c = definition->data[3]; + else + c = definition->data[4]; + + status = apr_file_putc(c, file_h); + + if (status) + break; + } + + if (status) + err = svn_error_wrap_apr(status, "Can't write to file '%s'", + definition->name); + + return svn_error_compose_create(err, + svn_io_file_close(file_h, scratch_pool)); +} + +/* Function to prepare the whole set of on-disk files to be compared. */ +static svn_error_t * +create_comparison_candidates(apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + struct test_file_definition_t *candidate; + svn_error_t *err = SVN_NO_ERROR; + + /* If there's already a directory named io-test-temp, delete it. + Doing things this way means that repositories stick around after + a failure for postmortem analysis, but also that tests can be + re-run without cleaning out the repositories created by prior + runs. */ + SVN_ERR(svn_io_check_path(TEST_DIR, &kind, scratch_pool)); + + if (kind == svn_node_dir) + SVN_ERR(svn_io_remove_dir2(TEST_DIR, TRUE, NULL, NULL, scratch_pool)); + else if (kind != svn_node_none) + return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "There is already a file named '%s'", + TEST_DIR); + + SVN_ERR(svn_io_dir_make(TEST_DIR, APR_OS_DEFAULT, scratch_pool)); + + svn_test_add_dir_cleanup(TEST_DIR); + + for (candidate = test_file_definitions; + candidate->name != NULL; + candidate += 1) + { + svn_pool_clear(iterpool); + err = create_test_file(candidate, scratch_pool, iterpool); + if (err) + break; + } + + svn_pool_destroy(iterpool); + + return err; +} + + +/* Functions to check the 2-way and 3-way file comparison functions. */ + +/* Test 2-way file size checking */ +static svn_error_t * +test_two_file_size_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *outer; + svn_boolean_t actual; + svn_boolean_t expected; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { + for (inner = test_file_definitions; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected = inner->size != outer->size; + + cmp_err = svn_io_filesizes_different_p(&actual, + inner->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else if (expected != actual) + { + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + + svn_pool_destroy(iterpool); + return err; +} + + +/* Test 2-way file content checking */ +static svn_error_t * +test_two_file_content_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *outer; + svn_boolean_t actual; + svn_boolean_t expected; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { + for (inner = test_file_definitions; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected = inner->size == outer->size + && strcmp(inner->data, outer->data) == 0; + + cmp_err = svn_io_files_contents_same_p(&actual, + inner->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected != actual) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "content comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + + svn_pool_destroy(iterpool); + return err; +} + + +/* Test 3-way file size checking */ +static svn_error_t * +test_three_file_size_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *middle, *outer; + svn_boolean_t actual12, actual23, actual13; + svn_boolean_t expected12, expected23, expected13; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { + for (middle = test_file_definitions; middle->name != NULL; middle += 1) + { + for (inner = test_file_definitions; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected12 = inner->size != middle->size; + expected23 = middle->size != outer->size; + expected13 = inner->size != outer->size; + + cmp_err = svn_io_filesizes_three_different_p(&actual12, + &actual23, + &actual13, + inner->created_path, + middle->created_path, + outer->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected12 != actual12) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + middle->created_path)); + + if (expected23 != actual23) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + middle->created_path, + outer->created_path)); + + if (expected13 != actual13) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + } + + svn_pool_destroy(iterpool); + + return err; +} + + +/* Test 3-way file content checking */ +static svn_error_t * +test_three_file_content_comparison(apr_pool_t *scratch_pool) +{ + struct test_file_definition_t *inner, *middle, *outer; + svn_boolean_t actual12, actual23, actual13; + svn_boolean_t expected12, expected23, expected13; + svn_error_t *err = SVN_NO_ERROR; + svn_error_t *cmp_err; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + + SVN_ERR(create_comparison_candidates(scratch_pool)); + + for (outer = test_file_definitions; outer->name != NULL; outer += 1) + { + for (middle = test_file_definitions; middle->name != NULL; middle += 1) + { + for (inner = test_file_definitions; inner->name != NULL; inner += 1) + { + svn_pool_clear(iterpool); + + expected12 = outer->size == middle->size + && strcmp(outer->data, middle->data) == 0; + expected23 = middle->size == inner->size + && strcmp(middle->data, inner->data) == 0; + expected13 = outer->size == inner->size + && strcmp(outer->data, inner->data) == 0; + + cmp_err = svn_io_files_contents_three_same_p(&actual12, + &actual23, + &actual13, + outer->created_path, + middle->created_path, + inner->created_path, + iterpool); + + if (cmp_err) + { + err = svn_error_compose_create(err, cmp_err); + } + else + { + if (expected12 != actual12) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + middle->created_path)); + + if (expected23 != actual23) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + middle->created_path, + outer->created_path)); + + if (expected13 != actual13) + err = svn_error_compose_create(err, + svn_error_createf(SVN_ERR_TEST_FAILED, NULL, + "size comparison problem: '%s' and '%s'", + inner->created_path, + outer->created_path)); + } + } + } + } + + return err; +} + + + +/* The test table. */ + +struct svn_test_descriptor_t test_funcs[] = + { + SVN_TEST_NULL, + SVN_TEST_PASS2(test_two_file_size_comparison, + "two file size comparison"), + SVN_TEST_PASS2(test_two_file_content_comparison, + "two file content comparison"), + SVN_TEST_PASS2(test_three_file_size_comparison, + "three file size comparison"), + SVN_TEST_PASS2(test_three_file_content_comparison, + "three file content comparison"), + SVN_TEST_NULL + }; Index: subversion/tests/libsvn_subr/io-test.c =================================================================== --- subversion/tests/libsvn_subr/io-test.c (revision 0) +++ subversion/tests/libsvn_subr/io-test.c (working copy) Property changes on: subversion/tests/libsvn_subr/io-test.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property