| /* |
| * Incomplete regression tests for the diff/diff3 library. |
| * |
| * ==================================================================== |
| * 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 "../svn_test.h" |
| |
| #include "svn_diff.h" |
| #include "svn_pools.h" |
| #include "svn_utf.h" |
| |
| /* Used to terminate lines in large multi-line string literals. */ |
| #define NL APR_EOL_STR |
| |
| /* Random number seed. Yes, it's global, just pretend you can't see it. */ |
| static apr_uint32_t diff_diff3_seed; |
| |
| /* Return the value of the current random number seed, initializing it if |
| necessary */ |
| static apr_uint32_t |
| seed_val(void) |
| { |
| static svn_boolean_t first = TRUE; |
| |
| if (first) |
| { |
| diff_diff3_seed = (apr_uint32_t) apr_time_now(); |
| first = FALSE; |
| } |
| |
| return diff_diff3_seed; |
| } |
| |
| /* Return a random number N such that MIN_VAL <= N <= MAX_VAL */ |
| static apr_uint32_t |
| range_rand(apr_uint32_t min_val, |
| apr_uint32_t max_val) |
| { |
| apr_uint64_t diff = max_val - min_val; |
| apr_uint64_t val = diff * svn_test_rand(&diff_diff3_seed); |
| val /= 0xffffffff; |
| return min_val + (apr_uint32_t) val; |
| } |
| |
| /* Make a file that is between MIN_LINES and MAX_LINES lines long, with at |
| most VAR_LINES distinct lines. If BLOCK_LINES is non-zero then every |
| other block of BLOCK_LINES lines will be identical, if BLOCK_LINES is |
| zero all lines will have contents chosen at random. If TRAILING_NEWLINE |
| is TRUE then the file will have a trailing newline, if not then it wont. */ |
| static svn_error_t * |
| make_random_file(const char *filename, |
| int min_lines, |
| int max_lines, |
| int var_lines, |
| int block_lines, |
| svn_boolean_t trailing_newline, |
| apr_pool_t *pool) |
| { |
| apr_file_t *file; |
| int num_lines; |
| |
| num_lines = range_rand(min_lines, max_lines); |
| |
| SVN_ERR(svn_io_file_open(&file, filename, |
| APR_WRITE | APR_CREATE | APR_TRUNCATE, |
| APR_OS_DEFAULT, |
| pool)); |
| |
| while (num_lines--) |
| { |
| int x; |
| if (! (block_lines && (num_lines / block_lines % 2))) |
| x = range_rand(1, var_lines); |
| else |
| x = 0; |
| if (num_lines || trailing_newline) |
| apr_file_printf(file, "line %d line %d line %d\n", x, x, x); |
| else |
| apr_file_printf(file, "line %d line %d line %d", x, x, x); |
| } |
| |
| SVN_ERR(svn_io_file_close(file, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Create a file called FILENAME containing CONTENTS */ |
| static svn_error_t * |
| make_file(const char *filename, |
| const char *contents, |
| apr_pool_t *pool) |
| { |
| apr_file_t *file; |
| apr_status_t status; |
| |
| SVN_ERR(svn_io_file_open(&file, filename, |
| APR_WRITE | APR_CREATE | APR_TRUNCATE, |
| APR_OS_DEFAULT, pool)); |
| |
| status = apr_file_write_full(file, contents, strlen(contents), NULL); |
| if (status) |
| return svn_error_createf(status, NULL, "failed to write '%s'", filename); |
| |
| SVN_ERR(svn_io_file_close(file, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Create three files called FILENAME1, FILENAME2 and FILENAME3 |
| containing CONTENTS1, CONTENTS2 and CONTENTS3 respectively. Run a |
| three way merge to merge the difference between CONTENTS1 and |
| CONTENTS2 into CONTENTS3, using OPTIONS, and verify that it results |
| in EXPECTED. The files FILENAME1, FILENAME2 and FILENAME3 will be |
| deleted if the merge is successful, and preserved otherwise. If |
| the merge fails the merge output will be in a file called |
| "merge-FILENAME1-FILENAME2-FILENAME3". The conflict style STYLE is |
| used. */ |
| static svn_error_t * |
| three_way_merge(const char *base_filename1, |
| const char *base_filename2, |
| const char *base_filename3, |
| const char *contents1, |
| const char *contents2, |
| const char *contents3, |
| const char *expected, |
| const svn_diff_file_options_t *options, |
| svn_diff_conflict_display_style_t style, |
| apr_pool_t *pool) |
| { |
| svn_diff_t *diff; |
| apr_file_t *output; |
| svn_stream_t *ostream; |
| svn_stringbuf_t *actual; |
| char *merge_name = apr_psprintf( |
| pool, "merge-%s-%s-%s", base_filename1, base_filename2, base_filename3); |
| |
| const char *filename1 = svn_test_data_path(base_filename1, pool); |
| const char *filename2 = svn_test_data_path(base_filename2, pool); |
| const char *filename3 = svn_test_data_path(base_filename3, pool); |
| |
| /* We have an EXPECTED string we can match, because we don't support |
| any other combinations (yet) than the ones above. */ |
| svn_string_t *original = svn_string_create(contents1, pool); |
| svn_string_t *modified = svn_string_create(contents2, pool); |
| svn_string_t *latest = svn_string_create(contents3, pool); |
| |
| options = options ? options : svn_diff_file_options_create(pool); |
| |
| SVN_ERR(svn_diff_mem_string_diff3(&diff, |
| original, modified, latest, options, pool)); |
| |
| actual = svn_stringbuf_create_empty(pool); |
| ostream = svn_stream_from_stringbuf(actual, pool); |
| |
| SVN_ERR(svn_diff_mem_string_output_merge3 |
| (ostream, diff, original, modified, latest, |
| apr_psprintf(pool, "||||||| %s", base_filename1), |
| apr_psprintf(pool, "<<<<<<< %s", base_filename2), |
| apr_psprintf(pool, ">>>>>>> %s", base_filename3), |
| NULL, /* separator */ |
| style, |
| NULL, NULL, /* cancel */ |
| pool)); |
| |
| SVN_ERR(svn_stream_close(ostream)); |
| if (strcmp(actual->data, expected) != 0) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "Failed mem-diff, expected and actual " |
| "outputs differ.\nEXPECTED:\n%s\n" |
| "ACTUAL:\n%s\n", expected, actual->data); |
| |
| SVN_ERR(make_file(filename1, contents1, pool)); |
| SVN_ERR(make_file(filename2, contents2, pool)); |
| SVN_ERR(make_file(filename3, contents3, pool)); |
| |
| SVN_ERR(svn_diff_file_diff3_2(&diff, filename1, filename2, filename3, |
| options, pool)); |
| SVN_ERR(svn_io_file_open(&output, merge_name, |
| APR_WRITE | APR_CREATE | APR_TRUNCATE, |
| APR_OS_DEFAULT, pool)); |
| |
| ostream = svn_stream_from_aprfile2(output, FALSE, pool); |
| SVN_ERR(svn_diff_file_output_merge3( |
| ostream, diff, |
| filename1, filename2, filename3, |
| apr_psprintf(pool, "||||||| %s", base_filename1), |
| apr_psprintf(pool, "<<<<<<< %s", base_filename2), |
| apr_psprintf(pool, ">>>>>>> %s", base_filename3), |
| NULL, /* separator */ |
| style, |
| NULL, NULL, /* cancel */ |
| pool)); |
| SVN_ERR(svn_stream_close(ostream)); |
| SVN_ERR(svn_stringbuf_from_file2(&actual, merge_name, pool)); |
| if (strcmp(actual->data, expected)) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "failed merging diff '%s' to '%s' into '%s'", |
| base_filename1, base_filename2, base_filename3); |
| |
| SVN_ERR(svn_io_remove_file2(filename1, TRUE, pool)); |
| if (strcmp(filename1, filename2)) |
| SVN_ERR(svn_io_remove_file2(filename2, TRUE, pool)); |
| if (strcmp(filename1, filename3) && strcmp(filename2, filename3)) |
| SVN_ERR(svn_io_remove_file2(filename3, TRUE, pool)); |
| SVN_ERR(svn_io_remove_file2(merge_name, TRUE, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* Create two files called FILENAME1 and FILENAME2 containing |
| CONTENTS1 and CONTENTS2 respectively. Run a two way diff between |
| CONTENTS1 and CONTENTS2, using OPTIONS, and verify that it results |
| in EXPECTED. Then run the trivial merges to update CONTENTS1 to |
| CONTENTS2 and CONTENTS2 to CONTENTS1. The files FILENAME1, |
| FILENAME2 and be deleted if the diff and merges are successful, and |
| preserved otherwise. If the diff fails the diff output will be in |
| a file called "diff-FILENAME1-FILENAME2". */ |
| static svn_error_t * |
| two_way_diff(const char *base_filename1, |
| const char *base_filename2, |
| const char *contents1, |
| const char *contents2, |
| const char *expected, |
| const svn_diff_file_options_t *options, |
| apr_pool_t *pool) |
| { |
| svn_diff_t *diff; |
| apr_file_t *output; |
| svn_stream_t *ostream; |
| svn_stringbuf_t *actual; |
| char *diff_name = (char *)apr_pstrdup( |
| pool, svn_test_data_path( |
| apr_psprintf(pool, "diff-%s-%s", base_filename1, base_filename2), |
| pool)); |
| |
| const char *filename1 = svn_test_data_path(base_filename1, pool); |
| const char *filename2 = svn_test_data_path(base_filename2, pool); |
| |
| /* Some of the tests have lots of lines, although not much data as |
| the lines are short, and the in-memory diffs allocate a lot of |
| memory. Since we are doing multiple diff in a single test we use |
| a subpool to reuse that memory. */ |
| apr_pool_t *subpool = svn_pool_create(pool); |
| |
| /* We have an EXPECTED string we can match, because we don't support |
| any other combinations (yet) than the ones above. */ |
| svn_string_t *original = svn_string_create(contents1, pool); |
| svn_string_t *modified = svn_string_create(contents2, pool); |
| |
| options = options ? options : svn_diff_file_options_create(pool); |
| |
| SVN_ERR(svn_diff_mem_string_diff(&diff, original, modified, options, |
| subpool)); |
| |
| actual = svn_stringbuf_create_empty(pool); |
| ostream = svn_stream_from_stringbuf(actual, pool); |
| |
| SVN_ERR(svn_diff_mem_string_output_unified(ostream, diff, |
| base_filename1, base_filename2, |
| SVN_APR_LOCALE_CHARSET, |
| original, modified, subpool)); |
| svn_pool_clear(subpool); |
| SVN_ERR(svn_stream_close(ostream)); |
| if (strcmp(actual->data, expected) != 0) |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "Failed mem-diff, expected and actual " |
| "outputs differ.\nEXPECTED:\n%s\n" |
| "ACTUAL:\n%s\n", expected, actual->data); |
| |
| SVN_ERR(make_file(filename1, contents1, pool)); |
| SVN_ERR(make_file(filename2, contents2, pool)); |
| |
| /* Check that two-way diff between contents1 and contents2 produces |
| expected output. */ |
| SVN_ERR(svn_diff_file_diff_2(&diff, filename1, filename2, options, pool)); |
| |
| SVN_ERR(svn_io_file_open(&output, diff_name, |
| APR_WRITE | APR_CREATE | APR_TRUNCATE, |
| APR_OS_DEFAULT, pool)); |
| |
| ostream = svn_stream_from_aprfile2(output, FALSE, pool); |
| SVN_ERR(svn_diff_file_output_unified2(ostream, diff, |
| filename1, filename2, |
| base_filename1, base_filename2, |
| SVN_APR_LOCALE_CHARSET, pool)); |
| SVN_ERR(svn_stream_close(ostream)); |
| |
| SVN_ERR(svn_stringbuf_from_file2(&actual, diff_name, pool)); |
| if (strcmp(actual->data, expected)) |
| { |
| /*svn_stringbuf_t *dump_actual; |
| svn_stream_t *dump_ostream; |
| dump_actual = svn_stringbuf_create_empty(pool); |
| dump_ostream = svn_stream_from_stringbuf(dump_actual, pool); |
| |
| SVN_ERR(svn_diff_mem_string_output_unified(dump_ostream, diff, |
| "expected", "actual", |
| SVN_APR_LOCALE_CHARSET, |
| svn_string_create(expected, pool), |
| svn_string_create(actual->data, pool), |
| pool)); |
| SVN_ERR(svn_stream_close(ostream)); |
| |
| SVN_DBG(("%s\n", dump_actual->data)); |
| |
| SVN_ERR(make_file("memory", expected, pool));*/ |
| return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, |
| "failed comparing '%s' and '%s'" |
| " (memory and file results are different)", |
| base_filename1, base_filename2); |
| } |
| |
| /* May as well do the trivial merges while we are here */ |
| SVN_ERR(three_way_merge(base_filename1, base_filename2, base_filename1, |
| contents1, contents2, contents1, contents2, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| svn_pool_clear(subpool); |
| SVN_ERR(three_way_merge(base_filename2, base_filename1, base_filename2, |
| contents2, contents1, contents2, contents1, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| svn_pool_destroy(subpool); |
| |
| SVN_ERR(svn_io_remove_file2(diff_name, TRUE, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| struct random_mod |
| { |
| int index; /* Zero based line number */ |
| int mod; /* Type of mod: 0, 1, 2 (can be interpreted as you like just |
| do it consistently) */ |
| }; |
| |
| /* Fill the SELECTED array of length NUM to select with randomly chosen |
| values, ensuring that none of SELECTED.INDEX are duplicates and that all |
| the SELECTED.INDEX values are less than NUM_LINES. Also ensure that for |
| each SELECTED.INDEX the three elements of LINES from SELECTED.INDEX-1 to |
| SELECTED.INDEX+1 are unset. Set all LINES[SELECTED.INDEX]. */ |
| static void |
| select_lines(struct random_mod *selected, |
| int num_to_select, |
| svn_boolean_t *lines, |
| int num_lines) |
| { |
| int i; |
| for (i = 0; i < num_to_select; ++i) |
| { |
| int j; |
| for (;;) |
| { |
| j= range_rand(0, num_lines - 1); |
| if (lines[j] /* already selected */ |
| || |
| (j > 0 && lines[j - 1]) /* previous selected */ |
| || |
| (j < num_lines - 1 && lines[j + 1])) /* next selected */ |
| continue; /* try again */ |
| break; /* got one */ |
| } |
| selected[i].index = j; |
| selected[i].mod = range_rand(0, 2); |
| lines[j] = TRUE; |
| } |
| } |
| |
| |
| /* Create a file called FILENAME where the contents are obtained by |
| applying the modifications in MOD_LINES, of which there are NUM_MODS, to |
| a theoretical pristine file of length NUM_LINES lines. */ |
| static svn_error_t * |
| make_random_merge_file(const char *filename, |
| int num_lines, |
| struct random_mod *mod_lines, |
| int num_mods, |
| apr_pool_t *pool) |
| { |
| apr_file_t *file; |
| int i; |
| |
| SVN_ERR(svn_io_file_open(&file, filename, |
| APR_WRITE | APR_CREATE | APR_TRUNCATE, |
| APR_OS_DEFAULT, pool)); |
| |
| for (i = 0; i < num_lines; ++i) |
| { |
| int j; |
| for (j = 0; j < num_mods; ++j) |
| if (mod_lines[j].index == i) |
| break; |
| |
| if (j < num_mods) |
| { |
| switch (mod_lines[j].mod) |
| { |
| case 0: |
| apr_file_printf(file, "replace line %d\n", i); |
| break; |
| case 1: |
| apr_file_printf(file, |
| "added line %d\n" |
| "unmodified line %d\n" |
| "added line %d\n", |
| i, i, i); |
| break; |
| default: |
| ; /* Delete the line */ |
| } |
| } |
| else |
| { |
| apr_file_printf(file, "unmodified line %d\n", i); |
| } |
| } |
| |
| SVN_ERR(svn_io_file_close(file, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* ========================================================================== */ |
| |
| static svn_error_t * |
| dump_core(apr_pool_t *pool) |
| { |
| SVN_ERR(two_way_diff("foo1", "bar1", |
| "", |
| "", |
| "", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo2", "bar2", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "", |
| |
| "--- foo2" NL |
| "+++ bar2" NL |
| "@@ -1,3 +0,0 @@" NL |
| "-Aa\n" |
| "-Bb\n" |
| "-Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo3", "bar3", |
| "", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo3" NL |
| "+++ bar3" NL |
| "@@ -0,0 +1,3 @@" NL |
| "+Aa\n" |
| "+Bb\n" |
| "+Cc\n", |
| NULL, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_two_way_unified(apr_pool_t *pool) |
| { |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| SVN_ERR(two_way_diff("foo4", "bar4", |
| "Aa\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo4" NL |
| "+++ bar4" NL |
| "@@ -1 +1,3 @@" NL |
| " Aa\n" |
| "+Bb\n" |
| "+Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo4b", "bar4b", |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo4b" NL |
| "+++ bar4b" NL |
| "@@ -1 +1,3 @@" NL |
| "+Aa\n" |
| "+Bb\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| diff_opts->ignore_eol_style = TRUE; |
| SVN_ERR(two_way_diff("foo4c", "bar4c", |
| "Cc\n", |
| |
| "Aa\r" |
| "Bb\r" |
| "Cc\r", |
| |
| "--- foo4c" NL |
| "+++ bar4c" NL |
| "@@ -1 +1,3 @@" NL |
| "+Aa\r" |
| "+Bb\r" |
| " Cc\n", |
| diff_opts, pool)); |
| diff_opts->ignore_eol_style = FALSE; |
| |
| SVN_ERR(two_way_diff("foo5", "bar5", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n", |
| |
| "--- foo5" NL |
| "+++ bar5" NL |
| "@@ -1,3 +1 @@" NL |
| " Aa\n" |
| "-Bb\n" |
| "-Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo5b", "bar5b", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Cc\n", |
| |
| "--- foo5b" NL |
| "+++ bar5b" NL |
| "@@ -1,3 +1 @@" NL |
| "-Aa\n" |
| "-Bb\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| diff_opts->ignore_eol_style = TRUE; |
| SVN_ERR(two_way_diff("foo5c", "bar5c", |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n", |
| |
| "Cc\n", |
| |
| "--- foo5c" NL |
| "+++ bar5c" NL |
| "@@ -1,3 +1 @@" NL |
| "-Aa\r\n" |
| "-Bb\r\n" |
| " Cc\r\n", |
| diff_opts, pool)); |
| |
| |
| SVN_ERR(two_way_diff("foo5d", "bar5d", |
| "Aa\r\n" |
| "\r\n" |
| "Bb\r\n" |
| "\r\n" |
| "Cc\r\n" |
| "\r\n", |
| |
| "Aa\n" |
| "\n" |
| "Bb\n" |
| "\n" |
| "Cc\n" |
| "\n", |
| |
| "", |
| diff_opts, pool)); |
| diff_opts->ignore_eol_style = FALSE; |
| |
| SVN_ERR(two_way_diff("foo6", "bar6", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo6b", "bar6b", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Xx\n" |
| "Cc\n", |
| |
| "--- foo6b" NL |
| "+++ bar6b" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\n" |
| "-Bb\n" |
| "+Xx\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo6c", "bar6c", |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n", |
| |
| "Aa\r\n" |
| "Xx\r\n" |
| "Cc\r\n", |
| |
| "--- foo6c" NL |
| "+++ bar6c" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\r\n" |
| "-Bb\r\n" |
| "+Xx\r\n" |
| " Cc\r\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo6d", "bar6d", |
| "Aa\r" |
| "Bb\r" |
| "Cc\r", |
| |
| "Aa\r" |
| "Xx\r" |
| "Cc\r", |
| |
| "--- foo6d" NL |
| "+++ bar6d" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\r" |
| "-Bb\r" |
| "+Xx\r" |
| " Cc\r", |
| NULL, pool)); |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_change; |
| SVN_ERR(two_way_diff("foo6e", "bar6e", |
| " A a \n" |
| " B b \r" |
| " C c \r\n", |
| |
| " A a \n" |
| " B b \r" |
| " C c \r\n", |
| |
| "", |
| diff_opts, pool)); |
| diff_opts->ignore_space = svn_diff_file_ignore_space_none; |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| SVN_ERR(two_way_diff("foo6f", "bar6f", |
| "Aa\n" |
| "Bb\r" |
| "Cc\r\n", |
| |
| " A a \n" |
| " B b \r" |
| " C c \r\n", |
| |
| "", |
| diff_opts, pool)); |
| diff_opts->ignore_space = svn_diff_file_ignore_space_none; |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| diff_opts->ignore_eol_style = TRUE; |
| SVN_ERR(two_way_diff("foo6f", "bar6f", |
| "Aa\n" |
| "Bb\r" |
| "Cc\r\n", |
| |
| " A a \r" |
| " B b \r\n" |
| " C c \n", |
| |
| "", |
| diff_opts, pool)); |
| diff_opts->ignore_space = svn_diff_file_ignore_space_none; |
| diff_opts->ignore_eol_style = FALSE; |
| |
| SVN_ERR(two_way_diff("foo7", "bar7", |
| "Aa\n", |
| |
| "Bb\n", |
| |
| "--- foo7" NL |
| "+++ bar7" NL |
| "@@ -1 +1 @@" NL |
| "-Aa\n" |
| "+Bb\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo7a", "bar7a", |
| "Aa\n" |
| "Cc\n", |
| |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo7a" NL |
| "+++ bar7a" NL |
| "@@ -1,2 +1,2 @@" NL |
| "-Aa\n" |
| "+Bb\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo7b", "bar7b", |
| "Aa\r" |
| "Cc\n", |
| |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo7b" NL |
| "+++ bar7b" NL |
| "@@ -1,2 +1,2 @@" NL |
| "-Aa\r" |
| "+Bb\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo8", "bar8", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Yy\n", |
| |
| "--- foo8" NL |
| "+++ bar8" NL |
| "@@ -1,3 +1,2 @@" NL |
| "-Aa\n" |
| "-Bb\n" |
| "-Cc\n" |
| "+Xx\n" |
| "+Yy\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo9", "bar9", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Bb\n", |
| |
| "--- foo9" NL |
| "+++ bar9" NL |
| "@@ -1,3 +1 @@" NL |
| "-Aa\n" |
| " Bb\n" |
| "-Cc\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo10", "bar10", |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "Aa\n" |
| "Xx\n" |
| "Yy\n", |
| |
| "--- foo10" NL |
| "+++ bar10" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\n" |
| "-Bb\n" |
| "-Cc" NL |
| "\\ No newline at end of file" NL |
| "+Xx\n" |
| "+Yy\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo11", "bar11", |
| "Aa\n" |
| "Xx\n" |
| "Yy\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "--- foo11" NL |
| "+++ bar11" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\n" |
| "-Xx\n" |
| "-Yy\n" |
| "+Bb\n" |
| "+Cc" NL |
| "\\ No newline at end of file" NL, |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo12", "bar12", |
| "Aa\n" |
| "Xx\n" |
| "Yy", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "--- foo12" NL |
| "+++ bar12" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\n" |
| "-Xx\n" |
| "-Yy" NL |
| "\\ No newline at end of file" NL |
| "+Bb\n" |
| "+Cc" NL |
| "\\ No newline at end of file" NL, |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo13", "bar13", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Yy\n", |
| |
| "--- foo13" NL |
| "+++ bar13" NL |
| "@@ -1,3 +1,4 @@" NL |
| "+Xx\n" |
| " Aa\n" |
| " Bb\n" |
| " Cc\n" |
| "@@ -5,3 +6,4 @@" NL |
| " Ee\n" |
| " Ff\n" |
| " Gg\n" |
| "+Yy\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo14", "bar14", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n", |
| |
| "Bb\n" |
| "Aa\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Gg\n" |
| "Ff\n", |
| |
| "--- foo14" NL |
| "+++ bar14" NL |
| "@@ -1,7 +1,7 @@" NL |
| "+Bb\n" |
| " Aa\n" |
| "-Bb\n" |
| " Cc\n" |
| " Dd\n" |
| " Ee\n" |
| "+Gg\n" |
| " Ff\n" |
| "-Gg\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo16", "bar16", |
| "Aa\n" |
| "\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "--- foo16" NL |
| "+++ bar16" NL |
| "@@ -1,3 +1,3 @@" NL |
| " Aa\n" |
| "-\n" |
| "+Bb\n" |
| " Cc\n", |
| NULL, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_two_way_unified_suspect(apr_pool_t *pool) |
| { |
| SVN_ERR(two_way_diff("foo15a", "bar15a", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "--- foo15a" NL |
| "+++ bar15a" NL |
| "@@ -2,7 +2,6 @@" NL |
| " Bb\n" |
| " Cc\n" |
| " Dd\n" |
| "-Ee\n" |
| " Ff\n" |
| " Gg\n" |
| " Hh\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo15b", "bar15b", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Xx\n" |
| "Yy\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "--- foo15b" NL |
| "+++ bar15b" NL |
| "@@ -3,6 +3,8 @@" NL |
| " Cc\n" |
| " Dd\n" |
| " Ee\n" |
| "+Xx\n" |
| "+Yy\n" |
| " Ff\n" |
| " Gg\n" |
| " Hh\n", |
| NULL, pool)); |
| |
| SVN_ERR(two_way_diff("foo15c", "bar15c", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Xx\n" |
| "Yy\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "--- foo15c" NL |
| "+++ bar15c" NL |
| "@@ -2,7 +2,8 @@" NL |
| " Bb\n" |
| " Cc\n" |
| " Dd\n" |
| "-Ee\n" |
| "+Xx\n" |
| "+Yy\n" |
| " Ff\n" |
| " Gg\n" |
| " Hh\n", |
| NULL, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_three_way_merge_no_overlap(apr_pool_t *pool) |
| { |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| SVN_ERR(three_way_merge("zig1", "zag1", "zog1", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig1a", "zag1a", "zog1a", |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n", |
| |
| "Xx\r\n" |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n", |
| |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n" |
| "Yy\r\n", |
| |
| "Xx\r\n" |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n" |
| "Yy\r\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig1b", "zag1b", "zog1b", |
| "Aa\r" |
| "Bb\r" |
| "Cc\r", |
| |
| "Xx\r" |
| "Aa\r" |
| "Bb\r" |
| "Cc\r", |
| |
| "Aa\r" |
| "Bb\r" |
| "Cc\r" |
| "Yy\r", |
| |
| "Xx\r" |
| "Aa\r" |
| "Bb\r" |
| "Cc\r" |
| "Yy\r", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| SVN_ERR(three_way_merge("zig1c", "zag1c", "zog1c", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "X x\n" |
| "A a\n" |
| "B b\n" |
| "C c\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n", |
| |
| "X x\n" |
| "A a\n" |
| "B b\n" |
| "C c\n" |
| "Yy\n", |
| diff_opts, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| diff_opts->ignore_space = svn_diff_file_ignore_space_none; |
| |
| SVN_ERR(three_way_merge("zig2", "zag2", "zog2", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Zz\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Zz\n" |
| "Cc\n" |
| "Yy\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig3a", "zag3a", "zog3a", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "Xx\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Bb\n" |
| "Cc", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig3b", "zag3b", "zog3b", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "Xx\n" |
| "Bb\n" |
| "Cc", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| diff_opts->ignore_eol_style = TRUE; |
| SVN_ERR(three_way_merge("zig2c", "zag2c", "zog2c", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| " Xx\r\n" |
| " Aa\r\n" |
| " Bb\r\n" |
| " Cc\r\n" |
| " Yy\r\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Zz\n" |
| "Cc\n", |
| |
| " Xx\r\n" |
| " Aa\r\n" |
| " Bb\r\n" |
| "Zz\n" |
| " Cc\r\n" |
| " Yy\r\n", |
| diff_opts, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| diff_opts->ignore_space = svn_diff_file_ignore_space_none; |
| diff_opts->ignore_eol_style = FALSE; |
| |
| SVN_ERR(three_way_merge("zig4", "zag4", "zog4", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Yy\n" |
| "Zz\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Ii\n", |
| |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Yy\n" |
| "Zz\n" |
| "Hh\n" |
| "Ii\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig5", "zag5", "zog5", |
| "Aa\r\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\r\n" |
| "Aa\r\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\r\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\r\n", |
| |
| "Xx\r\n" |
| "Aa\r\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\r\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig6", "zag6", "zog6", |
| "AaAaAaAaAaAa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "AaAaAaAaAaAa\n" |
| "Bb\n" |
| "CcCcCcCcCcCc\n" |
| "Yy\n", |
| |
| "Xx\n" |
| "Bb\n" |
| "CcCcCcCcCcCc\n" |
| "Yy\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("zig7", "zag7", "zog7", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| diff_opts->ignore_eol_style = FALSE; |
| SVN_ERR(three_way_merge("zig8", "zag8", "zog8", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| " Aa\n" |
| "B b\n" |
| "C c\n", |
| |
| "A a\n" |
| "Bb \n" |
| " Cc\n" |
| "New line in zog8\n", |
| |
| " Aa\n" |
| "B b\n" |
| "C c\n" |
| "New line in zog8\n", |
| diff_opts, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_three_way_merge_with_overlap(apr_pool_t *pool) |
| { |
| SVN_ERR(three_way_merge("splish1", "splash1", "splosh1", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n", |
| |
| "Aa\n" |
| "Xx\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n" |
| "Ee\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n" |
| "Ee\n" |
| "Zz\n", |
| |
| "Aa\n" |
| "Xx\n" |
| "Bb\n" |
| "Cc\n" |
| "Yy\n" |
| "Ee\n" |
| "Zz\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("splish2", "splash2", "splosh2", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n", |
| |
| "Aa\n" |
| "Yy\n" |
| "Zz\n" |
| "Dd\n" |
| "Pp\n" |
| "Qq\n" |
| "Ff\n", |
| |
| "Pp\n" |
| "Qq\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Pp\n" |
| "Qq\n" |
| "Ff\n" |
| "Pp\n" |
| "Qq\n", |
| |
| "Pp\n" |
| "Qq\n" |
| "Aa\n" |
| "Yy\n" |
| "Zz\n" |
| "Dd\n" |
| "Pp\n" |
| "Qq\n" |
| "Ff\n" |
| "Pp\n" |
| "Qq\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("splish3", "splash3", "splosh3", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Xx\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc", |
| |
| "Aa\n" |
| "Xx\n" |
| "Bb\n" |
| "Cc", |
| |
| "Xx\n" |
| "Aa\n" |
| "Xx\n" |
| "Bb\n" |
| "Cc", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("splish4", "splash4", "splosh4", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n", |
| |
| "Aa\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Bb\n" |
| "Cc\n" |
| "Xx\n" |
| "Dd\n" |
| "Ee\n" |
| "Yy\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Xx\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "Gg\n" |
| "Zz\n" |
| "Hh\n", |
| |
| "Aa\n" |
| "Ff\n" |
| "Gg\n" |
| "Hh\n" |
| "Bb\n" |
| "Cc\n" |
| "Xx\n" |
| "Dd\n" |
| "Ee\n" |
| "Yy\n" |
| "Ff\n" |
| "Gg\n" |
| "Zz\n" |
| "Hh\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_three_way_merge_with_conflict(apr_pool_t *pool) |
| { |
| SVN_ERR(three_way_merge("dig1", "dug1", "dag1", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "", |
| |
| "", |
| |
| "", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("dig2", "dug2", "dag2", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n", |
| |
| "", |
| |
| "<<<<<<< dug2\n" |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "=======\n" |
| ">>>>>>> dag2\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("dig2a", "dug2a", "dag2a", |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n", |
| |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n" |
| "Dd\r\n" |
| "Ee\r\n" |
| "Ff\r\n", |
| |
| "", |
| |
| "<<<<<<< dug2a\r\n" |
| "Aa\r\n" |
| "Bb\r\n" |
| "Cc\r\n" |
| "Dd\r\n" |
| "Ee\r\n" |
| "Ff\r\n" |
| "=======\r\n" |
| ">>>>>>> dag2a\r\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("dig2b", "dug2b", "dag2b", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\r" |
| "Bb\r" |
| "Cc\r" |
| "Dd\r" |
| "Ee\r" |
| "Ff\r", |
| |
| "", |
| |
| "<<<<<<< dug2b\r" |
| "Aa\r" |
| "Bb\r" |
| "Cc\r" |
| "Dd\r" |
| "Ee\r" |
| "Ff\r" |
| "=======\r" |
| ">>>>>>> dag2b\r", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("dig3", "dug3", "dag3", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n", |
| |
| "Aa\n" |
| "Bb\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "<<<<<<< dug3\n" |
| "Cc\n" |
| "Dd\n" |
| "Ee\n" |
| "Ff\n" |
| "=======\n" |
| ">>>>>>> dag3\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("dig4", "dug4", "dag4", |
| "Aa\n" |
| "Bb\n" |
| "Cc\n", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Dd", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "Ee", |
| |
| "Aa\n" |
| "Bb\n" |
| "Cc\n" |
| "<<<<<<< dug4\n" |
| "Dd=======\n" |
| "Ee>>>>>>> dag4\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| test_three_way_merge_conflict_styles(apr_pool_t *pool) |
| { |
| static const char *original = |
| "a\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "i\n" |
| "j\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "n\n" |
| "o\n" |
| "p\n" |
| "q\n" |
| "r\n" |
| "s\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "z\n" |
| ; |
| static const char *modified = |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "P\n" |
| "Q\n" |
| "r\n" |
| "s\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "z\n" |
| ; |
| static const char *latest = |
| "a\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "i\n" |
| "j\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "p\n" |
| "q\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n" |
| ; |
| /* So, 'modified' capitalized N through Q; 'latest' added numbers to |
| 'k' through 'o'; and they both inserted "hello world yay" in the |
| middle. Also, there are non-conflicting changes to the first and |
| last lines. */ |
| |
| SVN_ERR(three_way_merge("style-normal1", "style-normal2", "style-normal3", |
| original, modified, latest, |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "<<<<<<< style-normal2\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "P\n" |
| "Q\n" |
| "=======\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "p\n" |
| "q\n" |
| ">>>>>>> style-normal3\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n", |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("style-resolved1", "style-resolved2", |
| "style-resolved3", |
| original, modified, latest, |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "<<<<<<< style-resolved2\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "=======\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| ">>>>>>> style-resolved3\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "<<<<<<< style-resolved2\n" |
| "P\n" |
| "Q\n" |
| "=======\n" |
| "p\n" |
| "q\n" |
| ">>>>>>> style-resolved3\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n", |
| NULL, |
| svn_diff_conflict_display_resolved_modified_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("style-three1", "style-three2", "style-three3", |
| original, modified, latest, |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "<<<<<<< style-three2\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "P\n" |
| "Q\n" |
| "||||||| style-three1\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "n\n" |
| "o\n" |
| "p\n" |
| "q\n" |
| "=======\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "p\n" |
| "q\n" |
| ">>>>>>> style-three3\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n", |
| NULL, |
| svn_diff_conflict_display_modified_original_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("style-only1", "style-only2", "style-only3", |
| original, modified, latest, |
| "@@\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "<<<<<<< style-only2 (11,10)\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "P\n" |
| "Q\n" |
| "||||||| style-only1 (11,7)\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "n\n" |
| "o\n" |
| "p\n" |
| "q\n" |
| "=======\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "p\n" |
| "q\n" |
| ">>>>>>> style-only3 (11,10)\n" |
| "r\n" |
| "sLAT\n" |
| "t\n", |
| NULL, |
| svn_diff_conflict_display_only_conflicts, |
| pool)); |
| |
| SVN_ERR(three_way_merge("style-mod1", "style-mod2", "style-mod3", |
| original, modified, latest, |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "k\n" |
| "l\n" |
| "m\n" |
| "N\n" |
| "O\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "P\n" |
| "Q\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n", |
| NULL, |
| svn_diff_conflict_display_modified, |
| pool)); |
| |
| SVN_ERR(three_way_merge("style-latest1", "style-latest2", "style-latest3", |
| original, modified, latest, |
| "A\n" |
| "b\n" |
| "c\n" |
| "d\n" |
| "e\n" |
| "f\n" |
| "g\n" |
| "h\n" |
| "iMOD\n" |
| "j\n" |
| "k1\n" |
| "l2\n" |
| "m3\n" |
| "n4\n" |
| "o5\n" |
| "hello\n" |
| "world\n" |
| "yay\n" |
| "p\n" |
| "q\n" |
| "r\n" |
| "sLAT\n" |
| "t\n" |
| "u\n" |
| "v\n" |
| "w\n" |
| "x\n" |
| "y\n" |
| "Z\n", |
| NULL, |
| svn_diff_conflict_display_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| #define MAKE_STRING(cstr) { (cstr), sizeof((cstr))-1 } |
| |
| static svn_error_t * |
| test_diff4(apr_pool_t *pool) |
| { |
| svn_diff_t *diff; |
| svn_stream_t *actual, *expected; |
| svn_boolean_t same; |
| static svn_string_t B2 = MAKE_STRING( |
| "int main (int argc, char **argv)\n" |
| "{\n" |
| " /* line minus-five of context */\n" |
| " /* line minus-four of context */\n" |
| " /* line minus-three of context */\n" |
| " /* line -1 of context */\n" |
| " printf (\"Hello, world!\\n\");\n" |
| " /* newly inserted line of context */\n" |
| " /* line plus-one of context */\n" |
| " /* line plus-two of context */\n" |
| " /* line plus-three of context */\n" |
| " /* line plus-four of context */\n" |
| " /* line plus-five of context */\n" |
| "}\n"); |
| static svn_string_t B2new = MAKE_STRING( |
| "int main (int argc, char **argv)\n" |
| "{\n" |
| " /* line minus-five of context */\n" |
| " /* line minus-four of context */\n" |
| " /* line minus-three of context */\n" |
| " /* line -1 of context */\n" |
| " printf (\"Good-bye, cruel world!\\n\");\n" |
| " /* newly inserted line of context */\n" |
| " /* line plus-one of context */\n" |
| " /* line plus-two of context */\n" |
| " /* line plus-three of context */\n" |
| " /* line plus-four of context */\n" |
| " /* line plus-five of context */\n" |
| "}\n"); |
| static svn_string_t T1 = MAKE_STRING( |
| "int main (int argc, char **argv)\n" |
| "{\n" |
| " /* line minus-five of context */\n" |
| " /* line minus-four of context */\n" |
| " /* line minus-three of context */\n" |
| " /* line minus-two of context */\n" |
| " /* line minus-one of context */\n" |
| " printf (\"Hello, world!\\n\");\n" |
| " /* line plus-one of context */\n" |
| " /* line plus-two of context */\n" |
| " /* line plus-three of context */\n" |
| " /* line plus-four of context */\n" |
| " /* line plus-five of context */\n" |
| "}\n"); |
| static svn_string_t T2 = MAKE_STRING( |
| "#include <stdio.h>\n" |
| "\n" |
| "int main (int argc, char **argv)\n" |
| "{\n" |
| " /* line minus-five of context */\n" |
| " /* line minus-four of context */\n" |
| " /* line minus-three of context */\n" |
| " /* line minus-two of context */\n" |
| " /* line minus-one of context */\n" |
| " printf (\"Hello, world!\\n\");\n" |
| " /* line plus-one of context */\n" |
| " /* line plus-two of context */\n" |
| " /* line plus-three of context */\n" |
| " /* line plus-four of context */\n" |
| " /* line plus-five of context */\n" |
| "}\n"); |
| static svn_string_t T3 = MAKE_STRING( |
| "#include <stdio.h>\n" |
| "\n" |
| "int main (int argc, char **argv)\n" |
| "{\n" |
| " /* line minus-five of context */\n" |
| " /* line minus-four of context */\n" |
| " /* line minus-three of context */\n" |
| " /* line minus-two of context */\n" |
| " /* line minus-one of context */\n" |
| " printf (\"Good-bye, cruel world!\\n\");\n" |
| " /* line plus-one of context */\n" |
| " /* line plus-two of context */\n" |
| " /* line plus-three of context */\n" |
| " /* line plus-four of context */\n" |
| " /* line plus-five of context */\n" |
| "}\n"); |
| |
| const char *B2_path = svn_test_data_path("B2", pool); |
| const char *T1_path = svn_test_data_path("T1", pool); |
| const char *T2_path = svn_test_data_path("T2", pool); |
| const char *T3_path = svn_test_data_path("T3", pool); |
| |
| SVN_ERR(make_file(B2_path, B2.data, pool)); |
| SVN_ERR(make_file(T1_path, T1.data, pool)); |
| SVN_ERR(make_file(T2_path, T2.data, pool)); |
| SVN_ERR(make_file(T3_path, T3.data, pool)); |
| |
| /* Usage: tools/diff/diff4 <mine> <older> <yours> <ancestor> */ |
| /* tools/diff/diff4 B2 T2 T3 T1 > B2new */ |
| SVN_ERR(svn_diff_file_diff4(&diff, T2_path, B2_path, T3_path, T1_path, pool)); |
| |
| /* Sanity. */ |
| SVN_TEST_ASSERT(! svn_diff_contains_conflicts(diff)); |
| SVN_TEST_ASSERT(svn_diff_contains_diffs(diff)); |
| |
| /* Comparison. */ |
| expected = svn_stream_from_string(&B2new, pool); |
| |
| actual = svn_stream_from_stringbuf( |
| svn_stringbuf_create_ensure(417, pool), /* 417 == wc -c < B2new */ |
| pool); |
| SVN_ERR(svn_diff_file_output_merge(actual, diff, |
| T2_path, B2_path, T3_path, |
| NULL, NULL, NULL, NULL, |
| FALSE, |
| FALSE, |
| pool)); |
| SVN_ERR(svn_stream_contents_same2(&same, actual, expected, pool)); |
| SVN_TEST_ASSERT(same); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| static svn_error_t * |
| random_trivial_merge(apr_pool_t *pool) |
| { |
| int i; |
| apr_pool_t *subpool = svn_pool_create(pool); |
| |
| const char *base_filename1 = "trivial1"; |
| const char *base_filename2 = "trivial2"; |
| |
| const char *filename1 = svn_test_data_path(base_filename1, pool); |
| const char *filename2 = svn_test_data_path(base_filename2, pool); |
| |
| seed_val(); |
| |
| for (i = 0; i < 5; ++i) |
| { |
| int min_lines = 1000; |
| int max_lines = 1100; |
| int var_lines = 50; |
| int block_lines = 10; |
| svn_stringbuf_t *contents1, *contents2; |
| |
| SVN_ERR(make_random_file(filename1, |
| min_lines, max_lines, var_lines, block_lines, |
| i % 3, subpool)); |
| SVN_ERR(make_random_file(filename2, |
| min_lines, max_lines, var_lines, block_lines, |
| i % 2, subpool)); |
| |
| SVN_ERR(svn_stringbuf_from_file2(&contents1, filename1, subpool)); |
| SVN_ERR(svn_stringbuf_from_file2(&contents2, filename2, subpool)); |
| |
| SVN_ERR(three_way_merge(base_filename1, base_filename2, base_filename1, |
| contents1->data, contents2->data, |
| contents1->data, contents2->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| SVN_ERR(three_way_merge(base_filename2, base_filename1, base_filename2, |
| contents2->data, contents1->data, |
| contents2->data, contents1->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| svn_pool_clear(subpool); |
| } |
| svn_pool_destroy(subpool); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| /* The "original" file has a number of distinct lines. We generate two |
| random modifications by selecting two subsets of the original lines and |
| for each selected line either adding an additional line, replacing the |
| line, or deleting the line. The two subsets are chosen so that each |
| selected line is distinct and no two selected lines are adjacent. This |
| means the two sets of changes should merge without conflict. */ |
| static svn_error_t * |
| random_three_way_merge(apr_pool_t *pool) |
| { |
| int i; |
| apr_pool_t *subpool = svn_pool_create(pool); |
| |
| const char *base_filename1 = "original"; |
| const char *base_filename2 = "modified1"; |
| const char *base_filename3 = "modified2"; |
| const char *base_filename4 = "combined"; |
| |
| const char *filename1 = svn_test_data_path(base_filename1, pool); |
| const char *filename2 = svn_test_data_path(base_filename2, pool); |
| const char *filename3 = svn_test_data_path(base_filename3, pool); |
| const char *filename4 = svn_test_data_path(base_filename4, pool); |
| |
| seed_val(); |
| |
| for (i = 0; i < 20; ++i) |
| { |
| svn_stringbuf_t *original, *modified1, *modified2, *combined; |
| /* Pick NUM_LINES large enough so that the 'strip identical suffix' code |
| gets triggered with reasonable probability. (Currently it ignores |
| 50 lines or more, and empirically N=4000 suffices to trigger that |
| behaviour most of the time.) */ |
| int num_lines = 4000, num_src = 10, num_dst = 10; |
| svn_boolean_t *lines = apr_pcalloc(subpool, sizeof(*lines) * num_lines); |
| struct random_mod *src_lines = apr_palloc(subpool, |
| sizeof(*src_lines) * num_src); |
| struct random_mod *dst_lines = apr_palloc(subpool, |
| sizeof(*dst_lines) * num_dst); |
| struct random_mod *mrg_lines = apr_palloc(subpool, |
| (sizeof(*mrg_lines) |
| * (num_src + num_dst))); |
| |
| select_lines(src_lines, num_src, lines, num_lines); |
| select_lines(dst_lines, num_dst, lines, num_lines); |
| memcpy(mrg_lines, src_lines, sizeof(*mrg_lines) * num_src); |
| memcpy(mrg_lines + num_src, dst_lines, sizeof(*mrg_lines) * num_dst); |
| |
| SVN_ERR(make_random_merge_file(filename1, num_lines, NULL, 0, pool)); |
| SVN_ERR(make_random_merge_file(filename2, num_lines, src_lines, num_src, |
| pool)); |
| SVN_ERR(make_random_merge_file(filename3, num_lines, dst_lines, num_dst, |
| pool)); |
| SVN_ERR(make_random_merge_file(filename4, num_lines, mrg_lines, |
| num_src + num_dst, pool)); |
| |
| SVN_ERR(svn_stringbuf_from_file2(&original, filename1, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&modified1, filename2, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&modified2, filename3, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&combined, filename4, pool)); |
| |
| SVN_ERR(three_way_merge(base_filename1, base_filename2, base_filename3, |
| original->data, modified1->data, |
| modified2->data, combined->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| SVN_ERR(three_way_merge(base_filename1, base_filename3, base_filename2, |
| original->data, modified2->data, |
| modified1->data, combined->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| |
| SVN_ERR(svn_io_remove_file2(filename4, TRUE, pool)); |
| |
| svn_pool_clear(subpool); |
| } |
| svn_pool_destroy(subpool); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* This is similar to random_three_way_merge above, except this time half |
| of the original-to-modified1 changes are already present in modified2 |
| (or, equivalently, half the original-to-modified2 changes are already |
| present in modified1). Since the overlapping changes match exactly the |
| merge should work without a conflict. */ |
| static svn_error_t * |
| merge_with_part_already_present(apr_pool_t *pool) |
| { |
| int i; |
| apr_pool_t *subpool = svn_pool_create(pool); |
| |
| const char *base_filename1 = "pap-original"; |
| const char *base_filename2 = "pap-modified1"; |
| const char *base_filename3 = "pap-modified2"; |
| const char *base_filename4 = "pap-combined"; |
| |
| const char *filename1 = svn_test_data_path(base_filename1, pool); |
| const char *filename2 = svn_test_data_path(base_filename2, pool); |
| const char *filename3 = svn_test_data_path(base_filename3, pool); |
| const char *filename4 = svn_test_data_path(base_filename4, pool); |
| |
| seed_val(); |
| |
| for (i = 0; i < 20; ++i) |
| { |
| svn_stringbuf_t *original, *modified1, *modified2, *combined; |
| int num_lines = 200, num_src = 20, num_dst = 20; |
| svn_boolean_t *lines = apr_pcalloc(subpool, sizeof(*lines) * num_lines); |
| struct random_mod *src_lines = apr_palloc(subpool, |
| sizeof(*src_lines) * num_src); |
| struct random_mod *dst_lines = apr_palloc(subpool, |
| sizeof(*dst_lines) * num_dst); |
| struct random_mod *mrg_lines = apr_palloc(subpool, |
| (sizeof(*mrg_lines) |
| * (num_src + num_dst / 2))); |
| |
| select_lines(src_lines, num_src, lines, num_lines); |
| /* Select half the destination changes at random */ |
| select_lines(dst_lines, num_dst / 2, lines, num_lines); |
| /* Copy the other half from the source changes */ |
| memcpy(dst_lines + num_dst / 2, src_lines, |
| sizeof(*dst_lines) * (num_dst - num_dst / 2)); |
| memcpy(mrg_lines, src_lines, sizeof(*mrg_lines) * num_src); |
| memcpy(mrg_lines + num_src, dst_lines, |
| sizeof(*mrg_lines) * num_dst / 2); |
| |
| SVN_ERR(make_random_merge_file(filename1, num_lines, NULL, 0, pool)); |
| SVN_ERR(make_random_merge_file(filename2, num_lines, src_lines, num_src, |
| pool)); |
| SVN_ERR(make_random_merge_file(filename3, num_lines, dst_lines, num_dst, |
| pool)); |
| SVN_ERR(make_random_merge_file(filename4, num_lines, mrg_lines, |
| num_src + num_dst / 2, pool)); |
| |
| SVN_ERR(svn_stringbuf_from_file2(&original, filename1, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&modified1, filename2, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&modified2, filename3, pool)); |
| SVN_ERR(svn_stringbuf_from_file2(&combined, filename4, pool)); |
| |
| SVN_ERR(three_way_merge(base_filename1, base_filename2, base_filename3, |
| original->data, modified1->data, |
| modified2->data, combined->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| SVN_ERR(three_way_merge(base_filename1, base_filename3, base_filename2, |
| original->data, modified2->data, |
| modified1->data, combined->data, NULL, |
| svn_diff_conflict_display_modified_latest, |
| subpool)); |
| |
| SVN_ERR(svn_io_remove_file2(filename4, TRUE, pool)); |
| |
| svn_pool_clear(subpool); |
| } |
| svn_pool_destroy(subpool); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Merge is more "aggressive" about resolving conflicts than traditional |
| * patch or diff3. Some people consider this behaviour to be a bug, see |
| * http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=35014 |
| */ |
| static svn_error_t * |
| merge_adjacent_changes(apr_pool_t *pool) |
| { |
| SVN_ERR(three_way_merge("adj1", "adj2", "adj3", |
| |
| "foo\n" |
| "bar\n" |
| "baz\n", |
| |
| "foo\n" |
| "new_bar\n" |
| "baz\n", |
| |
| "zig\n" |
| "foo\n" |
| "bar\n" |
| "new_baz\n", |
| |
| "zig\n" |
| "foo\n" |
| "new_bar\n" |
| "new_baz\n", |
| |
| NULL, |
| svn_diff_conflict_display_modified_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Issue #4133, 'When sequences of whitespace characters at head of line |
| strides chunk boundary, "diff -x -w" showing wrong change'. |
| The magic number used in this test, 1<<17, is |
| CHUNK_SIZE from ../../libsvn_diff/diff_file.c |
| */ |
| static svn_error_t * |
| test_norm_offset(apr_pool_t *pool) |
| { |
| apr_size_t chunk_size = 1 << 17; |
| const char *pattern1 = " \n"; |
| const char *pattern2 = "\n\n\n\n\n\n\n\n"; |
| const char *pattern3 = " @@@@@@@\n"; |
| const char *pattern4 = " \n"; |
| svn_stringbuf_t *original, *modified; |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| /* The original contents become like this |
| |
| $ hexdump -C norm-offset-original |
| 00000000 20 20 20 20 20 20 20 0a 0a 0a 0a 0a 0a 0a 0a 0a | .........| |
| 00000010 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a |................| |
| * |
| 0001fff0 0a 0a 0a 0a 0a 0a 0a 0a 20 20 20 20 20 20 20 20 |........ | |
| 00020000 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | |
| 00020010 40 40 40 40 40 40 40 0a 0a 0a 0a 0a 0a 0a 0a 0a |@@@@@@@.........| |
| 00020020 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a |................| |
| * |
| 000203f0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0a | .| |
| 00020400 |
| */ |
| original = svn_stringbuf_create_ensure(chunk_size + 1024, pool); |
| svn_stringbuf_appendcstr(original, pattern1); |
| while (original->len < chunk_size - 8) |
| { |
| svn_stringbuf_appendcstr(original, pattern2); |
| } |
| svn_stringbuf_appendcstr(original, pattern3); |
| while (original->len < chunk_size +1024 - 16) |
| { |
| svn_stringbuf_appendcstr(original, pattern2); |
| } |
| svn_stringbuf_appendcstr(original, pattern4); |
| |
| /* The modified contents become like this. |
| |
| $ hexdump -C norm-offset-modified |
| 00000000 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 0a | .| |
| 00000010 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a |................| |
| * |
| 00020000 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | |
| 00020010 20 20 20 20 20 20 20 20 40 40 40 40 40 40 40 0a | @@@@@@@.| |
| 00020020 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a 0a |................| |
| * |
| 000203f0 0a 0a 0a 0a 0a 0a 0a 0a 20 20 20 20 20 20 20 0a |........ .| |
| 00020400 |
| */ |
| modified = svn_stringbuf_create_ensure(chunk_size + 1024, pool); |
| svn_stringbuf_appendcstr(modified, pattern4); |
| while (modified->len < chunk_size) |
| { |
| svn_stringbuf_appendcstr(modified, pattern2); |
| } |
| svn_stringbuf_appendcstr(modified, pattern3); |
| while (modified->len < chunk_size +1024 - 8) |
| { |
| svn_stringbuf_appendcstr(modified, pattern2); |
| } |
| svn_stringbuf_appendcstr(modified, pattern1); |
| |
| /* Diff them. Modulo whitespace, they are identical. */ |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| SVN_ERR(two_way_diff("norm-offset-original", "norm-offset-modified", |
| original->data, modified->data, "", |
| diff_opts, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* Issue #4283, 'When identical suffix started at a chunk boundary, |
| incorrect diff was generated'. |
| The magic number used in this test, (1<<17) and 50 are CHUNK_SIZE |
| and SUFFIX_LINES_TO_KEEP from ../../libsvn_diff/diff_file.c, respectively. |
| */ |
| #define ORIGINAL_CONTENTS_PATTERN "0123456789abcde\n" |
| #define INSERTED_LINE "0123456789ABCDE\n" |
| static svn_error_t * |
| test_identical_suffix(apr_pool_t *pool) |
| { |
| apr_size_t lines_in_chunk = (1 << 17) |
| / (sizeof(ORIGINAL_CONTENTS_PATTERN) - 1); |
| /* To let identical suffix start at a chunk boundary, |
| insert a line at before (SUFFIX_LINES_TO_KEEP + 1) lines |
| from tail of the previous chunk. */ |
| apr_size_t insert_pos = lines_in_chunk |
| #ifdef SUFFIX_LINES_TO_KEEP |
| - SUFFIX_LINES_TO_KEEP |
| #else |
| - 50 |
| #endif |
| - 1; |
| apr_size_t i; |
| svn_stringbuf_t *original, *modified; |
| |
| /* The original contents become like this. |
| |
| $ hexdump -C identical-suffix-original |
| 00000000 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a |0123456789abcde.| |
| * |
| 00020400 |
| */ |
| original = svn_stringbuf_create_ensure((1 << 17) + 1024, pool); |
| for (i = 0; i < lines_in_chunk + 64; i++) |
| { |
| svn_stringbuf_appendbytes(original, ORIGINAL_CONTENTS_PATTERN, |
| sizeof(ORIGINAL_CONTENTS_PATTERN) - 1); |
| } |
| |
| /* The modified contents become like this. |
| |
| $ hexdump -C identical-suffix-modified |
| 00000000 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a |0123456789abcde.| |
| * |
| 00000400 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 0a |0123456789ABCDE.| |
| 00000410 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a |0123456789abcde.| |
| * |
| 0001fcd0 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 0a |0123456789ABCDE.| |
| 0001fce0 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a |0123456789abcde.| |
| * |
| 00020420 |
| */ |
| modified = svn_stringbuf_dup(original, pool); |
| svn_stringbuf_insert(modified, |
| 64 * (sizeof(ORIGINAL_CONTENTS_PATTERN) - 1), |
| INSERTED_LINE, sizeof(INSERTED_LINE) - 1); |
| svn_stringbuf_insert(modified, |
| insert_pos * (sizeof(ORIGINAL_CONTENTS_PATTERN) - 1), |
| INSERTED_LINE, sizeof(INSERTED_LINE) - 1); |
| |
| SVN_ERR(two_way_diff("identical-suffix-original", |
| "identical-suffix-modified", |
| original->data, modified->data, |
| apr_psprintf(pool, |
| "--- identical-suffix-original" NL |
| "+++ identical-suffix-modified" NL |
| "@@ -62,6 +62,7 @@" NL |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| "+" INSERTED_LINE |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| "@@ -%u,6 +%u,7 @@" NL |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| "+" INSERTED_LINE |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN |
| " " ORIGINAL_CONTENTS_PATTERN, |
| 1 + (unsigned int)insert_pos - 3 - 1, |
| 1 + (unsigned int)insert_pos - 3), |
| NULL, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| #undef ORIGINAL_CONTENTS_PATTERN |
| #undef INSERTED_LINE |
| |
| /* The magic number used in this test, 1<<17, is |
| CHUNK_SIZE from ../../libsvn_diff/diff_file.c |
| */ |
| static svn_error_t * |
| test_token_compare(apr_pool_t *pool) |
| { |
| apr_size_t chunk_size = 1 << 17; |
| const char *pattern = "ABCDEFG\n"; |
| svn_stringbuf_t *original, *modified; |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| diff_opts->ignore_space = svn_diff_file_ignore_space_all; |
| |
| original = svn_stringbuf_create_ensure(chunk_size * 2 + 8, pool); |
| /* CHUNK_SIZE bytes */ |
| while (original->len < chunk_size - 8) |
| { |
| svn_stringbuf_appendcstr(original, pattern); |
| } |
| svn_stringbuf_appendcstr(original, " @@@\n"); |
| |
| modified = svn_stringbuf_create_ensure(chunk_size * 2 + 9, pool); |
| /* CHUNK_SIZE+1 bytes, one ' ' more than original */ |
| while (modified->len < chunk_size - 8) |
| { |
| svn_stringbuf_appendcstr(modified, pattern); |
| } |
| svn_stringbuf_appendcstr(modified, " @@@\n"); |
| |
| /* regression test for reading exceeding the file size */ |
| SVN_ERR(two_way_diff("token-compare-original1", "token-compare-modified1", |
| original->data, modified->data, "", |
| diff_opts, pool)); |
| |
| svn_stringbuf_appendcstr(original, "aaaaaaa\n"); |
| svn_stringbuf_appendcstr(modified, "bbbbbbb\n"); |
| |
| /* regression test for comparison beyond the end-of-line */ |
| SVN_ERR(two_way_diff("token-compare-original2", "token-compare-modified2", |
| original->data, modified->data, |
| apr_psprintf(pool, |
| "--- token-compare-original2" NL |
| "+++ token-compare-modified2" NL |
| "@@ -%u,4 +%u,4 @@" NL |
| " ABCDEFG\n" |
| " ABCDEFG\n" |
| " @@@\n" |
| "-aaaaaaa\n" |
| "+bbbbbbb\n", |
| (unsigned int)chunk_size/8 - 2, |
| (unsigned int)chunk_size/8 - 2), |
| diff_opts, pool)); |
| |
| /* CHUNK_SIZE*2 bytes */ |
| while (original->len <= chunk_size * 2 - 8) |
| { |
| svn_stringbuf_appendcstr(original, pattern); |
| } |
| |
| /* CHUNK_SIZE*2+1 bytes, one ' ' more than original */ |
| while (modified->len <= chunk_size * 2 - 7) |
| { |
| svn_stringbuf_appendcstr(modified, pattern); |
| } |
| |
| SVN_ERR(two_way_diff("token-compare-original2", "token-compare-modified2", |
| original->data, modified->data, |
| apr_psprintf(pool, |
| "--- token-compare-original2" NL |
| "+++ token-compare-modified2" NL |
| "@@ -%u,7 +%u,7 @@" NL |
| " ABCDEFG\n" |
| " ABCDEFG\n" |
| " @@@\n" |
| "-aaaaaaa\n" |
| "+bbbbbbb\n" |
| " ABCDEFG\n" |
| " ABCDEFG\n" |
| " ABCDEFG\n", |
| (unsigned int)chunk_size/8 - 2, |
| (unsigned int)chunk_size/8 - 2), |
| diff_opts, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| two_way_issue_3362_v1(apr_pool_t *pool) |
| { |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| SVN_ERR(two_way_diff("issue-3362-1-v1", |
| "issue-3362-2-v1", |
| /* File 1 */ |
| "line_1\n" |
| "line_2\n" |
| "line_3\n" |
| "line_4\n" |
| "line_5\n" |
| "line_6\n" |
| "line_7\n" |
| "line_8\n" |
| "line_9\n" |
| "line_10\n" |
| "line_11\n" |
| "line_12\n" |
| "line_13\n" |
| "line_14\n" |
| "line_15\n" |
| "line_16\n" |
| "line_17\n" |
| "line_18\n" |
| "line_19\n" |
| "line_20\n" |
| "line_21\n" |
| "line_22\n" |
| "line_23\n" |
| "line_24\n" |
| "line_25\n" |
| "line_26\n" |
| "line_27\n" |
| "line_28\n" |
| "line_29\n" |
| "line_30\n", |
| /* File 2 */ |
| "line_1a\n" |
| "line_2a\n" |
| "line_3a\n" |
| "line_1\n" |
| "line_2\n" |
| "line_3\n" |
| "line_4\n" |
| "line_5a\n" |
| "line_6b\n" |
| "line_7c\n" |
| "line_8\n" |
| "line_9\n" |
| "line_10\n" |
| "line_11a\n" |
| "line_11b\n" |
| "line_11c\n" |
| "line_12\n" |
| "line_13\n" |
| "line_14\n" |
| "line_15\n" |
| "line_16\n" |
| "line_17\n" |
| "line_18\n" |
| "line_19a\n" |
| "line_19b\n" |
| "line_19c\n" |
| "line_20\n" |
| "line_21\n" |
| "line_22\n" |
| "line_23\n" |
| "line_24\n" |
| "line_25\n" |
| "line_26\n" |
| "line_27\n" |
| "line_27a\n", |
| /* Expected */ |
| "--- issue-3362-1-v1" APR_EOL_STR |
| "+++ issue-3362-2-v1" APR_EOL_STR |
| "@@ -1,14 +1,19 @@" APR_EOL_STR |
| "+line_1a\n" |
| "+line_2a\n" |
| "+line_3a\n" |
| " line_1\n" /* 1.7 mem diff: line missing */ |
| " line_2\n" |
| " line_3\n" |
| " line_4\n" |
| "-line_5\n" |
| "-line_6\n" |
| "-line_7\n" |
| "+line_5a\n" |
| "+line_6b\n" |
| "+line_7c\n" |
| " line_8\n" |
| " line_9\n" |
| " line_10\n" |
| "-line_11\n" |
| "+line_11a\n" |
| "+line_11b\n" |
| "+line_11c\n" |
| " line_12\n" |
| " line_13\n" |
| " line_14\n" /* 1.7 mem diff: line missing */ |
| "@@ -16,7 +21,9 @@" APR_EOL_STR |
| " line_16\n" |
| " line_17\n" |
| " line_18\n" |
| "-line_19\n" |
| "+line_19a\n" |
| "+line_19b\n" |
| "+line_19c\n" |
| " line_20\n" |
| " line_21\n" |
| " line_22\n" |
| "@@ -25,6 +32,4 @@" APR_EOL_STR |
| " line_25\n" |
| " line_26\n" |
| " line_27\n" |
| "-line_28\n" |
| "-line_29\n" |
| "-line_30\n" |
| "+line_27a\n", |
| diff_opts, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| two_way_issue_3362_v2(apr_pool_t *pool) |
| { |
| svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); |
| |
| SVN_ERR(two_way_diff("issue-3362-1-v2", |
| "issue-3362-2-v2", |
| /* File 1 */ |
| "line_1\n" |
| "line_2\n" |
| "line_3\n" |
| "line_4\n" |
| "line_5\n" |
| "line_6\n" |
| "line_7\n" |
| "line_8\n" |
| "line_9\n" |
| "line_10\n" |
| "line_11\n" |
| "line_12\n" |
| "line_13\n" |
| "line_14\n" |
| "line_15\n" |
| "line_16\n" |
| "line_17\n" |
| "line_18\n" |
| "line_19\n" |
| "line_20\n" |
| "line_21\n" |
| "line_22\n" |
| "line_23\n" |
| "line_24\n" |
| "line_25\n" |
| "line_26\n" |
| "line_27\n" |
| "line_28\n" |
| "line_29\n" |
| "line_30\n", |
| /* File 2 */ |
| "line_1a\n" |
| "line_1b\n" |
| "line_1c\n" |
| "line_1\n" |
| "line_2\n" |
| "line_3\n" |
| "line_4\n" |
| "line_5a\n" |
| "line_5b\n" |
| "line_5c\n" |
| "line_6\n" |
| "line_7\n" |
| "line_8\n" |
| "line_9\n" |
| "line_10\n" |
| "line_11a\n" |
| "line_11b\n" |
| "line_11c\n" |
| "line_12\n" |
| "line_13\n" |
| "line_14\n" |
| "line_15\n" |
| "line_16\n" |
| "line_17\n" |
| "line_18\n" |
| "line_19a\n" |
| "line_19b\n" |
| "line_19c\n" |
| "line_20\n" |
| "line_21\n" |
| "line_22\n" |
| "line_23\n" |
| "line_24\n" |
| "line_25\n" |
| "line_26\n" |
| "line_27a\n" |
| "line_27b\n" |
| "line_27c\n" |
| "line_28\n" |
| "line_29\n" |
| "line_30\n", |
| /* Expected */ |
| "--- issue-3362-1-v2" APR_EOL_STR |
| "+++ issue-3362-2-v2" APR_EOL_STR |
| "@@ -1,14 +1,21 @@" APR_EOL_STR |
| "+line_1a\n" |
| "+line_1b\n" |
| "+line_1c\n" |
| " line_1\n" /* 1.7 mem diff: line missing */ |
| " line_2\n" |
| " line_3\n" |
| " line_4\n" |
| "-line_5\n" |
| "+line_5a\n" |
| "+line_5b\n" |
| "+line_5c\n" |
| " line_6\n" |
| " line_7\n" |
| " line_8\n" |
| " line_9\n" |
| " line_10\n" |
| "-line_11\n" |
| "+line_11a\n" |
| "+line_11b\n" |
| "+line_11c\n" |
| " line_12\n" |
| " line_13\n" |
| " line_14\n" /* 1.7 mem diff: line missing */ |
| "@@ -16,7 +23,9 @@" APR_EOL_STR |
| " line_16\n" |
| " line_17\n" |
| " line_18\n" |
| "-line_19\n" |
| "+line_19a\n" |
| "+line_19b\n" |
| "+line_19c\n" |
| " line_20\n" |
| " line_21\n" |
| " line_22\n" |
| "@@ -24,7 +33,9 @@" APR_EOL_STR |
| " line_24\n" |
| " line_25\n" |
| " line_26\n" |
| "-line_27\n" |
| "+line_27a\n" |
| "+line_27b\n" |
| "+line_27c\n" |
| " line_28\n" |
| " line_29\n" |
| " line_30\n", |
| diff_opts, pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| static svn_error_t * |
| three_way_double_add(apr_pool_t *pool) |
| { |
| SVN_ERR(three_way_merge("doubleadd1", "doubleadd2", "doubleadd3", |
| "A\n" |
| "B\n" |
| "C\n" |
| "J\n" |
| "K\n" |
| "L", |
| |
| "A\n" |
| "B\n" |
| "C\n" |
| "D\n" /* New line 1a */ |
| "E\n" /* New line 2a */ |
| "F\n" /* New line 3a*/ |
| "J\n" |
| "K\n" |
| "L", |
| |
| "A\n" |
| "B\n" |
| "O\n" /* Change C to O */ |
| "P\n" /* New line 1b */ |
| "Q\n" /* New line 2b */ |
| "R\n" /* New line 3b */ |
| "J\n" |
| "K\n" |
| "L", |
| |
| /* With s/C/O/ we expect something like this, |
| but the current (1.9/trunk) result is a |
| succeeded merge to a combined result. |
| |
| ### I'm guessing this result needs tweaks before it |
| will be a PASS. */ |
| "A\n" |
| "B\n" |
| "<<<<<<< doubleadd2\n" |
| "C\n" |
| "D\n" /* New line 1a */ |
| "E\n" /* New line 2a */ |
| "F\n" /* New line 3a*/ |
| "=======\n" |
| "O\n" |
| "P\n" /* New line 1b */ |
| "Q\n" /* New line 2b */ |
| "R\n" /* New line 3b */ |
| ">>>>>>> doubleadd3\n" |
| "J\n" |
| "K\n" |
| "L", |
| NULL, |
| svn_diff_conflict_display_modified_original_latest, |
| pool)); |
| |
| SVN_ERR(three_way_merge("doubleadd1", "doubleadd2", "doubleadd3", |
| "A\n" |
| "B\n" |
| "C\n" |
| "J\n" |
| "K\n" |
| "L", |
| |
| "A\n" |
| "B\n" |
| "C\n" |
| "D\n" /* New line 1a */ |
| "E\n" /* New line 2a */ |
| "F\n" /* New line 3a*/ |
| "K\n" |
| "L", |
| |
| "A\n" |
| "B\n" |
| "O\n" /* Change C to O */ |
| "P\n" /* New line 1b */ |
| "Q\n" /* New line 2b */ |
| "R\n" /* New line 3b */ |
| "J\n" |
| "K\n" |
| "L", |
| |
| /* With s/C/O/ we expect something like this, |
| but the current (1.9/trunk) result is a |
| succeeded merge to a combined result. |
| |
| ### I'm guessing this result needs tweaks before it |
| will be a PASS. */ |
| "A\n" |
| "B\n" |
| "<<<<<<< doubleadd2\n" |
| "C\n" |
| "D\n" /* New line 1a */ |
| "E\n" /* New line 2a */ |
| "F\n" /* New line 3a*/ |
| "=======\n" |
| "O\n" |
| "P\n" /* New line 1b */ |
| "Q\n" /* New line 2b */ |
| "R\n" /* New line 3b */ |
| "J\n" |
| ">>>>>>> doubleadd3\n" |
| "K\n" |
| "L", |
| NULL, |
| svn_diff_conflict_display_modified_original_latest, |
| pool)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| /* ========================================================================== */ |
| |
| |
| static int max_threads = 4; |
| |
| static struct svn_test_descriptor_t test_funcs[] = |
| { |
| SVN_TEST_NULL, |
| SVN_TEST_PASS2(dump_core, |
| "these dump core"), |
| SVN_TEST_PASS2(test_two_way_unified, |
| "2-way unified diff and trivial merge"), |
| SVN_TEST_PASS2(test_two_way_unified_suspect, |
| "2-way unified diff where output is suspect"), |
| SVN_TEST_PASS2(test_three_way_merge_no_overlap, |
| "3-way merge, non-overlapping changes"), |
| SVN_TEST_PASS2(test_three_way_merge_with_overlap, |
| "3-way merge, non-conflicting overlapping changes"), |
| SVN_TEST_PASS2(test_three_way_merge_with_conflict, |
| "3-way merge, conflicting overlapping changes"), |
| SVN_TEST_PASS2(random_trivial_merge, |
| "random trivial merge"), |
| SVN_TEST_PASS2(random_three_way_merge, |
| "random 3-way merge"), |
| SVN_TEST_PASS2(merge_with_part_already_present, |
| "merge with part already present"), |
| SVN_TEST_PASS2(merge_adjacent_changes, |
| "3-way merge, adjacent changes"), |
| SVN_TEST_PASS2(test_three_way_merge_conflict_styles, |
| "3-way merge with conflict styles"), |
| SVN_TEST_PASS2(test_diff4, |
| "4-way merge; see variance-adjusted-patching.html"), |
| SVN_TEST_PASS2(test_norm_offset, |
| "offset of the normalized token"), |
| SVN_TEST_PASS2(test_identical_suffix, |
| "identical suffix starts at the boundary of a chunk"), |
| SVN_TEST_PASS2(test_token_compare, |
| "compare tokens at the chunk boundary"), |
| SVN_TEST_PASS2(two_way_issue_3362_v1, |
| "2-way issue #3362 test v1"), |
| SVN_TEST_PASS2(two_way_issue_3362_v2, |
| "2-way issue #3362 test v2"), |
| SVN_TEST_XFAIL2(three_way_double_add, |
| "3-way merge, double add"), |
| SVN_TEST_NULL |
| }; |
| |
| SVN_TEST_MAIN |