📄 diff-diff3-test.c
字号:
/* * Incomplete regression tests for the diff/diff3 library. * * ==================================================================== * Copyright (c) 2003-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include "svn_diff.h"#include "svn_pools.h"#include "../svn_test.h"/* 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_tseed_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_trange_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; apr_status_t status; int num_lines; num_lines = range_rand(min_lines, max_lines); status = apr_file_open(&file, filename, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, pool); if (status) return svn_error_createf(status, NULL, "failed to open '%s'", filename); 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); } status = apr_file_close(file); if (status) return svn_error_createf(status, NULL, "failed to close '%s'", filename); 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; status = apr_file_open(&file, filename, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, pool); if (status) return svn_error_createf(status, NULL, "failed to open '%s'", filename); status = apr_file_write_full(file, contents, strlen(contents), NULL); if (status) return svn_error_createf(status, NULL, "failed to write '%s'", filename); status = apr_file_close(file); if (status) return svn_error_createf(status, NULL, "failed to close '%s'", filename); 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". */static svn_error_t *three_way_merge(const char *filename1, const char *filename2, const char *filename3, const char *contents1, const char *contents2, const char *contents3, 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; apr_status_t status; svn_stringbuf_t *actual; char *merge_name = apr_psprintf(pool, "merge-%s-%s-%s", filename1, filename2, filename3); 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 ? options : svn_diff_file_options_create(pool), pool)); status = apr_file_open(&output, merge_name, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, pool); if (status) return svn_error_createf(status, NULL, "failed to open '%s'", merge_name); ostream = svn_stream_from_aprfile(output, pool); SVN_ERR(svn_diff_file_output_merge(ostream, diff, filename1, filename2, filename3, NULL, NULL, NULL, NULL, FALSE, FALSE, pool)); SVN_ERR(svn_stream_close(ostream)); status = apr_file_close(output); if (status) return svn_error_createf(status, NULL, "failed to close '%s'", merge_name); SVN_ERR(svn_stringbuf_from_file(&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'", filename1, filename2, filename3); SVN_ERR(svn_io_remove_file(filename1, pool)); if (strcmp(filename1, filename2)) SVN_ERR(svn_io_remove_file(filename2, pool)); if (strcmp(filename1, filename3) && strcmp(filename2, filename3)) SVN_ERR(svn_io_remove_file(filename3, pool)); SVN_ERR(svn_io_remove_file(merge_name, 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 *filename1, const char *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; apr_status_t status; svn_stringbuf_t *actual; char *diff_name = apr_psprintf(pool, "diff-%s-%s", filename1, filename2); 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 ? options : svn_diff_file_options_create(pool), pool)); status = apr_file_open(&output, diff_name, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, pool); if (status) return svn_error_createf(status, NULL, "failed to open '%s'", diff_name); ostream = svn_stream_from_aprfile(output, pool); SVN_ERR(svn_diff_file_output_unified(ostream, diff, filename1, filename2, filename1, filename2, pool)); SVN_ERR(svn_stream_close(ostream)); status = apr_file_close(output); if (status) return svn_error_createf(status, NULL, "failed to close '%s'", diff_name); SVN_ERR(svn_stringbuf_from_file(&actual, diff_name, pool)); if (strcmp(actual->data, expected)) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "failed comparing '%s' and '%s'", filename1, filename2); /* May as well do the trivial merges while we are here */ SVN_ERR(three_way_merge(filename1, filename2, filename1, contents1, contents2, contents1, contents2, NULL, pool)); SVN_ERR(three_way_merge(filename2, filename1, filename2, contents2, contents1, contents2, contents1, NULL, pool)); SVN_ERR(svn_io_remove_file(diff_name, 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 voidselect_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; apr_status_t status; int i; status = apr_file_open(&file, filename, APR_WRITE | APR_CREATE | APR_TRUNCATE, APR_OS_DEFAULT, pool); if (status) return svn_error_createf(status, NULL, "failed to open '%s'", filename); 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); } } status = apr_file_close(file); if (status) return svn_error_createf(status, NULL, "failed to close '%s'", filename); return SVN_NO_ERROR;}/* ========================================================================== */static svn_error_t *dump_core(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool){ *msg = "these dump core"; if (msg_only) return SVN_NO_ERROR; SVN_ERR(two_way_diff("foo1", "bar1", "", "", "", NULL, pool)); SVN_ERR(two_way_diff("foo2", "bar2", "Aa\n" "Bb\n" "Cc\n", "", "--- foo2" APR_EOL_STR "+++ bar2" APR_EOL_STR "@@ -1,3 +0,0 @@" APR_EOL_STR "-Aa\n" "-Bb\n" "-Cc\n", NULL, pool)); SVN_ERR(two_way_diff("foo3", "bar3", "", "Aa\n" "Bb\n" "Cc\n", "--- foo3" APR_EOL_STR "+++ bar3" APR_EOL_STR "@@ -0,0 +1,3 @@" APR_EOL_STR "+Aa\n" "+Bb\n" "+Cc\n", NULL, pool)); return SVN_NO_ERROR;}static svn_error_t *test_two_way_unified(const char **msg, svn_boolean_t msg_only, svn_test_opts_t *opts, apr_pool_t *pool){ svn_diff_file_options_t *diff_opts = svn_diff_file_options_create(pool); *msg = "2-way unified diff and trivial merge"; if (msg_only) return SVN_NO_ERROR; SVN_ERR(two_way_diff("foo4", "bar4", "Aa\n", "Aa\n" "Bb\n" "Cc\n", "--- foo4" APR_EOL_STR "+++ bar4" APR_EOL_STR "@@ -1 +1,3 @@" APR_EOL_STR " Aa\n" "+Bb\n" "+Cc\n", NULL, pool)); SVN_ERR(two_way_diff("foo4b", "bar4b", "Cc\n", "Aa\n" "Bb\n" "Cc\n", "--- foo4b" APR_EOL_STR "+++ bar4b" APR_EOL_STR "@@ -1 +1,3 @@" APR_EOL_STR "+Aa\n" "+Bb\n" " Cc\n", NULL, pool)); diff_opts->ignore_eol_style = TRUE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -