summaryrefslogtreecommitdiff
path: root/subversion/tests
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/tests')
-rw-r--r--subversion/tests/afl/afl-svndiff-testcase/test1bin0 -> 4 bytes
-rw-r--r--subversion/tests/afl/afl-svndiff-testcase/test2bin0 -> 16 bytes
-rw-r--r--subversion/tests/afl/afl-svndiff-testcase/test3bin0 -> 18 bytes
-rw-r--r--subversion/tests/afl/afl-svndiff.c85
-rw-r--r--subversion/tests/afl/afl-x509-testcase/test2bin0 -> 694 bytes
-rw-r--r--subversion/tests/afl/afl-x509-testcase/test3bin0 -> 810 bytes
-rwxr-xr-xsubversion/tests/cmdline/authz_tests.py34
-rwxr-xr-xsubversion/tests/cmdline/basic_tests.py71
-rwxr-xr-xsubversion/tests/cmdline/blame_tests.py3
-rwxr-xr-xsubversion/tests/cmdline/commit_tests.py3
-rwxr-xr-xsubversion/tests/cmdline/copy_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/dav-mirror-autocheck.sh55
-rwxr-xr-xsubversion/tests/cmdline/dav_tests.py82
-rwxr-xr-xsubversion/tests/cmdline/davautocheck.sh20
-rwxr-xr-xsubversion/tests/cmdline/depth_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/diff_tests.py112
-rwxr-xr-xsubversion/tests/cmdline/entries_tests.py9
-rwxr-xr-xsubversion/tests/cmdline/export_tests.py14
-rwxr-xr-xsubversion/tests/cmdline/externals_tests.py24
-rw-r--r--subversion/tests/cmdline/getopt_tests_data/svn--help_stdout11
-rw-r--r--subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout4
-rw-r--r--subversion/tests/cmdline/getopt_tests_data/svn_help_stdout11
-rwxr-xr-xsubversion/tests/cmdline/iprop_authz_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/lock_tests.py4
-rwxr-xr-xsubversion/tests/cmdline/log_tests.py8
-rwxr-xr-xsubversion/tests/cmdline/merge_authz_tests.py4
-rwxr-xr-xsubversion/tests/cmdline/merge_automatic_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/merge_reintegrate_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/merge_tests.py32
-rwxr-xr-xsubversion/tests/cmdline/merge_tree_conflict_tests.py4
-rwxr-xr-xsubversion/tests/cmdline/mergeinfo_tests.py2
-rwxr-xr-x[-rw-r--r--]subversion/tests/cmdline/mod_dav_svn_tests.py0
-rwxr-xr-xsubversion/tests/cmdline/move_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/patch_tests.py231
-rwxr-xr-xsubversion/tests/cmdline/prop_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/revert_tests.py9
-rwxr-xr-xsubversion/tests/cmdline/shelf_tests.py995
-rwxr-xr-xsubversion/tests/cmdline/shelve_tests.py176
-rwxr-xr-xsubversion/tests/cmdline/special_tests.py5
-rwxr-xr-xsubversion/tests/cmdline/stat_tests.py3
-rwxr-xr-xsubversion/tests/cmdline/svnadmin_tests.py135
-rwxr-xr-xsubversion/tests/cmdline/svnauthz_tests.py10
-rwxr-xr-xsubversion/tests/cmdline/svndumpfilter_tests.py8
-rwxr-xr-xsubversion/tests/cmdline/svnfsfs_tests.py3
-rwxr-xr-xsubversion/tests/cmdline/svnmover_tests.py6
-rwxr-xr-xsubversion/tests/cmdline/svnmucc_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/svnrdump_tests.py6
-rwxr-xr-xsubversion/tests/cmdline/svnsync_authz_tests.py2
-rwxr-xr-xsubversion/tests/cmdline/svnsync_tests.py2
-rw-r--r--subversion/tests/cmdline/svntest/actions.py5
-rw-r--r--subversion/tests/cmdline/svntest/main.py36
-rw-r--r--[-rwxr-xr-x]subversion/tests/cmdline/svntest/mergetrees.py0
-rw-r--r--subversion/tests/cmdline/svntest/sandbox.py24
-rw-r--r--subversion/tests/cmdline/svntest/tree.py15
-rw-r--r--subversion/tests/cmdline/svntest/verify.py103
-rw-r--r--subversion/tests/cmdline/svntest/wc.py3
-rwxr-xr-xsubversion/tests/cmdline/svnversion_tests.py18
-rwxr-xr-xsubversion/tests/cmdline/trans_tests.py6
-rwxr-xr-xsubversion/tests/cmdline/tree_conflict_tests.py49
-rwxr-xr-xsubversion/tests/cmdline/update_tests.py3
-rwxr-xr-xsubversion/tests/cmdline/upgrade_tests.py6
-rw-r--r--subversion/tests/libsvn_client/conflicts-test.c1046
-rw-r--r--subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c4
-rw-r--r--subversion/tests/libsvn_repos/authz-test.c39
-rw-r--r--subversion/tests/libsvn_repos/dump-load-test.c4
-rw-r--r--subversion/tests/libsvn_repos/repos-test.c2
-rw-r--r--subversion/tests/libsvn_subr/priority-queue-test.c6
-rw-r--r--subversion/tests/libsvn_wc/conflict-data-test.c11
-rw-r--r--subversion/tests/libsvn_wc/op-depth-test.c2
-rw-r--r--subversion/tests/libsvn_wc/utils.c3
-rw-r--r--subversion/tests/libsvn_wc/wc-queries-test.c1
-rw-r--r--subversion/tests/libsvn_wc/wc-test-queries.h2
-rw-r--r--subversion/tests/svn_test_main.c5
73 files changed, 3173 insertions, 419 deletions
diff --git a/subversion/tests/afl/afl-svndiff-testcase/test1 b/subversion/tests/afl/afl-svndiff-testcase/test1
new file mode 100644
index 0000000..987db36
--- /dev/null
+++ b/subversion/tests/afl/afl-svndiff-testcase/test1
Binary files differ
diff --git a/subversion/tests/afl/afl-svndiff-testcase/test2 b/subversion/tests/afl/afl-svndiff-testcase/test2
new file mode 100644
index 0000000..984cbf5
--- /dev/null
+++ b/subversion/tests/afl/afl-svndiff-testcase/test2
Binary files differ
diff --git a/subversion/tests/afl/afl-svndiff-testcase/test3 b/subversion/tests/afl/afl-svndiff-testcase/test3
new file mode 100644
index 0000000..102ee8e
--- /dev/null
+++ b/subversion/tests/afl/afl-svndiff-testcase/test3
Binary files differ
diff --git a/subversion/tests/afl/afl-svndiff.c b/subversion/tests/afl/afl-svndiff.c
new file mode 100644
index 0000000..4ff00bf
--- /dev/null
+++ b/subversion/tests/afl/afl-svndiff.c
@@ -0,0 +1,85 @@
+/*
+ * afl-svndiff.c an American Fuzz Lop test
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ */
+
+/* The input data can either be a file on disk or provided via stdin:
+
+ afl-svndiff some-file
+ afl-svndiff < some-file
+
+ In practice the file simply contains random binary data. The data
+ are interpreted as an svndiff as sent by a client to the server. */
+
+#include "svn_delta.h"
+#include "svn_cmdline.h"
+#include "svn_pools.h"
+#include "svn_io.h"
+
+#include <stdlib.h>
+
+static svn_error_t *
+txdelta_window_handler(svn_txdelta_window_t *window,
+ void *baton)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+parse(const char *filename, apr_pool_t *pool)
+{
+ svn_stringbuf_t *buf;
+ svn_stream_t *svndiff;
+ svn_stream_t *stream;
+
+ SVN_ERR(svn_stringbuf_from_file2(&buf, filename, pool));
+ svndiff = svn_stream_from_stringbuf(buf, pool);
+
+ stream = svn_txdelta_parse_svndiff(txdelta_window_handler, NULL, TRUE, pool);
+ SVN_ERR(svn_stream_copy3(svndiff, stream, NULL, NULL, pool));
+
+ return SVN_NO_ERROR;
+}
+
+int main(int argc, char **argv)
+{
+ apr_pool_t *pool;
+ int exit_code = EXIT_SUCCESS;
+ svn_error_t *err;
+ const char *filename;
+
+ if (argc == 2)
+ filename = argv[1];
+ else
+ filename = "-";
+
+ if (svn_cmdline_init("afl-svndiff", stderr) != EXIT_SUCCESS)
+ return EXIT_FAILURE;
+ pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
+
+ err = parse(filename, pool);
+ if (err)
+ exit_code = EXIT_FAILURE;
+ svn_error_clear(err);
+ svn_pool_destroy(pool);
+ return exit_code;
+}
diff --git a/subversion/tests/afl/afl-x509-testcase/test2 b/subversion/tests/afl/afl-x509-testcase/test2
new file mode 100644
index 0000000..716ef59
--- /dev/null
+++ b/subversion/tests/afl/afl-x509-testcase/test2
Binary files differ
diff --git a/subversion/tests/afl/afl-x509-testcase/test3 b/subversion/tests/afl/afl-x509-testcase/test3
new file mode 100644
index 0000000..68d6e7e
--- /dev/null
+++ b/subversion/tests/afl/afl-x509-testcase/test3
Binary files differ
diff --git a/subversion/tests/cmdline/authz_tests.py b/subversion/tests/cmdline/authz_tests.py
index 8878418..e818c65 100755
--- a/subversion/tests/cmdline/authz_tests.py
+++ b/subversion/tests/cmdline/authz_tests.py
@@ -168,7 +168,7 @@ def authz_read_access(sbox):
fws_url = B_url + '/folder with spaces'
fws_empty_folder_url = fws_url + '/empty folder'
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -280,7 +280,7 @@ def authz_write_access(sbox):
write_restrictive_svnserve_conf(sbox.repo_dir)
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E220004: Access denied.*"
@@ -367,7 +367,7 @@ def authz_checkout_test(sbox):
# 1st part: disable all read access, checkout should fail
# write an authz file with *= on /
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -502,7 +502,7 @@ def authz_log_and_tracing_test(sbox):
write_restrictive_svnserve_conf(sbox.repo_dir)
# write an authz file with *=rw on /
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -533,7 +533,7 @@ def authz_log_and_tracing_test(sbox):
# now disable read access on the first version of rho, keep the copy in
# /A/D readable.
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -551,7 +551,7 @@ def authz_log_and_tracing_test(sbox):
'log', '-r', '2', '--limit', '1',
wc_dir)
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err2 = expected_err
else:
expected_err2 = ".*svn: E220001: ((Unreadable path encountered; " \
@@ -593,7 +593,7 @@ def authz_log_and_tracing_test(sbox):
svntest.actions.run_and_verify_svn(None, expected_err2,
'cat', '-r', '2', D_url+'/rho')
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err2 = expected_err
else:
expected_err2 = ".*svn: E220001: Unreadable path encountered; access denied.*"
@@ -624,7 +624,7 @@ def authz_aliases(sbox):
write_restrictive_svnserve_conf(sbox.repo_dir)
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -669,9 +669,9 @@ def authz_validate(sbox):
write_authz_file(sbox, { "/" : "* = r",
"/A/B" : "@undefined_group = rw" })
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
- elif sbox.repo_url.startswith("svn"):
+ elif svntest.main.is_ra_type_svn():
expected_err = ".*Invalid authz configuration"
else:
expected_err = ".*@undefined_group.*"
@@ -688,9 +688,9 @@ devs1 = @admins, dev1
devs2 = @admins, dev2
devs = @devs1, dev3, dev4""" })
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
- elif sbox.repo_url.startswith("svn"):
+ elif svntest.main.is_ra_type_svn():
expected_err = ".*Invalid authz configuration"
else:
expected_err = ".*Circular dependency.*"
@@ -726,7 +726,7 @@ def authz_locking(sbox):
write_authz_file(sbox, {"/": "", "/A": "jrandom = rw"})
write_restrictive_svnserve_conf(sbox.repo_dir)
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: warning: W170001: Authorization failed.*"
@@ -781,7 +781,7 @@ def authz_locking(sbox):
svntest.actions.run_and_verify_info([{'Lock Token' : None}],
sbox.ospath('A/mu'))
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: warning: W160039: .*([Aa]uth.*perf|[Ff]orbidden).*"
else:
expected_err = ".*svn: warning: W170001: Authorization failed.*"
@@ -863,7 +863,7 @@ def authz_svnserve_anon_access_read(sbox):
# is not really a branch (it's the same URL), but we only care about
# authz here, not the semantics of the merge. (Merges had been
# failing in authz, for the reasons summarized in
- # http://subversion.tigris.org/issues/show_bug.cgi?id=2712#desc13.)
+ # https://issues.apache.org/jira/browse/SVN-2712#desc13.)
svntest.actions.run_and_verify_svn(None, [],
'merge', '-c', '2',
B_url, B_path)
@@ -981,7 +981,7 @@ def multiple_matches(sbox):
sbox.build(create_wc = False)
root_url = sbox.repo_url
write_restrictive_svnserve_conf(sbox.repo_dir)
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*svn: E175013: .*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
@@ -1137,7 +1137,7 @@ def case_sensitive_authz(sbox):
# error messages
expected_error_for_commit = ".*Commit failed.*"
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_error_for_cat = ".*[Ff]orbidden.*"
else:
expected_error_for_cat = ".*svn: E170001: Authorization failed.*"
diff --git a/subversion/tests/cmdline/basic_tests.py b/subversion/tests/cmdline/basic_tests.py
index 4b0b8d5..581a23b 100755
--- a/subversion/tests/cmdline/basic_tests.py
+++ b/subversion/tests/cmdline/basic_tests.py
@@ -3024,16 +3024,19 @@ def peg_rev_on_non_existent_wc_path(sbox):
# setup some history
sbox.simple_move('A', 'A2')
sbox.simple_move('A2/mu', 'A2/mu2')
- open(sbox.ospath('A2/mu2'), 'w').write('r2\n')
+ with open(sbox.ospath('A2/mu2'), 'w') as f:
+ f.write('r2\n')
sbox.simple_commit(message='r2')
#
sbox.simple_move('A2/mu2', 'A2/mu3')
sbox.simple_move('A2', 'A3')
- open(sbox.ospath('A3/mu3'), 'w').write('r3\n')
+ with open(sbox.ospath('A3/mu3'), 'w') as f:
+ f.write('r3\n')
sbox.simple_commit(message='r3')
#
sbox.simple_move('A3/mu3', 'A3/mu4')
- open(sbox.ospath('A3/mu4'), 'w').write('r4\n')
+ with open(sbox.ospath('A3/mu4'), 'w') as f:
+ f.write('r4\n')
sbox.simple_move('A3', 'A4')
sbox.simple_commit(message='r4')
@@ -3121,24 +3124,27 @@ def filtered_ls(sbox):
sbox.build(read_only=True)
path = sbox.repo_url + "/A/D"
- # check plain info
- expected = [ "H/omega\n",
- "gamma\n" ]
+ # with and without externals, because without externals on a 1.10+ server
+ # a server-side code path is used
+ for extra_opts in [ [], ['--include-externals'] ]:
- exit_code, output, error = svntest.actions.run_and_verify_svn(
- expected, [], 'ls', path, '--depth=infinity', '--search=*a')
+ expected = [ "H/omega\n",
+ "gamma\n" ]
- # check case-insensitivity
- exit_code, output, error = svntest.actions.run_and_verify_svn(
- expected, [], 'ls', path, '--depth=infinity', '--search=*A')
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ expected, [], 'ls', path, '--depth=infinity', '--search=*a', *extra_opts)
- expected = [ "H/\n" ]
- exit_code, output, error = svntest.actions.run_and_verify_svn(
- expected, [], 'ls', path, '--depth=infinity', '--search=h')
+ # check case-insensitivity
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ expected, [], 'ls', path, '--depth=infinity', '--search=*A', *extra_opts)
- # we don't match full paths
- exit_code, output, error = svntest.actions.run_and_verify_svn(
- [], [], 'ls', path, '--depth=infinity', '--search=*/*')
+ expected = [ "H/\n" ]
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ expected, [], 'ls', path, '--depth=infinity', '--search=h', *extra_opts)
+
+ # we don't match full paths
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ [], [], 'ls', path, '--depth=infinity', '--search=*/*', *extra_opts)
@Issue(4700)
@XFail(svntest.main.is_fs_type_fsx)
@@ -3197,6 +3203,36 @@ def null_prop_update_last_changed_revision(sbox):
'info', sbox.path('iota'),
'--show-item', 'last-changed-revision')
+@Skip(svntest.main.is_os_windows)
+def filtered_ls_top_level_path(sbox):
+ "filtered 'svn ls' top level path"
+
+ sbox.build(read_only=True, create_wc=False)
+ d_path = sbox.repo_url + "/A/B"
+ f_path = sbox.repo_url + "/A/B/lambda"
+
+ d_expected = svntest.verify.RegexListOutput([
+ r".* \./", # expect '*B*' to match its name which is 'B'
+ r".* E/beta",
+ r".* lambda" ])
+ f_expected = [ "lambda\n" ]
+
+ # with and without externals, because without externals on a 1.10+ server
+ # a server-side code path is used
+ for extra_opts in [ [], ['--include-externals'] ]:
+
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ d_expected, [], 'ls', '-v', d_path, '-R', '--search=*B*', *extra_opts)
+
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ f_expected, [], 'ls', f_path, '--search=lambda', *extra_opts)
+
+ # we don't match full paths, even for the top level path
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ [], [], 'ls', '-v', d_path, '-R', '--search=*/*', *extra_opts)
+ exit_code, output, error = svntest.actions.run_and_verify_svn(
+ [], [], 'ls', f_path, '--search=*/*', *extra_opts)
+
########################################################################
# Run the tests
@@ -3271,6 +3307,7 @@ test_list = [ None,
filtered_ls,
null_update_last_changed_revision,
null_prop_update_last_changed_revision,
+ filtered_ls_top_level_path,
]
if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/blame_tests.py b/subversion/tests/cmdline/blame_tests.py
index f824d25..8620dd4 100755
--- a/subversion/tests/cmdline/blame_tests.py
+++ b/subversion/tests/cmdline/blame_tests.py
@@ -958,7 +958,8 @@ def blame_youngest_to_oldest(sbox):
sbox.simple_commit() #r3
# Delete a line.
- open(iota_moved, 'w').write(line)
+ with open(iota_moved, 'w') as f:
+ f.write(line)
sbox.simple_commit() #r4
expected_output = [
diff --git a/subversion/tests/cmdline/commit_tests.py b/subversion/tests/cmdline/commit_tests.py
index 4f4a6f9..9bdb305 100755
--- a/subversion/tests/cmdline/commit_tests.py
+++ b/subversion/tests/cmdline/commit_tests.py
@@ -2823,7 +2823,8 @@ def commit_add_subadd(sbox):
# prepare targets file
targets = "A/D A/D/H A/D/H/chi A/D/H/omega A/D/H/psi".split()
- open(targets_file, 'w').write("\n".join(targets))
+ with open(targets_file, 'w') as f:
+ f.write("\n".join(targets))
# r2: rm A/D
sbox.simple_rm('A/D')
diff --git a/subversion/tests/cmdline/copy_tests.py b/subversion/tests/cmdline/copy_tests.py
index 3bb8599..c87f36f 100755
--- a/subversion/tests/cmdline/copy_tests.py
+++ b/subversion/tests/cmdline/copy_tests.py
@@ -4333,7 +4333,7 @@ def copy_added_dir_with_copy(sbox):
def copy_broken_symlink(sbox):
"""copy broken symlink"""
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3303. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3303. ##
sbox.build()
wc_dir = sbox.wc_dir
diff --git a/subversion/tests/cmdline/dav-mirror-autocheck.sh b/subversion/tests/cmdline/dav-mirror-autocheck.sh
index 298a8ba..7bc5e11 100755
--- a/subversion/tests/cmdline/dav-mirror-autocheck.sh
+++ b/subversion/tests/cmdline/dav-mirror-autocheck.sh
@@ -31,7 +31,7 @@
#
# The set of changes sent through the system is currently
# just the test case for issue 2939, using svnmucc
-# http://subversion.tigris.org/issues/show_bug.cgi?id=2939
+# https://issues.apache.org/jira/browse/SVN-2939
# But of course, any svn traffic liable to break over
# mirroring would be a good addition.
#
@@ -100,6 +100,7 @@ function setup_config() {
say "setting up config: " $1
cat > "$1" <<__EOF__
+$LOAD_MOD_MPM
$LOAD_MOD_LOG_CONFIG
$LOAD_MOD_MIME
$LOAD_MOD_UNIXD
@@ -114,9 +115,30 @@ $LOAD_MOD_AUTHZ_CORE
$LOAD_MOD_AUTHZ_USER
$LOAD_MOD_AUTHZ_HOST
+__EOF__
+
+if "$HTTPD" -v | grep '/2\.[012]' >/dev/null; then
+ cat >> "$1" <<__EOF__
LockFile lock
User $(id -un)
Group $(id -gn)
+__EOF__
+else
+HTTPD_LOCK="$HTTPD_ROOT/lock"
+mkdir "$HTTPD_LOCK" \
+ || fail "couldn't create lock directory '$HTTPD_LOCK'"
+ cat >> "$1" <<__EOF__
+# worker and prefork MUST have a mpm-accept lockfile in 2.3.0+
+<IfModule worker.c>
+ Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
+<IfModule prefork.c>
+ Mutex "file:$HTTPD_LOCK" mpm-accept
+</IfModule>
+__EOF__
+fi
+
+cat >> "$1" <<__EOF__
Listen ${TEST_PORT}
ServerName localhost
PidFile "${HTTPD_ROOT}/pid"
@@ -133,6 +155,9 @@ MaxRequestsPerChild 0
<IfModule worker.c>
ThreadsPerChild 8
</IfModule>
+<IfModule event.c>
+ ThreadsPerChild 8
+</IfModule>
MaxClients 16
HostNameLookups Off
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" format
@@ -202,6 +227,12 @@ function usage() {
SCRIPT=$(basename $0)
+NO_TESTS=
+if [ "x$1" = 'x--no-tests' ]; then
+ NO_TESTS=1
+ shift
+fi
+
if [ $# -ne 1 ] ; then
usage
fi
@@ -249,7 +280,7 @@ HTPASSWD=$(get_prog_name htpasswd htpasswd2) \
SVN=$ABS_BUILDDIR/subversion/svn/svn
SVNADMIN=$ABS_BUILDDIR/subversion/svnadmin/svnadmin
SVNSYNC=$ABS_BUILDDIR/subversion/svnsync/svnsync
-SVNMUCC=${SVNMUCC:-$ABS_BUILDDIR/tools/client-side/svnmucc/svnmucc}
+SVNMUCC=$ABS_BUILDDIR/subversion/svnmucc/svnmucc
SVNLOOK=$ABS_BUILDDIR/subversion/svnlook/svnlook
[ -x $HTTPD ] || fail "HTTPD '$HTTPD' not executable"
@@ -259,9 +290,7 @@ SVNLOOK=$ABS_BUILDDIR/subversion/svnlook/svnlook
[ -x $SVNADMIN ] || fail "SVNADMIN $SVNADMIN not built"
[ -x $SVNSYNC ] || fail "SVNSYNC $SVNSYNC not built"
[ -x $SVNLOOK ] || fail "SVNLOOK $SVNLOOK not built"
-[ -x $SVNMUCC ] \
- || fail SVNMUCC $SVNMUCC executable not built, needed for test. \
- \'cd $ABS_BUILDDIR\; make svnmucc\' to fix.
+[ -x $SVNMUCC ] || fail "SVNMUCC $SVNMUCC not built"
say HTTPD: $HTTPD
say SVN: $SVN
@@ -309,6 +338,10 @@ LOAD_MOD_AUTHN_FILE="$(get_loadmodule_config mod_authn_file)" \
LOAD_MOD_AUTHZ_USER="$(get_loadmodule_config mod_authz_user)" \
|| fail "Authz_User module not found."
}
+if [ ${APACHE_MPM:+set} ]; then
+ LOAD_MOD_MPM=$(get_loadmodule_config mod_mpm_$APACHE_MPM) \
+ || fail "MPM module not found"
+fi
if [ ${MODULE_PATH:+set} ]; then
MOD_DAV_SVN="$MODULE_PATH/mod_dav_svn.so"
@@ -365,7 +398,9 @@ $SVNADMIN create "$SLAVE_REPOS" || fail "create slave repos failed"
$SVNADMIN dump "$MASTER_REPOS" | $SVNADMIN load "$SLAVE_REPOS" \
|| fail "duplicate repositories failed"
# make sure uuid's match
-[ `cat "$SLAVE_REPOS/db/uuid"` = `cat "$MASTER_REPOS/db/uuid"` ] \
+read MASTER_UUID < "$MASTER_REPOS/db/uuid"
+read SLAVE_UUID < "$SLAVE_REPOS/db/uuid"
+[ "$SLAVE_UUID" = "$MASTER_UUID" ] \
|| fail "master/slave uuid mismatch"
# setup hooks:
# slave allows revprop changes
@@ -397,12 +432,18 @@ $SVNSYNC initialize --non-interactive "$SYNC_URL" "$MASTER_URL" \
--username=svnsync --password=svnsync \
|| fail "svnsync initialize failed"
+if [ $NO_TESTS ]; then
+ echo "MASTER_URL=$MASTER_URL"
+ echo "SLAVE_URL=$SLAVE_URL"
+ exit
+fi
+
# OK, let's start testing! Commit changes to slave, expect
# them to proxy through to the master, and then
# svnsync back to the slave
#
# reproducible test case from:
-# http://subversion.tigris.org/issues/show_bug.cgi?id=2939
+# https://issues.apache.org/jira/browse/SVN-2939
#
BASE_URL="$SLAVE_URL"
say running svnmucc test to $BASE_URL
diff --git a/subversion/tests/cmdline/dav_tests.py b/subversion/tests/cmdline/dav_tests.py
new file mode 100755
index 0000000..a99cdf5
--- /dev/null
+++ b/subversion/tests/cmdline/dav_tests.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+#
+# dav_tests.py: testing connections to HTTP and DAV servers.
+#
+# Subversion is a tool for revision control.
+# See http://subversion.apache.org for more information.
+#
+# ====================================================================
+# 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.
+######################################################################
+
+# General modules
+import os, re
+
+# Our testing module
+import svntest
+
+# (abbreviation)
+Skip = svntest.testcase.Skip_deco
+SkipUnless = svntest.testcase.SkipUnless_deco
+XFail = svntest.testcase.XFail_deco
+Issues = svntest.testcase.Issues_deco
+Issue = svntest.testcase.Issue_deco
+Wimp = svntest.testcase.Wimp_deco
+Item = svntest.wc.StateItem
+
+
+######################################################################
+# Tests
+#
+# Each test must return on success or raise on failure.
+
+
+#----------------------------------------------------------------------
+
+@SkipUnless(svntest.main.is_ra_type_dav)
+def connect_plain_http_server(sbox):
+ "connect to a non-DAV HTTP server"
+ expected_errors = svntest.verify.RegexListOutput([
+ "^svn: E170013: Unable to connect to a repository at URL '[^']+'",
+ "^svn: E175003: The server at '[^']+' does not support the HTTP/DAV protocol"
+ ], False)
+ svntest.actions.run_and_verify_svn([], expected_errors,
+ 'info', svntest.main.non_dav_root_url)
+
+@SkipUnless(svntest.main.is_ra_type_dav)
+def connect_other_dav_server(sbox):
+ "connect to a DAV server which is not an SVN server"
+ svntest.actions.run_and_verify_svn([], svntest.verify.AnyOutput,
+ 'info', svntest.main.other_dav_root_url)
+
+########################################################################
+# Run the tests
+
+
+# list all tests here, starting with None:
+test_list = [ None,
+ connect_plain_http_server,
+ connect_other_dav_server,
+ ]
+
+if __name__ == '__main__':
+ svntest.main.run_tests(test_list)
+ # NOTREACHED
+
+
+### End of file.
diff --git a/subversion/tests/cmdline/davautocheck.sh b/subversion/tests/cmdline/davautocheck.sh
index 064feb1..7170b77 100755
--- a/subversion/tests/cmdline/davautocheck.sh
+++ b/subversion/tests/cmdline/davautocheck.sh
@@ -281,6 +281,9 @@ say "Using '$HTPASSWD'..."
LOAD_MOD_DAV=$(get_loadmodule_config mod_dav) \
|| fail "DAV module not found"
+LOAD_MOD_DAV_FS=$(get_loadmodule_config mod_dav_fs) \
+ || fail "Filesystem DAV module not found"
+
LOAD_MOD_LOG_CONFIG=$(get_loadmodule_config mod_log_config) \
|| fail "log_config module not found"
@@ -447,6 +450,7 @@ $LOAD_MOD_MIME
$LOAD_MOD_ALIAS
$LOAD_MOD_UNIXD
$LOAD_MOD_DAV
+$LOAD_MOD_DAV_FS
LoadModule dav_svn_module "$MOD_DAV_SVN"
$LOAD_MOD_AUTH
$LOAD_MOD_AUTHN_CORE
@@ -482,6 +486,13 @@ mkdir "$HTTPD_LOCK" \
__EOF__
fi
+HTTPD_DAV="$HTTPD_ROOT/dav"
+mkdir "$HTTPD_DAV" \
+ || fail "couldn't create DAV lock directory '$HTTPD_DAV'"
+cat >> "$HTTPD_CFG" <<__EOF__
+DavLockDB "$HTTPD_DAV/lock.db"
+__EOF__
+
if [ ${USE_SSL:+set} ]; then
cat >> "$HTTPD_CFG" <<__EOF__
SSLEngine on
@@ -525,6 +536,15 @@ CustomLog "$HTTPD_ROOT/ops" "%t %u %{SVN-REPOS-NAME}e %{SVN-ACTION}e"
#Require all granted
</Directory>
+Alias /nodavroot $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/nodavroot
+<Directory $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/nodavroot>
+</Directory>
+
+Alias /fsdavroot $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/fsdavroot
+<Directory $ABS_BUILDDIR/subversion/tests/cmdline/svn-test-work/fsdavroot>
+ DAV filesystem
+</Directory>
+
<Location /svn-test-work/repositories>
__EOF__
location_common() {
diff --git a/subversion/tests/cmdline/depth_tests.py b/subversion/tests/cmdline/depth_tests.py
index 1277594..18ce379 100755
--- a/subversion/tests/cmdline/depth_tests.py
+++ b/subversion/tests/cmdline/depth_tests.py
@@ -1073,7 +1073,7 @@ def commit_depth_immediates(sbox):
# Message-ID: <46968831.2070906@collab.net>
# Date: Thu, 12 Jul 2007 15:59:45 -0400
#
- # See also http://subversion.tigris.org/issues/show_bug.cgi?id=2882.
+ # See also https://issues.apache.org/jira/browse/SVN-2882.
#
# Outline of the test:
# ====================
diff --git a/subversion/tests/cmdline/diff_tests.py b/subversion/tests/cmdline/diff_tests.py
index 905bce3..63caa24 100755
--- a/subversion/tests/cmdline/diff_tests.py
+++ b/subversion/tests/cmdline/diff_tests.py
@@ -36,7 +36,6 @@ from svntest import err, wc
from prop_tests import binary_mime_type_on_text_file_warning
from svntest.verify import make_diff_header, make_no_diff_deleted_header, \
- make_diff_header, make_no_diff_deleted_header, \
make_git_diff_header, make_diff_prop_header, \
make_diff_prop_val, make_diff_prop_deleted, \
make_diff_prop_added, make_diff_prop_modified
@@ -2712,7 +2711,7 @@ def diff_ignore_eolstyle(sbox):
" Bb\n",
"-Cc\n",
"+Cc\n",
- "\ No newline at end of file\n" ]
+ "\\ No newline at end of file\n" ]
svntest.actions.run_and_verify_svn(expected_output, [],
'diff', '-x', '--ignore-eol-style',
@@ -3855,7 +3854,7 @@ def diff_arbitrary_files_and_dirs(sbox):
sbox.build()
wc_dir = sbox.wc_dir
- # diff iota with A/mu
+ # diff files (iota with A/mu)
expected_output = make_diff_header("iota", "working copy", "working copy",
"iota", "A/mu") + [
"@@ -1 +1 @@\n",
@@ -3866,7 +3865,11 @@ def diff_arbitrary_files_and_dirs(sbox):
'diff', '--old', sbox.ospath('iota'),
'--new', sbox.ospath('A/mu'))
- # diff A/B/E with A/D
+ # diff dirs (A/B/E with A/D)
+ # .../gamma is to show as replaced; .../beta is to show as modified
+ sbox.simple_mkdir('A/B/E/gamma')
+ sbox.simple_propset('p', 'v', 'A/B/E/gamma')
+ sbox.simple_add_text("This is a different beta file.\n", 'A/D/beta')
expected_output = make_diff_header("G/pi", "nonexistent", "working copy",
"B/E", "D") + [
"@@ -0,0 +1 @@\n",
@@ -3896,11 +3899,16 @@ def diff_arbitrary_files_and_dirs(sbox):
"@@ -1 +0,0 @@\n",
"-This is the file 'alpha'.\n"
] + make_diff_header("beta", "working copy",
- "nonexistent", "B/E", "D") + [
- "@@ -1 +0,0 @@\n",
- "-This is the file 'beta'.\n"
- ] + make_diff_header("gamma", "nonexistent",
"working copy", "B/E", "D") + [
+ "@@ -1 +1 @@\n",
+ "-This is the file 'beta'.\n",
+ "+This is a different beta file.\n"
+ ] + make_diff_header("gamma", "working copy",
+ "nonexistent", "B/E", "D") \
+ + make_diff_prop_header("gamma") \
+ + make_diff_prop_deleted("p", "v") \
+ + make_diff_header("gamma", "nonexistent",
+ "working copy", "B/E", "D") + [
"@@ -0,0 +1 @@\n",
"+This is the file 'gamma'.\n"
]
@@ -5160,6 +5168,90 @@ def diff_unversioned_files_git(sbox):
'--old', sbox.ospath('foo'),
'--new', sbox.ospath('A/bar'))
+# Summary diff with a repository source side and a local copy target side.
+# This particular combination crashed in 1.10.0 and earlier releases.
+def diff_summary_repo_wc_local_copy(sbox):
+ "diff summary repo wc local copy"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ sbox.simple_copy('iota', 'iota2')
+ sbox.simple_append('iota2', 'hello\n')
+ expected_diff = svntest.wc.State(wc_dir, {
+ 'iota': Item(status='M '),
+ })
+ svntest.actions.run_and_verify_diff_summarize(
+ expected_diff,
+ '--old=' + sbox.ospath('iota') + '@HEAD',
+ '--new=' + sbox.ospath('iota2'))
+
+# Summary diff with a repository source side and a local copy target side.
+# Svn reported the unmodified copy as modified in 1.10.0 and earlier releases.
+@XFail()
+def diff_summary_repo_wc_local_copy_unmodified(sbox):
+ "diff summary repo wc local copy unmodified"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+
+ sbox.simple_copy('iota', 'iota2')
+ expected_diff = svntest.wc.State(wc_dir, {
+ })
+ svntest.actions.run_and_verify_diff_summarize(
+ expected_diff,
+ '--old=' + sbox.ospath('iota') + '@HEAD',
+ '--new=' + sbox.ospath('iota2'))
+
+# Fails with "Can't open file '.../iota': Too many levels of symbolic links"
+# on Unix.
+@XFail()
+@Skip(svntest.main.is_os_windows)
+def diff_file_replaced_by_symlink(sbox):
+ "diff base vs working: symlink replaces a file"
+ sbox.build(read_only=True)
+ wc_dir = sbox.wc_dir
+
+ iota_path = sbox.ospath('iota')
+ os.remove(iota_path)
+
+ # create a symlink pointing to itself
+ # alternatively it could point to a non-existing path
+ sbox.simple_symlink('iota', 'iota')
+
+ # TODO: add a full expected output
+ expected_output = svntest.verify.AnyOutput
+ svntest.actions.run_and_verify_svn(expected_output, [], 'diff', wc_dir)
+
+# Test 'svn diff --git' with a copy.
+#
+# When this diff is rooted at a path below the repository root directory,
+# it errored out while printing the git diff header, due to confusion of
+# diff-relative and repository-relative copyfrom paths.
+@XFail()
+def diff_git_format_copy(sbox):
+ "diff git format copy"
+ sbox.build(create_wc=False)
+ svntest.actions.run_and_verify_svn(None, [], 'checkout',
+ sbox.repo_url + '/A/B',
+ sbox.wc_dir)
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ sbox.simple_copy('E/alpha', 'alpha_copied')
+ sbox.simple_append('alpha_copied', "This is a copy of 'alpha'.\n")
+
+ expected_output = \
+ make_git_diff_header('alpha_copied', 'A/B/alpha_copied',
+ "revision 1", "working copy",
+ copyfrom_path="A/B/E/alpha",
+ copyfrom_rev='1', cp=True,
+ text_changes=True) + [
+ "@@ -1 +1,2 @@\n",
+ " This is the file 'alpha'.\n",
+ "+This is a copy of 'alpha'.\n",
+ ]
+
+ svntest.actions.run_and_verify_svn(expected_output, [], 'diff',
+ '--git', '.')
########################################################################
#Run the tests
@@ -5257,6 +5349,10 @@ test_list = [ None,
diff_symlinks,
diff_peg_resolve,
diff_unversioned_files_git,
+ diff_summary_repo_wc_local_copy,
+ diff_summary_repo_wc_local_copy_unmodified,
+ diff_file_replaced_by_symlink,
+ diff_git_format_copy,
]
if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/entries_tests.py b/subversion/tests/cmdline/entries_tests.py
index 0fcdc1a..78ba037 100755
--- a/subversion/tests/cmdline/entries_tests.py
+++ b/subversion/tests/cmdline/entries_tests.py
@@ -121,14 +121,17 @@ def basic_entries(sbox):
G_path, iota_path)
# Add a file over the DELETED 'alpha'. It should be schedule-add.
- open(alpha_path, 'w').write('New alpha contents\n')
+ with open(alpha_path, 'w') as f:
+ f.write('New alpha contents\n')
# Delete 'beta', then add a file over it. Should be schedule-replace.
svntest.actions.run_and_verify_svn(None, [], 'rm', beta_path)
- open(beta_path, 'w').write('New beta contents\n')
+ with open(beta_path, 'w') as f:
+ f.write('New beta contents\n')
# Plain old add. Should have revision == 0.
- open(added_path, 'w').write('Added file contents\n')
+ with open(added_path, 'w') as f:
+ f.write('Added file contents\n')
svntest.actions.run_and_verify_svn(None, [], 'add',
alpha_path, beta_path, added_path)
diff --git a/subversion/tests/cmdline/export_tests.py b/subversion/tests/cmdline/export_tests.py
index a8ac7b8..c524ff3 100755
--- a/subversion/tests/cmdline/export_tests.py
+++ b/subversion/tests/cmdline/export_tests.py
@@ -610,7 +610,8 @@ def export_file_overwrite_fails(sbox):
os.mkdir(tmpdir)
# Run it for source local
- open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+ with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+ f.write(not_iota_contents)
svntest.actions.run_and_verify_svn([], '.*exist.*',
'export', iota_path, tmpdir)
@@ -621,7 +622,8 @@ def export_file_overwrite_fails(sbox):
svntest.actions.verify_disk(tmpdir, expected_disk)
# Run it for source URL
- open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+ with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+ f.write(not_iota_contents)
svntest.actions.run_and_verify_svn([], '.*exist.*',
'export', iota_url, tmpdir)
@@ -721,7 +723,7 @@ def export_working_copy_ignoring_keyword_translation(sbox):
def export_with_url_unsafe_characters(sbox):
"export file with URL unsafe characters"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3683 ##
+ ## See https://issues.apache.org/jira/browse/SVN-3683 ##
sbox.build()
wc_dir = sbox.wc_dir
@@ -904,14 +906,16 @@ def export_file_overwrite_with_force(sbox):
})
# Run it for WC export
- open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+ with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+ f.write(not_iota_contents)
svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput,
[], 'export', '--force',
iota_path, tmpdir)
svntest.actions.verify_disk(tmpdir, expected_disk)
# Run it for URL export
- open(os.path.join(tmpdir, 'iota'), 'w').write(not_iota_contents)
+ with open(os.path.join(tmpdir, 'iota'), 'w') as f:
+ f.write(not_iota_contents)
svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput,
[], 'export', '--force',
iota_url, tmpdir)
diff --git a/subversion/tests/cmdline/externals_tests.py b/subversion/tests/cmdline/externals_tests.py
index c0ac029..ba1320c 100755
--- a/subversion/tests/cmdline/externals_tests.py
+++ b/subversion/tests/cmdline/externals_tests.py
@@ -1146,7 +1146,7 @@ def external_into_path_with_spaces(sbox):
repo_url = sbox.repo_url
ext = '^/A/D "A/copy of D"\n' +\
- '^/A/D A/another\ copy\ of\ D'
+ '^/A/D A/another\\ copy\\ of\\ D'
change_external(wc_dir, ext)
expected_output = svntest.wc.State(wc_dir, {
@@ -2818,7 +2818,7 @@ def remap_file_external_with_prop_del(sbox):
# Now update to bring the new external down.
# This previously segfaulted as described in
- # http://subversion.tigris.org/issues/show_bug.cgi?id=4093#desc1
+ # https://issues.apache.org/jira/browse/SVN-4093#desc1
svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
@@ -3871,12 +3871,14 @@ def copy_pin_externals_whitespace_dir(sbox):
extdef = sbox.get_tempname('extdef')
info = sbox.get_tempname('info')
- open(extdef, 'w').write(
+ with open(extdef, 'w') as f:
+ f.write(
'"' + ss_path +'/deps/sqlite" ext/sqlite\n' +
'"^/deps/A P R" \'ext/A P R\'\n' +
- '^/deps/B\ D\ B\' ext/B\ D\ B\'\n' +
+ '^/deps/B\\ D\\ B\' ext/B\\ D\\ B\'\n' +
repo_url + '/deps/wors%23+t ext/wors#+t')
- open(info, 'w').write('info\n')
+ with open(info, 'w') as f:
+ f.write('info\n')
svntest.actions.run_and_verify_svnmucc(None, [], '-U', repo_url,
'mkdir', 'trunk',
@@ -4408,7 +4410,17 @@ def update_dir_external_exclude(sbox):
# Create an external in r2
sbox.simple_propset('svn:externals', '^/A/D/H X', 'A/B/E')
sbox.simple_commit()
- sbox.simple_update()
+
+ # Update to fetch externals
+ expected_output = svntest.wc.State(sbox.wc_dir, {
+ 'A/B/E/X/chi' : Item(status='A '),
+ 'A/B/E/X/omega' : Item(status='A '),
+ 'A/B/E/X/psi' : Item(status='A '),
+ })
+ svntest.actions.run_and_verify_update(sbox.wc_dir,
+ expected_output, None, None,
+ [], False,
+ sbox.ospath('A/B/E'))
# Now make A/B/E shallow by updating with "--set-depth exclude"
expected_output = svntest.wc.State(sbox.wc_dir, {
diff --git a/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout b/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
index 05f0aa8..ff73b51 100644
--- a/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
+++ b/subversion/tests/cmdline/getopt_tests_data/svn--help_stdout
@@ -47,9 +47,14 @@ Available subcommands:
unlock
update (up)
upgrade
- x-shelve (shelve)
- x-unshelve (unshelve)
- x-shelves (shelves)
+ 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/
diff --git a/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout b/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
index 5dddc70..2b81052 100644
--- a/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
+++ b/subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
@@ -84,6 +84,10 @@ usage: 1. log [PATH][@REV]
was created:
svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo
+ Show all log messages for commits between the tags ^/tags/2.0 and
+ ^/tags/3.0; assuming that tag 2.0 was created in revision 100:
+ svn log -rHEAD:100 ^/tags/3.0
+
If ^/trunk/foo.c was moved to ^/trunk/bar.c' in revision 22, 'svn log -v'
shows a deletion and a copy in its changed paths list, such as:
D /trunk/foo.c
diff --git a/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout b/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
index 05f0aa8..ff73b51 100644
--- a/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
+++ b/subversion/tests/cmdline/getopt_tests_data/svn_help_stdout
@@ -47,9 +47,14 @@ Available subcommands:
unlock
update (up)
upgrade
- x-shelve (shelve)
- x-unshelve (unshelve)
- x-shelves (shelves)
+ 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/
diff --git a/subversion/tests/cmdline/iprop_authz_tests.py b/subversion/tests/cmdline/iprop_authz_tests.py
index 835cd37..c2a9d10 100755
--- a/subversion/tests/cmdline/iprop_authz_tests.py
+++ b/subversion/tests/cmdline/iprop_authz_tests.py
@@ -105,7 +105,7 @@ def iprops_authz(sbox):
write_authz_file(sbox, {
"/" : svntest.main.wc_author + "=rw",
"/A/D/H/psi" : svntest.main.wc_author + "=",})
- if sbox.repo_url.startswith("http"):
+ if svntest.main.is_ra_type_dav():
expected_err = ".*[Ff]orbidden.*"
else:
expected_err = ".*svn: E170001: Authorization failed.*"
diff --git a/subversion/tests/cmdline/lock_tests.py b/subversion/tests/cmdline/lock_tests.py
index cd8e0d2..3e2952c 100755
--- a/subversion/tests/cmdline/lock_tests.py
+++ b/subversion/tests/cmdline/lock_tests.py
@@ -1571,7 +1571,8 @@ def cp_isnt_ro(sbox):
mu2_path = sbox.ospath('A/mu2')
mu3_path = sbox.ospath('A/mu3')
kappa_path = sbox.ospath('kappa')
- open(kappa_path, 'w').write("This is the file 'kappa'.\n")
+ with open(kappa_path, 'w') as f:
+ f.write("This is the file 'kappa'.\n")
## added file
sbox.simple_add('kappa')
@@ -2251,7 +2252,6 @@ def dav_lock_refresh(sbox):
if r.status != httplib.OK:
raise svntest.Failure('Lock refresh failed: %d %s' % (r.status, r.reason))
-@SkipUnless(svntest.main.is_ra_type_dav)
def delete_locked_file_with_percent(sbox):
"lock and delete a file called 'a %( ) .txt'"
diff --git a/subversion/tests/cmdline/log_tests.py b/subversion/tests/cmdline/log_tests.py
index 484103a..559dfb5 100755
--- a/subversion/tests/cmdline/log_tests.py
+++ b/subversion/tests/cmdline/log_tests.py
@@ -1592,7 +1592,7 @@ def merge_sensitive_log_added_mergeinfo_replaces_inherited(sbox):
# a merge results in added explicit mergeinfo on a path, but that
# path previously inherited mergeinfo (rather than had no explicit
# or inherited mergeinfo). See issue #3235, specifically
- # http://subversion.tigris.org/issues/show_bug.cgi?id=3235#desc8.
+ # https://issues.apache.org/jira/browse/SVN-3235#desc8.
sbox.build()
wc_dir = sbox.wc_dir
@@ -1752,7 +1752,7 @@ def merge_sensitive_log_added_mergeinfo_replaces_inherited(sbox):
def merge_sensitive_log_propmod_merge_inheriting_path(sbox):
"log -g and simple propmod to merge-inheriting path"
- # Issue #3285 (http://subversion.tigris.org/issues/show_bug.cgi?id=3285)
+ # Issue #3285 (https://issues.apache.org/jira/browse/SVN-3285)
sbox.build()
wc_dir = sbox.wc_dir
@@ -2166,13 +2166,13 @@ def log_diff(sbox):
+ [ "@@ -1 +1,2 @@\n",
" This is the file 'beta'.\n",
"+9\n",
- "\ No newline at end of file\n",
+ "\\ No newline at end of file\n",
]
]
r8diff = [ make_diff_header('A2/D/G/rho', 'nonexistent', 'revision 8')
+ [ "@@ -0,0 +1 @@\n",
"+88\n",
- "\ No newline at end of file\n",
+ "\\ No newline at end of file\n",
]
]
log_chain = parse_log_output(output, with_diffs=True)
diff --git a/subversion/tests/cmdline/merge_authz_tests.py b/subversion/tests/cmdline/merge_authz_tests.py
index 8e14089..b26e9c7 100755
--- a/subversion/tests/cmdline/merge_authz_tests.py
+++ b/subversion/tests/cmdline/merge_authz_tests.py
@@ -486,7 +486,7 @@ def mergeinfo_and_skipped_paths(sbox):
def merge_fails_if_subtree_is_deleted_on_src(sbox):
"merge fails if subtree is deleted on src"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2876. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2876. ##
# Create a WC
sbox.build()
@@ -613,7 +613,7 @@ def reintegrate_fails_if_no_root_access(sbox):
# should be able to reintegrate, regardless of what authorization
# they have to parents of the source and target.
#
- # See http://subversion.tigris.org/issues/show_bug.cgi?id=3242#desc78
+ # See https://issues.apache.org/jira/browse/SVN-3242#desc78
# Some paths we'll care about
wc_dir = sbox.wc_dir
diff --git a/subversion/tests/cmdline/merge_automatic_tests.py b/subversion/tests/cmdline/merge_automatic_tests.py
index f4bb231..fa00e04 100755
--- a/subversion/tests/cmdline/merge_automatic_tests.py
+++ b/subversion/tests/cmdline/merge_automatic_tests.py
@@ -1163,7 +1163,7 @@ def effective_sync_results_in_reintegrate(sbox):
# Now try an explicit --reintegrate merge from ^/branch to A.
# This should work because since the resolution of
- # http://subversion.tigris.org/issues/show_bug.cgi?id=3577
+ # https://issues.apache.org/jira/browse/SVN-3577
# if B is *effectively* synced with A, then B can be reintegrated
# to A.
sbox.simple_update()
diff --git a/subversion/tests/cmdline/merge_reintegrate_tests.py b/subversion/tests/cmdline/merge_reintegrate_tests.py
index 7a27373..1479400 100755
--- a/subversion/tests/cmdline/merge_reintegrate_tests.py
+++ b/subversion/tests/cmdline/merge_reintegrate_tests.py
@@ -1363,7 +1363,7 @@ def reintegrate_with_subtree_mergeinfo(sbox):
# how can any prop changes be merged to it? The answer is that
# the merge code does some quiet housekeeping, merging gamma_moved's
# inherited mergeinfo into its incoming mergeinfo, see
- # http://subversion.tigris.org/issues/show_bug.cgi?id=4309
+ # https://issues.apache.org/jira/browse/SVN-4309
# This test is not covering issue #4309 so we let the current
# behavior pass.
# r17 - B) Synch merge from A to A_COPY
diff --git a/subversion/tests/cmdline/merge_tests.py b/subversion/tests/cmdline/merge_tests.py
index a67dada..22b02dd 100755
--- a/subversion/tests/cmdline/merge_tests.py
+++ b/subversion/tests/cmdline/merge_tests.py
@@ -697,7 +697,7 @@ def simple_property_merges(sbox):
def merge_similar_unrelated_trees(sbox):
"merging similar trees ancestrally unrelated"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=1249. ##
+ ## See https://issues.apache.org/jira/browse/SVN-1249. ##
sbox.build()
wc_dir = sbox.wc_dir
@@ -3939,7 +3939,7 @@ def avoid_repeated_merge_on_subtree_with_merge_info(sbox):
check_props=True)
# Test for part of Issue #2821, see
- # http://subversion.tigris.org/issues/show_bug.cgi?id=2821#desc22
+ # https://issues.apache.org/jira/browse/SVN-2821#desc22
#
# Revert all local changes.
svntest.actions.run_and_verify_svn(None, [], 'revert', '-R', wc_dir)
@@ -6007,7 +6007,7 @@ def foreign_repos_does_not_update_mergeinfo(sbox):
def avoid_reflected_revs(sbox):
"avoid repeated merges for cyclic merging"
- # See <http://subversion.tigris.org/issues/show_bug.cgi?id=2897>.
+ # See <https://issues.apache.org/jira/browse/SVN-2897>.
#
# This test cherry-picks some changes (all of them, in fact) from the
# parent branch 'A' to the child branch 'A_COPY', and then tries to
@@ -8294,7 +8294,7 @@ def cherry_picking(sbox):
def propchange_of_subdir_raises_conflict(sbox):
"merge of propchange on subdir raises conflict"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2969. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2969. ##
# Create a WC with a single branch
sbox.build()
@@ -8515,7 +8515,7 @@ def reverse_merge_prop_add_on_child(sbox):
def merge_target_with_non_inheritable_mergeinfo(sbox):
"merge target with non inheritable mergeinfo"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2970. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2970. ##
# Create a WC with a single branch
sbox.build()
@@ -8547,7 +8547,7 @@ def merge_target_with_non_inheritable_mergeinfo(sbox):
expected_output = wc.State(A_COPY_B_path, {
'lambda' : Item(status='U '),
})
- # Issue #3642 http://subversion.tigris.org/issues/show_bug.cgi?id=3642
+ # Issue #3642 https://issues.apache.org/jira/browse/SVN-3642
#
# We don't expect A_COPY/B/F to have mergeinfo recorded on it because
# not only is it unaffected by the merge at depth immediates, it could
@@ -8820,7 +8820,7 @@ def merge_from_renamed_branch_fails_while_avoiding_repeat_merge(sbox):
#Merge r4 from A/RENAMED_C to A/C
#Merge r2:5 from A/RENAMED_C to A/C <-- This fails tracked via #3032.
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3032. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3032. ##
# Create a WC with a single branch
sbox.build()
@@ -9250,7 +9250,7 @@ def new_subtrees_should_not_break_merge(sbox):
# so we expect only subtree merges on A_COPY/D, A_COPY_D_H, and
# A_COPY/D/H/nu. The fact that A/D/H/nu doesn't exist at r6 should not cause
# the merge to fail -- see
- # http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc7.
+ # https://issues.apache.org/jira/browse/SVN-3067#desc7.
expected_output = wc.State(A_COPY_path, {
'D/H/omega': Item(status='U '),
})
@@ -10495,7 +10495,7 @@ def reverse_merge_away_all_mergeinfo(sbox):
# Another test for issue #3067: 'subtrees with intersecting mergeinfo,
# that don't exist at the start of a merge range shouldn't break the
# merge'. Specifically see
-# http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc5
+# https://issues.apache.org/jira/browse/SVN-3067#desc5
@SkipUnless(server_has_mergeinfo)
@Issues(3138,3067,4217)
def dont_merge_revs_into_subtree_that_predate_it(sbox):
@@ -10614,7 +10614,7 @@ def dont_merge_revs_into_subtree_that_predate_it(sbox):
# Cherry harvest all eligible revisions from 'A/D/H' to 'H_COPY'.
#
# This is where we see the problem described in
- # http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc5.
+ # https://issues.apache.org/jira/browse/SVN-3067#desc5.
#
# Use run_and_verify_svn() because run_and_verify_merge*() require
# explicit revision ranges.
@@ -11786,7 +11786,7 @@ def subtree_source_missing_in_requested_range(sbox):
# Another test for issue #3067: 'subtrees that don't exist at the start
# or end of a merge range shouldn't break the merge'
#
-# See http://subversion.tigris.org/issues/show_bug.cgi?id=3067#desc34
+# See https://issues.apache.org/jira/browse/SVN-3067#desc34
@Issue(3067)
@SkipUnless(server_has_mergeinfo)
def subtrees_with_empty_mergeinfo(sbox):
@@ -13305,7 +13305,7 @@ def no_self_referential_filtering_on_added_path(sbox):
# how can any prop changes be merged to it? The answer is that
# the merge code does some quiet housekeeping, merging C_MOVED's
# inherited mergeinfo into its incoming mergeinfo, see
- # http://subversion.tigris.org/issues/show_bug.cgi?id=4309
+ # https://issues.apache.org/jira/browse/SVN-4309
# This test is not covering issue #4309 so we let the current
# behavior pass.
expected_mergeinfo_output = wc.State(A_COPY_2_path, {
@@ -13374,7 +13374,7 @@ def no_self_referential_filtering_on_added_path(sbox):
#----------------------------------------------------------------------
# Test for issue #3324
-# http://subversion.tigris.org/issues/show_bug.cgi?id=3324
+# https://issues.apache.org/jira/browse/SVN-3324
@Issue(3324)
@SkipUnless(server_has_mergeinfo)
def merge_range_prior_to_rename_source_existence(sbox):
@@ -13881,7 +13881,7 @@ def dont_merge_gaps_in_history(sbox):
#----------------------------------------------------------------------
# Test for issue #3432 'Merge can record mergeinfo from natural history
-# gaps'. See http://subversion.tigris.org/issues/show_bug.cgi?id=3432
+# gaps'. See https://issues.apache.org/jira/browse/SVN-3432
@Issue(3432)
@SkipUnless(server_has_mergeinfo)
def handle_gaps_in_implicit_mergeinfo(sbox):
@@ -15122,7 +15122,7 @@ def foreign_repos_del_and_props(sbox):
#----------------------------------------------------------------------
# Test for issue #3642 'immediate depth merges don't create proper subtree
-# mergeinfo'. See http://subversion.tigris.org/issues/show_bug.cgi?id=3642
+# mergeinfo'. See https://issues.apache.org/jira/browse/SVN-3642
@Issue(3642)
def immediate_depth_merge_creates_minimal_subtree_mergeinfo(sbox):
"no spurious mergeinfo from immediate depth merges"
@@ -15817,7 +15817,7 @@ def subtree_merges_inherit_invalid_working_mergeinfo(sbox):
#----------------------------------------------------------------------
# Test for issue #3686 'executable flag not correctly set on merge'
-# See http://subversion.tigris.org/issues/show_bug.cgi?id=3686
+# See https://issues.apache.org/jira/browse/SVN-3686
@Issue(3686)
@SkipUnless(server_has_mergeinfo)
@SkipUnless(svntest.main.is_posix_os)
diff --git a/subversion/tests/cmdline/merge_tree_conflict_tests.py b/subversion/tests/cmdline/merge_tree_conflict_tests.py
index 4f1805b..344c2d2 100755
--- a/subversion/tests/cmdline/merge_tree_conflict_tests.py
+++ b/subversion/tests/cmdline/merge_tree_conflict_tests.py
@@ -540,7 +540,7 @@ def merge_add_over_versioned_file_conflicts(sbox):
def mergeinfo_recording_in_skipped_merge(sbox):
"mergeinfo recording in skipped merge"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2829. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2829. ##
# Create a WC with a single branch
sbox.build()
@@ -705,7 +705,7 @@ def del_differing_file(sbox):
def tree_conflicts_and_obstructions(sbox):
"tree conflicts and obstructions"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3146. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3146. ##
sbox.build()
wc_dir = sbox.wc_dir
diff --git a/subversion/tests/cmdline/mergeinfo_tests.py b/subversion/tests/cmdline/mergeinfo_tests.py
index 328a9f2..fccf5fb 100755
--- a/subversion/tests/cmdline/mergeinfo_tests.py
+++ b/subversion/tests/cmdline/mergeinfo_tests.py
@@ -196,7 +196,7 @@ def mergeinfo_on_unknown_url(sbox):
# Test for issue #3126 'svn mergeinfo shows too few or too many
# eligible revisions'. Specifically
-# http://subversion.tigris.org/issues/show_bug.cgi?id=3126#desc5.
+# https://issues.apache.org/jira/browse/SVN-3126#desc5.
@SkipUnless(server_has_mergeinfo)
@Issue(3126)
def non_inheritable_mergeinfo(sbox):
diff --git a/subversion/tests/cmdline/mod_dav_svn_tests.py b/subversion/tests/cmdline/mod_dav_svn_tests.py
index db30533..db30533 100644..100755
--- a/subversion/tests/cmdline/mod_dav_svn_tests.py
+++ b/subversion/tests/cmdline/mod_dav_svn_tests.py
diff --git a/subversion/tests/cmdline/move_tests.py b/subversion/tests/cmdline/move_tests.py
index 1542af9..6da4a1d 100755
--- a/subversion/tests/cmdline/move_tests.py
+++ b/subversion/tests/cmdline/move_tests.py
@@ -1261,7 +1261,7 @@ def nested_replaces(sbox):
' D /A/B/C/Y',
]))
expected_output = svntest.verify.UnorderedRegexListOutput(escaped
- + [ '^-', '^r2', '^-', '^Changed paths:', ])
+ + [ '^--*', '^r2.*', '^--*', '^Changed paths:', ])
svntest.actions.run_and_verify_svn(expected_output, [],
'log', '-qvr2', repo_url)
diff --git a/subversion/tests/cmdline/patch_tests.py b/subversion/tests/cmdline/patch_tests.py
index 5d41dd6..3cd4dd7 100755
--- a/subversion/tests/cmdline/patch_tests.py
+++ b/subversion/tests/cmdline/patch_tests.py
@@ -4246,7 +4246,7 @@ def patch_git_with_index_line(sbox):
"+++ b/src/tools/ConsoleRunner/hi.txt\n",
"@@ -0,0 +1 @@\n",
"+hihihihihihi\n",
- "\ No newline at end of file\n",
+ "\\ No newline at end of file\n",
]
svntest.main.file_write(patch_file_path, ''.join(unidiff_patch))
@@ -4413,7 +4413,7 @@ def patch_replace_dir_with_file_and_vv(sbox):
"+++ A/D\t(working copy)\n",
"@@ -0,0 +1 @@\n",
"+New file\n",
- "\ No newline at end of file\n",
+ "\\ No newline at end of file\n",
# Add iota as directory
"Index: iota\n",
@@ -4426,7 +4426,7 @@ def patch_replace_dir_with_file_and_vv(sbox):
"Added: k\n",
"## -0,0 +1 ##\n",
"+v\n",
- "\ No newline at end of property\n",
+ "\\ No newline at end of property\n",
]))
expected_output = wc.State(wc_dir, {
@@ -6541,7 +6541,7 @@ def patch_prop_madness(sbox):
"Property: del_n\n"
"## -1,1 +0,0 ##\n"
"-no-eol\n"
- "\ No newline at end of property\n"
+ "\\ No newline at end of property\n"
% (sbox.path('iota'),
sbox.path('iota'))),
})
@@ -7791,6 +7791,225 @@ def patch_merge(sbox):
expected_disk, None,
expected_skip)
+def patch_mergeinfo_in_regular_prop_format(sbox):
+ "patch mergeinfo in regular prop format"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ strip_count = wc_dir.count(os.path.sep)+1
+
+ sbox.simple_copy('A/B/E', 'E')
+ sbox.simple_append('A/B/E/alpha', 'extra\nlines\n')
+ sbox.simple_commit()
+
+ sbox.simple_propset('a', 'A', 'E') # 'a' < 'svn:mergeinfo'
+ sbox.simple_propset('z', 'Z', 'E') # 'z' > 'svn:mergeinfo'
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'merge', '^/A/B/E', sbox.ospath('E'))
+ # Rename 'svn:mergeinfo' to 'svn_mergeinfo' so that 'diff' doesn't
+ # pretty-print it; then rename it back before we run it through 'patch'.
+ # (Alternatively, we could disable pretty-printing when we implement a
+ # command-line switch to do so.)
+ mergeinfo_value = sbox.simple_propget('svn:mergeinfo', 'E')
+ sbox.simple_propdel('svn:mergeinfo', 'E')
+ sbox.simple_propset('svn_mergeinfo', mergeinfo_value, 'E')
+
+ _, diff, _ = svntest.actions.run_and_verify_svn(None, [],
+ 'diff', wc_dir)
+ diff = re.sub('svn_mergeinfo', 'svn:mergeinfo', ''.join(diff))
+
+ sbox.simple_revert('E', 'E/alpha')
+
+ patch = sbox.get_tempname('recurse.patch')
+ svntest.main.file_write(patch, diff, mode='wb')
+
+ expected_output = wc.State(wc_dir, {
+ 'E' : Item(status=' U'),
+ 'E/alpha' : Item(status='U '),
+ })
+ expected_skip = wc.State(wc_dir, {})
+ expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
+ expected_status.add({
+ 'E' : Item(status=' M', wc_rev='2'),
+ 'E/alpha' : Item(status='M ', wc_rev='2'),
+ 'E/beta' : Item(status=' ', wc_rev='2'),
+ })
+ expected_status.tweak('A/B/E/alpha', wc_rev=2)
+ expected_disk = svntest.main.greek_state.copy()
+ expected_disk.tweak('A/B/E/alpha', contents="This is the file 'alpha'.\nextra\nlines\n")
+ expected_disk.add({
+ 'E' : Item(props={'a': 'A',
+ # here is the correctly patched mergeinfo
+ 'svn:mergeinfo': '/A/B/E:2',
+ 'z': 'Z'}),
+ 'E/beta' : Item(contents="This is the file 'beta'.\n"),
+ 'E/alpha' : Item(contents="This is the file 'alpha'.\nextra\nlines\n"),
+ })
+
+ svntest.actions.run_and_verify_patch(wc_dir, patch,
+ expected_output, expected_disk,
+ expected_status, expected_skip,
+ [], True, True,
+ '--strip', strip_count)
+
+@XFail()
+def patch_empty_prop(sbox):
+ "patch empty prop"
+ sbox.build(empty=True)
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = ''
+
+ # start with a file with an empty prop
+ sbox.simple_add_text('', 'f')
+ sbox.simple_propset('p', '', 'f')
+ sbox.simple_commit()
+
+ # a patch that modifies the prop to a non-empty value
+ unidiff_patch = [
+ "--- f\n",
+ "+++ f\n",
+ "\n",
+ "Property changes on: f\n",
+ "___________________________________________________________________\n",
+ "Modified: p\n",
+ "## -0,0 +1 ##\n",
+ "+v\n",
+ ]
+
+ trailing_eol = False
+ if trailing_eol:
+ value = "v\n"
+ else:
+ value = "v"
+ unidiff_patch += ['\ No newline at end of property\n']
+
+ patch_file_path = sbox.get_tempname('my.patch')
+ svntest.main.file_write(patch_file_path, ''.join(unidiff_patch), 'wb')
+
+ expected_output = [
+ ' U %s\n' % sbox.ospath('f'),
+ ]
+
+ expected_disk = svntest.wc.State(wc_dir, {})
+ expected_disk.add({'f': Item(props={'p' : value})})
+ expected_status = svntest.wc.State(wc_dir, {})
+ expected_status.add({'f': Item(status=' M')})
+ expected_skip = wc.State('', { })
+
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, # expected err
+ 1, # check-props
+ False, # dry-run
+ )
+
+ svntest.actions.check_prop('p', wc_dir, [value.encode()])
+
+ os.chdir(was_cwd)
+
+# Test that 'patch' can apply a patch that modifies properties on the root
+# of a WC and/or the root of a repository. This test uses the format of
+# diff output produced by 'svn diff --git' in svn <= 1.10.0: paths are given
+# as 'a/' and 'b/' and 'Property changes on: ' (with no following '.').
+#
+# See dev@ email thread 2018-07-09 from Dmitry Pavlenko,
+# '[PATCH] can't "svn patch" working copy root if the patch is in --git format',
+# https://lists.apache.org/thread.html/d1d9811ca36fac8cabb9339634840099e22811beac505be2ea59f19f@%3Cdev.subversion.apache.org%3E
+@XFail()
+def patch_git_wcroot(sbox):
+ "patch working copy root"
+ sbox.build(empty=True)
+ wc_dir = sbox.wc_dir
+
+ git_patch = [ "Index: .\n",
+ "===================================================================\n",
+ "diff --git a/ b/\n",
+ "--- a/ (revision 0)\n",
+ "+++ b/ (working copy)\n",
+ "Property changes on: \n",
+ "___________________________________________________________________\n",
+ "Added: p\n",
+ "## -0,0 +1 ##\n",
+ "+v\n",
+ "\\ No newline at end of property\n",
+ ]
+ value = 'v'
+
+ patch_file_path = sbox.get_tempname('my.patch')
+ svntest.main.file_write(patch_file_path, ''.join(git_patch), 'wb')
+
+ expected_output = wc.State(wc_dir, {
+ '.' : Item(status=' U'),
+ })
+ expected_disk = svntest.wc.State('', {})
+ expected_disk.add({'': Item(props={'p' : value })})
+ expected_status = svntest.wc.State(wc_dir, {})
+ expected_status.add({'': Item(status=' M', wc_rev='0')})
+ expected_skip = wc.State('', { })
+
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, # expected err
+ True, # check-props
+ False, # dry-run
+ )
+
+ svntest.actions.check_prop('p', wc_dir, [value.encode()])
+
+# Test that 'patch' can apply a patch that modifies properties on the root
+# of a WC and/or the root of a repository. This test performs a round-trip
+# through 'diff --git' and 'patch' rather than assuming any particular
+# variant of the patch format.
+#
+# See dev@ email thread 2018-07-09 from Dmitry Pavlenko,
+# '[PATCH] can't "svn patch" working copy root if the patch is in --git format',
+# https://lists.apache.org/thread.html/d1d9811ca36fac8cabb9339634840099e22811beac505be2ea59f19f@%3Cdev.subversion.apache.org%3E
+@XFail()
+def patch_git_wcroot2(sbox):
+ "patch working copy root"
+ sbox.build(empty=True)
+ wc_dir = sbox.wc_dir
+
+ value = 'v'
+ sbox.simple_propset('p', value, '')
+ exit_code, git_patch, err_output = svntest.main.run_svn(None, 'diff',
+ '--git', wc_dir)
+
+ patch_file_path = sbox.get_tempname('my.patch')
+ svntest.main.file_write(patch_file_path, ''.join(git_patch), 'wb')
+
+ sbox.simple_revert('')
+
+ expected_output = wc.State(wc_dir, {
+ '.' : Item(status=' U'),
+ })
+ expected_disk = svntest.wc.State('', {})
+ expected_disk.add({'': Item(props={'p' : value })})
+ expected_status = svntest.wc.State(wc_dir, {})
+ expected_status.add({'': Item(status=' M', wc_rev='0')})
+ expected_skip = wc.State('', { })
+
+ svntest.actions.run_and_verify_patch(wc_dir, patch_file_path,
+ expected_output,
+ expected_disk,
+ expected_status,
+ expected_skip,
+ None, # expected err
+ True, # check-props
+ False, # dry-run
+ )
+
+ svntest.actions.check_prop('p', wc_dir, [value.encode()])
+
########################################################################
#Run the tests
@@ -7874,6 +8093,10 @@ test_list = [ None,
missing_trailing_context,
patch_missed_trail,
patch_merge,
+ patch_mergeinfo_in_regular_prop_format,
+ patch_empty_prop,
+ patch_git_wcroot,
+ patch_git_wcroot2,
]
if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/prop_tests.py b/subversion/tests/cmdline/prop_tests.py
index 3709b20..ada2e09 100755
--- a/subversion/tests/cmdline/prop_tests.py
+++ b/subversion/tests/cmdline/prop_tests.py
@@ -1768,7 +1768,7 @@ def rm_of_replaced_file(sbox):
'proplist', '-v',
mu_path + '@base')
expected_output = svntest.verify.UnorderedRegexListOutput([
- 'Properties on',
+ 'Properties on.*',
' yellow',
' submarine',
' orange',
diff --git a/subversion/tests/cmdline/revert_tests.py b/subversion/tests/cmdline/revert_tests.py
index 39ce3c6..5bb6870 100755
--- a/subversion/tests/cmdline/revert_tests.py
+++ b/subversion/tests/cmdline/revert_tests.py
@@ -1550,11 +1550,14 @@ def revert_with_unversioned_targets(sbox):
psi_contents = "modified psi\n"
# touch delta
- open(delta_path, 'w').write(delta_contents)
+ with open(delta_path, 'w') as f:
+ f.write(delta_contents)
# modify chi psi
- open(chi_path, 'w').write(chi_contents)
- open(psi_path, 'w').write(psi_contents)
+ with open(chi_path, 'w') as f:
+ f.write(chi_contents)
+ with open(psi_path, 'w') as f:
+ f.write(psi_contents)
# revert
expected_output = svntest.verify.UnorderedOutput([
diff --git a/subversion/tests/cmdline/shelf_tests.py b/subversion/tests/cmdline/shelf_tests.py
new file mode 100755
index 0000000..899d250
--- /dev/null
+++ b/subversion/tests/cmdline/shelf_tests.py
@@ -0,0 +1,995 @@
+#!/usr/bin/env python
+#
+# shelf_tests.py: testing shelving
+#
+# Subversion is a tool for revision control.
+# See http://subversion.apache.org for more information.
+#
+# ====================================================================
+# 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.
+######################################################################
+
+# General modules
+import shutil, stat, re, os, logging
+
+logger = logging.getLogger()
+
+# Our testing module
+import svntest
+from svntest import wc
+from svntest.verify import make_diff_header, make_no_diff_deleted_header, \
+ make_git_diff_header, make_diff_prop_header, \
+ make_diff_prop_val, make_diff_prop_deleted, \
+ make_diff_prop_added, make_diff_prop_modified
+
+# (abbreviation)
+Skip = svntest.testcase.Skip_deco
+SkipUnless = svntest.testcase.SkipUnless_deco
+XFail = svntest.testcase.XFail_deco
+Issues = svntest.testcase.Issues_deco
+Issue = svntest.testcase.Issue_deco
+Wimp = svntest.testcase.Wimp_deco
+Item = wc.StateItem
+
+#----------------------------------------------------------------------
+
+def state_from_status(wc_dir,
+ v=True, u=True, q=True):
+ opts = ()
+ if v:
+ opts += ('-v',)
+ if u:
+ opts += ('-u',)
+ if q:
+ opts += ('-q',)
+ _, output, _ = svntest.main.run_svn(None, 'status', wc_dir, *opts)
+ return svntest.wc.State.from_status(output, wc_dir)
+
+def get_wc_state(wc_dir):
+ """Return a description of the WC state. Include as much info as shelving
+ should be capable of restoring.
+ """
+ return (state_from_status(wc_dir),
+ svntest.wc.State.from_wc(wc_dir, load_props=True),
+ )
+
+def check_wc_state(wc_dir, expected):
+ """Check a description of the WC state. Include as much info as shelving
+ should be capable of restoring.
+ """
+ expect_st, expect_wc = expected
+ actual_st, actual_wc = get_wc_state(wc_dir)
+
+ # Verify actual status against expected status.
+ try:
+ expect_st.compare_and_display('status', actual_st)
+ except svntest.tree.SVNTreeError:
+ svntest.actions._log_tree_state("EXPECT STATUS TREE:", expect_st.old_tree(),
+ wc_dir)
+ svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_st.old_tree(),
+ wc_dir)
+ raise
+
+ # Verify actual WC against expected WC.
+ try:
+ expect_wc.compare_and_display('status', actual_wc)
+ except svntest.tree.SVNTreeError:
+ svntest.actions._log_tree_state("EXPECT WC TREE:", expect_wc.old_tree(),
+ wc_dir)
+ svntest.actions._log_tree_state("ACTUAL WC TREE:", actual_wc.old_tree(),
+ wc_dir)
+ raise
+
+def shelve_unshelve_verify(sbox, modifier, cannot_shelve=False):
+ """Round-trip: shelve; verify all changes are reverted;
+ unshelve; verify all changes are restored.
+ """
+
+ wc_dir = sbox.wc_dir
+ virginal_state = get_wc_state(wc_dir)
+
+ # Make some changes to the working copy
+ modifier(sbox)
+
+ # Save the modified state
+ modified_state = get_wc_state(wc_dir)
+
+ if cannot_shelve:
+ svntest.actions.run_and_verify_svn(None, '.* could not be shelved.*',
+ 'x-shelve', 'foo')
+ return
+
+ # Shelve; check there are no longer any modifications
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelve', 'foo')
+ check_wc_state(wc_dir, virginal_state)
+
+ # List; ensure the shelf is listed
+ expected_output = svntest.verify.RegexListOutput(
+ [r'foo\s*version \d+.*',
+ r' ',
+ ])
+ svntest.actions.run_and_verify_svn(expected_output, [], 'x-shelves')
+
+ # Unshelve; check the original modifications are here again
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-unshelve', 'foo')
+ check_wc_state(wc_dir, modified_state)
+
+#----------------------------------------------------------------------
+
+def shelve_unshelve(sbox, modifier, cannot_shelve=False):
+ """Round-trip: build 'sbox'; apply changes by calling 'modifier(sbox)';
+ shelve and unshelve; verify changes are fully reverted and restored.
+ """
+
+ if not sbox.is_built():
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ shelve_unshelve_verify(sbox, modifier, cannot_shelve)
+
+ os.chdir(was_cwd)
+
+######################################################################
+# Tests
+#
+# Each test must return on success or raise on failure.
+
+def shelve_text_mods(sbox):
+ "shelve text mods"
+
+ def modifier(sbox):
+ sbox.simple_append('A/mu', 'appended mu text')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_prop_changes(sbox):
+ "shelve prop changes"
+
+ def modifier(sbox):
+ sbox.simple_propset('p', 'v', 'A')
+ sbox.simple_propset('p', 'v', 'A/mu')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_adds(sbox):
+ "shelve adds"
+
+ def modifier(sbox):
+ sbox.simple_add_text('A new file\n', 'A/new')
+ sbox.simple_add_text('A new file\n', 'A/new2')
+ sbox.simple_propset('p', 'v', 'A/new2')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+@Issue(4709)
+def shelve_deletes(sbox):
+ "shelve deletes"
+
+ def modifier(sbox):
+ sbox.simple_rm('A/mu')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_replace(sbox):
+ "shelve replace"
+
+ def modifier(sbox):
+ sbox.simple_rm('A/mu')
+ sbox.simple_add_text('Replacement\n', 'A/mu')
+ sbox.simple_propset('p', 'v', 'A/mu')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_empty_adds(sbox):
+ "shelve empty adds"
+ sbox.build(empty=True)
+
+ def modifier(sbox):
+ sbox.simple_add_text('', 'empty')
+ sbox.simple_add_text('', 'empty-with-prop')
+ sbox.simple_propset('p', 'v', 'empty-with-prop')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_empty_deletes(sbox):
+ "shelve empty deletes"
+ sbox.build(empty=True)
+ sbox.simple_add_text('', 'empty')
+ sbox.simple_add_text('', 'empty-with-prop')
+ sbox.simple_propset('p', 'v', 'empty-with-prop')
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ sbox.simple_rm('empty', 'empty-with-prop')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_from_inner_path(sbox):
+ "shelve from inner path"
+
+ def modifier(sbox):
+ sbox.simple_append('A/mu', 'appended mu text')
+
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.ospath('A'))
+ sbox.wc_dir = '..'
+
+ shelve_unshelve_verify(sbox, modifier)
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def save_revert_restore(sbox, modifier1, modifier2):
+ "Save 2 checkpoints; revert; restore 1st"
+
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = ''
+
+ initial_state = get_wc_state(wc_dir)
+
+ # Make some changes to the working copy
+ modifier1(sbox)
+
+ # Remember the modified state
+ modified_state1 = get_wc_state(wc_dir)
+
+ # Save a checkpoint; check nothing changed
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'foo')
+ check_wc_state(wc_dir, modified_state1)
+
+ # Modify again; remember the state; save a checkpoint
+ modifier2(sbox)
+ modified_state2 = get_wc_state(wc_dir)
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'foo')
+ check_wc_state(wc_dir, modified_state2)
+
+ # Revert
+ svntest.actions.run_and_verify_svn(None, [],
+ 'revert', '-R', '.')
+ check_wc_state(wc_dir, initial_state)
+
+ # Restore; check the original modifications are here again
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-unshelve', 'foo', '1')
+ check_wc_state(wc_dir, modified_state1)
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def checkpoint_basic(sbox):
+ "checkpoint basic"
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', 'appended mu text\n')
+
+ def modifier2(sbox):
+ sbox.simple_append('iota', 'appended iota text\n')
+ sbox.simple_append('A/mu', 'appended another line\n')
+
+ save_revert_restore(sbox, modifier1, modifier2)
+
+#----------------------------------------------------------------------
+
+@Issue(3747)
+def shelve_mergeinfo(sbox):
+ "shelve mergeinfo"
+
+ def modifier(sbox):
+ sbox.simple_propset('svn:mergeinfo', '/trunk/A:1-3,10', 'A')
+ sbox.simple_propset('svn:mergeinfo', '/trunk/A/mu:1-3,10', 'A/mu')
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def unshelve_refuses_if_conflicts(sbox):
+ "unshelve refuses if conflicts"
+
+ def modifier1(sbox):
+ sbox.simple_append('alpha', 'A-mod1\nB\nC\nD\n', truncate=True)
+ sbox.simple_append('beta', 'A-mod1\nB\nC\nD\n', truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('beta', 'A-mod2\nB\nC\nD\n', truncate=True)
+
+ sbox.build(empty=True)
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = ''
+
+ sbox.simple_add_text('A\nB\nC\nD\n', 'alpha')
+ sbox.simple_add_text('A\nB\nC\nD\n', 'beta')
+ sbox.simple_commit()
+ initial_state = get_wc_state(wc_dir)
+
+ # Make initial mods; remember this modified state
+ modifier1(sbox)
+ modified_state1 = get_wc_state(wc_dir)
+ assert modified_state1 != initial_state
+
+ # Shelve; check there are no longer any local mods
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelve', 'foo')
+ check_wc_state(wc_dir, initial_state)
+
+ # Make a different local mod that will conflict with the shelf
+ modifier2(sbox)
+ modified_state2 = get_wc_state(wc_dir)
+
+ # Try to unshelve; check it fails with an error about a conflict
+ svntest.actions.run_and_verify_svn(None, '.*[Cc]onflict.*',
+ 'x-unshelve', 'foo')
+ # Check nothing changed in the attempt
+ check_wc_state(wc_dir, modified_state2)
+
+#----------------------------------------------------------------------
+
+def shelve_binary_file_mod(sbox):
+ "shelve binary file mod"
+
+ sbox.build(empty=True)
+
+ existing_files = ['A/B/existing']
+ mod_files = ['bin', 'A/B/bin']
+
+ sbox.simple_mkdir('A', 'A/B')
+ for f in existing_files + mod_files:
+ sbox.simple_add_text('\0\1\2\3\4\5', f)
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ for f in mod_files:
+ sbox.simple_append(f, '\6\5\4\3\2\1\0', truncate=True)
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_binary_file_add(sbox):
+ "shelve binary file add"
+
+ sbox.build(empty=True)
+
+ existing_files = ['A/B/existing']
+ mod_files = ['bin', 'A/B/bin']
+
+ sbox.simple_mkdir('A', 'A/B')
+ for f in existing_files:
+ sbox.simple_add_text('\0\1\2\3\4\5', f)
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ for f in mod_files:
+ sbox.simple_add_text('\0\1\2\3\4\5', f)
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_binary_file_del(sbox):
+ "shelve binary file del"
+
+ sbox.build(empty=True)
+
+ existing_files = ['A/B/existing']
+ mod_files = ['bin', 'A/B/bin']
+
+ sbox.simple_mkdir('A', 'A/B')
+ for f in existing_files + mod_files:
+ sbox.simple_add_text('\0\1\2\3\4\5', f)
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ for f in mod_files:
+ sbox.simple_rm(f)
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_binary_file_replace(sbox):
+ "shelve binary file replace"
+
+ sbox.build(empty=True)
+
+ existing_files = ['A/B/existing']
+ mod_files = ['bin', 'A/B/bin']
+
+ sbox.simple_mkdir('A', 'A/B')
+ for f in existing_files + mod_files:
+ sbox.simple_add_text('\0\1\2\3\4\5', f)
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ for f in mod_files:
+ sbox.simple_rm(f)
+ sbox.simple_add_text('\6\5\4\3\2\1\0', f)
+
+ shelve_unshelve(sbox, modifier)
+
+#----------------------------------------------------------------------
+
+def shelve_with_log_message(sbox):
+ "shelve with log message"
+
+ sbox.build(empty=True)
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ sbox.simple_add_text('New file', 'f')
+ log_message = 'Log message for foo'
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelve', 'foo', '-m', log_message)
+ expected_output = svntest.verify.RegexListOutput(
+ ['foo .*',
+ ' ' + log_message
+ ])
+ svntest.actions.run_and_verify_svn(expected_output, [],
+ 'x-shelf-list')
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def run_and_verify_status(wc_dir_name, status_tree, changelists=[]):
+ """Run 'status' on WC_DIR_NAME and compare it with the
+ expected STATUS_TREE.
+ Returns on success, raises on failure."""
+
+ if not isinstance(status_tree, wc.State):
+ raise TypeError('wc.State tree expected')
+
+ cl_opts = ('--cl=' + cl for cl in changelists)
+ exit_code, output, errput = svntest.main.run_svn(None, 'status', '-q',
+ wc_dir_name, *cl_opts)
+
+ actual_status = svntest.wc.State.from_status(output, wc_dir=wc_dir_name)
+
+ # Verify actual output against expected output.
+ try:
+ status_tree.compare_and_display('status', actual_status)
+ except svntest.tree.SVNTreeError:
+ svntest.actions._log_tree_state("ACTUAL STATUS TREE:", actual_status.old_tree(),
+ wc_dir_name)
+ raise
+
+def run_and_verify_shelf_status(wc_dir, expected_status, shelf):
+ run_and_verify_status(wc_dir, expected_status,
+ changelists=['svn:shelf:' + shelf])
+
+def shelf_status(sbox):
+ "shelf status"
+
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ sbox.simple_add_text('New file', 'f')
+ sbox.simple_append('iota', 'New text')
+ sbox.simple_propset('p', 'v', 'A/mu')
+ sbox.simple_rm('A/B/lambda')
+ # Not yet supported:
+ #sbox.simple_rm('A/B/E')
+ expected_status = state_from_status(sbox.wc_dir, v=False, u=False, q=False)
+ run_and_verify_status(sbox.wc_dir, expected_status)
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelve', 'foo')
+ run_and_verify_shelf_status(sbox.wc_dir, expected_status, shelf='foo')
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def shelve_mkdir(sbox):
+ "shelve mkdir"
+
+ sbox.build()
+
+ def modifier(sbox):
+ sbox.simple_mkdir('D', 'D/D2')
+ sbox.simple_propset('p', 'v', 'D', 'D/D2')
+
+ shelve_unshelve(sbox, modifier, cannot_shelve=True)
+
+#----------------------------------------------------------------------
+
+def shelve_rmdir(sbox):
+ "shelve rmdir"
+
+ sbox.build()
+ sbox.simple_propset('p', 'v', 'A/C')
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ sbox.simple_rm('A/C', 'A/D/G')
+
+ shelve_unshelve(sbox, modifier, cannot_shelve=True)
+
+#----------------------------------------------------------------------
+
+def shelve_replace_dir(sbox):
+ "shelve replace dir"
+
+ sbox.build()
+ sbox.simple_propset('p', 'v', 'A/C')
+ sbox.simple_commit()
+
+ def modifier(sbox):
+ sbox.simple_rm('A/C', 'A/D/G')
+ sbox.simple_mkdir('A/C', 'A/C/D2')
+
+ shelve_unshelve(sbox, modifier, cannot_shelve=True)
+
+#----------------------------------------------------------------------
+
+def shelve_file_copy(sbox):
+ "shelve file copy"
+
+ sbox.build()
+
+ def modifier(sbox):
+ sbox.simple_copy('iota', 'A/ii')
+ sbox.simple_propset('p', 'v', 'A/ii')
+
+ shelve_unshelve(sbox, modifier, cannot_shelve=True)
+
+#----------------------------------------------------------------------
+
+def shelve_dir_copy(sbox):
+ "shelve dir copy"
+
+ sbox.build()
+
+ def modifier(sbox):
+ sbox.simple_copy('A/B', 'BB')
+ sbox.simple_propset('p', 'v', 'BB')
+
+ shelve_unshelve(sbox, modifier, cannot_shelve=True)
+
+#----------------------------------------------------------------------
+
+def list_shelves(sbox):
+ "list_shelves"
+
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ # an empty list
+ svntest.actions.run_and_verify_svn([], [],
+ 'x-shelf-list', '-q')
+
+ # make two shelves
+ sbox.simple_append('A/mu', 'appended mu text')
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'foo')
+ sbox.simple_append('A/mu', 'appended more text')
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'foo', '-m', 'log msg')
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'bar', '-m', 'log msg')
+
+ # We don't check for time-ordering of the shelves. If we want to do so, we
+ # would need to sleep for timestamps to differ, between creating them.
+
+ # a quiet list
+ expected_out = svntest.verify.UnorderedRegexListOutput(['foo', 'bar'])
+ svntest.actions.run_and_verify_svn(expected_out, [],
+ 'x-shelf-list', '-q')
+
+ # a detailed list
+ expected_out = svntest.verify.UnorderedRegexListOutput(['foo .* 1 path.*',
+ ' log msg',
+ 'bar .* 1 path.*',
+ ' log msg'])
+ svntest.actions.run_and_verify_svn(expected_out, [],
+ 'x-shelf-list')
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def refuse_to_shelve_conflict(sbox):
+ "refuse to shelve conflict"
+
+ sbox.build(empty=True)
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+
+ # create a tree conflict victim at an unversioned path
+ sbox.simple_mkdir('topdir')
+ sbox.simple_commit()
+ sbox.simple_mkdir('topdir/subdir')
+ sbox.simple_commit()
+ sbox.simple_update()
+ sbox.simple_rm('topdir')
+ sbox.simple_commit()
+ sbox.simple_update()
+ svntest.actions.run_and_verify_svn(
+ None, [],
+ 'merge', '-c2', '.', '--ignore-ancestry', '--accept', 'postpone')
+ svntest.actions.run_and_verify_svn(
+ None, 'svn: E155015:.*existing.*conflict.*',
+ 'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
+
+ # attempt to shelve
+ expected_out = svntest.verify.RegexListOutput([
+ r'--- .*',
+ r'--- .*',
+ r'\? C topdir',
+ r' > .*',
+ r' > not shelved'])
+ svntest.actions.run_and_verify_svn(expected_out,
+ '.* 1 path could not be shelved',
+ 'x-shelf-save', 'foo')
+
+ os.chdir(was_cwd)
+
+#----------------------------------------------------------------------
+
+def unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state):
+ """Run a test scenario in which 'unshelve' needs to merge some shelved
+ changes made by modifier1() with some committed changes made by
+ modifier2(). tweak_expected_state() must produce the expected WC state.
+ """
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = sbox.wc_dir
+
+ setup(sbox)
+ sbox.simple_commit()
+ initial_state = get_wc_state(wc_dir)
+
+ # Make some changes to the working copy
+ modifier1(sbox)
+ modified_state = get_wc_state(wc_dir)
+
+ # Shelve; check there are no longer any modifications
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelve', 'foo')
+ check_wc_state(wc_dir, initial_state)
+
+ # Make a different change, with which we shall merge
+ modifier2(sbox)
+ sbox.simple_commit()
+ modified_state[0].tweak('A/mu', wc_rev='3')
+
+ # Unshelve; check the expected result of the merge
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-unshelve', 'foo')
+ tweak_expected_state(modified_state)
+ check_wc_state(wc_dir, modified_state)
+
+ os.chdir(was_cwd)
+
+def unshelve_text_mod_merge(sbox):
+ "unshelve text mod merge"
+
+ orig_contents='A\nB\nC\nD\nE\n'
+ mod1_contents='A\nBB\nC\nD\nE\n'
+ mod2_contents='A\nB\nC\nDD\nE\n'
+ merged_contents='A\nBB\nC\nDD\nE\n'
+
+ def setup(sbox):
+ sbox.simple_append('A/mu', orig_contents, truncate=True)
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', mod1_contents, truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('A/mu', mod2_contents, truncate=True)
+
+ def tweak_expected_state(modified_state):
+ modified_state[1].tweak('A/mu', contents=merged_contents)
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def unshelve_text_mod_conflict(sbox):
+ "unshelve text mod conflict"
+
+ orig_contents='A\nB\nC\nD\nE\n'
+ mod1_contents='A\nBB\nC\nD\nE\n'
+ mod2_contents='A\nBCD\nC\nD\nE\n'
+ merged_contents = 'A\n<<<<<<< .working\nBCD\n||||||| .merge-left\nB\n=======\nBB\n>>>>>>> .merge-right\nC\nD\nE\n'
+
+ def setup(sbox):
+ sbox.simple_append('A/mu', orig_contents, truncate=True)
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', mod1_contents, truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('A/mu', mod2_contents, truncate=True)
+
+ def tweak_expected_state(modified_state):
+ modified_state[0].tweak('A/mu', status='C ')
+ modified_state[1].tweak('A/mu', contents=merged_contents)
+ modified_state[1].add({
+ 'A/mu.merge-left': Item(contents=orig_contents),
+ 'A/mu.merge-right': Item(contents=mod1_contents),
+ 'A/mu.working': Item(contents=mod2_contents),
+ })
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def unshelve_undeclared_binary_mod_conflict(sbox):
+ "unshelve undeclared binary mod conflict"
+
+ orig_contents='\1\2\3\4\5'
+ mod1_contents='\1\2\2\3\4\5'
+ mod2_contents='\1\2\3\4\3\4\5'
+ merged_contents = '<<<<<<< .working\n' + mod2_contents + '||||||| .merge-left\n' + orig_contents + '=======\n' + mod1_contents + '>>>>>>> .merge-right\n'
+
+ def setup(sbox):
+ sbox.simple_append('A/mu', orig_contents, truncate=True)
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', mod1_contents, truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('A/mu', mod2_contents, truncate=True)
+
+ def tweak_expected_state(modified_state):
+ modified_state[0].tweak('A/mu', status='C ')
+ modified_state[1].tweak('A/mu', contents=merged_contents)
+ modified_state[1].add({
+ 'A/mu.merge-left': Item(contents=orig_contents),
+ 'A/mu.merge-right': Item(contents=mod1_contents),
+ 'A/mu.working': Item(contents=mod2_contents),
+ })
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def unshelve_binary_mod_conflict(sbox):
+ "unshelve binary mod conflict"
+
+ orig_contents='\1\2\3\4\5'
+ mod1_contents='\1\2\2\3\4\5'
+ mod2_contents='\1\2\3\4\3\4\5'
+
+ def setup(sbox):
+ sbox.simple_append('A/mu', orig_contents, truncate=True)
+ sbox.simple_propset('svn:mime-type', 'application/octet-stream', 'A/mu')
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', mod1_contents, truncate=True)
+
+ def modifier2(sbox):
+ sbox.simple_append('A/mu', mod2_contents, truncate=True)
+
+ def tweak_expected_state(modified_state):
+ modified_state[0].tweak('A/mu', status='C ')
+ modified_state[1].tweak('A/mu', contents=mod2_contents)
+ modified_state[1].add({
+ 'A/mu.merge-left': Item(contents=orig_contents),
+ 'A/mu.merge-right': Item(contents=mod1_contents),
+ })
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def unshelve_text_prop_merge(sbox):
+ "unshelve text prop merge"
+
+ def setup(sbox):
+ sbox.simple_propset('p1', 'v', 'A/mu')
+ sbox.simple_propset('p2', 'v', 'A/mu')
+
+ def modifier1(sbox):
+ sbox.simple_propset('p1', 'changed', 'A/mu')
+
+ def modifier2(sbox):
+ sbox.simple_propset('p2', 'changed', 'A/mu')
+
+ def tweak_expected_state(wc_state):
+ wc_state[1].tweak('A/mu', props={'p1':'changed',
+ 'p2':'changed'})
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def unshelve_text_prop_conflict(sbox):
+ "unshelve text prop conflict"
+
+ orig_contents='A'
+ mod1_contents='B'
+ mod2_contents='C'
+ merged_contents='C'
+ prej_contents='''Trying to change property 'p'
+but the local property value conflicts with the incoming change.
+<<<<<<< (local property value)
+C||||||| (incoming 'changed from' value)
+A=======
+B>>>>>>> (incoming 'changed to' value)
+'''
+
+ def setup(sbox):
+ sbox.simple_propset('p', orig_contents, 'A/mu')
+
+ def modifier1(sbox):
+ sbox.simple_propset('p', mod1_contents, 'A/mu')
+
+ def modifier2(sbox):
+ sbox.simple_propset('p', mod2_contents, 'A/mu')
+
+ def tweak_expected_state(wc_state):
+ wc_state[0].tweak('A/mu', status=' C')
+ wc_state[1].tweak('A/mu', props={'p':merged_contents})
+ wc_state[1].add({
+ 'A/mu.prej': Item(contents=prej_contents),
+ })
+
+ unshelve_with_merge(sbox, setup, modifier1, modifier2, tweak_expected_state)
+
+#----------------------------------------------------------------------
+
+def run_and_verify_shelf_diff_summarize(output_tree, shelf, *args):
+ """Run 'svn shelf-diff --summarize' with the arguments *ARGS.
+
+ The subcommand output will be verified against OUTPUT_TREE. Returns
+ on success, raises on failure.
+ """
+
+ if isinstance(output_tree, wc.State):
+ output_tree = output_tree.old_tree()
+
+ exit_code, output, errput = svntest.actions.run_and_verify_svn(
+ None, [],
+ 'x-shelf-diff', '--summarize', shelf, *args)
+
+ actual = svntest.tree.build_tree_from_diff_summarize(output)
+
+ # Verify actual output against expected output.
+ try:
+ svntest.tree.compare_trees("output", actual, output_tree)
+ except svntest.tree.SVNTreeError:
+ svntest.verify.display_trees(None, 'DIFF OUTPUT TREE', output_tree, actual)
+ raise
+
+# Exercise a very basic case of shelf-diff.
+def shelf_diff_simple(sbox):
+ "shelf diff simple"
+
+ sbox.build()
+ was_cwd = os.getcwd()
+ os.chdir(sbox.wc_dir)
+ sbox.wc_dir = ''
+ wc_dir = sbox.wc_dir
+
+ def setup(sbox):
+ sbox.simple_propset('p1', 'v', 'A/mu')
+ sbox.simple_propset('p2', 'v', 'A/mu')
+
+ def modifier1(sbox):
+ sbox.simple_append('A/mu', 'New line.\n')
+ sbox.simple_propset('p1', 'changed', 'A/mu')
+
+ setup(sbox)
+ sbox.simple_commit()
+ initial_state = get_wc_state(wc_dir)
+
+ # Make some changes to the working copy
+ modifier1(sbox)
+ modified_state = get_wc_state(wc_dir)
+
+ svntest.actions.run_and_verify_svn(None, [],
+ 'x-shelf-save', 'foo')
+
+ # basic svn-style diff
+ expected_output = make_diff_header('A/mu', 'revision 2', 'working copy') + [
+ "@@ -1 +1,2 @@\n",
+ " This is the file 'mu'.\n",
+ "+New line.\n",
+ ] + make_diff_prop_header('A/mu') \
+ + make_diff_prop_modified('p1', 'v', 'changed')
+ svntest.actions.run_and_verify_svn(expected_output, [],
+ 'x-shelf-diff', 'foo')
+
+ # basic summary diff
+ expected_diff = svntest.wc.State(wc_dir, {
+ 'A/mu': Item(status='MM'),
+ })
+ run_and_verify_shelf_diff_summarize(expected_diff, 'foo')
+
+
+########################################################################
+# Run the tests
+
+# list all tests here, starting with None:
+test_list = [ None,
+ shelve_text_mods,
+ shelve_prop_changes,
+ shelve_adds,
+ shelve_deletes,
+ shelve_replace,
+ shelve_empty_adds,
+ shelve_empty_deletes,
+ shelve_from_inner_path,
+ checkpoint_basic,
+ shelve_mergeinfo,
+ unshelve_refuses_if_conflicts,
+ shelve_binary_file_mod,
+ shelve_binary_file_add,
+ shelve_binary_file_del,
+ shelve_binary_file_replace,
+ shelve_with_log_message,
+ shelf_status,
+ shelve_mkdir,
+ shelve_rmdir,
+ shelve_replace_dir,
+ shelve_file_copy,
+ shelve_dir_copy,
+ list_shelves,
+ refuse_to_shelve_conflict,
+ unshelve_text_mod_merge,
+ unshelve_text_mod_conflict,
+ unshelve_undeclared_binary_mod_conflict,
+ unshelve_binary_mod_conflict,
+ unshelve_text_prop_merge,
+ unshelve_text_prop_conflict,
+ shelf_diff_simple,
+ ]
+
+if __name__ == '__main__':
+ svntest.main.run_tests(test_list)
+ # NOTREACHED
+
+
+### End of file.
diff --git a/subversion/tests/cmdline/shelve_tests.py b/subversion/tests/cmdline/shelve_tests.py
deleted file mode 100755
index a71ddbb..0000000
--- a/subversion/tests/cmdline/shelve_tests.py
+++ /dev/null
@@ -1,176 +0,0 @@
-#!/usr/bin/env python
-#
-# shelve_tests.py: testing shelving
-#
-# Subversion is a tool for revision control.
-# See http://subversion.apache.org for more information.
-#
-# ====================================================================
-# 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.
-######################################################################
-
-# General modules
-import shutil, stat, re, os, logging
-
-logger = logging.getLogger()
-
-# Our testing module
-import svntest
-from svntest import wc
-
-# (abbreviation)
-Skip = svntest.testcase.Skip_deco
-SkipUnless = svntest.testcase.SkipUnless_deco
-XFail = svntest.testcase.XFail_deco
-Issues = svntest.testcase.Issues_deco
-Issue = svntest.testcase.Issue_deco
-Wimp = svntest.testcase.Wimp_deco
-Item = wc.StateItem
-
-#----------------------------------------------------------------------
-
-def shelve_unshelve_verify(sbox):
- """Round-trip: shelve; verify all changes are reverted;
- unshelve; verify all changes are restored.
- """
-
- wc_dir = sbox.wc_dir
-
- # Save the modified state
- _, output, _ = svntest.main.run_svn(None, 'status', '-v', '-u', '-q',
- wc_dir)
- modified_state = svntest.wc.State.from_status(output, wc_dir)
-
- # Shelve; check there are no longer any modifications
- svntest.actions.run_and_verify_svn(None, [],
- 'shelve', 'foo')
- virginal_state = svntest.actions.get_virginal_state(wc_dir, 1)
- svntest.actions.run_and_verify_status(wc_dir, virginal_state)
-
- # Unshelve; check the original modifications are here again
- svntest.actions.run_and_verify_svn(None, [],
- 'unshelve', 'foo')
- svntest.actions.run_and_verify_status(wc_dir, modified_state)
-
-#----------------------------------------------------------------------
-
-def shelve_unshelve(sbox, modifier):
- """Round-trip: build 'sbox'; apply changes by calling 'modifier(sbox)';
- shelve and unshelve; verify changes are fully reverted and restored.
- """
-
- sbox.build()
- was_cwd = os.getcwd()
- os.chdir(sbox.wc_dir)
- sbox.wc_dir = ''
-
- # Make some changes to the working copy
- modifier(sbox)
-
- shelve_unshelve_verify(sbox)
-
- os.chdir(was_cwd)
-
-######################################################################
-# Tests
-#
-# Each test must return on success or raise on failure.
-
-def shelve_text_mods(sbox):
- "shelve text mods"
-
- def modifier(sbox):
- sbox.simple_append('A/mu', 'appended mu text')
-
- shelve_unshelve(sbox, modifier)
-
-#----------------------------------------------------------------------
-
-def shelve_prop_changes(sbox):
- "shelve prop changes"
-
- def modifier(sbox):
- sbox.simple_propset('p', 'v', 'A')
- sbox.simple_propset('p', 'v', 'A/mu')
-
- shelve_unshelve(sbox, modifier)
-
-#----------------------------------------------------------------------
-
-def shelve_adds(sbox):
- "shelve adds"
-
- def modifier(sbox):
- sbox.simple_append('A/new', 'A new file\n')
- sbox.simple_add('A/new')
- sbox.simple_append('A/new2', 'A new file\n')
- sbox.simple_add('A/new2')
- sbox.simple_propset('p', 'v', 'A/new2')
-
- shelve_unshelve(sbox, modifier)
-
-#----------------------------------------------------------------------
-
-@XFail()
-@Issue(4709)
-def shelve_deletes(sbox):
- "shelve deletes"
-
- def modifier(sbox):
- sbox.simple_rm('A/mu')
-
- shelve_unshelve(sbox, modifier)
-
-#----------------------------------------------------------------------
-
-def shelve_from_inner_path(sbox):
- "shelve from inner path"
-
- def modifier(sbox):
- sbox.simple_append('A/mu', 'appended mu text')
-
- sbox.build()
- was_cwd = os.getcwd()
- os.chdir(sbox.ospath('A'))
- sbox.wc_dir = '..'
-
- modifier(sbox)
- shelve_unshelve_verify(sbox)
-
- os.chdir(was_cwd)
-
-#----------------------------------------------------------------------
-
-########################################################################
-# Run the tests
-
-# list all tests here, starting with None:
-test_list = [ None,
- shelve_text_mods,
- shelve_prop_changes,
- shelve_adds,
- shelve_deletes,
- shelve_from_inner_path,
- ]
-
-if __name__ == '__main__':
- svntest.main.run_tests(test_list)
- # NOTREACHED
-
-
-### End of file.
diff --git a/subversion/tests/cmdline/special_tests.py b/subversion/tests/cmdline/special_tests.py
index db612c1..2ec22cb 100755
--- a/subversion/tests/cmdline/special_tests.py
+++ b/subversion/tests/cmdline/special_tests.py
@@ -535,7 +535,7 @@ def diff_symlink_to_dir(sbox):
"+++ link\t(working copy)\n",
"@@ -0,0 +1 @@\n",
"+link A/D\n",
- "\ No newline at end of file\n",
+ "\\ No newline at end of file\n",
"\n",
"Property changes on: link\n",
"___________________________________________________________________\n",
@@ -730,7 +730,8 @@ def unrelated_changed_special_status(sbox):
os.chdir(os.path.join(sbox.wc_dir, 'A/D/H'))
- open('chi', 'a').write('random local mod')
+ with open('chi', 'a') as f:
+ f.write('random local mod')
os.unlink('psi')
os.symlink('omega', 'psi') # omega is versioned!
svntest.main.run_svn(None, 'changelist', 'chi cl', 'chi')
diff --git a/subversion/tests/cmdline/stat_tests.py b/subversion/tests/cmdline/stat_tests.py
index afab961..e0f1306 100755
--- a/subversion/tests/cmdline/stat_tests.py
+++ b/subversion/tests/cmdline/stat_tests.py
@@ -1949,7 +1949,8 @@ def modified_modulo_translation(sbox):
sbox.simple_commit()
# CRLF it.
- open(sbox.ospath('iota'), 'wb').write("This is the file 'iota'.\r\n")
+ with open(sbox.ospath('iota'), 'wb') as f:
+ f.write("This is the file 'iota'.\r\n")
# Run status. Expect some output.
# TODO: decide how such files should show in the output; whether they
diff --git a/subversion/tests/cmdline/svnadmin_tests.py b/subversion/tests/cmdline/svnadmin_tests.py
index 40b5e97..4d5303c 100755
--- a/subversion/tests/cmdline/svnadmin_tests.py
+++ b/subversion/tests/cmdline/svnadmin_tests.py
@@ -53,6 +53,23 @@ Wimp = svntest.testcase.Wimp_deco
SkipDumpLoadCrossCheck = svntest.testcase.SkipDumpLoadCrossCheck_deco
Item = svntest.wc.StateItem
+def read_rep_cache(repo_dir):
+ """Return the rep-cache contents as a dict {hash: (rev, index, ...)}.
+ """
+ db_path = os.path.join(repo_dir, 'db', 'rep-cache.db')
+ db1 = svntest.sqlite3.connect(db_path)
+ schema1 = db1.execute("pragma user_version").fetchone()[0]
+ # Can't test newer rep-cache schemas with an old built-in SQLite.
+ if schema1 >= 2 and svntest.sqlite3.sqlite_version_info < (3, 8, 2):
+ raise svntest.Failure("Can't read rep-cache schema %d using old "
+ "Python-SQLite version %s < (3,8,2)" %
+ (schema1,
+ svntest.sqlite3.sqlite_version_info))
+
+ content = { row[0]: row[1:] for row in
+ db1.execute("select * from rep_cache") }
+ return content
+
def check_hotcopy_bdb(src, dst):
"Verify that the SRC BDB repository has been correctly copied to DST."
### TODO: This function should be extended to verify all hotcopied files,
@@ -256,7 +273,8 @@ def patch_format(repo_dir, shard_size):
new_contents = b"\n".join(processed_lines)
os.chmod(format_path, svntest.main.S_ALL_RW)
- open(format_path, 'wb').write(new_contents)
+ with open(format_path, 'wb') as f:
+ f.write(new_contents)
def is_sharded(repo_dir):
"""Return whether the FSFS repository REPO_DIR is sharded."""
@@ -777,9 +795,13 @@ def verify_windows_paths_in_repos(sbox):
def fsfs_file(repo_dir, kind, rev):
if svntest.main.options.server_minor_version >= 5:
if svntest.main.options.fsfs_sharding is None:
+ if svntest.main.is_fs_type_fsx():
+ rev = 'r' + rev
return os.path.join(repo_dir, 'db', kind, '0', rev)
else:
shard = int(rev) // svntest.main.options.fsfs_sharding
+ if svntest.main.is_fs_type_fsx():
+ rev = 'r' + rev
path = os.path.join(repo_dir, 'db', kind, str(shard), rev)
if svntest.main.options.fsfs_packing is None or kind == 'revprops':
@@ -1043,7 +1065,7 @@ def fsfs_recover_old_db_current(sbox):
def load_with_parent_dir(sbox):
"'svnadmin load --parent-dir' reparents mergeinfo"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2983. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2983. ##
sbox.build(empty=True)
dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
@@ -1134,7 +1156,7 @@ def set_uuid(sbox):
def reflect_dropped_renumbered_revs(sbox):
"reflect dropped renumbered revs in svn:mergeinfo"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3020. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3020. ##
sbox.build(empty=True)
@@ -1326,7 +1348,7 @@ def verify_with_invalid_revprops(sbox):
# 2) Dump 'SOURCE-REPOS' in a series of incremental dumps and load
# each of them to 'TARGET-REPOS'.
#
-# See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc13
+# See https://issues.apache.org/jira/browse/SVN-3020#desc13
@Issue(3020)
def dont_drop_valid_mergeinfo_during_incremental_loads(sbox):
"don't filter mergeinfo revs from incremental dump"
@@ -1512,7 +1534,7 @@ def dont_drop_valid_mergeinfo_during_incremental_loads(sbox):
# Check the resulting mergeinfo. We expect the exact same results
# as Part 3.
- # See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
+ # See https://issues.apache.org/jira/browse/SVN-3020#desc16.
svntest.actions.run_and_verify_svn(expected_output, [],
'propget', 'svn:mergeinfo', '-R',
sbox.repo_url)
@@ -1523,7 +1545,7 @@ def dont_drop_valid_mergeinfo_during_incremental_loads(sbox):
def hotcopy_symlink(sbox):
"'svnadmin hotcopy' replicates symlink"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2591. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2591. ##
# Create a repository.
sbox.build(create_wc=False, empty=True)
@@ -1629,9 +1651,9 @@ text
sbox.build(empty=True)
# Try to load the dumpstream, expecting a failure (because of mixed EOLs).
- exp_err = svntest.verify.RegexListOutput(['svnadmin: E125005',
- 'svnadmin: E125005',
- 'svnadmin: E125017'],
+ exp_err = svntest.verify.RegexListOutput(['svnadmin: E125005:.*',
+ 'svnadmin: E125005:.*',
+ 'svnadmin: E125017:.*'],
match_all=False)
load_and_verify_dumpstream(sbox, [], exp_err, dumpfile_revisions,
False, dump_str, '--ignore-uuid')
@@ -1760,10 +1782,10 @@ def test_lslocks_and_rmlocks(sbox):
def expected_output_list(path):
return [
"Path: " + path,
- "UUID Token: opaquelocktoken",
+ "UUID Token: opaquelocktoken:.*",
"Owner: jrandom",
- "Created:",
- "Expires:",
+ "Created:.*",
+ "Expires:.*",
"Comment \(1 line\):",
"Locking files",
"\n", # empty line
@@ -1814,7 +1836,7 @@ def test_lslocks_and_rmlocks(sbox):
def load_ranges(sbox):
"'svnadmin load --revision X:Y'"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3734. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3734. ##
sbox.build(empty=True)
dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
@@ -2165,7 +2187,7 @@ def verify_keep_going(sbox):
sbox.repo_dir)
if (svntest.main.is_fs_log_addressing()):
- exp_out = svntest.verify.RegexListOutput([".*Verifying metadata at revision 0"])
+ exp_out = svntest.verify.RegexListOutput([".*Verifying metadata at revision 0.*"])
else:
exp_out = svntest.verify.RegexListOutput([".*Verified revision 0.",
".*Verified revision 1."])
@@ -2856,10 +2878,7 @@ def verify_quickly(sbox):
"verify quickly using metadata"
sbox.build(create_wc = False)
- if svntest.main.is_fs_type_fsfs():
- rev_file = open(fsfs_file(sbox.repo_dir, 'revs', '1'), 'r+b')
- else:
- rev_file = open(fsfs_file(sbox.repo_dir, 'revs', 'r1'), 'r+b')
+ rev_file = open(fsfs_file(sbox.repo_dir, 'revs', '1'), 'r+b')
# set new contents
rev_file.seek(8)
@@ -3458,7 +3477,8 @@ def load_from_file(sbox):
sbox.build(empty=True)
file = sbox.get_tempname()
- open(file, 'wb').writelines(clean_dumpfile())
+ with open(file, 'wb') as f:
+ f.writelines(clean_dumpfile())
svntest.actions.run_and_verify_svnadmin2(None, [],
0, 'load', '--file', file,
'--ignore-uuid', sbox.repo_dir)
@@ -3762,7 +3782,7 @@ def dump_exclude_all_rev_changes(sbox):
# Check log. Revision properties ('svn:log' etc.) should be empty for r2.
expected_output = svntest.verify.RegexListOutput([
'-+\\n',
- 'r3\ |\ jrandom\ |\ .*\ |\ 1\ line\\n',
+ 'r3 | jrandom | .* | 1 line\\n',
re.escape('Changed paths:'),
re.escape(' A /r3a'),
re.escape(' A /r3b'),
@@ -3774,7 +3794,7 @@ def dump_exclude_all_rev_changes(sbox):
'',
'',
'-+\\n',
- 'r1\ |\ jrandom\ |\ .*\ |\ 1\ line\\n',
+ 'r1 | jrandom | .* | 1 line\\n',
re.escape('Changed paths:'),
re.escape(' A /r1a'),
re.escape(' A /r1b'),
@@ -3823,6 +3843,76 @@ def load_issue4725(sbox):
sbox2.build(create_wc=False, empty=True)
load_and_verify_dumpstream(sbox2, None, [], None, False, dump, '-M100')
+@Issue(4767)
+def dump_no_canonicalize_svndate(sbox):
+ "svnadmin dump shouldn't canonicalize svn:date"
+
+ sbox.build(create_wc=False, empty=True)
+ svntest.actions.enable_revprop_changes(sbox.repo_dir)
+
+ # set svn:date in a non-canonical format (not six decimal places)
+ propval = "2015-01-01T00:00:00.0Z"
+ svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
+ "propset", "--revprop", "-r0", "svn:date",
+ propval,
+ sbox.repo_url)
+
+ dump_lines = svntest.actions.run_and_verify_dump(sbox.repo_dir)
+ assert propval + '\n' in dump_lines
+
+def check_recover_prunes_rep_cache(sbox, enable_rep_sharing):
+ """Check 'recover' prunes the rep-cache while enable-rep-sharing is
+ true/false.
+ """
+ # Remember the initial rep cache content.
+ rep_cache_r1 = read_rep_cache(sbox.repo_dir)
+ #print '\n'.join([h + ": " + repr(ref) for h, ref in rep_cache_r1.items()])
+
+ # Commit one new rep and check the rep-cache is extended.
+ sbox.simple_append('iota', 'New line.\n')
+ sbox.simple_commit()
+ rep_cache_r2 = read_rep_cache(sbox.repo_dir)
+ assert len(rep_cache_r2) == len(rep_cache_r1) + 1
+
+ # To test 'recover' while rep-sharing is disabled, disable it now.
+ if not enable_rep_sharing:
+ fsfs_conf = svntest.main.get_fsfs_conf_file_path(sbox.repo_dir)
+ svntest.main.file_append(fsfs_conf,
+ "[rep-sharing]\n"
+ "enable-rep-sharing = false\n")
+
+ # Break r2 in such a way that 'recover' will discard it
+ head_rev_path = fsfs_file(sbox.repo_dir, 'revs', '2')
+ os.remove(head_rev_path)
+ current_path = os.path.join(sbox.repo_dir, 'db', 'current')
+ svntest.main.file_write(current_path, '1\n')
+
+ # Recover back to r1.
+ svntest.actions.run_and_verify_svnadmin(None, [],
+ "recover", sbox.repo_dir)
+
+ # Check the rep-cache is pruned.
+ rep_cache_recovered = read_rep_cache(sbox.repo_dir)
+ assert rep_cache_recovered == rep_cache_r1
+
+@Issue(4077)
+@SkipUnless(svntest.main.is_fs_type_fsfs)
+@SkipUnless(svntest.main.python_sqlite_can_read_without_rowid)
+def recover_prunes_rep_cache_when_enabled(sbox):
+ "recover prunes rep cache when enabled"
+ sbox.build()
+
+ check_recover_prunes_rep_cache(sbox, enable_rep_sharing=True)
+
+@Issue(4077)
+@SkipUnless(svntest.main.is_fs_type_fsfs)
+@SkipUnless(svntest.main.python_sqlite_can_read_without_rowid)
+def recover_prunes_rep_cache_when_disabled(sbox):
+ "recover prunes rep cache when disabled"
+ sbox.build()
+
+ check_recover_prunes_rep_cache(sbox, enable_rep_sharing=False)
+
########################################################################
# Run the tests
@@ -3897,6 +3987,9 @@ test_list = [ None,
dump_exclude_all_rev_changes,
dump_invalid_filtering_option,
load_issue4725,
+ dump_no_canonicalize_svndate,
+ recover_prunes_rep_cache_when_enabled,
+ recover_prunes_rep_cache_when_disabled,
]
if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/svnauthz_tests.py b/subversion/tests/cmdline/svnauthz_tests.py
index fd1de40..ac6a375 100755
--- a/subversion/tests/cmdline/svnauthz_tests.py
+++ b/subversion/tests/cmdline/svnauthz_tests.py
@@ -197,9 +197,8 @@ def svnauthz_validate_txn_test(sbox):
svntest.main.create_python_hook_script(pre_commit_hook, hook_instance)
svntest.main.file_append(authz_path, 'x')
expected_status.tweak('A/authz', status=' ', wc_rev=4)
- if svntest.actions.run_and_verify_commit(wc_dir, expected_output,
- expected_status):
- raise svntest.Failure
+ svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+ expected_status)
expected_data = svntest.verify.ExpectedOutput("Exit 2\n", match_all=False)
verify_logfile(logfilepath, expected_data)
@@ -275,9 +274,8 @@ def svnauthz_accessof_repo_test(sbox):
expected_status.add({
'A/authz' : Item(status=' ', wc_rev=2),
})
- if svntest.actions.run_and_verify_commit(wc_dir, expected_output,
- expected_status):
- raise svntest.Failure
+ svntest.actions.run_and_verify_commit(wc_dir, expected_output,
+ expected_status)
# Anonymous access with no path, and no repository should be rw
# since it returns the highest level of access granted anywhere.
diff --git a/subversion/tests/cmdline/svndumpfilter_tests.py b/subversion/tests/cmdline/svndumpfilter_tests.py
index 7ee09a4..a45b50b 100755
--- a/subversion/tests/cmdline/svndumpfilter_tests.py
+++ b/subversion/tests/cmdline/svndumpfilter_tests.py
@@ -83,7 +83,7 @@ def filter_and_return_output(dump, bufsize=0, *varargs):
def reflect_dropped_renumbered_revs(sbox):
"reflect dropped renumbered revs in svn:mergeinfo"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2982. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2982. ##
# Test svndumpfilter with include option
sbox.build(empty=True)
@@ -134,7 +134,7 @@ def svndumpfilter_loses_mergeinfo(sbox):
"svndumpfilter loses mergeinfo"
#svndumpfilter loses mergeinfo if invoked without --renumber-revs
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=3181. ##
+ ## See https://issues.apache.org/jira/browse/SVN-3181. ##
sbox.build(empty=True)
dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
@@ -217,7 +217,7 @@ def _simple_dumpfilter_test(sbox, dumpfile, *dumpargs):
@Issue(2697)
def dumpfilter_with_targets(sbox):
"svndumpfilter --targets blah"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=2697. ##
+ ## See https://issues.apache.org/jira/browse/SVN-2697. ##
sbox.build(empty=True)
@@ -677,7 +677,7 @@ def accepts_deltas(sbox):
@Issue(4234)
def dumpfilter_targets_expect_leading_slash_prefixes(sbox):
"dumpfilter targets expect leading '/' in prefixes"
- ## See http://subversion.tigris.org/issues/show_bug.cgi?id=4234. ##
+ ## See https://issues.apache.org/jira/browse/SVN-4234. ##
sbox.build(empty=True)
diff --git a/subversion/tests/cmdline/svnfsfs_tests.py b/subversion/tests/cmdline/svnfsfs_tests.py
index e1fd73d..aff3c2f 100755
--- a/subversion/tests/cmdline/svnfsfs_tests.py
+++ b/subversion/tests/cmdline/svnfsfs_tests.py
@@ -94,7 +94,8 @@ def patch_format(repo_dir, shard_size):
new_contents = b"\n".join(processed_lines)
os.chmod(format_path, svntest.main.S_ALL_RW)
- open(format_path, 'wb').write(new_contents)
+ with open(format_path, 'wb') as f:
+ f.write(new_contents)
######################################################################
# Tests
diff --git a/subversion/tests/cmdline/svnmover_tests.py b/subversion/tests/cmdline/svnmover_tests.py
index bfdbb1f..6c98b64 100755
--- a/subversion/tests/cmdline/svnmover_tests.py
+++ b/subversion/tests/cmdline/svnmover_tests.py
@@ -469,7 +469,7 @@ rm A/B/C/Y
' D /top0/A/B/C/Y',
]))
expected_output = svntest.verify.UnorderedRegexListOutput(escaped
- + ['^-', '^r2', '^-', '^Changed paths:',])
+ + ['^--*', '^r2.*', '^--*', '^Changed paths:',])
svntest.actions.run_and_verify_svn(expected_output, [],
'log', '-qvr2', repo_url)
@@ -755,7 +755,7 @@ def simple_moves_within_a_branch(sbox):
'mv lib/foo/y2 y2')
# move and rename, dir with children
test_svnmover2(sbox, '/trunk',
- reported_br_diff('') +
+ reported_br_diff('trunk') +
reported_add('subdir') +
reported_move('lib', 'subdir/lib2'),
'mkdir subdir',
@@ -765,7 +765,7 @@ def simple_moves_within_a_branch(sbox):
# moves and renames together
# (put it all back to how it was, in one commit)
test_svnmover2(sbox, '/trunk',
- reported_br_diff('') +
+ reported_br_diff('trunk') +
reported_move('subdir/lib2/README.txt', 'README') +
reported_move('subdir/lib2', 'lib') +
reported_move('y2', 'lib/foo/y') +
diff --git a/subversion/tests/cmdline/svnmucc_tests.py b/subversion/tests/cmdline/svnmucc_tests.py
index f95c558..3159ec2 100755
--- a/subversion/tests/cmdline/svnmucc_tests.py
+++ b/subversion/tests/cmdline/svnmucc_tests.py
@@ -458,7 +458,7 @@ rm A/B/C/Y
' D /A/B/C/Y',
]))
expected_output = svntest.verify.UnorderedRegexListOutput(excaped
- + ['^-', '^r3', '^-', '^Changed paths:',])
+ + ['^--*', '^r3.*', '^--*', '^Changed paths:',])
svntest.actions.run_and_verify_svn(expected_output, [],
'log', '-qvr3', repo_url)
diff --git a/subversion/tests/cmdline/svnrdump_tests.py b/subversion/tests/cmdline/svnrdump_tests.py
index ae6a7e0..b6d4c6f 100755
--- a/subversion/tests/cmdline/svnrdump_tests.py
+++ b/subversion/tests/cmdline/svnrdump_tests.py
@@ -80,7 +80,7 @@ def compare_repos_dumps(sbox, other_dumpfile,
### This call kind-of assumes EXPECTED is first and ACTUAL is second.
svntest.verify.compare_dump_files(
- "Dump files", "DUMP", other_dumpfile, sbox_dumpfile)
+ None, None, other_dumpfile, sbox_dumpfile)
def run_dump_test(sbox, dumpfile_name, expected_dumpfile_name = None,
subdir = None, bypass_prop_validation = False,
@@ -455,7 +455,7 @@ def reflect_dropped_renumbered_revs(sbox):
# 2) Dump 'SOURCE-REPOS' in a series of incremental dumps and load
# each of them to 'TARGET-REPOS'.
#
-# See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc13
+# See https://issues.apache.org/jira/browse/SVN-3020#desc13
#
# This test replicates svnadmin_tests.py 20 'don't filter mergeinfo revs
# from incremental dump' but uses 'svnrdump [dump|load]' in place of
@@ -704,7 +704,7 @@ def dont_drop_valid_mergeinfo_during_incremental_svnrdump_loads(sbox):
# Check the resulting mergeinfo. We expect the exact same results
# as Part 3.
- # See http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
+ # See https://issues.apache.org/jira/browse/SVN-3020#desc16.
svntest.actions.run_and_verify_svn(expected_output, [],
'propget', 'svn:mergeinfo', '-R',
sbox.repo_url)
diff --git a/subversion/tests/cmdline/svnsync_authz_tests.py b/subversion/tests/cmdline/svnsync_authz_tests.py
index e8b9444..e464cde 100755
--- a/subversion/tests/cmdline/svnsync_authz_tests.py
+++ b/subversion/tests/cmdline/svnsync_authz_tests.py
@@ -415,7 +415,7 @@ def specific_deny_authz(sbox):
# For mod_dav_svn's parent path setup we need per-repos permissions in
# the authz file...
- if sbox.repo_url.startswith('http'):
+ if svntest.main.is_ra_type_dav():
src_authz = sbox.authz_name()
dst_authz = dest_sbox.authz_name()
write_authz_file(sbox, None,
diff --git a/subversion/tests/cmdline/svnsync_tests.py b/subversion/tests/cmdline/svnsync_tests.py
index ba55fb5..744fbbb 100755
--- a/subversion/tests/cmdline/svnsync_tests.py
+++ b/subversion/tests/cmdline/svnsync_tests.py
@@ -167,7 +167,7 @@ def verify_mirror(dest_sbox, exp_dump_file_contents):
dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
svntest.verify.compare_dump_files(
- "Dump files", "DUMP", exp_dump_file_contents, dest_dump)
+ None, None, exp_dump_file_contents, dest_dump)
def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
bypass_prop_validation=False, source_prop_encoding=None,
diff --git a/subversion/tests/cmdline/svntest/actions.py b/subversion/tests/cmdline/svntest/actions.py
index 8930b63..1051844 100644
--- a/subversion/tests/cmdline/svntest/actions.py
+++ b/subversion/tests/cmdline/svntest/actions.py
@@ -74,6 +74,11 @@ def setup_pristine_greek_repository():
if not os.path.exists(main.general_repo_dir):
os.makedirs(main.general_repo_dir) # this also creates all the intermediate dirs
+ if not os.path.exists(main.other_dav_root_dir):
+ os.makedirs(main.other_dav_root_dir)
+ if not os.path.exists(main.non_dav_root_dir):
+ os.makedirs(main.non_dav_root_dir)
+
# If there's no pristine repos, create one.
if not os.path.exists(main.pristine_greek_repos_dir):
if main.options.fsfs_version is not None:
diff --git a/subversion/tests/cmdline/svntest/main.py b/subversion/tests/cmdline/svntest/main.py
index 09a9722..4aa6041 100644
--- a/subversion/tests/cmdline/svntest/main.py
+++ b/subversion/tests/cmdline/svntest/main.py
@@ -56,7 +56,7 @@ import svntest
from svntest import Failure
from svntest import Skip
-SVN_VER_MINOR = 10
+SVN_VER_MINOR = 11
######################################################################
#
@@ -224,6 +224,10 @@ SVN_PROP_INHERITABLE_IGNORES = "svn:global-ignores"
general_repo_dir = os.path.join(work_dir, "repositories")
general_wc_dir = os.path.join(work_dir, "working_copies")
+# Directories used for DAV tests
+other_dav_root_dir = os.path.join(work_dir, "fsdavroot")
+non_dav_root_dir = os.path.join(work_dir, "nodavroot")
+
# temp directory in which we will create our 'pristine' local
# repository and other scratch data. This should be removed when we
# quit and when we startup.
@@ -978,7 +982,8 @@ def file_write(path, contents, mode='w'):
which is (w)rite by default."""
if sys.version_info < (3, 0):
- open(path, mode).write(contents)
+ with open(path, mode) as f:
+ f.write(contents)
else:
# Python 3: Write data in the format required by MODE, i.e. byte arrays
# to 'b' files, utf-8 otherwise."""
@@ -990,9 +995,11 @@ def file_write(path, contents, mode='w'):
contents = contents.decode("utf-8")
if isinstance(contents, str):
- codecs.open(path, mode, "utf-8").write(contents)
+ with codecs.open(path, mode, "utf-8") as f:
+ f.write(contents)
else:
- open(path, mode).write(contents)
+ with open(path, mode) as f:
+ f.write(contents)
# For making local mods to files
def file_append(path, new_text):
@@ -1008,7 +1015,8 @@ def file_append_binary(path, new_text):
def file_substitute(path, contents, new_contents):
"""Replace the CONTENTS in the file at PATH using the NEW_CONTENTS"""
fcontent = open(path, 'r').read().replace(contents, new_contents)
- open(path, 'w').write(fcontent)
+ with open(path, 'w') as f:
+ f.write(fcontent)
# For setting up authz, hooks and making other tweaks to created repos
def _post_create_repos(path, minor_version = None):
@@ -1035,7 +1043,8 @@ def _post_create_repos(path, minor_version = None):
users += (crosscheck_username + " = " + crosscheck_password + "\n")
file_append(os.path.join(path, "conf", "passwd"), users)
- if options.fs_type is None or options.fs_type == 'fsfs':
+ if options.fs_type is None or options.fs_type == 'fsfs' or \
+ options.fs_type == 'fsx':
# fsfs.conf file
if (minor_version is None or minor_version >= 6):
confpath = get_fsfs_conf_file_path(path)
@@ -1633,6 +1642,15 @@ def server_has_atomic_revprop():
def server_has_reverse_get_file_revs():
return options.server_caps.has_reverse_get_file_revs
+def python_sqlite_can_read_our_wc_db():
+ """Check if the Python builtin is capable enough to peek into wc.db"""
+ # Currently enough (1.7-1.9)
+ return svntest.sqlite3.sqlite_version_info >= (3, 6, 18)
+
+def python_sqlite_can_read_without_rowid():
+ """Check if the Python builtin is capable enough to read new rep-cache"""
+ return svntest.sqlite3.sqlite_version_info >= (3, 8, 2)
+
def is_plaintext_password_storage_disabled():
try:
predicate = re.compile("^WARNING: Plaintext password storage is enabled!")
@@ -2335,6 +2353,8 @@ def execute_tests(test_list, serial_only = False, test_name = None,
global pristine_url
global pristine_greek_repos_url
+ global other_dav_root_url
+ global non_dav_root_url
global svn_binary
global svnadmin_binary
global svnlook_binary
@@ -2414,6 +2434,10 @@ def execute_tests(test_list, serial_only = False, test_name = None,
pristine_greek_repos_dir.replace(
os.path.sep, '/'))
+ other_dav_root_url = options.test_area_url + '/fsdavroot'
+ non_dav_root_url = options.test_area_url + '/nodavroot'
+
+
if options.use_jsvn:
if options.svn_bin is None:
options.svn_bin = ''
diff --git a/subversion/tests/cmdline/svntest/mergetrees.py b/subversion/tests/cmdline/svntest/mergetrees.py
index 0cee3d2..0cee3d2 100755..100644
--- a/subversion/tests/cmdline/svntest/mergetrees.py
+++ b/subversion/tests/cmdline/svntest/mergetrees.py
diff --git a/subversion/tests/cmdline/svntest/sandbox.py b/subversion/tests/cmdline/svntest/sandbox.py
index b1c9861..cc8df2e 100644
--- a/subversion/tests/cmdline/svntest/sandbox.py
+++ b/subversion/tests/cmdline/svntest/sandbox.py
@@ -168,7 +168,8 @@ class Sandbox:
or open(self.authz_file,'r').read() != default_authz)):
tmp_authz_file = os.path.join(svntest.main.work_dir, "authz-" + self.name)
- open(tmp_authz_file, 'w').write(default_authz)
+ with open(tmp_authz_file, 'w') as f:
+ f.write(default_authz)
shutil.move(tmp_authz_file, self.authz_file)
def authz_name(self, repo_dir=None):
@@ -492,7 +493,8 @@ class Sandbox:
if not svnrdump_headers_always.match(l)]
# Ignore differences in number of blank lines between node records,
# as svnrdump puts 3 whereas svnadmin puts 2 after a replace-with-copy.
- svntest.verify.compare_dump_files(None, None,
+ svntest.verify.compare_dump_files('svnadmin dump, tweaked',
+ 'svnrdump dump, tweaked',
dumpfile_a_d_cmp,
dumpfile_r_d_cmp,
ignore_number_of_blank_lines=True)
@@ -523,20 +525,22 @@ class Sandbox:
reloaded_dumpfile_a_n = svntest.actions.run_and_verify_dump(repo_dir_a_n)
reloaded_dumpfile_a_d = svntest.actions.run_and_verify_dump(repo_dir_a_d)
reloaded_dumpfile_r_d = svntest.actions.run_and_verify_dump(repo_dir_r_d)
- svntest.verify.compare_dump_files(None, None,
+ svntest.verify.compare_dump_files('svnadmin dump no delta, loaded, dumped',
+ 'svnadmin dump --deltas, loaded, dumped',
reloaded_dumpfile_a_n,
reloaded_dumpfile_a_d,
ignore_uuid=True)
- svntest.verify.compare_dump_files(None, None,
+ svntest.verify.compare_dump_files('svnadmin dump, loaded, dumped',
+ 'svnrdump dump, loaded, dumped',
reloaded_dumpfile_a_d,
reloaded_dumpfile_r_d,
ignore_uuid=True)
# Run each dump through svndumpfilter and check for no further change.
- for dumpfile in [dumpfile_a_n,
- dumpfile_a_d,
- dumpfile_r_d
- ]:
+ for dumpfile, dumpfile_desc in [(dumpfile_a_n, 'svnadmin dump'),
+ (dumpfile_a_d, 'svnadmin dump --deltas'),
+ (dumpfile_r_d, 'svnrdump dump'),
+ ]:
### No buffer size seems to work for update_tests-2. So skip that test?
### (Its dumpfile size is ~360 KB non-delta, ~180 KB delta.)
if len(''.join(dumpfile)) > 100000:
@@ -550,7 +554,9 @@ class Sandbox:
# svndumpfilter strips them.
# Ignore differences in number of blank lines between node records,
# as svndumpfilter puts 3 instead of 2 after an add or delete record.
- svntest.verify.compare_dump_files(None, None, dumpfile, dumpfile2,
+ svntest.verify.compare_dump_files(dumpfile_desc,
+ 'after svndumpfilter include /',
+ dumpfile, dumpfile2,
expect_content_length_always=True,
ignore_empty_prop_sections=True,
ignore_number_of_blank_lines=True)
diff --git a/subversion/tests/cmdline/svntest/tree.py b/subversion/tests/cmdline/svntest/tree.py
index 6c34238..fbcbcfc 100644
--- a/subversion/tests/cmdline/svntest/tree.py
+++ b/subversion/tests/cmdline/svntest/tree.py
@@ -267,19 +267,8 @@ class SVNTreeNode:
line += "%-20s: Item(" % ("'%s'" % path.replace(os.sep, '/'))
comma = False
- mime_type = self.props.get("svn:mime-type")
- if not mime_type or mime_type.startswith("text/"):
- if self.contents is not None:
- # Escape some characters for nicer script and readability.
- # (This is error output. I guess speed is no consideration here.)
- line += "contents=\"%s\"" % (self.contents
- .replace('\n','\\n')
- .replace('"','\\"')
- .replace('\r','\\r')
- .replace('\t','\\t'))
- comma = True
- else:
- line += 'content is binary data'
+ if self.contents is not None:
+ line += "contents=" + repr(self.contents)
comma = True
if self.props:
diff --git a/subversion/tests/cmdline/svntest/verify.py b/subversion/tests/cmdline/svntest/verify.py
index 904a044..0fb7bcd 100644
--- a/subversion/tests/cmdline/svntest/verify.py
+++ b/subversion/tests/cmdline/svntest/verify.py
@@ -150,8 +150,9 @@ class ExpectedOutput(object):
MESSAGE unless it is None, the expected lines, the ACTUAL lines,
and a diff, all labeled with LABEL.
"""
- display_lines(message, self.expected, actual, label, label)
- display_lines_diff(self.expected, actual, label, label)
+ e_label = label + ' (match_all=%s)' % (self.match_all,)
+ display_lines(message, self.expected, actual, e_label, label)
+ display_lines_diff(self.expected, actual, e_label, label)
class AnyOutput(ExpectedOutput):
@@ -181,12 +182,36 @@ class AnyOutput(ExpectedOutput):
logger.warn(message)
+def re_fullmatch(pattern, string, flags=0):
+ """If the whole STRING matches the regular expression PATTERN,
+ return a corresponding match object.
+ Based on re.fullmatch() in Python 3.4.
+ """
+ if pattern.endswith('$'):
+ return re.match(pattern, string, flags)
+
+ return re.match(pattern + '$', string, flags)
+
+def regex_fullmatch(rx, string):
+ """If the whole STRING matches the compiled regular expression RX,
+ return a corresponding match object.
+ Based on regex.fullmatch() in Python 3.4.
+ """
+ if rx.pattern.endswith('$'):
+ return rx.match(string)
+
+ return re_fullmatch(rx.pattern, string, rx.flags)
+
class RegexOutput(ExpectedOutput):
"""Matches a single regular expression.
If MATCH_ALL is true, every actual line must match the RE. If
MATCH_ALL is false, at least one actual line must match the RE. In
any case, there must be at least one line of actual output.
+
+ The RE must match a prefix of the actual line, in contrast to the
+ RegexListOutput and UnorderedRegexListOutput classes which match
+ whole lines.
"""
def __init__(self, expected, match_all=True):
@@ -212,7 +237,8 @@ class RegexOutput(ExpectedOutput):
return any(self.expected_re.match(line) for line in actual)
def display_differences(self, message, label, actual):
- display_lines(message, self.expected, actual, label + ' (regexp)', label)
+ e_label = label + ' (regexp, match_all=%s)' % (self.match_all,)
+ display_lines(message, self.expected, actual, e_label, label)
def insert(self, index, line):
self.expected.insert(index, line)
@@ -228,6 +254,9 @@ class RegexListOutput(ExpectedOutput):
ones.
In any case, there must be at least one line of actual output.
+
+ The REs must match whole actual lines, in contrast to the RegexOutput
+ class which matches a prefix of the actual line.
"""
def __init__(self, expected, match_all=True):
@@ -243,18 +272,37 @@ class RegexListOutput(ExpectedOutput):
if self.match_all:
return (len(self.expected_res) == len(actual) and
- all(e.match(a) for e, a in zip(self.expected_res, actual)))
+ all(regex_fullmatch(e, a) for e, a in zip(self.expected_res, actual)))
i_expected = 0
for actual_line in actual:
- if self.expected_res[i_expected].match(actual_line):
+ if regex_fullmatch(self.expected_res[i_expected], actual_line):
i_expected += 1
if i_expected == len(self.expected_res):
return True
return False
def display_differences(self, message, label, actual):
- display_lines(message, self.expected, actual, label + ' (regexp)', label)
+ e_label = label + ' (regexp, match_all=%s)' % (self.match_all,)
+ display_lines(message, self.expected, actual, e_label, label)
+
+ assert actual is not None
+ if not isinstance(actual, list):
+ actual = [actual]
+
+ if self.match_all:
+ logger.warn('DIFF ' + label + ':')
+ if len(self.expected) != len(actual):
+ logger.warn('# Expected %d lines; actual %d lines' %
+ (len(self.expected), len(actual)))
+ for e, a in map(None, self.expected_res, actual):
+ if e is not None and a is not None and regex_fullmatch(e, a):
+ logger.warn("| " + a.rstrip())
+ else:
+ if e is not None:
+ logger.warn("| -" + repr(e.pattern))
+ if a is not None:
+ logger.warn("| +" + repr(a))
def insert(self, index, line):
self.expected.insert(index, line)
@@ -279,8 +327,9 @@ class UnorderedOutput(ExpectedOutput):
return sorted(self.expected) == sorted(actual)
def display_differences(self, message, label, actual):
- display_lines(message, self.expected, actual, label + ' (unordered)', label)
- display_lines_diff(self.expected, actual, label + ' (unordered)', label)
+ e_label = label + ' (unordered)'
+ display_lines(message, self.expected, actual, e_label, label)
+ display_lines_diff(sorted(self.expected), sorted(actual), e_label, label)
class UnorderedRegexListOutput(ExpectedOutput):
@@ -295,6 +344,9 @@ class UnorderedRegexListOutput(ExpectedOutput):
expressions. The implementation matches each expression in turn to
the first unmatched actual line that it can match, and does not try
all the permutations when there are multiple possible matches.
+
+ The REs must match whole actual lines, in contrast to the RegexOutput
+ class which matches a prefix of the actual line.
"""
def __init__(self, expected):
@@ -305,13 +357,16 @@ class UnorderedRegexListOutput(ExpectedOutput):
assert actual is not None
if not isinstance(actual, list):
actual = [actual]
+ else:
+ # copy the list so we can remove elements without affecting caller
+ actual = actual[:]
if len(self.expected) != len(actual):
return False
for e in self.expected:
expect_re = re.compile(e)
for actual_line in actual:
- if expect_re.match(actual_line):
+ if regex_fullmatch(expect_re, actual_line):
actual.remove(actual_line)
break
else:
@@ -320,9 +375,30 @@ class UnorderedRegexListOutput(ExpectedOutput):
return True
def display_differences(self, message, label, actual):
- display_lines(message, self.expected, actual,
- label + ' (regexp) (unordered)', label)
+ e_label = label + ' (regexp) (unordered)'
+ display_lines(message, self.expected, actual, e_label, label)
+
+ assert actual is not None
+ if not isinstance(actual, list):
+ actual = [actual]
+ else:
+ # copy the list so we can remove elements without affecting caller
+ actual = actual[:]
+ logger.warn('DIFF ' + label + ':')
+ if len(self.expected) != len(actual):
+ logger.warn('# Expected %d lines; actual %d lines' %
+ (len(self.expected), len(actual)))
+ for e in self.expected:
+ expect_re = re.compile(e)
+ for actual_line in actual:
+ if regex_fullmatch(expect_re, actual_line):
+ actual.remove(actual_line)
+ break
+ else:
+ logger.warn("| -" + expect_re.pattern.rstrip())
+ for a in actual:
+ logger.warn("| +" + a.rstrip())
class AlternateOutput(ExpectedOutput):
"""Matches any one of a list of ExpectedOutput instances.
@@ -730,7 +806,8 @@ class DumpParser:
self.parse_all_revisions()
return self.parsed
-def compare_dump_files(message, label, expected, actual,
+def compare_dump_files(label_expected, label_actual,
+ expected, actual,
ignore_uuid=False,
expect_content_length_always=False,
ignore_empty_prop_sections=False,
@@ -772,6 +849,8 @@ def compare_dump_files(message, label, expected, actual,
if parsed_expected != parsed_actual:
print('DIFF of raw dumpfiles (including expected differences)')
+ print('--- ' + (label_expected or 'expected'))
+ print('+++ ' + (label_actual or 'actual'))
print(''.join(ndiff(expected, actual)))
raise svntest.Failure('DIFF of parsed dumpfiles (ignoring expected differences)\n'
+ '\n'.join(ndiff(
diff --git a/subversion/tests/cmdline/svntest/wc.py b/subversion/tests/cmdline/svntest/wc.py
index f805dc9..ddfc439 100644
--- a/subversion/tests/cmdline/svntest/wc.py
+++ b/subversion/tests/cmdline/svntest/wc.py
@@ -1092,8 +1092,7 @@ def svn_uri_quote(url):
def python_sqlite_can_read_wc():
"""Check if the Python builtin is capable enough to peek into wc.db"""
- # Currently enough (1.7-1.9)
- return svntest.sqlite3.sqlite_version_info >= (3, 6, 18)
+ return svntest.main.python_sqlite_can_read_our_wc_db()
def open_wc_db(local_path):
"""Open the SQLite DB for the WC path LOCAL_PATH.
diff --git a/subversion/tests/cmdline/svnversion_tests.py b/subversion/tests/cmdline/svnversion_tests.py
index 2ed6e46..e9d0927 100755
--- a/subversion/tests/cmdline/svnversion_tests.py
+++ b/subversion/tests/cmdline/svnversion_tests.py
@@ -71,9 +71,8 @@ def svnversion_test(sbox):
expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
expected_status.tweak('A/mu', wc_rev=2)
- if svntest.actions.run_and_verify_commit(wc_dir,
- expected_output, expected_status):
- raise svntest.Failure
+ svntest.actions.run_and_verify_commit(wc_dir,
+ expected_output, expected_status)
# Unmodified, mixed
svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
@@ -98,13 +97,12 @@ def svnversion_test(sbox):
+ 'appended mu text')
expected_disk.tweak('iota',
contents=expected_disk.desc['A/D/gamma'].contents)
- if svntest.actions.run_and_verify_switch(wc_dir, iota_path, gamma_url,
- expected_output,
- expected_disk,
- expected_status,
- [],
- False, '--ignore-ancestry'):
- raise svntest.Failure
+ svntest.actions.run_and_verify_switch(wc_dir, iota_path, gamma_url,
+ expected_output,
+ expected_disk,
+ expected_status,
+ [],
+ False, '--ignore-ancestry')
# Prop modified, mixed, part wc switched
svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
diff --git a/subversion/tests/cmdline/trans_tests.py b/subversion/tests/cmdline/trans_tests.py
index 0cab75e..71ca45e 100755
--- a/subversion/tests/cmdline/trans_tests.py
+++ b/subversion/tests/cmdline/trans_tests.py
@@ -814,7 +814,8 @@ def props_only_file_update(sbox):
]
# Create r2 with iota's contents and svn:keywords modified
- open(iota_path, 'w').writelines(content)
+ with open(iota_path, 'w') as f:
+ f.writelines(content)
svntest.main.run_svn(None, 'propset', 'svn:keywords', 'Author', iota_path)
expected_output = wc.State(wc_dir, {
@@ -831,7 +832,8 @@ def props_only_file_update(sbox):
# Create r3 that drops svn:keywords
# put the content back to its untranslated form
- open(iota_path, 'w').writelines(content)
+ with open(iota_path, 'w') as f:
+ f.writelines(content)
svntest.main.run_svn(None, 'propdel', 'svn:keywords', iota_path)
diff --git a/subversion/tests/cmdline/tree_conflict_tests.py b/subversion/tests/cmdline/tree_conflict_tests.py
index b3335b2..61f3231 100755
--- a/subversion/tests/cmdline/tree_conflict_tests.py
+++ b/subversion/tests/cmdline/tree_conflict_tests.py
@@ -473,6 +473,7 @@ def ensure_tree_conflict(sbox, operation,
run_and_verify_svn(expected_stdout, [],
'merge',
'--allow-mixed-revisions',
+ '--accept=postpone',
'-r', str(source_left_rev) + ':' + str(source_right_rev),
source_url, target_path)
else:
@@ -1096,13 +1097,15 @@ def at_directory_external(sbox):
svntest.main.run_svn(None, 'update', wc_dir)
# r3: modify ^/A/B/E/alpha
- open(sbox.ospath('A/B/E/alpha'), 'a').write('This is still A/B/E/alpha.\n')
+ with open(sbox.ospath('A/B/E/alpha'), 'a') as f:
+ f.write('This is still A/B/E/alpha.\n')
svntest.main.run_svn(None, 'commit', '-m', 'file mod', wc_dir)
svntest.main.run_svn(None, 'update', wc_dir)
merge_rev = svntest.main.youngest(sbox.repo_dir)
# r4: create ^/A/B/E/alpha2
- open(sbox.ospath('A/B/E/alpha2'), 'a').write("This is the file 'alpha2'.\n")
+ with open(sbox.ospath('A/B/E/alpha2'), 'a') as f:
+ f.write("This is the file 'alpha2'.\n")
svntest.main.run_svn(None, 'add', sbox.ospath('A/B/E/alpha2'))
svntest.main.run_svn(None, 'commit', '-m', 'file add', wc_dir)
svntest.main.run_svn(None, 'update', wc_dir)
@@ -1503,6 +1506,47 @@ def update_delete_mixed_rev(sbox):
}
run_and_verify_info([expected_info], sbox.repo_url + '/A/B/E/alpha2')
+# NB: This test will run forever if the bug it is testing for is present!
+def local_missing_dir_endless_loop(sbox):
+ "endless loop when resolving local-missing dir"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ sbox.simple_copy('A', 'A1')
+ sbox.simple_commit()
+ sbox.simple_update()
+ sbox.simple_move('A/B', 'A/B2')
+ sbox.simple_commit()
+ sbox.simple_update()
+ main.file_append_binary(sbox.ospath("A/B2/lambda"), "This is more content.\n")
+ sbox.simple_commit()
+ sbox.simple_update()
+
+ # Create a config which enables the interactive conflict resolver
+ config_contents = '''\
+[auth]
+password-stores =
+
+[miscellany]
+interactive-conflicts = true
+'''
+ config_dir = sbox.create_config_dir(config_contents)
+
+ # Bug: 'svn' keeps retrying interactive conflict resolution while the library
+ # keeps signalling 'SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE' -> endless loop
+ main.run_svn("Tree conflict on '%s'" % sbox.ospath("A1/B2"),
+ 'merge', '-c4', '^/A', sbox.ospath('A1'),
+ '--config-dir', config_dir, '--force-interactive')
+
+ # If everything works as expected the resolver will recommended a
+ # resolution option and 'svn' will resolve the conflict automatically.
+ # Verify that 'A1/B/lambda' contains the merged content:
+ contents = open(sbox.ospath('A1/B/lambda'), 'rb').readlines()
+ svntest.verify.compare_and_display_lines(
+ "A1/B/lambda has unexpectected contents", sbox.ospath("A1/B/lambda"),
+ [ "This is the file 'lambda'.\n", "This is more content.\n"], contents)
+
+
#######################################################################
# Run the tests
@@ -1534,6 +1578,7 @@ test_list = [ None,
actual_only_node_behaviour,
update_dir_with_not_present,
update_delete_mixed_rev,
+ local_missing_dir_endless_loop,
]
if __name__ == '__main__':
diff --git a/subversion/tests/cmdline/update_tests.py b/subversion/tests/cmdline/update_tests.py
index 16c7237..185fcb1 100755
--- a/subversion/tests/cmdline/update_tests.py
+++ b/subversion/tests/cmdline/update_tests.py
@@ -480,7 +480,7 @@ def update_to_rev_zero(sbox):
def receive_overlapping_same_change(sbox):
"overlapping identical changes should not conflict"
- ### (See http://subversion.tigris.org/issues/show_bug.cgi?id=682.)
+ ### (See https://issues.apache.org/jira/browse/SVN-682.)
###
### How this test works:
###
@@ -6719,6 +6719,7 @@ def update_conflict_details(sbox):
# Keywords should be updated in local file even if text change is shortcut
# (due to the local change being the same as the incoming change, for example).
@XFail()
+@Issue(4585)
def update_keywords_on_shortcut(sbox):
"update_keywords_on_shortcut"
diff --git a/subversion/tests/cmdline/upgrade_tests.py b/subversion/tests/cmdline/upgrade_tests.py
index 43258f6..c60dab5 100755
--- a/subversion/tests/cmdline/upgrade_tests.py
+++ b/subversion/tests/cmdline/upgrade_tests.py
@@ -392,7 +392,8 @@ def xml_entries_relocate(path, from_url, to_url):
entries = os.path.join(path, adm_name, 'entries')
txt = open(entries).read().replace('url="' + from_url, 'url="' + to_url)
os.chmod(entries, svntest.main.S_ALL_RWX)
- open(entries, 'w').write(txt)
+ with open(entries, 'w') as f:
+ f.write(txt)
for dirent in os.listdir(path):
item_path = os.path.join(path, dirent)
@@ -410,7 +411,8 @@ def simple_entries_replace(path, from_url, to_url):
entries = os.path.join(path, adm_name, 'entries')
txt = open(entries).read().replace(from_url, to_url)
os.chmod(entries, svntest.main.S_ALL_RWX)
- open(entries, 'wb').write(txt.encode())
+ with open(entries, 'wb') as f:
+ f.write(txt.encode())
for dirent in os.listdir(path):
item_path = os.path.join(path, dirent)
diff --git a/subversion/tests/libsvn_client/conflicts-test.c b/subversion/tests/libsvn_client/conflicts-test.c
index 0bcb464..67e516a 100644
--- a/subversion/tests/libsvn_client/conflicts-test.c
+++ b/subversion/tests/libsvn_client/conflicts-test.c
@@ -164,6 +164,7 @@ static const char *trunk_path = "A";
static const char *branch_path = "A_branch";
static const char *branch2_path = "A_branch2";
static const char *new_file_name = "newfile.txt";
+static const char *new_file2_name = "newfile2.txt";
static const char *new_file_name_branch = "newfile-on-branch.txt";
static const char *deleted_file_name = "mu";
static const char *deleted_dir_name = "B";
@@ -540,7 +541,7 @@ create_wc_with_dir_add_vs_dir_add_merge_conflict(
/* Now move the new directory to the colliding path. */
new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
- sbox_wc_move(b, move_src_path, new_dir_path);
+ SVN_ERR(sbox_wc_move(b, move_src_path, new_dir_path));
SVN_ERR(sbox_wc_commit(b, ""));
}
new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
@@ -608,7 +609,6 @@ create_wc_with_dir_add_vs_dir_add_merge_conflict(
svn_client_conflict_option_postpone,
svn_client_conflict_option_accept_current_wc_state,
svn_client_conflict_option_incoming_add_ignore,
- svn_client_conflict_option_incoming_added_dir_merge,
svn_client_conflict_option_incoming_added_dir_replace,
svn_client_conflict_option_incoming_added_dir_replace_and_merge,
-1 /* end of list */
@@ -1292,6 +1292,7 @@ test_merge_incoming_added_dir_replace_and_merge2(const svn_test_opts_t *opts,
static svn_error_t *
create_wc_with_incoming_delete_file_merge_conflict(svn_test__sandbox_t *b,
svn_boolean_t move,
+ svn_boolean_t move_unrelated,
svn_boolean_t do_switch)
{
svn_client_ctx_t *ctx;
@@ -1313,6 +1314,17 @@ create_wc_with_incoming_delete_file_merge_conflict(svn_test__sandbox_t *b,
deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
move_target_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path));
+ if (move_unrelated)
+ {
+ /* Move an unrelated file on trunk as part of the same commit. */
+ deleted_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(deleted_dir_name,
+ deleted_dir_child,
+ b->pool),
+ b->pool);
+ move_target_path = svn_relpath_join(trunk_path, new_file2_name, b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path));
+ }
SVN_ERR(sbox_wc_commit(b, ""));
}
else
@@ -1378,7 +1390,8 @@ test_merge_incoming_delete_file_ignore(const svn_test_opts_t *opts,
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_file_ignore",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1470,7 +1483,8 @@ test_merge_incoming_delete_file_accept(const svn_test_opts_t *opts,
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_file_accept",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1565,7 +1579,8 @@ test_merge_incoming_move_file_text_merge(const svn_test_opts_t *opts,
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_file_text_merge",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1971,7 +1986,8 @@ test_switch_incoming_move_file_text_merge(const svn_test_opts_t *opts,
SVN_ERR(svn_test__sandbox_create(b, "switch_incoming_move_file_text_merge",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, TRUE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, FALSE,
+ TRUE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -2888,10 +2904,13 @@ test_merge_incoming_edit_file_moved_away(const svn_test_opts_t *opts,
svn_client_ctx_t *ctx;
svn_opt_revision_t opt_rev;
svn_client_conflict_t *conflict;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
svn_boolean_t text_conflicted;
apr_array_header_t *props_conflicted;
svn_boolean_t tree_conflicted;
svn_stringbuf_t *buf;
+ apr_array_header_t *possible_moved_to_abspaths;
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_edit_file_moved_away", opts, pool));
@@ -2953,6 +2972,19 @@ test_merge_incoming_edit_file_moved_away(const svn_test_opts_t *opts,
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
}
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_local_move_file_text_merge);
+ SVN_TEST_ASSERT(option != NULL);
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A1/mu-moved"));
+
/* Resolve the tree conflict by applying the incoming edit to the local
* move destination "mu-moved". */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
@@ -4966,7 +4998,7 @@ test_cherry_pick_post_move_edit(const svn_test_opts_t *opts,
svn_client_conflict_option_id_t expected_opts[] = {
svn_client_conflict_option_postpone,
svn_client_conflict_option_accept_current_wc_state,
- svn_client_conflict_option_local_move_file_text_merge,
+ svn_client_conflict_option_sibling_move_file_text_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
@@ -4976,7 +5008,7 @@ test_cherry_pick_post_move_edit(const svn_test_opts_t *opts,
/* Try to resolve the conflict. */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
- svn_client_conflict_option_local_move_file_text_merge,
+ svn_client_conflict_option_sibling_move_file_text_merge,
ctx, b->pool));
/* The node "A1/mu-moved" should no longer exist. */
@@ -4987,7 +5019,14 @@ test_cherry_pick_post_move_edit(const svn_test_opts_t *opts,
/* And "A1/mu" should have expected contents. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu"), pool));
- SVN_TEST_STRING_ASSERT(buf->data, "More modified content." APR_EOL_STR);
+ SVN_TEST_STRING_ASSERT(buf->data,
+ "<<<<<<< .working" "\n"
+ "This is the file 'mu'." "\n"
+ "||||||| .old" "\n"
+ "Modified content." APR_EOL_STR
+ "=======" "\n"
+ "More modified content." APR_EOL_STR
+ ">>>>>>> .new" "\n");
return SVN_NO_ERROR;
}
@@ -5179,6 +5218,977 @@ test_merge_incoming_move_dir_across_branches(const svn_test_opts_t *opts,
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_update_incoming_delete_locally_deleted_file(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_wc_status3_t *wc_status;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "update_incoming_delete_locally_deleted_file", opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+ /* Delete the file. */
+ SVN_ERR(sbox_wc_delete(b, "A/mu"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* Update to revision before delete. */
+ SVN_ERR(sbox_wc_update(b, "", 1));
+ /* Delete the file locally. */
+ SVN_ERR(sbox_wc_delete(b, "A/mu"));
+ /* Attempt an update to HEAD. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* We should have a tree conflict in the file "mu". */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/mu"),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict accepting the incoming deletion. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_delete_accept,
+ ctx, pool));
+
+ /* Check the status. */
+ SVN_ERR(svn_wc_status3(&wc_status, ctx->wc_ctx, sbox_wc_path(b, "A/mu"),
+ pool, pool));
+ SVN_TEST_INT_ASSERT(wc_status->kind, svn_node_unknown);
+ SVN_TEST_ASSERT(!wc_status->versioned);
+ SVN_TEST_ASSERT(!wc_status->conflicted);
+ SVN_TEST_INT_ASSERT(wc_status->node_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->text_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->prop_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->actual_kind, svn_node_none);
+
+ return SVN_NO_ERROR;
+}
+
+/* A helper function which prepares a working copy for the test below. */
+static svn_error_t *
+create_wc_with_added_dir_conflict_across_branches(svn_test__sandbox_t *b,
+ svn_client_ctx_t *ctx)
+{
+ const char *trunk_url;
+ const char *branch_url;
+ svn_opt_revision_t opt_rev;
+ const char *new_dir_path;
+ const char *new_file_path;
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+
+ /* Create a branch of node "A". */
+ SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Create a second branch ("branch2") of node "A". */
+ SVN_ERR(sbox_wc_copy(b, trunk_path, branch2_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Add directories with differing content to both branches. */
+ new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
+ new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool);
+ SVN_ERR(sbox_file_write(b, new_file_path,
+ "This is a new file on branch 1\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ new_dir_path = svn_relpath_join(branch2_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
+ new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool);
+ SVN_ERR(sbox_file_write(b, new_file_path,
+ "This is a new file on branch 2\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Merge the differences between trunk and branch into branch2.
+ * This merge should raise an add vs. add conflict on the new directory. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path,
+ SVN_VA_NULL);
+ branch_url = apr_pstrcat(b->pool, b->repos_url, "/", branch2_path,
+ SVN_VA_NULL);
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_client_merge5(trunk_url, &opt_rev, branch_url, &opt_rev,
+ sbox_wc_path(b, branch2_path),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_merge_two_added_dirs_assertion_failure(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_wc_status3_t *wc_status;
+ const char *new_dir_path;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "test_merge_two_added_dirs_assertion_failure", opts, pool));
+
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(create_wc_with_added_dir_conflict_across_branches(b, ctx));
+
+ /* We should have a tree conflict in the directory "A_branch2/newdir". */
+ new_dir_path = svn_relpath_join(branch2_path, new_dir_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_replace,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* This call used to run into an assertion failure (start_rev > end_rev). */
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_replace,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict by replace + merge. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ ctx, pool));
+
+ /* Check the status. */
+ SVN_ERR(svn_wc_status3(&wc_status, ctx->wc_ctx, sbox_wc_path(b, new_dir_path),
+ pool, pool));
+ SVN_TEST_INT_ASSERT(wc_status->kind, svn_node_dir);
+ SVN_TEST_ASSERT(wc_status->versioned);
+ SVN_TEST_ASSERT(!wc_status->conflicted);
+ SVN_TEST_INT_ASSERT(wc_status->node_status, svn_wc_status_replaced);
+ SVN_TEST_INT_ASSERT(wc_status->text_status, svn_wc_status_normal);
+ SVN_TEST_INT_ASSERT(wc_status->prop_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->actual_kind, svn_node_dir);
+
+ return SVN_NO_ERROR;
+}
+
+/* Test for issue #4766: resolver adds unrelated moves to move target list */
+static svn_error_t *
+test_merge_incoming_delete_file_unrelated_move(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ const char *deleted_path;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+ apr_array_header_t *possible_moved_to_repos_relpaths;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ const char *new_file_path;
+ const char *moved_to_repos_path;
+ svn_node_kind_t kind;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "merge_incoming_delete_file_unrelated_move",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, TRUE,
+ FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_file_text_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ /* Assert that only one move target candidate has been found in repository. */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ &possible_moved_to_repos_relpaths, option, b->pool, b->pool));
+
+ SVN_TEST_INT_ASSERT(possible_moved_to_repos_relpaths->nelts, 1);
+ moved_to_repos_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *), moved_to_repos_path);
+
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_move_file_text_merge,
+ ctx, b->pool));
+
+ /* Ensure that the deleted file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, deleted_path),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool);
+ SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
+ sbox_wc_path(b, new_file_path));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ /* The file should not be in conflict. */
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(!text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* Ensure that the moved file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_added);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_STRING_ASSERT(status->moved_from_abspath,
+ sbox_wc_path(b, deleted_path));
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ /* Ensure that the original file was removed. */
+ SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &kind, b->pool));
+ SVN_TEST_ASSERT(kind == svn_node_none);
+
+ /* Ensure that the moved file has the expected content. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_path),
+ b->pool));
+ SVN_TEST_STRING_ASSERT(buf->data, modified_file_on_branch_content);
+
+ /* Ensure that the unrelated moved file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx,
+ sbox_wc_path(b, svn_relpath_join(branch_path,
+ new_file2_name, b->pool)),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_added);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_cherry_pick_post_move_edit_dir(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ const char *trunk_url;
+ svn_opt_revision_t peg_rev;
+ apr_array_header_t *ranges_to_merge;
+ svn_opt_revision_range_t merge_range;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "test_cherry_pick_post_move_edit_dir",
+ opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+ /* Create a copy of node "A". */
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+ /* On "trunk", move the directory B. */
+ SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+ /* On "trunk", edit B-moved/lambda. This will be r4. */
+ SVN_ERR(sbox_file_write(b, "A/B-moved/lambda", "Modified content."
+ APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Perform a cherry-pick merge of r4 from A to A1. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ trunk_url = apr_pstrcat(b->pool, b->repos_url, "/A", SVN_VA_NULL);
+ peg_rev.kind = svn_opt_revision_number;
+ peg_rev.value.number = 4;
+ merge_range.start.kind = svn_opt_revision_number;
+ merge_range.start.value.number = 3;
+ merge_range.end.kind = svn_opt_revision_number;
+ merge_range.end.value.number = 4;
+ ranges_to_merge = apr_array_make(b->pool, 1,
+ sizeof(svn_opt_revision_range_t *));
+ APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = &merge_range;
+ /* This should raise a "local missing vs incoming edit" conflict. */
+ SVN_ERR(svn_client_merge_peg5(trunk_url, ranges_to_merge, &peg_rev,
+ sbox_wc_path(b, "A1"), svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, "A1/B-moved"),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(tree_conflicted);
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_sibling_move_dir_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* Try to resolve the conflict. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_sibling_move_dir_merge,
+ ctx, b->pool));
+
+ /* The node "B-moved" should no longer exist. */
+ SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, "A1/B-moved"),
+ ctx, pool, pool),
+ SVN_ERR_WC_PATH_NOT_FOUND);
+
+ /* And "A1/B/lambda" should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/B/lambda"), pool));
+ SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_local_missing_abiguous_moves(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_opt_revision_t opt_rev;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ apr_array_header_t *possible_moved_to_repos_relpaths;
+ apr_array_header_t *possible_moved_to_abspaths;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b, "local_missing_ambiguous_moves", opts,
+ pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+
+ /* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ /* Copy a file across branch boundaries (gives ambiguous WC targets later). */
+ SVN_ERR(sbox_wc_copy(b, "A/mu", "A1/mu-copied-from-A"));
+ /* Create an ambiguous move with the "trunk". */
+ SVN_ERR(sbox_wc_copy(b, "A/mu", "A/mu-copied"));
+ SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+
+ /* Modify the moved file on the "branch". */
+ SVN_ERR(sbox_file_write(b, "A1/mu", "Modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Merge "A1" ("branch") into "A" ("trunk"). */
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A1",
+ pool),
+ NULL, &opt_rev, sbox_wc_path(b, "A"),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/mu"),
+ ctx, b->pool, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_local_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_local_move_file_text_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ /*
+ * Possible repository destinations for moved-away 'A/mu' are:
+ * (1): '^/A/mu-copied'
+ * (2): '^/A/mu-moved'
+ * (3): '^/A1/mu-copied-from-A'
+ */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ &possible_moved_to_repos_relpaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_repos_relpaths->nelts, 3);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *),
+ "A/mu-copied");
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 1, const char *),
+ "A/mu-moved");
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 2, const char *),
+ "A1/mu-copied-from-A");
+
+ /* Move target for "A/mu-copied" (selected by default) is not ambiguous. */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/mu-copied"));
+
+ /* Move target for "A/mu-moved" is not ambiguous. */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
+ ctx, b->pool));
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/mu-moved"));
+
+ /* Select move target "A1/mu-copied-from-A". */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 2,
+ ctx, b->pool));
+
+ /*
+ * Possible working copy destinations for moved-away 'A/mu' are:
+ * (1): 'A/mu-copied-from-A'
+ * (2): 'A1/mu-copied-from-A'
+ */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 2);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/mu-copied-from-A"));
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
+ sbox_wc_path(b, "A1/mu-copied-from-A"));
+
+ /* Select move target "A/mu-moved". */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
+ ctx, b->pool));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_local_move_file_text_merge, ctx,
+ b->pool));
+
+ /* The node "A/mu" should no longer exist. */
+ SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, "A/mu"),
+ ctx, pool, pool),
+ SVN_ERR_WC_PATH_NOT_FOUND);
+
+ /* Ensure that the merged file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_modified);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ /* And it should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"),
+ pool));
+ SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_local_missing_abiguous_moves_dir(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_opt_revision_t opt_rev;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ apr_array_header_t *possible_moved_to_repos_relpaths;
+ apr_array_header_t *possible_moved_to_abspaths;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b, "local_missing_ambiguous_moves_dir",
+ opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+
+ /* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ /* Copy a dir across branch boundaries (gives ambiguous WC targets later). */
+ SVN_ERR(sbox_wc_copy(b, "A/B", "A1/B-copied-from-A"));
+ /* Create an ambiguous move with the "trunk". */
+ SVN_ERR(sbox_wc_copy(b, "A/B", "A/B-copied"));
+ SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+
+ /* Modify a file in the moved directory on the "branch". */
+ SVN_ERR(sbox_file_write(b, "A1/B/lambda", "Modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Merge "A1" ("branch") into "A" ("trunk"). */
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A1",
+ pool),
+ NULL, &opt_rev, sbox_wc_path(b, "A"),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/B"),
+ ctx, b->pool, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_local_move_dir_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_local_move_dir_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ /*
+ * Possible repository destinations for moved-away 'A/mu' are:
+ * (1): '^/A/B-copied'
+ * (2): '^/A/B-moved'
+ * (3): '^/A1/B-copied-from-A'
+ */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ &possible_moved_to_repos_relpaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_repos_relpaths->nelts, 3);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *),
+ "A/B-copied");
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 1, const char *),
+ "A/B-moved");
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 2, const char *),
+ "A1/B-copied-from-A");
+
+ /* Move target for "A/B-copied" (selected by default) is not ambiguous. */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/B-copied"));
+
+ /* Move target for "A/mu-moved" is not ambiguous. */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
+ ctx, b->pool));
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/B-moved"));
+
+ /* Select move target "A1/mu-copied-from-A". */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 2,
+ ctx, b->pool));
+
+ /*
+ * Possible working copy destinations for moved-away 'A/mu' are:
+ * (1): 'A/B-copied-from-A'
+ * (2): 'A1/B-copied-from-A'
+ */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 2);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A/B-copied-from-A"));
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
+ sbox_wc_path(b, "A1/B-copied-from-A"));
+
+ /* Select move target "A/B-moved". */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
+ ctx, b->pool));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_local_move_dir_merge, ctx,
+ b->pool));
+
+ /* The node "A/mu" should no longer exist. */
+ SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, "A/B"),
+ ctx, pool, pool),
+ SVN_ERR_WC_PATH_NOT_FOUND);
+
+ /* Ensure that the merged file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-moved/lambda"),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_modified);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ /* And it should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/lambda"),
+ pool));
+ SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_file_vs_dir_move_merge_assertion_failure(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_opt_revision_t opt_rev, peg_rev;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ const char *wc_path;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "file_vs_dir_move_merge_assertion_failure",
+ opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+
+ /* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ /* Move and modify file on the "branch" */
+ SVN_ERR(sbox_wc_move(b, "A1/B/lambda", "A1/B/lambda-moved"));
+ SVN_ERR(sbox_file_write(b, "A1/B/lambda-moved",
+ "Modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+
+ /* Move a directory and modify a file inside of it on the "trunk". */
+ SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
+ SVN_ERR(sbox_file_write(b, "A/B-moved/lambda",
+ "Modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Create a fresh working copy for "A1" ("branch"). */
+ wc_path = svn_test_data_path("file_vs_dir_move_merge_assertion_failure2",
+ pool);
+ SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool));
+ SVN_ERR(svn_io_make_dir_recursively(wc_path, pool));
+ svn_test_add_dir_cleanup(wc_path);
+
+ /* Merge "A" ("trunk") into a fresh working copy of "A1" ("branch"). */
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ peg_rev.kind = svn_opt_revision_unspecified;
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(svn_client_checkout3(NULL, svn_path_url_add_component2(b->repos_url,
+ "A1", pool),
+ wc_path, &peg_rev, &opt_rev, svn_depth_infinity,
+ TRUE, FALSE, ctx, pool));
+
+ SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A",
+ pool),
+ NULL, &opt_rev, wc_path, svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict,
+ svn_dirent_join(wc_path, "B", b->pool),
+ ctx, b->pool, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_dir_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_dir_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ /* Resolve this conflict. Another one will be raised. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_incoming_move_dir_merge, ctx,
+ b->pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict,
+ svn_dirent_join_many(b->pool,
+ wc_path, "B-moved", "lambda", NULL),
+ ctx, b->pool, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* This used to trigger an assertion failure:
+ * svn_tests: E235000: In file 'subversion/libsvn_client/conflicts.c' \
+ * line 2242: assertion failed (start_rev > end_rev) */
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* Subversion is not yet smart enough to resolve this tree conflict. */
+
+ return SVN_NO_ERROR;
+}
+
/* ========================================================================== */
@@ -5265,10 +6275,24 @@ static struct svn_test_descriptor_t test_funcs[] =
"merge incoming move file merge with CRLF eols"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge_native_eol,
"merge incoming move file merge with native eols"),
- SVN_TEST_OPTS_XFAIL(test_cherry_pick_post_move_edit,
+ SVN_TEST_OPTS_PASS(test_cherry_pick_post_move_edit,
"cherry-pick edit from moved file"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir_across_branches,
- "merge incoming dir move across branches"),
+ "merge incoming dir move across branches"),
+ SVN_TEST_OPTS_PASS(test_update_incoming_delete_locally_deleted_file,
+ "update incoming delete to deleted file (#4739)"),
+ SVN_TEST_OPTS_PASS(test_merge_two_added_dirs_assertion_failure,
+ "merge two added dirs assertion failure (#4744)"),
+ SVN_TEST_OPTS_PASS(test_merge_incoming_delete_file_unrelated_move,
+ "do not suggest unrelated move targets (#4766)"),
+ SVN_TEST_OPTS_PASS(test_cherry_pick_post_move_edit_dir,
+ "cherry-pick edit from moved directory"),
+ SVN_TEST_OPTS_PASS(test_local_missing_abiguous_moves,
+ "local missing conflict with ambiguous moves"),
+ SVN_TEST_OPTS_PASS(test_local_missing_abiguous_moves_dir,
+ "local missing conflict with ambiguous dir moves"),
+ SVN_TEST_OPTS_PASS(test_file_vs_dir_move_merge_assertion_failure,
+ "file v dir move merge assertion failure"),
SVN_TEST_NULL
};
diff --git a/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c b/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
index 7a6e910..469eb4e 100644
--- a/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
+++ b/subversion/tests/libsvn_fs_fs/fs-fs-pack-test.c
@@ -1631,8 +1631,8 @@ delta_chain_with_plain(const svn_test_opts_t *opts,
svn_hash_sets(props, "p", svn_string_create(prop_value->data, pool));
hash_rep = svn_stringbuf_create_empty(pool);
- svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool), "END",
- pool);
+ SVN_ERR(svn_hash_write2(props, svn_stream_from_stringbuf(hash_rep, pool),
+ "END", pool));
SVN_ERR(svn_fs_begin_txn(&txn, fs, rev, pool));
SVN_ERR(svn_fs_txn_root(&root, txn, pool));
diff --git a/subversion/tests/libsvn_repos/authz-test.c b/subversion/tests/libsvn_repos/authz-test.c
index 6ee2448..1f6a4dd 100644
--- a/subversion/tests/libsvn_repos/authz-test.c
+++ b/subversion/tests/libsvn_repos/authz-test.c
@@ -277,9 +277,9 @@ test_authz_parse(const svn_test_opts_t *opts,
printf("[users]\n");
if (authz->has_anon_rights)
- print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool);
+ SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->anon_rights, pool));
if (authz->has_authn_rights)
- print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool);
+ SVN_ERR(print_user_rights(NULL, NULL, 0, &authz->authn_rights, pool));
SVN_ERR(svn_iter_apr_hash(NULL, authz->user_rights,
print_user_rights, NULL, pool));
printf("\n\n");
@@ -478,6 +478,39 @@ issue_4741_groups(apr_pool_t *pool)
return SVN_NO_ERROR;
}
+static svn_error_t *
+reposful_reposless_stanzas_inherit(apr_pool_t *pool)
+{
+ const char rules[] =
+ "[groups]" NL
+ "company = user1, user2, user3" NL
+ "customer = customer1, customer2" NL
+ "" NL
+ "# company can read-write on everything" NL
+ "[/]" NL
+ "@company = rw" NL
+ "" NL
+ "[project1:/]" NL
+ "@customer = r" NL
+ "" NL
+ "[project2:/]" NL;
+
+ svn_stringbuf_t *buf = svn_stringbuf_create(rules, pool);
+ svn_stream_t *stream = svn_stream_from_stringbuf(buf, pool);
+ svn_authz_t *authz;
+ svn_boolean_t access_granted;
+
+ SVN_ERR(svn_repos_authz_parse(&authz, stream, NULL, pool));
+
+ SVN_ERR(svn_repos_authz_check_access(authz, "project1", "/foo", "user1",
+ svn_authz_write | svn_authz_recursive,
+ &access_granted,
+ pool));
+ SVN_TEST_ASSERT(access_granted == TRUE);
+
+ return SVN_NO_ERROR;
+}
+
static int max_threads = 4;
static struct svn_test_descriptor_t test_funcs[] =
@@ -489,6 +522,8 @@ static struct svn_test_descriptor_t test_funcs[] =
"test svn_authz__get_global_rights"),
SVN_TEST_PASS2(issue_4741_groups,
"issue 4741 groups"),
+ SVN_TEST_XFAIL2(reposful_reposless_stanzas_inherit,
+ "[foo:/] inherits [/]"),
SVN_TEST_NULL
};
diff --git a/subversion/tests/libsvn_repos/dump-load-test.c b/subversion/tests/libsvn_repos/dump-load-test.c
index 6940c85..e7450df 100644
--- a/subversion/tests/libsvn_repos/dump-load-test.c
+++ b/subversion/tests/libsvn_repos/dump-load-test.c
@@ -81,7 +81,7 @@ test_dump_bad_props(svn_stringbuf_t **dump_data_p,
notify_func, notify_baton,
NULL, NULL, NULL, NULL,
pool));
- svn_stream_close(stream);
+ SVN_ERR(svn_stream_close(stream));
/* Check that the property appears in the dump data */
expected_str = apr_psprintf(pool, "K %d\n%s\n"
@@ -131,7 +131,7 @@ test_load_bad_props(svn_stringbuf_t *dump_data,
notify_func, notify_baton,
NULL, NULL, /*cancellation*/
pool));
- svn_stream_close(stream);
+ SVN_ERR(svn_stream_close(stream));
/* Check the loaded property */
fs = svn_repos_fs(repos);
diff --git a/subversion/tests/libsvn_repos/repos-test.c b/subversion/tests/libsvn_repos/repos-test.c
index 8d5472c..63b76e1 100644
--- a/subversion/tests/libsvn_repos/repos-test.c
+++ b/subversion/tests/libsvn_repos/repos-test.c
@@ -4461,7 +4461,7 @@ test_list(const svn_test_opts_t *opts,
SVN_ERR(svn_repos_list(rev_root, "/A", patterns, svn_depth_infinity, FALSE,
NULL, NULL, list_callback, &counter, NULL, NULL,
pool));
- SVN_TEST_ASSERT(counter == 6);
+ SVN_TEST_ASSERT(counter == 7);
return SVN_NO_ERROR;
}
diff --git a/subversion/tests/libsvn_subr/priority-queue-test.c b/subversion/tests/libsvn_subr/priority-queue-test.c
index bd2d991..3fc5791 100644
--- a/subversion/tests/libsvn_subr/priority-queue-test.c
+++ b/subversion/tests/libsvn_subr/priority-queue-test.c
@@ -125,7 +125,7 @@ verify_queue_order(svn_priority_queue__t *queue)
}
/* the queue should now be empty */
- verify_empty_queue(queue);
+ SVN_ERR(verify_empty_queue(queue));
return SVN_NO_ERROR;
}
@@ -154,7 +154,7 @@ test_empty_queue(apr_pool_t *pool)
svn_priority_queue__t *queue
= svn_priority_queue__create(elements, compare_func);
- verify_empty_queue(queue);
+ SVN_ERR(verify_empty_queue(queue));
return SVN_NO_ERROR;
}
@@ -214,7 +214,7 @@ test_update(apr_pool_t *pool)
}
/* the queue should now be empty */
- verify_empty_queue(queue);
+ SVN_ERR(verify_empty_queue(queue));
return SVN_NO_ERROR;
}
diff --git a/subversion/tests/libsvn_wc/conflict-data-test.c b/subversion/tests/libsvn_wc/conflict-data-test.c
index 7d89825..239c18f 100644
--- a/subversion/tests/libsvn_wc/conflict-data-test.c
+++ b/subversion/tests/libsvn_wc/conflict-data-test.c
@@ -594,6 +594,7 @@ test_serialize_tree_conflict(const svn_test_opts_t *opts,
svn_wc_conflict_reason_moved_away,
svn_wc_conflict_action_delete,
sbox_wc_path(&sbox, "A/B"),
+ sbox_wc_path(&sbox, "A/C"),
pool, pool));
SVN_ERR(svn_wc__conflict_skel_set_op_switch(
@@ -610,11 +611,13 @@ test_serialize_tree_conflict(const svn_test_opts_t *opts,
{
svn_wc_conflict_reason_t reason;
svn_wc_conflict_action_t action;
- const char *moved_away_op_root_abspath;
+ const char *moved_away_src_op_root_abspath;
+ const char *moved_away_dst_op_root_abspath;
SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
&action,
- &moved_away_op_root_abspath,
+ &moved_away_src_op_root_abspath,
+ &moved_away_dst_op_root_abspath,
sbox.wc_ctx->db,
sbox.wc_abspath,
conflict_skel,
@@ -622,8 +625,10 @@ test_serialize_tree_conflict(const svn_test_opts_t *opts,
SVN_TEST_ASSERT(reason == svn_wc_conflict_reason_moved_away);
SVN_TEST_ASSERT(action == svn_wc_conflict_action_delete);
- SVN_TEST_STRING_ASSERT(moved_away_op_root_abspath,
+ SVN_TEST_STRING_ASSERT(moved_away_src_op_root_abspath,
sbox_wc_path(&sbox, "A/B"));
+ SVN_TEST_STRING_ASSERT(moved_away_dst_op_root_abspath,
+ sbox_wc_path(&sbox, "A/C"));
}
return SVN_NO_ERROR;
diff --git a/subversion/tests/libsvn_wc/op-depth-test.c b/subversion/tests/libsvn_wc/op-depth-test.c
index bf6592d..66e5928 100644
--- a/subversion/tests/libsvn_wc/op-depth-test.c
+++ b/subversion/tests/libsvn_wc/op-depth-test.c
@@ -535,7 +535,7 @@ check_db_conflicts(svn_test__sandbox_t *b,
SVN_ERR(svn_wc__conflict_read_tree_conflict(&info->tc.reason,
&info->tc.action,
&move_src_abspath,
- b->wc_ctx->db,
+ NULL, b->wc_ctx->db,
local_abspath,
conflict,
b->pool, iterpool));
diff --git a/subversion/tests/libsvn_wc/utils.c b/subversion/tests/libsvn_wc/utils.c
index 766f93d..51a0729 100644
--- a/subversion/tests/libsvn_wc/utils.c
+++ b/subversion/tests/libsvn_wc/utils.c
@@ -417,11 +417,12 @@ sbox_wc_revert(svn_test__sandbox_t *b, const char *path, svn_depth_t depth)
SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, b->wc_ctx,
dir_abspath, FALSE /* lock_anchor */,
b->pool, b->pool));
- SVN_ERR(svn_wc_revert5(b->wc_ctx, abspath, depth,
+ SVN_ERR(svn_wc_revert6(b->wc_ctx, abspath, depth,
FALSE /* use_commit_times */,
NULL /* changelist_filter */,
FALSE /* clear_changelists */,
FALSE /* metadata_only */,
+ TRUE /*added_keep_local*/,
NULL, NULL, /* cancel baton + func */
NULL, NULL, /* notify baton + func */
b->pool));
diff --git a/subversion/tests/libsvn_wc/wc-queries-test.c b/subversion/tests/libsvn_wc/wc-queries-test.c
index 0a828c5..650f363 100644
--- a/subversion/tests/libsvn_wc/wc-queries-test.c
+++ b/subversion/tests/libsvn_wc/wc-queries-test.c
@@ -99,6 +99,7 @@ static const int slow_statements[] =
STMT_SELECT_DELETE_LIST,
STMT_SELECT_UPDATE_MOVE_LIST,
STMT_FIND_REPOS_PATH_IN_WC,
+ STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND,
/* Designed as slow to avoid penalty on other queries */
STMT_SELECT_UNREFERENCED_PRISTINES,
diff --git a/subversion/tests/libsvn_wc/wc-test-queries.h b/subversion/tests/libsvn_wc/wc-test-queries.h
index b454af3..0a58a37 100644
--- a/subversion/tests/libsvn_wc/wc-test-queries.h
+++ b/subversion/tests/libsvn_wc/wc-test-queries.h
@@ -1,4 +1,4 @@
-/* This file is automatically generated from wc-test-queries.sql and subversion/tests/libsvn_wc/token-map.h.
+/* This file is automatically generated from wc-test-queries.sql and token-map.h.
* Do not edit this file -- edit the source and rerun gen-make.py */
#define STMT_SELECT_NODES_INFO 0
diff --git a/subversion/tests/svn_test_main.c b/subversion/tests/svn_test_main.c
index c3537d3..31d670f 100644
--- a/subversion/tests/svn_test_main.c
+++ b/subversion/tests/svn_test_main.c
@@ -903,9 +903,12 @@ svn_test_main(int argc, const char *argv[], int max_threads,
apr_err = apr_getopt_long(os, cl_options, &opt_id, &opt_arg);
if (APR_STATUS_IS_EOF(apr_err))
break;
- else if (apr_err && (apr_err != APR_BADCH))
+ else if (apr_err)
{
/* Ignore invalid option error to allow passing arbitrary options */
+ if (apr_err == APR_BADCH)
+ continue;
+
fprintf(stderr, "apr_getopt_long failed : [%d] %s\n",
apr_err, apr_strerror(apr_err, errmsg, sizeof(errmsg)));
exit(1);