#!/usr/bin/env python # # svnrdump_tests.py: Tests svnrdump's remote repository dumping capabilities. # # 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 sys, os import re # Our testing module import svntest from svntest.verify import SVNUnexpectedStdout, SVNUnexpectedStderr from svntest.verify import SVNExpectedStderr from svntest.main import write_restrictive_svnserve_conf from svntest.main import server_has_partial_replay # (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 ## Mismatched headers during dumping operation # Text-copy-source-* and *-sha1 headers are not provided by the RA # layer. `svnadmin dump` is able to provide them because it works on # the FS layer. Also, svnrdump attaches "Prop-delta: true" with # everything whether it's really a delta or a new prop (delta from # /dev/null). This is really harmless, but `svnadmin dump` contains # the logic for differentiating between these two cases. mismatched_headers_re = re.compile( b"Prop-delta: .*|Text-content-sha1: .*|Text-copy-source-md5: .*|" + b"Text-copy-source-sha1: .*|Text-delta-base-sha1: .*" ) ###################################################################### # Helper routines def compare_repos_dumps(sbox, other_dumpfile, bypass_prop_validation=False): """Compare two dumpfiles, one created from SBOX, and other given by OTHER_DUMPFILE. The dumpfiles do not need to match linewise, as the OTHER_DUMPFILE contents will first be loaded into a repository and then re-dumped to do the match, which should generate the same dumpfile as dumping SBOX.""" sbox_dumpfile = svntest.actions.run_and_verify_dump(sbox.repo_dir) # Load and dump the other dumpfile (using svnadmin) other_sbox = sbox.clone_dependent() other_sbox.build(create_wc=False, empty=True) svntest.actions.run_and_verify_load(other_sbox.repo_dir, other_dumpfile, bypass_prop_validation) other_dumpfile = svntest.actions.run_and_verify_dump(other_sbox.repo_dir) ### This call kind-of assumes EXPECTED is first and ACTUAL is second. svntest.verify.compare_dump_files( "Dump files", "DUMP", other_dumpfile, sbox_dumpfile) def run_dump_test(sbox, dumpfile_name, expected_dumpfile_name = None, subdir = None, bypass_prop_validation = False, ignore_base_checksums = False, extra_options = []): """Load a dumpfile using 'svnadmin load', dump it with 'svnrdump dump' and check that the same dumpfile is produced or that expected_dumpfile_name is produced if provided. Additionally, the subdir argument appends itself to the URL. EXTRA_OPTIONS is an array of optional additional options to pass to 'svnrdump dump'.""" # Create an empty sandbox repository sbox.build(create_wc=False, empty=True) # This directory contains all the dump files svnrdump_tests_dir = os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data') # Load the specified dump file into the sbox repository using # svnadmin load original_dumpfile = open(os.path.join(svnrdump_tests_dir, dumpfile_name), 'rb').readlines() svntest.actions.run_and_verify_load(sbox.repo_dir, original_dumpfile, bypass_prop_validation) repo_url = sbox.repo_url if subdir: repo_url = repo_url + subdir # Create a dump file using svnrdump opts = extra_options + ['-q', 'dump', repo_url] svnrdump_dumpfile = \ svntest.actions.run_and_verify_svnrdump(None, svntest.verify.AnyOutput, [], 0, *opts) if expected_dumpfile_name: expected_dumpfile = open(os.path.join(svnrdump_tests_dir, expected_dumpfile_name), 'rb').readlines() # Compare the output from stdout if ignore_base_checksums: expected_dumpfile = [l for l in expected_dumpfile if not l.startswith(b'Text-delta-base-md5')] svnrdump_dumpfile = [l for l in svnrdump_dumpfile if not l.startswith(b'Text-delta-base-md5')] expected_dumpfile = [l for l in expected_dumpfile if not mismatched_headers_re.match(l)] svnrdump_dumpfile = [l for l in svnrdump_dumpfile if not mismatched_headers_re.match(l)] expected_dumpfile = svntest.verify.UnorderedOutput(expected_dumpfile) svntest.verify.compare_and_display_lines( "Dump files", "DUMP", expected_dumpfile, svnrdump_dumpfile, None) else: # The expected dumpfile is the result of dumping SBOX. compare_repos_dumps(sbox, svnrdump_dumpfile, bypass_prop_validation) def run_load_test(sbox, dumpfile_name, expected_dumpfile_name = None, expect_deltas = True): """Load a dumpfile using 'svnrdump load', dump it with 'svnadmin dump' and check that the same dumpfile is produced""" # Create an empty sandbox repository sbox.build(create_wc=False, empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # This directory contains all the dump files svnrdump_tests_dir = os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data') # Load the specified dump file into the sbox repository using # svnrdump load original_dumpfile = open(os.path.join(svnrdump_tests_dir, dumpfile_name), 'rb').readlines() # Set the UUID of the sbox repository to the UUID specified in the # dumpfile ### RA layer doesn't have a set_uuid functionality uuid = original_dumpfile[2].split(b' ')[1][:-1].decode() svntest.actions.run_and_verify_svnadmin2(None, None, 0, 'setuuid', sbox.repo_dir, uuid) svntest.actions.run_and_verify_svnrdump(original_dumpfile, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) # Re-dump the rdump-loaded repo using svnadmin dump resulted_dumpfile = svntest.actions.run_and_verify_dump(sbox.repo_dir, expect_deltas) if expected_dumpfile_name: expected_dumpfile = open(os.path.join(svnrdump_tests_dir, expected_dumpfile_name), 'rb').readlines() # Compare the output from stdout svntest.verify.compare_and_display_lines( "Dump files", "DUMP", expected_dumpfile, resulted_dumpfile) else: expected_dumpfile = original_dumpfile compare_repos_dumps(sbox, expected_dumpfile) ###################################################################### # Tests def basic_dump(sbox): "dump: standard sbox repos" sbox.build(read_only = True, create_wc = False) out = \ svntest.actions.run_and_verify_svnrdump(None, svntest.verify.AnyOutput, [], 0, '-q', 'dump', sbox.repo_url) if not out[0].startswith(b'SVN-fs-dump-format-version:'): raise svntest.Failure('No valid output') def revision_0_dump(sbox): "dump: revision zero" run_dump_test(sbox, "revision-0.dump") def revision_0_load(sbox): "load: revision zero" run_load_test(sbox, "revision-0.dump") # skeleton.dump repository layout # # Projects/ (Added r1) # README (Added r2) # Project-X (Added r3) # Project-Y (Added r4) # Project-Z (Added r5) # docs/ (Added r6) # README (Added r6) def skeleton_dump(sbox): "dump: skeleton repository" run_dump_test(sbox, "skeleton.dump") def skeleton_load(sbox): "load: skeleton repository" run_load_test(sbox, "skeleton.dump") def sparse_propchanges_dump(sbox): "dump: sparse file/dir propchanges" run_dump_test(sbox, "sparse-propchanges.dump") @Issue(3902) def sparse_propchanges_load(sbox): "load: sparse file/dir propchanges" run_load_test(sbox, "sparse-propchanges.dump") def copy_and_modify_dump(sbox): "dump: copy and modify" run_dump_test(sbox, "copy-and-modify.dump") def copy_and_modify_load(sbox): "load: copy and modify" run_load_test(sbox, "copy-and-modify.dump") def no_author_dump(sbox): "dump: copy revs with no svn:author revprops" run_dump_test(sbox, "no-author.dump") def no_author_load(sbox): "load: copy revs with no svn:author revprops" run_load_test(sbox, "no-author.dump") def copy_from_previous_version_and_modify_dump(sbox): "dump: copy from previous version and modify" run_dump_test(sbox, "copy-from-previous-version-and-modify.dump") def copy_from_previous_version_and_modify_load(sbox): "load: copy from previous version and modify" run_load_test(sbox, "copy-from-previous-version-and-modify.dump") def modified_in_place_dump(sbox): "dump: modified in place" run_dump_test(sbox, "modified-in-place.dump") def modified_in_place_load(sbox): "load: modified in place" run_load_test(sbox, "modified-in-place.dump") def move_and_modify_in_the_same_revision_dump(sbox): "dump: move parent & modify child file in same rev" run_dump_test(sbox, "move-and-modify.dump") def move_and_modify_in_the_same_revision_load(sbox): "load: move parent & modify child file in same rev" run_load_test(sbox, "move-and-modify.dump") def tag_empty_trunk_dump(sbox): "dump: tag empty trunk" run_dump_test(sbox, "tag-empty-trunk.dump") def tag_empty_trunk_load(sbox): "load: tag empty trunk" run_load_test(sbox, "tag-empty-trunk.dump") def tag_trunk_with_file_dump(sbox): "dump: tag trunk containing a file" run_dump_test(sbox, "tag-trunk-with-file.dump") def tag_trunk_with_file_load(sbox): "load: tag trunk containing a file" run_load_test(sbox, "tag-trunk-with-file.dump") def tag_trunk_with_file2_dump(sbox): "dump: tag trunk containing a file (#2)" run_dump_test(sbox, "tag-trunk-with-file2.dump") def tag_trunk_with_file2_load(sbox): "load: tag trunk containing a file (#2)" run_load_test(sbox, "tag-trunk-with-file2.dump") def dir_prop_change_dump(sbox): "dump: directory property changes" run_dump_test(sbox, "dir-prop-change.dump") def dir_prop_change_load(sbox): "load: directory property changes" run_load_test(sbox, "dir-prop-change.dump") def copy_parent_modify_prop_dump(sbox): "dump: copy parent and modify prop" run_dump_test(sbox, "copy-parent-modify-prop.dump") def copy_parent_modify_prop_load(sbox): "load: copy parent and modify prop" run_load_test(sbox, "copy-parent-modify-prop.dump") def copy_revprops_dump(sbox): "dump: copy revprops other than svn:*" run_dump_test(sbox, "revprops.dump") def copy_revprops_load(sbox): "load: copy revprops other than svn:*" run_load_test(sbox, "revprops.dump") def only_trunk_dump(sbox): "dump: subdirectory" run_dump_test(sbox, "trunk-only.dump", subdir="/trunk", expected_dumpfile_name="trunk-only.expected.dump") def only_trunk_A_with_changes_dump(sbox): "dump: subdirectory with changes on root" run_dump_test(sbox, "trunk-A-changes.dump", subdir="/trunk/A", expected_dumpfile_name="trunk-A-changes.expected.dump") def url_encoding_dump(sbox): "dump: url encoding issues" run_dump_test(sbox, "url-encoding-bug.dump") def url_encoding_load(sbox): "load: url encoding issues" run_load_test(sbox, "url-encoding-bug.dump") def copy_bad_line_endings_dump(sbox): "dump: inconsistent line endings in svn:* props" run_dump_test(sbox, "copy-bad-line-endings.dump", expected_dumpfile_name="copy-bad-line-endings.expected.dump", bypass_prop_validation=True) @Issue(4263) def copy_bad_line_endings_load(sbox): "load: inconsistent line endings in svn:* props" run_load_test(sbox, "copy-bad-line-endings.dump", expected_dumpfile_name="copy-bad-line-endings.expected.dump") def copy_bad_line_endings2_dump(sbox): "dump: non-LF line endings in svn:* props" run_dump_test(sbox, "copy-bad-line-endings2.dump", expected_dumpfile_name="copy-bad-line-endings2.expected.dump", bypass_prop_validation=True, ignore_base_checksums=True) def commit_a_copy_of_root_dump(sbox): "dump: commit a copy of root" run_dump_test(sbox, "repo-with-copy-of-root-dir.dump") def commit_a_copy_of_root_load(sbox): "load: commit a copy of root" run_load_test(sbox, "repo-with-copy-of-root-dir.dump") def descend_into_replace_dump(sbox): "dump: descending into replaced dir looks in src" run_dump_test(sbox, "descend-into-replace.dump", subdir='/trunk/H', expected_dumpfile_name = "descend-into-replace.expected.dump") def descend_into_replace_load(sbox): "load: descending into replaced dir looks in src" run_load_test(sbox, "descend-into-replace.dump") @Issue(3847) def add_multi_prop_dump(sbox): "dump: add with multiple props" run_dump_test(sbox, "add-multi-prop.dump") @Issue(3844) def multi_prop_edit_load(sbox): "load: multiple prop edits on a file" run_load_test(sbox, "multi-prop-edits.dump", None, False) #---------------------------------------------------------------------- # This test replicates svnadmin_tests.py 16 'reflect dropped renumbered # revs in svn:mergeinfo' but uses 'svnrdump load' in place of # 'svnadmin load'. @Issue(3890) def reflect_dropped_renumbered_revs(sbox): "svnrdump renumbers dropped revs in mergeinfo" # Create an empty sandbox repository sbox.build(create_wc=False, empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # Load the specified dump file into the sbox repository using # svnrdump load dump_file = open(os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data', 'with_merges.dump'), 'rb') svnrdump_dumpfile = dump_file.readlines() dump_file.close() # svnrdump load the dump file. svntest.actions.run_and_verify_svnrdump(svnrdump_dumpfile, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) # Create the 'toplevel' directory in repository and then load the same # dumpfile into that subtree. svntest.actions.run_and_verify_svn(['Committing transaction...\n', 'Committed revision 10.\n'], [], "mkdir", sbox.repo_url + "/toplevel", "-m", "Create toplevel dir to load into") svntest.actions.run_and_verify_svnrdump(svnrdump_dumpfile, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url + "/toplevel") # Verify the svn:mergeinfo properties url = sbox.repo_url expected_output = svntest.verify.UnorderedOutput([ url + "/trunk - /branch1:4-8\n", url + "/toplevel/trunk - /toplevel/branch1:14-18\n", ]) svntest.actions.run_and_verify_svn(expected_output, [], 'propget', 'svn:mergeinfo', '-R', sbox.repo_url) #---------------------------------------------------------------------- # Full or incremental dump-load cycles should result in the same # mergeinfo in the loaded repository. # # Given a repository 'SOURCE-REPOS' with mergeinfo, and a repository # 'TARGET-REPOS' (which may or may not be empty), either of the following # methods to move 'SOURCE-REPOS' to 'TARGET-REPOS' should result in # the same mergeinfo on 'TARGET-REPOS': # # 1) Dump -r1:HEAD from 'SOURCE-REPOS' and load it in one shot to # 'TARGET-REPOS'. # # 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 # # This test replicates svnadmin_tests.py 20 'don't filter mergeinfo revs # from incremental dump' but uses 'svnrdump [dump|load]' in place of # 'svnadmin [dump|load]'. @Issue(3890) def dont_drop_valid_mergeinfo_during_incremental_svnrdump_loads(sbox): "don't drop mergeinfo revs in incremental svnrdump" # Create an empty repos. sbox.build(empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # PART 1: Load a full dump to an empty repository. # # The test repository used here, 'mergeinfo_included_full.dump', is # this repos: # __________________________________________ # | | # | ____________________________|_____ # | | | | # trunk---r2---r3-----r5---r6-------r8---r9---------------> | | # r1 | | | | | | # initial | | | |______ | | # import copy | copy | merge merge # | | | merge (r5) (r8) # | | | (r9) | | # | | | | | | # | | V V | | # | | branches/B2-------r11---r12----> | | # | | r7 |____| | | # | | | | | # | merge |___ | | # | (r6) | | | # | |_________________ | | | # | | merge | | # | | (r11-12) | | # | | | | | # V V V | | # branches/B1-------------------r10--------r13--> | | # r4 | | # | V V # branches/B1/B/E------------------------------r14---r15-> # # # The mergeinfo on this repos@15 is: # # Properties on 'branches/B1': # svn:mergeinfo # /branches/B2:11-12 # /trunk:6,9 # Properties on 'branches/B1/B/E': # svn:mergeinfo # /branches/B2/B/E:11-12 # /trunk/B/E:5-6,8-9 # Properties on 'branches/B2': # svn:mergeinfo # /trunk:9 dump_fp = open(os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data', 'mergeinfo_included_full.dump'), 'rb') dumpfile_full = dump_fp.readlines() dump_fp.close() svntest.actions.run_and_verify_svnrdump(dumpfile_full, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) # Check that the mergeinfo is as expected. url = sbox.repo_url + '/branches/' expected_output = svntest.verify.UnorderedOutput([ url + "B1 - /branches/B2:11-12\n", "/trunk:6,9\n", url + "B2 - /trunk:9\n", url + "B1/B/E - /branches/B2/B/E:11-12\n", "/trunk/B/E:5-6,8-9\n"]) svntest.actions.run_and_verify_svn(expected_output, [], 'propget', 'svn:mergeinfo', '-R', sbox.repo_url) # PART 2: Load a series of incremental dumps to an empty repository. # # Incrementally dump the repository into three dump files: dump_file_r1_10 = sbox.get_tempname("r1-10-dump") output = svntest.actions.run_and_verify_svnrdump(None, svntest.verify.AnyOutput, [], 0, '-q', 'dump', '-r1:10', sbox.repo_url) dump_fp = open(dump_file_r1_10, 'wb') dump_fp.writelines(output) dump_fp.close() dump_file_r11_13 = sbox.get_tempname("r11-13-dump") output = svntest.actions.run_and_verify_svnrdump(None, svntest.verify.AnyOutput, [], 0, '-q', 'dump', '--incremental', '-r11:13', sbox.repo_url) dump_fp = open(dump_file_r11_13, 'wb') dump_fp.writelines(output) dump_fp.close() dump_file_r14_15 = sbox.get_tempname("r14-15-dump") output = svntest.actions.run_and_verify_svnrdump(None, svntest.verify.AnyOutput, [], 0, '-q', 'dump', '--incremental', '-r14:15', sbox.repo_url) dump_fp = open(dump_file_r14_15, 'wb') dump_fp.writelines(output) dump_fp.close() # Blow away the current repos and create an empty one in its place. svntest.main.safe_rmtree(sbox.repo_dir, True) # Fix race with bdb in svnserve sbox.build(empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # Load the three incremental dump files in sequence. dump_fp = open(dump_file_r1_10, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) dump_fp.close() dump_fp = open(dump_file_r11_13, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) dump_fp.close() dump_fp = open(dump_file_r14_15, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) dump_fp.close() # Check the mergeinfo, we use the same expected output as before, # as it (duh!) should be exactly the same as when we loaded the # repos in one shot. svntest.actions.run_and_verify_svn(expected_output, [], 'propget', 'svn:mergeinfo', '-R', sbox.repo_url) # Now repeat the above two scenarios, but with an initially non-empty target # repository. First, try the full dump-load in one shot. # # PART 3: Load a full dump to an non-empty repository. # # Reset our sandbox. svntest.main.safe_rmtree(sbox.repo_dir, True) # Fix race with bdb in svnserve sbox.build(empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # Load this skeleton repos into the empty target: # # Projects/ (Added r1) # README (Added r2) # Project-X (Added r3) # Project-Y (Added r4) # Project-Z (Added r5) # docs/ (Added r6) # README (Added r6) dump_fp = open(os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data', 'skeleton.dump'), 'rb') dumpfile_skeleton = dump_fp.readlines() dump_fp.close() svntest.actions.run_and_verify_svnrdump(dumpfile_skeleton, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) # Load 'svnadmin_tests_data/mergeinfo_included_full.dump' in one shot: svntest.actions.run_and_verify_svnrdump(dumpfile_full, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url + '/Projects/Project-X') # Check that the mergeinfo is as expected. This is exactly the # same expected mergeinfo we previously checked, except that the # revisions are all offset +6 to reflect the revions already in # the skeleton target before we began loading and the leading source # paths are adjusted by the --parent-dir: # # Properties on 'branches/B1': # svn:mergeinfo # /Projects/Project-X/branches/B2:17-18 # /Projects/Project-X/trunk:12,15 # Properties on 'branches/B1/B/E': # svn:mergeinfo # /Projects/Project-X/branches/B2/B/E:17-18 # /Projects/Project-X/trunk/B/E:11-12,14-15 # Properties on 'branches/B2': # svn:mergeinfo # /Projects/Project-X/trunk:15 url = sbox.repo_url + '/Projects/Project-X/branches/' expected_output = svntest.verify.UnorderedOutput([ url + "B1 - /Projects/Project-X/branches/B2:17-18\n", "/Projects/Project-X/trunk:12,15\n", url + "B2 - /Projects/Project-X/trunk:15\n", url + "B1/B/E - /Projects/Project-X/branches/B2/B/E:17-18\n", "/Projects/Project-X/trunk/B/E:11-12,14-15\n"]) svntest.actions.run_and_verify_svn(expected_output, [], 'propget', 'svn:mergeinfo', '-R', sbox.repo_url) # PART 4: Load a a series of incremental dumps to an non-empty repository. # # Reset our sandbox. svntest.main.safe_rmtree(sbox.repo_dir, True) # Fix race with bdb in svnserve sbox.build(empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # Load the skeleton repos into the empty target: svntest.actions.run_and_verify_svnrdump(dumpfile_skeleton, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) # Load the three incremental dump files in sequence. # # The first load fails the same as PART 3. dump_fp = open(dump_file_r1_10, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url + '/Projects/Project-X') dump_fp.close() dump_fp = open(dump_file_r11_13, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url + '/Projects/Project-X') dump_fp.close() dump_fp = open(dump_file_r14_15, 'rb') svntest.actions.run_and_verify_svnrdump(dump_fp.readlines(), svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url + '/Projects/Project-X') dump_fp.close() # 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. svntest.actions.run_and_verify_svn(expected_output, [], 'propget', 'svn:mergeinfo', '-R', sbox.repo_url) #---------------------------------------------------------------------- @Issue(3890) def svnrdump_load_partial_incremental_dump(sbox): "svnrdump load partial incremental dump" # Create an empty sandbox repository sbox.build(create_wc=False, empty=True) # Create the revprop-change hook for this test svntest.actions.enable_revprop_changes(sbox.repo_dir) # Create the 'A' directory in repository and then load the partial # incremental dump into the root of the repository. svntest.actions.run_and_verify_svn(['Committing transaction...\n', 'Committed revision 1.\n'], [], "mkdir", sbox.repo_url + "/A", "-m", "Create toplevel dir to load into") # Load the specified dump file into the sbox repository using # svnrdump load dump_file = open(os.path.join(os.path.dirname(sys.argv[0]), 'svnrdump_tests_data', 'partial_incremental.dump'), 'rb') svnrdump_dumpfile = dump_file.readlines() dump_file.close() svntest.actions.run_and_verify_svnrdump(svnrdump_dumpfile, svntest.verify.AnyOutput, [], 0, 'load', sbox.repo_url) #---------------------------------------------------------------------- @Issue(4101) def range_dump(sbox): "dump: using -rX:Y" run_dump_test(sbox, "trunk-only.dump", expected_dumpfile_name="root-range.expected.dump", extra_options=['-r2:HEAD']) @Issue(4101) def only_trunk_range_dump(sbox): "dump: subdirectory using -rX:Y" run_dump_test(sbox, "trunk-only.dump", subdir="/trunk", expected_dumpfile_name="trunk-only-range.expected.dump", extra_options=['-r1:HEAD']) @Issue(4101) def only_trunk_A_range_dump(sbox): "dump: deeper subdirectory using -rX:Y" run_dump_test(sbox, "trunk-only.dump", subdir="/trunk/A", expected_dumpfile_name="trunk-A-range.expected.dump", extra_options=['-r2:HEAD']) #---------------------------------------------------------------------- @Issue(4490) def load_prop_change_in_non_deltas_dump(sbox): "load: prop change in non-deltas dump" # 'svnrdump load' crashed when processing a node record with a non-delta # properties block if the node previously had any svn:* properties. sbox.build() sbox.simple_propset('svn:eol-style', 'native', 'iota', 'A/mu', 'A/B/lambda') sbox.simple_commit() # Any prop change on a node that had an svn:* prop triggered the crash, # so test an svn:* prop deletion and also some other prop changes. sbox.simple_propdel('svn:eol-style', 'iota') sbox.simple_propset('svn:eol-style', 'LF', 'A/mu') sbox.simple_propset('p1', 'v1', 'A/B/lambda') sbox.simple_commit() # Create a non-deltas dump. Use 'svnadmin', as svnrdump doesn't have that # option. dump = svntest.actions.run_and_verify_dump(sbox.repo_dir, deltas=False) # Try to load that dump. sbox.build(create_wc=False, empty=True) svntest.actions.enable_revprop_changes(sbox.repo_dir) svntest.actions.run_and_verify_svnrdump(dump, [], [], 0, '-q', 'load', sbox.repo_url) #---------------------------------------------------------------------- @Issue(4476) def dump_mergeinfo_contains_r0(sbox): "dump: mergeinfo that contains r0" ### We pass the original dump file name as 'expected_dumpfile_name' because ### run_dump_test is currently broken when we don't. run_dump_test(sbox, "mergeinfo-contains-r0.dump", bypass_prop_validation=True) #---------------------------------------------------------------------- @XFail() @Issue(4476) def load_mergeinfo_contains_r0(sbox): "load: mergeinfo that contains r0" run_load_test(sbox, "mergeinfo-contains-r0.dump", expected_dumpfile_name="mergeinfo-contains-r0.expected.dump") #---------------------------------------------------------------------- # Regression test for issue 4551 "svnrdump load commits wrong properties, # or fails, on a non-deltas dumpfile". In this test, the copy source does # not exist and the failure mode is to error out. @Issue(4551) def load_non_deltas_copy_with_props(sbox): "load non-deltas copy with props" sbox.build() # Case (1): Copies that do not replace anything: the copy target path # at (new rev - 1) does not exist # Set properties on each node to be copied sbox.simple_propset('p', 'v', 'A/mu', 'A/B', 'A/B/E') sbox.simple_propset('q', 'v', 'A/mu', 'A/B', 'A/B/E') sbox.simple_commit() sbox.simple_update() # avoid mixed-rev # Do the copies sbox.simple_copy('A/mu@2', 'A/mu_COPY') sbox.simple_copy('A/B@2', 'A/B_COPY') # Also add new nodes inside the copied dir, to test more code paths sbox.simple_copy('A/B/E@2', 'A/B_COPY/copied') sbox.simple_mkdir('A/B_COPY/added') sbox.simple_copy('A/B/E@2', 'A/B_COPY/added/copied') # On each copied node, delete a prop sbox.simple_propdel('p', 'A/mu_COPY', 'A/B_COPY', 'A/B_COPY/E', 'A/B_COPY/copied', 'A/B_COPY/added/copied') sbox.simple_commit() # Dump with 'svnadmin' (non-deltas mode) dumpfile = svntest.actions.run_and_verify_dump(sbox.repo_dir, deltas=False) # Load with 'svnrdump'. This used to throw an error: # svnrdump: E160013: File not found: revision 2, path '/A/B_COPY' new_repo_dir, new_repo_url = sbox.add_repo_path('new_repo') svntest.main.create_repos(new_repo_dir) svntest.actions.enable_revprop_changes(new_repo_dir) svntest.actions.run_and_verify_svnrdump(dumpfile, svntest.verify.AnyOutput, [], 0, 'load', new_repo_url) # Check that property 'p' really was deleted on each copied node for tgt_path in ['A/mu_COPY', 'A/B_COPY', 'A/B_COPY/E', 'A/B_COPY/copied', 'A/B_COPY/added/copied']: tgt_url = new_repo_url + '/' + tgt_path _, out, _ = svntest.main.run_svn(None, 'proplist', tgt_url) expected = ["Properties on '%s':" % (tgt_url,), 'q'] actual = map(str.strip, out) svntest.verify.compare_and_display_lines(None, 'PROPS', expected, actual) # Regression test for issue 4551 "svnrdump load commits wrong properties, # or fails, on a non-deltas dumpfile". In this test, the copy source does # exist and the failure mode is to fail to delete a property. @Issue(4551) def load_non_deltas_replace_copy_with_props(sbox): "load non-deltas replace© with props" sbox.build() # Case (2): Copies that replace something: the copy target path # at (new rev - 1) exists and has no property named 'p' # Set props on the copy source nodes (a file, a dir, a child of the dir) sbox.simple_propset('p', 'v', 'A/mu', 'A/B', 'A/B/E') sbox.simple_propset('q', 'v', 'A/mu', 'A/B', 'A/B/E') sbox.simple_commit() sbox.simple_update() # avoid mixed-rev # Do the copies, replacing something sbox.simple_rm('A/D/gamma', 'A/C') sbox.simple_copy('A/mu@2', 'A/D/gamma') sbox.simple_copy('A/B@2', 'A/C') # On the copy, delete a prop that wasn't present on the node that it replaced sbox.simple_propdel('p', 'A/D/gamma', 'A/C', 'A/C/E') sbox.simple_commit() # Dump with 'svnadmin' (non-deltas mode) dumpfile = svntest.actions.run_and_verify_dump(sbox.repo_dir, deltas=False) # Load with 'svnrdump' new_repo_dir, new_repo_url = sbox.add_repo_path('new_repo') svntest.main.create_repos(new_repo_dir) svntest.actions.enable_revprop_changes(new_repo_dir) svntest.actions.run_and_verify_svnrdump(dumpfile, svntest.verify.AnyOutput, [], 0, 'load', new_repo_url) # Check that property 'p' really was deleted on each copied node # This used to fail, finding that property 'p' was still present for tgt_path in ['A/D/gamma', 'A/C', 'A/C/E']: tgt_url = new_repo_url + '/' + tgt_path _, out, _ = svntest.main.run_svn(None, 'proplist', tgt_url) expected = ["Properties on '%s':" % (tgt_url,), 'q'] actual = map(str.strip, out) svntest.verify.compare_and_display_lines(None, 'PROPS', expected, actual) # Regression test for issue #4552 "svnrdump writes duplicate headers for a # replace-with-copy". 'svnrdump dump' wrote the Node-path and Node-kind # headers twice for the 'delete' record of a replace-with-copy. @Issue(4552) def dump_replace_with_copy(sbox): "dump replace with copy" sbox.build() # Copy file/dir, replacing something sbox.simple_rm('A/D/gamma', 'A/C') sbox.simple_copy('A/mu@1', 'A/D/gamma') sbox.simple_copy('A/B@1', 'A/C') sbox.simple_commit() # Dump with 'svnrdump' dumpfile = svntest.actions.run_and_verify_svnrdump( None, svntest.verify.AnyOutput, [], 0, 'dump', '--quiet', '--incremental', '-r2', sbox.repo_url) # Check the 'delete' record headers: expect this parse to fail if headers # are duplicated svntest.verify.DumpParser(dumpfile).parse() # Regression test for issue 4551 "svnrdump load commits wrong properties, # or fails, on a non-deltas dumpfile". In this test, a node's props are # modified, and the failure mode is that RA-serf would end up deleting # properties that should remain on the node. @Issue(4551) def load_non_deltas_with_props(sbox): "load non-deltas with props" sbox.build() # Case (3): A node's props are modified, and at least one of its previous # props remains after the modification. svnrdump made two prop mod method # calls for the same property (delete, then set). RA-serf's commit editor # didn't expect this and performed the deletes after the non-deletes, and # so ended up deleting a property that should not be deleted. # Set properties on each node to be modified sbox.simple_propset('p', 'v', 'A/mu') sbox.simple_propset('q', 'v', 'A/mu', 'A/B') sbox.simple_commit() # Do the modifications: a different kind of mod on each node sbox.simple_propdel('p', 'A/mu') sbox.simple_propset('q', 'v2', 'A/B') sbox.simple_commit() # Dump with 'svnadmin' (non-deltas mode) dumpfile = svntest.actions.run_and_verify_dump(sbox.repo_dir, deltas=False) # Load with 'svnrdump' new_repo_dir, new_repo_url = sbox.add_repo_path('new_repo') svntest.main.create_repos(new_repo_dir) svntest.actions.enable_revprop_changes(new_repo_dir) svntest.actions.run_and_verify_svnrdump(dumpfile, svntest.verify.AnyOutput, [], 0, 'load', new_repo_url) # Check that property 'q' remains on each modified node for tgt_path in ['A/mu', 'A/B']: tgt_url = new_repo_url + '/' + tgt_path _, out, _ = svntest.main.run_svn(None, 'proplist', tgt_url) expected = ["Properties on '%s':" % (tgt_url,), 'q'] actual = map(str.strip, out) svntest.verify.compare_and_display_lines(None, 'PROPS', expected, actual) ######################################################################## # Run the tests # list all tests here, starting with None: test_list = [ None, basic_dump, revision_0_dump, revision_0_load, skeleton_dump, skeleton_load, sparse_propchanges_dump, sparse_propchanges_load, copy_and_modify_dump, copy_and_modify_load, copy_from_previous_version_and_modify_dump, copy_from_previous_version_and_modify_load, modified_in_place_dump, modified_in_place_load, tag_empty_trunk_dump, tag_empty_trunk_load, tag_trunk_with_file_dump, tag_trunk_with_file_load, tag_trunk_with_file2_dump, tag_trunk_with_file2_load, dir_prop_change_dump, dir_prop_change_load, copy_parent_modify_prop_dump, copy_parent_modify_prop_load, url_encoding_dump, url_encoding_load, copy_revprops_dump, copy_revprops_load, only_trunk_dump, only_trunk_A_with_changes_dump, no_author_dump, no_author_load, move_and_modify_in_the_same_revision_dump, move_and_modify_in_the_same_revision_load, copy_bad_line_endings_dump, copy_bad_line_endings_load, copy_bad_line_endings2_dump, commit_a_copy_of_root_dump, commit_a_copy_of_root_load, descend_into_replace_dump, descend_into_replace_load, add_multi_prop_dump, multi_prop_edit_load, reflect_dropped_renumbered_revs, dont_drop_valid_mergeinfo_during_incremental_svnrdump_loads, svnrdump_load_partial_incremental_dump, range_dump, only_trunk_range_dump, only_trunk_A_range_dump, load_prop_change_in_non_deltas_dump, dump_mergeinfo_contains_r0, load_mergeinfo_contains_r0, load_non_deltas_copy_with_props, load_non_deltas_replace_copy_with_props, dump_replace_with_copy, load_non_deltas_with_props, ] if __name__ == '__main__': svntest.main.run_tests(test_list) # NOTREACHED ### End of file.