Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/entries-dump.c Fri Jan 14 14:01:45 2022 @@ -23,6 +23,7 @@ #include <stdlib.h> #include <stdio.h> +#include <assert.h> #include <apr_pools.h> #include <apr_general.h> @@ -34,6 +35,7 @@ #include "svn_pools.h" #include "svn_wc.h" #include "svn_dirent_uri.h" +#include "svn_xml.h" #include "private/svn_wc_private.h" @@ -41,14 +43,61 @@ #include "../../libsvn_wc/lock.h" static void -str_value(const char *name, const char *value) +print_prefix(void) +{ + puts("if b'' == '':\n" + " def _to_str(s):\n" + " return s\n" + "else:\n" + " def _to_str(s):\n" + " return s.decode('utf-8', 'surrogateescape')\n" + "\n" + "class Entry(object):\n" + " \"\"\"An Entry object represents one node's entry in a pre-1.6" + " .svn/entries file.\n\n" + "Similar to #svn_wc_entry_t, but not all fields are populated.\n\n" + "Entry objects are generated by the 'entries-dump'" + " test helper tool.\"\"\"\n\n" + " if b'' == '':\n" + " def set_strval(self, name, val):\n" + " self.__setattr__(name, val)\n" + " else:\n" + " def set_strval(self, name, val):\n" + " global _to_str\n" + " self.__setattr__(name, _to_str(val))\n"); +} + +static void +print_as_bytes(const char *val) +{ + printf("b'"); + while(*val) + { + printf("\\x%02x", (unsigned int)(unsigned char)*val++); + } + printf("'"); +} + +static void +str_value(const char *name, const char *value, apr_pool_t *pool) { if (value == NULL) printf("e.%s = None\n", name); else - printf("e.%s = '%s'\n", name, value); -} + { + svn_stringbuf_t *escaped_value = NULL; + svn_xml_escape_attr_cstring(&escaped_value, value, pool); + /* Print the human-readable value. */ + assert(NULL == strchr(escaped_value->data, '\n')); + printf("# e.%s = '%s'\n", name, escaped_value->data); + + /* Print the machine-readable value. */ + printf("e.set_strval('%s', ", name); + print_as_bytes(value); + printf(")\n"); + } +} static void int_value(const char *name, long int value) @@ -76,6 +125,7 @@ entries_dump(const char *dir_path, svn_w svn_error_t *err; svn_wc_context_t *wc_ctx = NULL; const char *dir_abspath; + apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(svn_dirent_get_absolute(&dir_abspath, dir_path, pool)); @@ -124,44 +174,45 @@ entries_dump(const char *dir_path, svn_w for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) { + svn_stringbuf_t *escaped_key; const char *key = apr_hash_this_key(hi); const svn_wc_entry_t *entry = apr_hash_this_val(hi); + svn_pool_clear(iterpool); SVN_ERR_ASSERT(strcmp(key, entry->name) == 0); - printf("e = Entry()\n"); - str_value("name", entry->name); + str_value("name", entry->name, iterpool); int_value("revision", entry->revision); - str_value("url", entry->url); - str_value("repos", entry->repos); - str_value("uuid", entry->uuid); + str_value("url", entry->url, iterpool); + str_value("repos", entry->repos, iterpool); + str_value("uuid", entry->uuid, iterpool); int_value("kind", entry->kind); int_value("schedule", entry->schedule); bool_value("copied", entry->copied); bool_value("deleted", entry->deleted); bool_value("absent", entry->absent); bool_value("incomplete", entry->incomplete); - str_value("copyfrom_url", entry->copyfrom_url); + str_value("copyfrom_url", entry->copyfrom_url, iterpool); int_value("copyfrom_rev", entry->copyfrom_rev); - str_value("conflict_old", entry->conflict_old); - str_value("conflict_new", entry->conflict_new); - str_value("conflict_wrk", entry->conflict_wrk); - str_value("prejfile", entry->prejfile); + str_value("conflict_old", entry->conflict_old, iterpool); + str_value("conflict_new", entry->conflict_new, iterpool); + str_value("conflict_wrk", entry->conflict_wrk, iterpool); + str_value("prejfile", entry->prejfile, iterpool); /* skip: text_time */ /* skip: prop_time */ /* skip: checksum */ int_value("cmt_rev", entry->cmt_rev); /* skip: cmt_date */ - str_value("cmt_author", entry->cmt_author); - str_value("lock_token", entry->lock_token); - str_value("lock_owner", entry->lock_owner); - str_value("lock_comment", entry->lock_comment); + str_value("cmt_author", entry->cmt_author, iterpool); + str_value("lock_token", entry->lock_token, iterpool); + str_value("lock_owner", entry->lock_owner, iterpool); + str_value("lock_comment", entry->lock_comment, iterpool); /* skip: lock_creation_date */ /* skip: has_props */ /* skip: has_prop_mods */ /* skip: cachable_props */ /* skip: present_props */ - str_value("changelist", entry->changelist); + str_value("changelist", entry->changelist, iterpool); /* skip: working_size */ /* skip: keep_local */ int_value("depth", entry->depth); @@ -170,8 +221,17 @@ entries_dump(const char *dir_path, svn_w /* skip: file_external_peg_rev */ /* skip: file_external_rev */ bool_value("locked", locked && *entry->name == '\0'); - printf("entries['%s'] = e\n", (const char *)key); + /* Print the human-readable value. */ + escaped_key = NULL; + svn_xml_escape_attr_cstring(&escaped_key, key, iterpool); + assert(NULL == strchr(escaped_key->data, '\n')); + printf("# entries['%s'] = e\n", escaped_key->data); + /* Print the machine-readable value. */ + printf("entries[_to_str("); + print_as_bytes(key); + printf(")] = e\n"); } + svn_pool_destroy(iterpool); if (wc_ctx) SVN_ERR(svn_wc_context_destroy(wc_ctx)); @@ -282,6 +342,7 @@ tree_dump_dir(const char *local_abspath, { struct directory_walk_baton *bt = walk_baton; const char *path; + svn_stringbuf_t *escaped_path; if (kind != svn_node_dir) return SVN_NO_ERROR; @@ -307,7 +368,15 @@ tree_dump_dir(const char *local_abspath, printf("entries = {}\n"); SVN_ERR(entries_dump(path, bt->adm_access, scratch_pool)); - printf("dirs['%s'] = entries\n", path); + /* Print the human-readable value. */ + escaped_path = NULL; + svn_xml_escape_attr_cstring(&escaped_path, path, scratch_pool); + assert(NULL == strchr(escaped_path->data, '\n')); + printf("# dirs['%s'] = entries\n", escaped_path->data); + /* Print the machine-readable value. */ + printf("dirs[_to_str("); + print_as_bytes(path); + printf(")] = entries\n"); return SVN_NO_ERROR; } @@ -384,15 +453,25 @@ main(int argc, const char *argv[]) cmd = NULL; if (!cmd || !strcmp(cmd, "--entries")) - err = entries_dump(path, NULL, pool); + { + print_prefix(); + err = entries_dump(path, NULL, pool); + } else if (!strcmp(cmd, "--subdirs")) - err = directory_dump(path, pool); + { + err = directory_dump(path, pool); + } else if (!strcmp(cmd, "--tree-dump")) - err = tree_dump(path, pool); + { + print_prefix(); + err = tree_dump(path, pool); + } else - err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, - "Invalid command '%s'", - cmd); + { + err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, + "Invalid command '%s'", + cmd); + } if (err) { svn_handle_error2(err, stderr, FALSE, "entries-dump: ");
Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/export_tests.py Fri Jan 14 14:01:45 2022 @@ -1124,6 +1124,81 @@ def export_revision_with_root_relative_e expected_disk, '-r', 2) +def export_keyword_translation_inconsistent_eol(sbox): + "export keyword translation with inconsistent EOLs" + sbox.build(empty=True) + sbox.simple_mkdir('dir') + # Create a file with keywords and inconsistent EOLs, don't set svn:eol-style. + sbox.simple_add_text('$LastChangedRevision$\n\r\n', 'dir/file') + sbox.simple_propset('svn:keywords', 'LastChangedRevision', 'dir/file') + sbox.simple_commit() + + export_target = sbox.add_wc_path('export') + + expected_disk = svntest.wc.State('', { + 'dir' : Item(), + 'dir/file' : Item("$LastChangedRevision: 1 $\n\r\n"), + }) + + expected_output = svntest.wc.State(export_target, { + '' : Item(status='A '), + 'dir' : Item(status='A '), + 'dir/file' : Item(status='A ') + }) + + # We should be able to export without any unexpected errors. + svntest.actions.run_and_verify_export2(sbox.repo_url, + export_target, + expected_output, + expected_disk, + keep_eol_style=True) + +def export_working_copy_eol_translation(sbox): + "export working copy with EOL translation" + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_propset('svn:eol-style', 'CRLF', 'dir/file') + sbox.simple_commit() + + export_target = sbox.add_wc_path('export') + + expected_disk = svntest.wc.State('', { + 'dir' : Item(), + 'dir/file' : Item("test\r\n"), + }) + + expected_output = svntest.wc.State(export_target, { + 'dir' : Item(status='A '), + 'dir/file' : Item(status='A ') + }) + + svntest.actions.run_and_verify_export2(sbox.wc_dir, + export_target, + expected_output, + expected_disk, + keep_eol_style=True) + +def export_working_copy_inconsistent_eol(sbox): + "export working copy with inconsistent EOLs" + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_propset('svn:eol-style', 'CRLF', 'dir/file') + sbox.simple_commit() + + # Edit the file so that it would have inconsistent EOLs. + sbox.simple_append('dir/file', 'test\n\r\n', truncate=True) + + # Attempt to export the working copy, expect an error. + export_target = sbox.add_wc_path('export') + svntest.actions.run_and_verify_svn( + None, + "svn: E135000: Inconsistent line ending style\n", + 'export', + sbox.wc_dir, + export_target) + ######################################################################## # Run the tests @@ -1162,6 +1237,9 @@ test_list = [ None, export_file_external, export_file_externals2, export_revision_with_root_relative_external, + export_keyword_translation_inconsistent_eol, + export_working_copy_eol_translation, + export_working_copy_inconsistent_eol, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/externals_tests.py Fri Jan 14 14:01:45 2022 @@ -2946,7 +2946,7 @@ def url_to_wc_copy_of_externals(sbox): external_tau_path = os.path.join(wc_dir, "External-WC-to-URL-Copy", "external", "tau") expected_stdout = verify.UnorderedOutput([ - " U " + external_root_path + "\n", + "A " + external_root_path + "\n", "\n", "Fetching external item into '" + external_ex_path + "':\n", "A " + external_pi_path + "\n", @@ -2954,8 +2954,6 @@ def url_to_wc_copy_of_externals(sbox): "A " + external_tau_path + "\n", "Checked out external at revision 2.\n", "\n", - "Checked out revision 2.\n", - "A " + external_root_path + "\n" ]) exit_code, stdout, stderr = svntest.actions.run_and_verify_svn2( expected_stdout, [], 0, 'copy', repo_url + '/A/C', @@ -3913,7 +3911,7 @@ def copy_pin_externals_whitespace_dir(sb branches_url = repo_url + '/branches' trunk_wc = sbox.ospath('trunk') - # Create a new revision to creat interesting pinning revisions + # Create a new revision to create interesting pinning revisions sbox.simple_propset('A', 'B', 'trunk') sbox.simple_commit('trunk') Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests.py Fri Jan 14 14:01:45 2022 @@ -38,10 +38,6 @@ import svntest #---------------------------------------------------------------------- -# This directory contains all the expected output from svn. -getopt_output_dir = os.path.join(os.path.dirname(sys.argv[0]), - 'getopt_tests_data') - # Naming convention for golden files: take the svn command line as a # single string and apply the following sed transformations: # echo svn option1 option2 ... | sed -e 's/ /_/g' -e 's/_--/--/g' @@ -51,6 +47,10 @@ getopt_output_dir = os.path.join(os.path def load_expected_output(basename): "load the expected standard output and standard error" + # This directory contains all the expected output from svn. + getopt_output_dir = os.path.join(os.path.dirname(sys.argv[0]), + 'getopt_tests_data') + stdout_filename = os.path.join(getopt_output_dir, basename + '_stdout') stderr_filename = os.path.join(getopt_output_dir, basename + '_stderr') @@ -233,7 +233,7 @@ def getopt_config_option(sbox): expected_stderr = '.*W205000.*did you mean.*' expected_stdout = svntest.verify.AnyOutput svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr, 0, - 'info', + 'info', '--config-option', 'config:miscellanous:diff-extensions=' + '-u -p', Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout Fri Jan 14 14:01:45 2022 @@ -47,14 +47,6 @@ Available subcommands: unlock update (up) upgrade - x-shelf-diff - x-shelf-drop - x-shelf-list (x-shelves) - x-shelf-list-by-paths - x-shelf-log - x-shelf-save - x-shelve - x-unshelve Subversion is a tool for version control. For additional information, see http://subversion.apache.org/ Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version--verbose_stdout Fri Jan 14 14:01:45 2022 @@ -6,7 +6,7 @@ This software consists of contributions see the NOTICE file for more information. Subversion is open source software, see http://subversion.apache.org/ -Supported working copy (WC) versions: from 1.8 to 1.12 +Supported working copy (WC) versions: from 1.8 to 1.15 The following repository access (RA) modules are available: Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn--version_stdout Fri Jan 14 14:01:45 2022 @@ -6,7 +6,7 @@ This software consists of contributions see the NOTICE file for more information. Subversion is open source software, see http://subversion.apache.org/ -Supported working copy (WC) versions: from 1.8 to 1.12 +Supported working copy (WC) versions: from 1.8 to 1.15 The following repository access (RA) modules are available: Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout Fri Jan 14 14:01:45 2022 @@ -135,32 +135,10 @@ Valid options: to prevent shell expansion) --search-and ARG : combine ARG with the previous search pattern -Global options: - --username ARG : specify a username ARG - --password ARG : specify a password ARG (caution: on many operating - systems, other users will be able to see this) - --password-from-stdin : read password from stdin - --no-auth-cache : do not cache authentication tokens - --non-interactive : do no interactive prompting (default is to prompt - only if standard input is a terminal device) - --force-interactive : do interactive prompting even if standard input - is not a terminal device - --trust-server-cert : deprecated; same as - --trust-server-cert-failures=unknown-ca - --trust-server-cert-failures ARG : with --non-interactive, accept SSL server - certificates with failures; ARG is comma-separated - list of 'unknown-ca' (Unknown Authority), - 'cn-mismatch' (Hostname mismatch), 'expired' - (Expired certificate), 'not-yet-valid' (Not yet - valid certificate) and 'other' (all other not - separately classified certificate errors). - --config-dir ARG : read user configuration files from directory ARG - --config-option ARG : set user configuration option in the format: - FILE:SECTION:OPTION=[VALUE] - For example: - servers:global:http-library=serf +(Use '-v' to show global and experimental options.) -switch (sw): Update the working copy to a different URL within the same repository. +switch (sw): Update the working copy to a different URL within the same +repository. usage: 1. switch URL[@PEGREV] [PATH] 2. switch --relocate FROM-PREFIX TO-PREFIX [PATH...] @@ -196,9 +174,6 @@ usage: 1. switch URL[@PEGREV] [PATH] Examples: svn switch ^/branches/1.x-release - svn switch --relocate http:// svn:// - svn switch --relocate http://www.example.com/repo/project \ - svn://svn.example.com/repo/project Valid options: -r [--revision] ARG : ARG (some commands also take ARG1:ARG2 range) @@ -226,28 +201,5 @@ Valid options: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l', 'r') --relocate : deprecated; use 'svn relocate' -Global options: - --username ARG : specify a username ARG - --password ARG : specify a password ARG (caution: on many operating - systems, other users will be able to see this) - --password-from-stdin : read password from stdin - --no-auth-cache : do not cache authentication tokens - --non-interactive : do no interactive prompting (default is to prompt - only if standard input is a terminal device) - --force-interactive : do interactive prompting even if standard input - is not a terminal device - --trust-server-cert : deprecated; same as - --trust-server-cert-failures=unknown-ca - --trust-server-cert-failures ARG : with --non-interactive, accept SSL server - certificates with failures; ARG is comma-separated - list of 'unknown-ca' (Unknown Authority), - 'cn-mismatch' (Hostname mismatch), 'expired' - (Expired certificate), 'not-yet-valid' (Not yet - valid certificate) and 'other' (all other not - separately classified certificate errors). - --config-dir ARG : read user configuration files from directory ARG - --config-option ARG : set user configuration option in the format: - FILE:SECTION:OPTION=[VALUE] - For example: - servers:global:http-library=serf +(Use '-v' to show global and experimental options.) Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout Fri Jan 14 14:01:45 2022 @@ -47,14 +47,6 @@ Available subcommands: unlock update (up) upgrade - x-shelf-diff - x-shelf-drop - x-shelf-list (x-shelves) - x-shelf-list-by-paths - x-shelf-log - x-shelf-save - x-shelve - x-unshelve Subversion is a tool for version control. For additional information, see http://subversion.apache.org/ Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/info_tests.py Fri Jan 14 14:01:45 2022 @@ -716,6 +716,72 @@ def info_item_uncommmitted(sbox): sbox.ospath('newfile'), sbox.ospath('newdir')) +def info_item_size_wc_recursive(sbox): + "recursive '--show-item=repos-size' on local path" + + sbox.build(read_only=True) + + svntest.actions.run_and_verify_svn( + [], [], + 'info', '--show-item=repos-size', '--recursive', + sbox.ospath('')) + + +def info_item_size_repos(sbox): + "non-recursive '--show-item=repos-size' on URL" + + sbox.build(read_only=True) + + svntest.actions.run_and_verify_svn( + "25\n", [], + 'info', '--show-item=repos-size', + sbox.repo_url + "/iota") + + # Same, but without the newline. + svntest.actions.run_and_verify_svn( + "25", [], + 'info', '--show-item=repos-size', '--no-newline', + sbox.repo_url + "/iota") + + # Same, but with "human-readable" output. + svntest.actions.run_and_verify_svn( + "25 B", [], + 'info', '--show-item=repos-size', '--human-readable', + sbox.repo_url + "/iota") + + # No output when the URL is a directory. + svntest.actions.run_and_verify_svn( + [], [], + 'info', '--show-item=repos-size', + sbox.repo_url) + + +def info_item_size_repos_recursive(sbox): + "recursive '--show-item=repos-size' on dir URL" + + sbox.build(read_only=True) + + expected_output = svntest.verify.UnorderedOutput([ + "25 " + sbox.repo_url + "/iota\n", + "27 " + sbox.repo_url + "/A/B/lambda\n", + "25 " + sbox.repo_url + "/A/B/E/beta\n", + "26 " + sbox.repo_url + "/A/B/E/alpha\n", + "23 " + sbox.repo_url + "/A/mu\n", + "26 " + sbox.repo_url + "/A/D/gamma\n", + "23 " + sbox.repo_url + "/A/D/G/pi\n", + "24 " + sbox.repo_url + "/A/D/G/rho\n", + "24 " + sbox.repo_url + "/A/D/G/tau\n", + "26 " + sbox.repo_url + "/A/D/H/omega\n", + "24 " + sbox.repo_url + "/A/D/H/psi\n", + "24 " + sbox.repo_url + "/A/D/H/chi\n", + ]) + + svntest.actions.run_and_verify_svn( + expected_output, [], + 'info', '--show-item=repos-size', '--recursive', + sbox.repo_url) + + def info_item_failures(sbox): "failure modes of 'svn info --show-item'" @@ -746,6 +812,158 @@ def info_item_failures(sbox): 'info', '--show-item=revision', '--no-newline', sbox.ospath('A'), sbox.ospath('iota')) + svntest.actions.run_and_verify_svn( + None, (r".*E200007: can't show in-repository size.*"), + 'info', '--show-item=repos-size', + sbox.ospath('iota')) + + +@Issue(4837) +def info_file_in_file_replaced_dir(sbox): + "info, file in file-replaced dir" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('text\n', 'dir/file') + sbox.simple_commit(message='Add file') + + sbox.simple_copy('dir/file', 'file-moved') + sbox.simple_rm('dir') + sbox.simple_add_text('replaced\n', 'dir') + sbox.simple_commit(message='Replace dir with file') + + sbox.simple_update() + + expected = {'Relative URL' : r'\^/dir/file', + 'Node Kind' : 'file', + 'Revision': '1', + 'Last Changed Rev': '1', + } + + svntest.actions.run_and_verify_info([expected], + sbox.repo_url + '/dir/file@1') + +@Issue(4869) +def info_tree_conflict_source(sbox): + "info --xml: verify source-left, source-right" + + sbox.build() + wc_dir = sbox.wc_dir + + B_path = os.path.join(wc_dir, 'A', 'B') + lambda_path = os.path.join(B_path, 'lambda') + alpha_path = os.path.join(B_path, 'E', 'alpha') + F_path = os.path.join(B_path, 'F') + + B2_url = sbox.repo_url + '/A/B2' + B2_path = os.path.join(wc_dir, 'A', 'B2') + lambda2_path = os.path.join(B2_path, 'lambda') + + # Rev 2 copy B to B2 + sbox.simple_repo_copy('A/B', 'A/B2') + sbox.simple_update() + + # Rev 3: + # edit A/B/lambda to test text conflict case + # add property to A/B/E/alpha to test property conflict case + # rename A/B/F to A/B/Z to test tree conflict case + + svntest.main.file_write(lambda_path, 'B/lambda side of conflict') + sbox.simple_propset('blue', 'azul', 'A/B/E/alpha') + sbox.simple_move('A/B/F', 'A/B/Z'); + sbox.simple_commit() + + # Rev 4: + # edit A/B2/lambda + # add property to A/B2/E/alpha + # rename A/B2/F to A/B2/Y + + svntest.main.file_write(lambda2_path, 'B2/lambda side of conflict') + sbox.simple_propset('blue', 'bleue', 'A/B2/E/alpha') + sbox.simple_move('A/B2/F', 'A/B2/Y'); + sbox.simple_commit() + + # Now merge B2 into B to cause a text conflict, property conflict, and + # tree conflict + sbox.simple_update() + + svntest.actions.run_and_verify_svn2(svntest.verify.AnyOutput, [], + 0, 'merge', B2_url, B_path) + + # Verify 'svn info --xml' on the text conflicted case + + exit_code, output, error = svntest.actions.run_and_verify_svn(None, + [], 'info', + lambda_path, + '--xml') + + verify_xml_elements(output, + [('version', {'revision' : '1', + 'side' : 'source-left', + 'kind' : 'file', + 'path-in-repos': 'A/B/lambda', + 'repos-url' : sbox.repo_url, + }, + )]) + + verify_xml_elements(output, + [('version', {'revision' : '4', + 'side' : 'source-right', + 'kind' : 'file', + 'path-in-repos': 'A/B2/lambda', + 'repos-url' : sbox.repo_url, + }, + )]) + + # Verify 'svn info --xml' on the property conflicted case + + exit_code, output, error = svntest.actions.run_and_verify_svn(None, + [], 'info', + alpha_path, + '--xml') + + verify_xml_elements(output, + [('version', {'revision' : '1', + 'side' : 'source-left', + 'kind' : 'file', + 'path-in-repos': 'A/B/E/alpha', + 'repos-url' : sbox.repo_url, + }, + )]) + + verify_xml_elements(output, + [('version', {'revision' : '4', + 'side' : 'source-right', + 'kind' : 'file', + 'path-in-repos': 'A/B2/E/alpha', + 'repos-url' : sbox.repo_url, + }, + )]) + + # Verify 'svn info --xml' on the tree conflicted case + + exit_code, output, error = svntest.actions.run_and_verify_svn(None, + [], 'info', + F_path, + '--xml') + + verify_xml_elements(output, + [('version', {'revision' : '1', + 'side' : 'source-left', + 'kind' : 'dir', + 'path-in-repos': 'A/B/F', + 'repos-url' : sbox.repo_url, + }, + )]) + + verify_xml_elements(output, + [('version', {'revision' : '4', + 'side' : 'source-right', + 'kind' : 'none', + 'path-in-repos': 'A/B2/F', + 'repos-url' : sbox.repo_url, + }, + )]) ######################################################################## # Run the tests @@ -767,7 +985,12 @@ test_list = [ None, info_item_simple_multiple, info_item_url, info_item_uncommmitted, + info_item_size_wc_recursive, + info_item_size_repos, + info_item_size_repos_recursive, info_item_failures, + info_file_in_file_replaced_dir, + info_tree_conflict_source, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/lock_tests.py Fri Jan 14 14:01:45 2022 @@ -1001,7 +1001,7 @@ def lock_and_exebit1(sbox): if (not gamma_stat & mode_r or gamma_stat & mode_w or not gamma_stat & mode_x): - logger.warn("Commiting a file with 'svn:needs-lock, svn:executable'") + logger.warn("Committing a file with 'svn:needs-lock, svn:executable'") logger.warn("after unlocking modified file's permissions") raise svntest.Failure @@ -1065,7 +1065,7 @@ def lock_and_exebit2(sbox): if (not gamma_stat & mode_r or gamma_stat & mode_w or not gamma_stat & mode_x): - logger.warn("Commiting a file with 'svn:needs-lock, svn:executable'") + logger.warn("Committing a file with 'svn:needs-lock, svn:executable'") logger.warn("did not set the file to read-only, executable") raise svntest.Failure @@ -1454,7 +1454,7 @@ def lock_path_not_in_head(sbox): svntest.actions.run_and_verify_svn(None, [], 'up', '-r1', wc_dir) expected_lock_fail_err_re = "svn: warning: W160042: " \ "(Path .* doesn't exist in HEAD revision)" - # Issue #3524 These lock attemtps were triggering an assert over ra_serf: + # Issue #3524 These lock attempts were triggering an assert over ra_serf: # # working_copies\lock_tests-37>svn lock A\D # ..\..\..\subversion\libsvn_client\ra.c:275: (apr_err=235000) @@ -1835,7 +1835,7 @@ def commit_stolen_lock(sbox): # When removing directories, the locks of contained files were not # correctly removed from the working copy database, thus they later # magically reappeared when new files or directories with the same -# pathes were added. +# paths were added. @Issue(4364) def drop_locks_on_parent_deletion(sbox): "drop locks when the parent is deleted" @@ -1856,7 +1856,7 @@ def drop_locks_on_parent_deletion(sbox): [], expected_status) - # now re-add entities to the deleted pathes. + # now re-add entities to the deleted paths. sbox.simple_mkdir('A/B') sbox.simple_add_text('new file replacing old file', 'A/B/lambda') sbox.simple_add_text('file replacing former dir', 'A/B/F') @@ -2479,6 +2479,94 @@ def replace_dir_with_lots_of_locked_file # This problem was introduced on the 1.8.x branch in r1606976. sbox.simple_commit() +def update_add_file_needs_lock(sbox): + "update adding a file with svn:needs-lock" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file') + sbox.simple_commit() + + sbox.simple_update(revision=0) + sbox.simple_update(revision=1) + is_readonly(sbox.ospath('dir/file')) + +def update_edit_file_needs_lock(sbox): + "update editing a file with svn:needs-lock" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_commit() + + sbox.simple_append('dir/file', 'edited\n', truncate=True) + sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file') + sbox.simple_commit() + + sbox.simple_update(revision=1) + is_writable(sbox.ospath('dir/file')) + sbox.simple_update(revision=2) + is_readonly(sbox.ospath('dir/file')) + +def update_add_file_has_lock(sbox): + "update adding svn:needs-lock file with lock" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file') + sbox.simple_commit() + + # Acquire the lock for a file. + svntest.actions.run_and_verify_svn(".*locked by user", [], 'lock', + '-m', '', sbox.ospath('dir/file')) + + sbox.simple_update(revision=0) + sbox.simple_update(revision=1) + # We have a lock for that file, so it should be writable. + is_writable(sbox.ospath('dir/file')) + +def update_edit_file_has_lock(sbox): + "update editing svn:needs-lock file with lock" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_commit() + + sbox.simple_append('dir/file', 'edited\n', truncate=True) + sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file') + sbox.simple_commit() + + # Acquire the lock for a file. + svntest.actions.run_and_verify_svn(".*locked by user", [], 'lock', + '-m', '', sbox.ospath('dir/file')) + + sbox.simple_update(revision=1) + # No svn:needs-lock on the file, so it should be writable. + is_writable(sbox.ospath('dir/file')) + sbox.simple_update(revision=2) + # We have a lock for that file, so it should be writable. + is_writable(sbox.ospath('dir/file')) + +def update_remove_needs_lock(sbox): + "update removing svn:needs-lock on a file" + + sbox.build(empty=True) + sbox.simple_mkdir('dir') + sbox.simple_add_text('test\n', 'dir/file') + sbox.simple_propset('svn:needs-lock', 'yes', 'dir/file') + sbox.simple_commit() + + sbox.simple_propdel('svn:needs-lock', 'dir/file') + sbox.simple_commit() + + sbox.simple_update(revision=1) + is_readonly(sbox.ospath('dir/file')) + sbox.simple_update(revision=2) + is_writable(sbox.ospath('dir/file')) + ######################################################################## # Run the tests @@ -2547,6 +2635,11 @@ test_list = [ None, delete_dir_with_lots_of_locked_files, delete_locks_on_depth_commit, replace_dir_with_lots_of_locked_files, + update_add_file_needs_lock, + update_edit_file_needs_lock, + update_add_file_has_lock, + update_edit_file_has_lock, + update_remove_needs_lock, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/log_tests.py Fri Jan 14 14:01:45 2022 @@ -2094,7 +2094,7 @@ def merge_sensitive_log_copied_path_inhe svntest.main.run_svn(None, 'move', old_gamma_path, new_gamma_path) sbox.simple_commit(message='Move file') - # 'svn log -g --stop-on-copy ^/A/C/gamma' hould return *only* r5 + # 'svn log -g --stop-on-copy ^/A/C/gamma' should return *only* r5 # Previously this test failed because the change in gamma's inherited # mergeinfo between r4 and r5, due to the move, was understood as a merge: # @@ -2779,6 +2779,42 @@ def log_on_deleted_deep(sbox): '', '-q', '-c', '1-2') +@XFail() +@Issue(4711) +def log_with_merge_history_and_search(sbox): + "log --use-merge-history --search" + + sbox.build() + + # r2: create branch + sbox.simple_repo_copy('A', 'A2') # r2 + + # r3: mod in trunk + sbox.simple_append('A/mu', 'line 2') + sbox.simple_commit(message='r3: mod') + sbox.simple_update() + + # r4: merge + svntest.main.run_svn(None, 'merge', sbox.repo_url + '/A', sbox.ospath('A2')) + sbox.simple_commit(message='r4: merge') + sbox.simple_update() + + # Helper function + def count(haystack, needle): + """Return the number of times the string NEEDLE occurs in the string + HAYSTACK.""" + return len(haystack.split(needle)) - 1 + + # Check the output is valid + # ### Since the test is currently XFail, we only smoke test the output. + # ### When fixing this test to PASS, extend this validation. + _, output, _ = svntest.main.run_svn(None, 'log', '--xml', '-g', + '--search', "this will have no matches", + sbox.ospath('A2')) + + output = '\n'.join(output) + if count(output, "<logentry") != count(output, "</logentry"): + raise svntest.Failure("Apparently invalid XML in " + repr(output)) ######################################################################## # Run the tests @@ -2830,6 +2866,7 @@ test_list = [ None, merge_sensitive_log_xml_reverse_merges, log_revision_move_copy, log_on_deleted_deep, + log_with_merge_history_and_search, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_authz_tests.py Fri Jan 14 14:01:45 2022 @@ -97,7 +97,7 @@ def mergeinfo_and_skipped_paths(sbox): wc_disk, wc_status = set_up_branch(sbox, False, 3) # Create a restrictive authz where part of the merge source and part - # of the target are inaccesible. + # of the target are inaccessible. write_restrictive_svnserve_conf(sbox.repo_dir) write_authz_file(sbox, {"/" : svntest.main.wc_author +"=rw", # Make a directory in the merge source inaccessible. @@ -342,7 +342,7 @@ def mergeinfo_and_skipped_paths(sbox): # this. # # Merge -c5 -c8 to the restricted WC's A_COPY_2/D/H. r5 gets merged first - # but is a no-op, r8 get's merged next and is operative so the mergeinfo + # but is a no-op, r8 gets merged next and is operative so the mergeinfo # should be updated on the merge target to reflect both merges. expected_output = wc.State(A_COPY_2_H_path, { 'omega' : Item(status='U '), Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_reintegrate_tests.py Fri Jan 14 14:01:45 2022 @@ -1508,7 +1508,7 @@ def reintegrate_with_subtree_mergeinfo(s # is all quite ugly as the intersection or multiple known issues # is likely to be. However, given that none of this mergeinfo is # particularly harmful and that this test is *not* about issues #3669 - # or #4309, we are tolerting it. + # or #4309, we are tolerating it. 'D/gamma_moved' : Item( "Even newer content", props={SVN_PROP_MERGEINFO : '/A/D/gamma_moved:2-7,9-12\n' Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tests.py Fri Jan 14 14:01:45 2022 @@ -3323,15 +3323,11 @@ def merge_conflict_markers_matching_eol( mu_path = sbox.ospath('A/mu') - # CRLF is a string that will match a CRLF sequence read from a text file. - # ### On Windows, we assume CRLF will be read as LF, so it's a poor test. if os.name == 'nt': - crlf = '\n' + native_nl = '\r\n' else: - crlf = '\r\n' - - # Strict EOL style matching breaks Windows tests at least with Python 2 - keep_eol_style = not svntest.main.is_os_windows() + native_nl = '\n' + crlf = '\r\n' # Checkout a second working copy wc_backup = sbox.add_wc_path('backup') @@ -3349,8 +3345,8 @@ def merge_conflict_markers_matching_eol( path_backup = os.path.join(wc_backup, 'A', 'mu') # do the test for each eol-style - for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'], - [crlf, '\015', '\n', '\012']): + for eol, eolchar in zip(['CRLF', 'CR','native', 'LF'], + [crlf, '\015', native_nl, '\012']): # rewrite file mu and set the eol-style property. svntest.main.file_write(mu_path, "This is the file 'mu'."+ eolchar, 'wb') svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path) @@ -3375,8 +3371,8 @@ def merge_conflict_markers_matching_eol( svntest.main.run_svn(None, 'update', wc_backup) # Make a local mod to mu - svntest.main.file_append(mu_path, - 'Original appended text for mu' + eolchar) + svntest.main.file_append_binary(mu_path, + 'Original appended text for mu' + eolchar) # Commit the original change and note the 'theirs' revision number svntest.main.run_svn(None, 'commit', '-m', 'test log', wc_dir) @@ -3384,8 +3380,9 @@ def merge_conflict_markers_matching_eol( theirs_rev = cur_rev # Make a local mod to mu, will conflict with the previous change - svntest.main.file_append(path_backup, - 'Conflicting appended text for mu' + eolchar) + svntest.main.file_append_binary(path_backup, + 'Conflicting appended text for mu' + + eolchar) # Create expected output tree for an update of the wc_backup. expected_backup_output = svntest.wc.State(wc_backup, { @@ -3445,7 +3442,7 @@ def merge_conflict_markers_matching_eol( expected_backup_disk, expected_backup_status, expected_backup_skip, - keep_eol_style=keep_eol_style) + keep_eol_style=True) # cleanup for next run svntest.main.run_svn(None, 'revert', '-R', wc_backup) @@ -3468,15 +3465,7 @@ def merge_eolstyle_handling(sbox): mu_path = sbox.ospath('A/mu') - # CRLF is a string that will match a CRLF sequence read from a text file. - # ### On Windows, we assume CRLF will be read as LF, so it's a poor test. - if os.name == 'nt': - crlf = '\n' - else: - crlf = '\r\n' - - # Strict EOL style matching breaks Windows tests at least with Python 2 - keep_eol_style = not svntest.main.is_os_windows() + crlf = '\r\n' # Checkout a second working copy wc_backup = sbox.add_wc_path('backup') @@ -3518,7 +3507,7 @@ def merge_eolstyle_handling(sbox): expected_backup_disk, expected_backup_status, expected_backup_skip, - keep_eol_style=keep_eol_style) + keep_eol_style=True) # Test 2: now change the eol-style property to another value and commit, # merge this revision in the still changed mu in the second working copy; @@ -3549,7 +3538,7 @@ def merge_eolstyle_handling(sbox): expected_backup_disk, expected_backup_status, expected_backup_skip, - keep_eol_style=keep_eol_style) + keep_eol_style=True) # Test 3: now delete the eol-style property and commit, merge this revision # in the still changed mu in the second working copy; there should be no @@ -3578,7 +3567,7 @@ def merge_eolstyle_handling(sbox): expected_backup_disk, expected_backup_status, expected_backup_skip, - keep_eol_style=keep_eol_style) + keep_eol_style=True) #---------------------------------------------------------------------- def create_deep_trees(wc_dir): @@ -4794,7 +4783,7 @@ def mergeinfo_inheritance_and_discontinu # Merge r2:6 into A_COPY/D # # A_COPY/D should inherit the mergeinfo '/A:4' from A_COPY - # combine it with the discontinous merges performed directly on + # combine it with the discontinuous merges performed directly on # it (A/D/ 2:3 and A/D 4:6) resulting in '/A/D:3-6'. expected_output = wc.State(D_COPY_path, { 'H/psi' : Item(status='U '), @@ -4966,11 +4955,11 @@ def merge_to_switched_path(sbox): A_COPY_D_G_rho_path = sbox.ospath('A_COPY/D/G/rho') expected = svntest.verify.UnorderedOutput( - ["A " + os.path.join(G_COPY_path, "pi") + "\n", - "A " + os.path.join(G_COPY_path, "rho") + "\n", - "A " + os.path.join(G_COPY_path, "tau") + "\n", - "Checked out revision 6.\n", - "A " + G_COPY_path + "\n"]) + ["A " + G_COPY_path + "\n", + "A " + os.path.join(G_COPY_path, "pi") + "\n", + "A " + os.path.join(G_COPY_path, "rho") + "\n", + "A " + os.path.join(G_COPY_path, "tau") + "\n", + ]) # r7 - Copy A/D/G to A/D/G_COPY and commit. svntest.actions.run_and_verify_svn(expected, [], 'copy', @@ -12848,6 +12837,39 @@ def natural_history_filtering(sbox): # the revisions on 'trunk' which occurred after 'branch2' was copied as # these are not part of 'branch2's natural history. + def path_join(head, tail): + if not head: return tail + if not tail: return head + return head + '/' + tail + + def greek_file_item(path): + if path[-1:].islower(): + basename = re.sub('.*/', '', path) + return Item("This is the file '" + basename + "'.\n") + return Item() + + A_paths = [ + "", + "B", + "B/lambda", + "B/E", + "B/E/alpha", + "B/E/beta", + "B/F", + "mu", + "C", + "D", + "D/gamma", + "D/G", + "D/G/pi", + "D/G/rho", + "D/G/tau", + "D/H", + "D/H/chi", + "D/H/omega", + "D/H/psi", + ] + sbox.build() wc_dir = sbox.wc_dir @@ -12861,68 +12883,16 @@ def natural_history_filtering(sbox): # r7: Make a second 'branch': Copy A to A_COPY_2 expected = svntest.verify.UnorderedOutput( - ["A " + os.path.join(A_COPY_2_path, "B") + "\n", - "A " + os.path.join(A_COPY_2_path, "B", "lambda") + "\n", - "A " + os.path.join(A_COPY_2_path, "B", "E") + "\n", - "A " + os.path.join(A_COPY_2_path, "B", "E", "alpha") + "\n", - "A " + os.path.join(A_COPY_2_path, "B", "E", "beta") + "\n", - "A " + os.path.join(A_COPY_2_path, "B", "F") + "\n", - "A " + os.path.join(A_COPY_2_path, "mu") + "\n", - "A " + os.path.join(A_COPY_2_path, "C") + "\n", - "A " + os.path.join(A_COPY_2_path, "D") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "gamma") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "G") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "G", "pi") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "G", "rho") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "G", "tau") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "H") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "H", "chi") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "H", "omega") + "\n", - "A " + os.path.join(A_COPY_2_path, "D", "H", "psi") + "\n", - "Checked out revision 6.\n", - "A " + A_COPY_2_path + "\n"]) - wc_status.add({ - "A_COPY_2" + "/B" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/B/lambda" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/B/E" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/B/E/alpha" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/B/E/beta" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/B/F" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/mu" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/C" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/gamma" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/G" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/G/pi" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/G/rho" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/G/tau" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/H" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/H/chi" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/H/omega" : Item(status=' ', wc_rev=7), - "A_COPY_2" + "/D/H/psi" : Item(status=' ', wc_rev=7), - "A_COPY_2" : Item(status=' ', wc_rev=7), - }) - wc_disk.add({ - "A_COPY_2" : Item(), - "A_COPY_2" + '/B' : Item(), - "A_COPY_2" + '/B/lambda' : Item("This is the file 'lambda'.\n"), - "A_COPY_2" + '/B/E' : Item(), - "A_COPY_2" + '/B/E/alpha' : Item("This is the file 'alpha'.\n"), - "A_COPY_2" + '/B/E/beta' : Item("New content"), - "A_COPY_2" + '/B/F' : Item(), - "A_COPY_2" + '/mu' : Item("This is the file 'mu'.\n"), - "A_COPY_2" + '/C' : Item(), - "A_COPY_2" + '/D' : Item(), - "A_COPY_2" + '/D/gamma' : Item("This is the file 'gamma'.\n"), - "A_COPY_2" + '/D/G' : Item(), - "A_COPY_2" + '/D/G/pi' : Item("This is the file 'pi'.\n"), - "A_COPY_2" + '/D/G/rho' : Item("New content"), - "A_COPY_2" + '/D/G/tau' : Item("This is the file 'tau'.\n"), - "A_COPY_2" + '/D/H' : Item(), - "A_COPY_2" + '/D/H/chi' : Item("New content"), - "A_COPY_2" + '/D/H/omega' : Item("This is the file 'omega'.\n"), - "A_COPY_2" + '/D/H/psi' : Item("New content"), - }) + [ "A " + sbox.ospath(path_join("A_COPY_2", p)) + "\n" + for p in A_paths ]) + wc_status.add( + { path_join("A_COPY_2", p) : Item(status=' ', wc_rev=7) + for p in A_paths }) + wc_disk.add( + { path_join("A_COPY_2", p) : + Item("New content") if p in ['B/E/beta', 'D/G/rho', 'D/H/chi', 'D/H/psi'] + else greek_file_item(p) + for p in A_paths }) svntest.actions.run_and_verify_svn(expected, [], 'copy', sbox.repo_url + "/A", A_COPY_2_path) @@ -13380,7 +13350,7 @@ def no_self_referential_filtering_on_add def merge_range_prior_to_rename_source_existence(sbox): "merge prior to rename src existence still dels src" - # Replicate a merge bug found while synching up a feature branch on the + # Replicate a merge bug found while syncing up a feature branch on the # Subversion repository with trunk. See r874121 of # http://svn.apache.org/repos/asf/subversion/branches/ignore-mergeinfo, in which # a move was merged to the target, but the delete half of the move @@ -18530,6 +18500,276 @@ def merge_dir_delete_force(sbox): 'merge', '-c2', '^/', sbox.wc_dir, '--ignore-ancestry', '--force') +# Issue #4859: Merge removing a folder with non-inheritable mergeinfo -> +# E155023: can't set properties: invalid status for updating properties +@Issue(4859) +def merge_deleted_folder_with_mergeinfo(sbox): + "merge deleted folder with mergeinfo" + + sbox.build() + + was_cwd = os.getcwd() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + # Some non-inheritable mergeinfo + sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D') + sbox.simple_commit() # r2 + + # Branching + sbox.simple_repo_copy('A', 'branch_A') # r3 + sbox.simple_update() + + # On branch, remove a folder that has non-inheritable mergeinfo + sbox.simple_rm('branch_A/D') + sbox.simple_commit() # r4 + + sbox.simple_update() + + # A merge that removes that folder + # (merge revision 4 only from 'branch_A' to 'A') + expected_output = wc.State(sbox.ospath(''), { + 'A/D' : Item(status='D '), + }) + expected_mergeinfo_output = wc.State(sbox.ospath(''), { + 'A' : Item(status=' U'), + }) + expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 4).subtree('A') + expected_status.add({ '': Item(status=' M', wc_rev=4) }) + expected_status.tweak_some( + lambda path, item: [True] if path.split('/')[0] == 'D' else [], + status='D ') + svntest.actions.run_and_verify_merge(sbox.ospath('A'), 3, 4, + '^/branch_A', None, + expected_output, + expected_mergeinfo_output, + None, + None, + expected_status, + wc.State('', {}), + [], + check_props=False, + dry_run=False # as dry run is broken + ) + + os.chdir(was_cwd) + +# Issue #4859: Merge removing a folder with non-inheritable mergeinfo -> +# E155023: can't set properties: invalid status for updating properties +# +# In this test we split the merge into two separate operable parts, a +# delete followed later by an add, to check it will set the mergeinfo on the +# subtree paths if the deleted folder is later replaced within the same +# overall merge. +@Issue(4859) +def merge_deleted_folder_with_mergeinfo_2(sbox): + "merge deleted folder with mergeinfo 2" + + sbox.build() + + was_cwd = os.getcwd() + os.chdir(sbox.wc_dir) + sbox.wc_dir = '' + + # Some non-inheritable mergeinfo + sbox.simple_propset('svn:mergeinfo', '/A/C:1*', 'A/D') + sbox.simple_commit() # r2 + + # Branching + sbox.simple_repo_copy('A', 'branch_A') # r3 + sbox.simple_update() + + # On branch, remove a folder that has non-inheritable mergeinfo + sbox.simple_rm('branch_A/D') + sbox.simple_commit() # r4 + + # A commit that we don't want to merge from the branch, to split the merge + # into two separate operable parts. + sbox.simple_mkdir('branch_A/IgnoreThis') + sbox.simple_commit() # r5 + + # On branch, replace the deleted folder with a new one, with mergeinfo, + # to check we don't omit setting mergeinfo on this. + sbox.simple_mkdir('branch_A/D') + sbox.simple_propset('svn:mergeinfo', '/branch_B/C:1*', 'branch_A/D') + sbox.simple_mkdir('branch_A/D/G', 'branch_A/D/G2') + sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G:1*', 'branch_A/D/G') + sbox.simple_propset('svn:mergeinfo', '/branch_B/C/G2:1*', 'branch_A/D/G2') + sbox.simple_commit() # r6 + + sbox.simple_propset('svn:mergeinfo', '/branch_A:5', 'A') + sbox.simple_commit() # r7 + + sbox.simple_update() + + # A merge that removes that folder + expected_output = wc.State(sbox.ospath(''), { + 'A/D' : Item(status='A ', prev_status='D '), + 'A/D/G' : Item(status='A '), + 'A/D/G2' : Item(status='A '), + }) + # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2. + expected_mergeinfo_output = wc.State(sbox.ospath(''), { + 'A' : Item(status=' U'), + 'A/D' : Item(status=' G'), + 'A/D/G' : Item(status=' G'), + 'A/D/G2' : Item(status=' G'), + }) + expected_status = svntest.actions.get_virginal_state(sbox.ospath('A'), 7).subtree('A') + expected_status.tweak_some( + lambda path, item: [True] if path.split('/')[0] == 'D' else [], + status='D ') + expected_status.add({ + '' : Item(status=' M', wc_rev=7), + 'D' : Item(status='RM', copied='+', wc_rev='-'), + 'D/G' : Item(status=' M', copied='+', wc_rev='-'), + 'D/G2' : Item(status=' M', copied='+', wc_rev='-'), + }) + svntest.actions.run_and_verify_merge(sbox.ospath('A'), None, None, + '^/branch_A', None, + expected_output, + expected_mergeinfo_output, + None, + None, + expected_status, + wc.State('', {}), + [], + check_props=False, + dry_run=False # as dry run is broken + ) + + # verify that mergeinfo is set/changed on A/D, A/D/G, A/D/G2. + + # NOTE: When writing out multi-line prop values in svn:* props, the + # client converts to local encoding and local eol style. + # Therefore, the expected output must contain the right kind of eoln + # strings. That's why we use os.linesep in the tests below, not just + # plain '\n'. + + expected_mergeinfo = [ + ('A', ['/branch_A:3-7']), + ('A/D', ['/branch_A/D:5-7'+os.linesep, '/branch_B/C:1*']), + ('A/D/G', ['/branch_A/D/G:5-7'+os.linesep, '/branch_B/C/G:1*']), + ('A/D/G2', ['/branch_A/D/G2:5-7'+os.linesep, '/branch_B/C/G2:1*']), + ] + for path, mergeinfo in expected_mergeinfo: + svntest.actions.check_prop('svn:mergeinfo', sbox.ospath(path), + [m.encode() for m in mergeinfo]) + + os.chdir(was_cwd) + +#---------------------------------------------------------------------- +# Test that mismatched source repository root URLs in a two-URL merge +# throws an error E170000 SVN_ERR_RA_ILLEGAL_URL. +# +# (Since issue 4874 (Subversion 1.15) that error is also wrapped in +# E195012 SVN_ERR_CLIENT_UNRELATED_RESOURCES for consistency with +# the source-target mismatch errors.) +# +# For mismatched URLs we use two repositories with the same UUID. +@Issue(4874) +def merge_error_if_source_urls_differ(sbox): + "merge error if source urls differ" + + sbox.build() + expected_disk, expected_status = set_up_branch(sbox) + wc_dir = sbox.wc_dir + repo_dir = sbox.repo_dir + + # Create a second repository with the same content, same UUID + other_repo_dir, other_repo_url = sbox.add_repo_path("other") + other_wc_dir = sbox.add_wc_path("other") + svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False) + + G_COPY_path = sbox.ospath('A_COPY/D/G') + svntest.actions.run_and_verify_svn2(None, '.*: E170000: .*', 1, + 'merge', + sbox.repo_url + '/A/D/G@3', + other_repo_url + '/A/D/G@4', + G_COPY_path) + +#---------------------------------------------------------------------- +# Test that a merge with mismatched source and target repository root URLs +# but identical repository UUIDs throws a warning error, for two-URL and +# pegged merges. +# +# Issue #4874 makes this a warning in Subversion 1.15 and an error in 1.16. +# Previously it was treated as a foreign repository merge. +# +# For mismatched URLs we use two repositories with the same UUID. +@Issue(4874) +def merge_error_if_ambiguous_foreign_merge(sbox): + "merge error if ambiguous foreign merge" + + sbox.build() + expected_disk, expected_status = set_up_branch(sbox) + wc_dir = sbox.wc_dir + repo_dir = sbox.repo_dir + + # Create a second repository with the same content, same UUID + other_repo_dir, other_repo_url = sbox.add_repo_path("other") + other_wc_dir = sbox.add_wc_path("other") + svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False) + + # With Issue #4874 implemented, the attempted merges should error out with + # SVN_ERR_CLIENT_UNRELATED_RESOURCES, because of mismatched source and + # target URLs. + + # expect warning or error (E195012 SVN_ERR_CLIENT_UNRELATED_RESOURCES)? + expected_stdout = None if svntest.main.SVN_VER_MINOR < 16 else [] + expected_stderr = '.*: E195012: .*' if svntest.main.SVN_VER_MINOR >= 15 else [] + expected_exit = 0 if svntest.main.SVN_VER_MINOR < 16 else 1 + + # two-URL merge + svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr, + expected_exit, + 'merge', + other_repo_url + '/A/D/G@3', + other_repo_url + '/A/D/G@4', + sbox.ospath('A_COPY/D/G')) + svntest.main.run_svn(False, 'revert', '-qR', sbox.wc_dir) + # pegged merge + svntest.actions.run_and_verify_svn2(expected_stdout, expected_stderr, + expected_exit, + 'merge', '-c4', + other_repo_url + '/A/D/G', + sbox.ospath('A_COPY/D/G')) + svntest.main.run_svn(False, 'revert', '-qR', sbox.wc_dir) + +#---------------------------------------------------------------------- +# Test that a merge with mismatched source and target repository root URLs +# throws an error, for automatic and reintegrate merges. +# +# This behaviour is unchanged by issue #4874, just reinforced and with more +# informative error messages. +# +# For mismatched URLs we use two repositories with the same UUID. +@Issue(4874) +def merge_error_if_source_target_url_mismatch(sbox): + "merge error if source target url mismatch" + + sbox.build() + expected_disk, expected_status = set_up_branch(sbox) + wc_dir = sbox.wc_dir + repo_dir = sbox.repo_dir + + # Create a second repository with the same content, same UUID + other_repo_dir, other_repo_url = sbox.add_repo_path("other") + other_wc_dir = sbox.add_wc_path("other") + svntest.main.copy_repos(repo_dir, other_repo_dir, 6, ignore_uuid=False) + + expected_stderr = '.*: E195012: .*' # SVN_ERR_CLIENT_UNRELATED_RESOURCES + # automatic merge + svntest.actions.run_and_verify_svn2([], expected_stderr, 1, + 'merge', + other_repo_url + '/A/D/G', + sbox.ospath('A_COPY/D/G')) + # reintegrate merge + svntest.actions.run_and_verify_svn2([], expected_stderr, 1, + 'merge', '--reintegrate', + other_repo_url + '/A/D/G', + sbox.ospath('A_COPY/D/G')) + ######################################################################## # Run the tests @@ -18677,6 +18917,11 @@ test_list = [ None, merge_to_empty_target_merge_to_infinite_target, conflict_naming, merge_dir_delete_force, + merge_deleted_folder_with_mergeinfo, + merge_deleted_folder_with_mergeinfo_2, + merge_error_if_source_urls_differ, + merge_error_if_ambiguous_foreign_merge, + merge_error_if_source_target_url_mismatch, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/merge_tree_conflict_tests.py Fri Jan 14 14:01:45 2022 @@ -2364,6 +2364,233 @@ def spurios_tree_conflict_with_added_fil [], False, True, '--reintegrate', sbox.ospath('A_branch')) +def merge_local_missing_node_kind_none(sbox): + "crash in resolver triggered by none-type node" + + sbox.build() + wc_dir = sbox.wc_dir + + sbox.simple_mkdir('branches') + sbox.simple_commit() # r2 + sbox.simple_update() + + # Create a feature branch of A + sbox.simple_copy('A', 'branches/feature1') + sbox.simple_commit() #r3 + sbox.simple_update() + + # On the branch, move file alpha to another directory + sbox.simple_move('branches/feature1/B/E/alpha', + 'branches/feature1/D/H/alpha-from-B-E') + sbox.simple_commit() # r4 + sbox.simple_update() + + # Cherry-pick the delete-half of the above move into A (the trunk) + expected_output = svntest.wc.State(sbox.ospath('A/B/E'), { + 'alpha' : Item(status='D '), + }) + expected_mergeinfo_output = wc.State(sbox.ospath('A/B/E'), { + '' : Item(status=' U') + }) + expected_elision_output = wc.State(wc_dir, { + }) + expected_disk = wc.State('', { + 'beta' : Item(contents="This is the file 'beta'.\n"), + '.' : Item(props={u'svn:mergeinfo': + u'/branches/feature1/B/E:4'}), + }) + expected_status = wc.State(sbox.ospath('A/B/E'), { + '' : Item(status=' M', wc_rev='4'), + 'alpha' : Item(status='D ', wc_rev='4'), + 'beta' : Item(status=' ', wc_rev='4'), + }) + expected_skip = wc.State('', { + }) + svntest.actions.run_and_verify_merge(sbox.ospath('A/B/E'), 3, 4, + sbox.repo_url + '/branches/feature1/B/E', + None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + check_props=True) + sbox.simple_commit() # r5 + sbox.simple_update() + + # Create a new file on the feature branch + sbox.simple_add_text("This is the file 'pi'\n", 'branches/feature1/B/E/pi') + sbox.simple_commit() #r6 + sbox.simple_update() + + # Create a second branch based on the feature branch. + sbox.simple_copy('branches/feature1', 'branches/feature2') + sbox.simple_commit() #r7 + sbox.simple_update() + + # Create a new file kappa on this second branch + sbox.simple_add_text("This is the file 'kappa'\n", + 'branches/feature2/B/E/kappa') + sbox.simple_commit() #r8 + sbox.simple_update() + + # An unrelated additional change on the second branch. + sbox.simple_append('branches/feature2/B/E/beta', + "This is a change to file 'beta'.\n") + sbox.simple_commit() #r9 + sbox.simple_update() + + # Merge the second branch back into the feature branch + expected_output = svntest.wc.State(sbox.ospath('branches/feature1'), { + 'B/E/kappa' : Item(status='A '), + 'B/E/beta' : Item(status='U '), + }) + expected_mergeinfo_output = wc.State(sbox.ospath('branches/feature1'), { + '.' : Item(status=' U'), + }) + expected_elision_output = wc.State(wc_dir, { + }) + expected_disk = wc.State('', { + 'C' : Item(), + 'B/E/kappa' : Item(contents="This is the file 'kappa'\n"), + 'B/E/pi' : Item(contents="This is the file 'pi'\n"), + 'B/E/beta' : Item(contents="This is the file 'beta'.\n" + + "This is a change to file 'beta'.\n"), + 'B/lambda' : Item(contents="This is the file 'lambda'.\n"), + 'B/F' : Item(), + 'D/H/omega' : Item(contents="This is the file 'omega'.\n"), + 'D/H/alpha-from-B-E': Item(contents="This is the file 'alpha'.\n"), + 'D/H/psi' : Item(contents="This is the file 'psi'.\n"), + 'D/H/chi' : Item(contents="This is the file 'chi'.\n"), + 'D/G/pi' : Item(contents="This is the file 'pi'.\n"), + 'D/G/rho' : Item(contents="This is the file 'rho'.\n"), + 'D/G/tau' : Item(contents="This is the file 'tau'.\n"), + 'D/gamma' : Item(contents="This is the file 'gamma'.\n"), + 'mu' : Item(contents="This is the file 'mu'.\n"), + '.' : Item(props={u'svn:mergeinfo': + u'/branches/feature2:7-9'}), + }) + expected_status = wc.State(sbox.ospath('branches/feature1'), { + '' : Item(status=' M', wc_rev='9'), + 'mu' : Item(status=' ', wc_rev='9'), + 'D' : Item(status=' ', wc_rev='9'), + 'D/H' : Item(status=' ', wc_rev='9'), + 'D/H/alpha-from-B-E': Item(status=' ', wc_rev='9'), + 'D/H/psi' : Item(status=' ', wc_rev='9'), + 'D/H/chi' : Item(status=' ', wc_rev='9'), + 'D/H/omega' : Item(status=' ', wc_rev='9'), + 'D/G' : Item(status=' ', wc_rev='9'), + 'D/G/pi' : Item(status=' ', wc_rev='9'), + 'D/G/tau' : Item(status=' ', wc_rev='9'), + 'D/G/rho' : Item(status=' ', wc_rev='9'), + 'D/gamma' : Item(status=' ', wc_rev='9'), + 'B' : Item(status=' ', wc_rev='9'), + 'B/E' : Item(status=' ', wc_rev='9'), + 'B/E/beta' : Item(status='M ', wc_rev='9'), + 'B/E/kappa' : Item(status='A ', copied='+', wc_rev='-'), + 'B/E/pi' : Item(status=' ', wc_rev='9'), + 'B/lambda' : Item(status=' ', wc_rev='9'), + 'B/F' : Item(status=' ', wc_rev='9'), + 'C' : Item(status=' ', wc_rev='9'), + }) + expected_skip = wc.State('', { + }) + svntest.actions.run_and_verify_merge(sbox.ospath('branches/feature1'), + None, None, + sbox.repo_url + '/branches/feature2', + None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + check_props=True) + sbox.simple_commit() # r10 + sbox.simple_update() + + # On the feature branch, rename the file kappa + sbox.simple_move('branches/feature1/B/E/kappa', + 'branches/feature1/B/E/kappanew') + sbox.simple_commit() # r11 + sbox.simple_update() + + # On the feature branch, move file kappanew to another directory + sbox.simple_move('branches/feature1/B/E/kappanew', + 'branches/feature1/D/H/kappanew') + sbox.simple_commit() # r12 + sbox.simple_update() + + # Cherry-pick r12 into A (the trunk) + # This triggers an assertion failure in Subversion 1.14.1 because of + # a node type lookup which uses the wrong token map: + # W: subversion/libsvn_subr/token.c:40: (apr_err=SVN_ERR_ASSERTION_FAIL) + # svn: E235000: In file 'subversion/libsvn_subr/token.c' line 40: internal malfunction + expected_output = svntest.wc.State(sbox.ospath('A'), { + 'B/E/kappanew' : Item(status=' ', treeconflict='C'), + 'D/H/kappanew' : Item(status='A '), + }) + expected_mergeinfo_output = wc.State(sbox.ospath('A'), { + '' : Item(status=' U'), + 'B/E' : Item(status=' U'), + }) + expected_elision_output = wc.State(wc_dir, { + }) + expected_disk = wc.State('', { + 'C' : Item(), + 'B/E' : Item(props={u'svn:mergeinfo': + u'/branches/feature1/B/E:4,12'}), + 'B/E/beta' : Item(contents="This is the file 'beta'.\n"), + 'B/lambda' : Item(contents="This is the file 'lambda'.\n"), + 'B/F' : Item(), + 'D/H/omega' : Item(contents="This is the file 'omega'.\n"), + 'D/H/kappanew' : Item(contents="This is the file 'kappa'\n"), + 'D/H/psi' : Item(contents="This is the file 'psi'.\n"), + 'D/H/chi' : Item(contents="This is the file 'chi'.\n"), + 'D/G/pi' : Item(contents="This is the file 'pi'.\n"), + 'D/G/rho' : Item(contents="This is the file 'rho'.\n"), + 'D/G/tau' : Item(contents="This is the file 'tau'.\n"), + 'D/gamma' : Item(contents="This is the file 'gamma'.\n"), + 'mu' : Item(contents="This is the file 'mu'.\n"), + '.' : Item(props={u'svn:mergeinfo': + u'/branches/feature1:12'}), + }) + expected_status = wc.State(sbox.ospath('A'), { + '' : Item(status=' M', wc_rev='12'), + 'D' : Item(status=' ', wc_rev='12'), + 'D/H' : Item(status=' ', wc_rev='12'), + 'D/H/chi' : Item(status=' ', wc_rev='12'), + 'D/H/psi' : Item(status=' ', wc_rev='12'), + 'D/H/kappanew' : Item(status='A ', copied='+', wc_rev='-'), + 'D/H/omega' : Item(status=' ', wc_rev='12'), + 'D/G' : Item(status=' ', wc_rev='12'), + 'D/G/pi' : Item(status=' ', wc_rev='12'), + 'D/G/tau' : Item(status=' ', wc_rev='12'), + 'D/G/rho' : Item(status=' ', wc_rev='12'), + 'D/gamma' : Item(status=' ', wc_rev='12'), + 'B' : Item(status=' ', wc_rev='12'), + 'B/E' : Item(status=' M', wc_rev='12'), + 'B/E/beta' : Item(status=' ', wc_rev='12'), + 'B/E/kappanew' : Item(status='! ', treeconflict='C'), + 'B/F' : Item(status=' ', wc_rev='12'), + 'B/lambda' : Item(status=' ', wc_rev='12'), + 'mu' : Item(status=' ', wc_rev='12'), + 'C' : Item(status=' ', wc_rev='12'), + }) + expected_skip = wc.State('', { + }) + svntest.actions.run_and_verify_merge(sbox.ospath('A'), 11, 12, + sbox.repo_url + '/branches/feature1', + None, + expected_output, + expected_mergeinfo_output, + expected_elision_output, + expected_disk, + expected_status, + expected_skip, + check_props=True) + ######################################################################## # Run the tests @@ -2399,6 +2626,7 @@ test_list = [ None, merge_obstruction_recording, added_revision_recording_in_tree_conflict, spurios_tree_conflict_with_added_file, + merge_local_missing_node_kind_none, ] if __name__ == '__main__': Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_authz_svn_tests.py Fri Jan 14 14:01:45 2022 @@ -25,7 +25,7 @@ ###################################################################### # General modules -import os, re, logging +import os, re, logging, shutil logger = logging.getLogger() @@ -167,7 +167,7 @@ def anon(sbox): write_authz_file(sbox) - anon_tests = ( + anon_tests = ( { 'path': '', 'status': 301 }, { 'path': '/', 'status': 200 }, { 'path': '/repos', 'status': 301 }, @@ -1043,6 +1043,73 @@ def authn_sallrall(sbox): verify_gets(test_area_url, sallrall_tests) +@SkipUnless(svntest.main.is_ra_type_dav) +def repos_relative_access_file(sbox): + "repos-relative access file" + + sbox.build() + + test_area_url = sbox.repo_url.replace('/svn-test-work/repositories/', + '/authz-test-work/in-repos-authz/') + + svntest.main.write_authz_file(sbox, {"/": "", "/A": "%s = rw" % user1}) + shutil.move(sbox.authz_file, os.path.join(sbox.wc_dir, 'authz')) + sbox.simple_add('authz') + svntest.actions.run_and_verify_svn(None, [], 'relocate', + sbox.file_protocol_repo_url(), sbox.wc_dir) + sbox.simple_commit(message="adding in-repository authz rules file") + + in_repos_authz_tests = ( + { 'path': '', 'status': 401, }, + { 'path': '/authz', 'status': 401, }, + { 'path': '/authz', 'user' : user1, 'pw' : user1_pass, + 'status': 403, }, + { 'path': '/A', 'user' : user1, 'pw' : user1_pass, + 'status': 301, }, + { 'path': '/A/', 'user' : user1, 'pw' : user1_pass, + 'status': 200, }, + ) + + verify_gets(test_area_url, in_repos_authz_tests) + +# test for the bug also known as CVE-2020-17525 +@SkipUnless(svntest.main.is_ra_type_dav) +def nonexistent_repos_relative_access_file(sbox): + "repos-relative access file with bad repository URL" + + sbox.build() + + test_area_url = sbox.repo_url.replace('/svn-test-work/repositories/', + '/authz-test-work/in-repos-authz/') + + # Construct a bad test-area URL to see what happens if we attempt to access + # a repository in a subdirectory which does not exist in SVNParentPath. + # This used to crash the server with a NULL-pointer dereference upon + # unauthenticated access. + test_area_url += '-this/does/not/exist' + + svntest.main.write_authz_file(sbox, {"/": "", "/A": "%s = rw" % user1}) + shutil.move(sbox.authz_file, os.path.join(sbox.wc_dir, 'authz')) + sbox.simple_add('authz') + svntest.actions.run_and_verify_svn(None, [], 'relocate', + sbox.file_protocol_repo_url(), sbox.wc_dir) + sbox.simple_commit(message="adding in-repository authz rules file") + + # access is denied across the board since this repository does not exist + in_repos_authz_tests = ( + { 'path': '', 'status': 401, }, + { 'path': '/authz', 'status': 401, }, + { 'path': '/authz', 'user' : user1, 'pw' : user1_pass, + 'status': 403, }, + { 'path': '/A', 'user' : user1, 'pw' : user1_pass, + 'status': 403, }, + { 'path': '/A/', 'user' : user1, 'pw' : user1_pass, + 'status': 403, }, + ) + + verify_gets(test_area_url, in_repos_authz_tests) + + ######################################################################## # Run the tests @@ -1058,6 +1125,8 @@ test_list = [ None, authn_group, authn_sallrany, authn_sallrall, + repos_relative_access_file, + nonexistent_repos_relative_access_file, ] serial_only = True Modified: subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py (original) +++ subversion/branches/multi-wc-format/subversion/tests/cmdline/mod_dav_svn_tests.py Fri Jan 14 14:01:45 2022 @@ -102,7 +102,7 @@ def compare_xml_elem(a, b): # iteration. def sortcmp(x, y): return compare_xml_elem(x, y)[0] - + a_children = sorted(list(a), key=functools.cmp_to_key(sortcmp)) b_children = sorted(list(b), key=functools.cmp_to_key(sortcmp)) @@ -640,6 +640,53 @@ def propfind_propname(sbox): actual_response = r.read() verify_xml_response(expected_response, actual_response) +@SkipUnless(svntest.main.is_ra_type_dav) +def last_modified_header(sbox): + "verify 'Last-Modified' header on 'external' GETs" + + sbox.build(create_wc=False, read_only=True) + + headers = { + 'Authorization': 'Basic ' + base64.b64encode(b'jconstant:rayjandom').decode(), + } + + h = svntest.main.create_http_connection(sbox.repo_url) + + # GET /repos/iota + # Expect to see a Last-Modified header. + h.request('GET', sbox.repo_url + '/iota', None, headers) + r = h.getresponse() + if r.status != httplib.OK: + raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason)) + svntest.verify.compare_and_display_lines(None, 'Last-Modified', + svntest.verify.RegexOutput('.+'), + r.getheader('Last-Modified')) + r.read() + + # HEAD /repos/iota + # Expect to see a Last-Modified header. + h.request('HEAD', sbox.repo_url + '/iota', None, headers) + r = h.getresponse() + if r.status != httplib.OK: + raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason)) + svntest.verify.compare_and_display_lines(None, 'Last-Modified', + svntest.verify.RegexOutput('.+'), + r.getheader('Last-Modified')) + r.read() + + # GET /repos/!svn/rvr/1/iota + # There should not be a Last-Modified header (it's costly and not useful, + # see r1724790) + h.request('GET', sbox.repo_url + '/!svn/rvr/1/iota', None, headers) + r = h.getresponse() + if r.status != httplib.OK: + raise svntest.Failure('Request failed: %d %s' % (r.status, r.reason)) + last_modified = r.getheader('Last-Modified') + if last_modified: + raise svntest.Failure('Unexpected Last-Modified header: %s' % last_modified) + r.read() + + ######################################################################## # Run the tests @@ -652,6 +699,7 @@ test_list = [ None, propfind_404, propfind_allprop, propfind_propname, + last_modified_header, ] serial_only = True
