blob: ae6a7e022dfdcea3d2b84181ff4c97640c1b2045 [file] [log] [blame]
#!/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&copy 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.