32
32
logger = logging.getLogger()
35
def make_mirror(sbox, source_prop_encoding=None):
36
"""Make a mirror of the repository in SBOX.
38
# Set up the mirror repository.
39
dest_sbox = sbox.clone_dependent()
40
dest_sbox.build(create_wc=False, empty=True)
41
exit_code, output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
42
svntest.actions.run_and_verify_svnadmin2(None, None, 0,
43
'setuuid', dest_sbox.repo_dir,
45
svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
47
repo_url = sbox.repo_url
48
dest_repo_url = dest_sbox.repo_url
51
args = (svntest.main.svnrdump_crosscheck_authentication,)
52
if source_prop_encoding:
53
args = args + ("--source-prop-encoding=" + source_prop_encoding,)
54
svntest.actions.run_and_verify_svnsync(svntest.verify.AnyOutput, [],
56
dest_repo_url, repo_url, *args)
57
svntest.actions.run_and_verify_svnsync(None, [],
59
dest_repo_url, repo_url, *args)
63
def verify_mirror(repo_url, repo_dir, expected_dumpfile):
64
"""Compare the repository content at REPO_URL/REPO_DIR with that in
65
EXPECTED_DUMPFILE (which is a non-delta dump).
67
# Remove some SVNSync-specific housekeeping properties from the
68
# mirror repository in preparation for the comparison dump.
69
for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
70
"svn:sync-last-merged-rev"):
71
svntest.actions.run_and_verify_svn(
72
None, [], "propdel", "--revprop", "-r", "0",
74
# Create a dump file from the mirror repository.
75
dumpfile_s_n = svntest.actions.run_and_verify_dump(repo_dir)
76
# Compare the mirror's dumpfile, ignoring any expected differences:
77
# The original dumpfile in some cases lacks 'Text-content-sha1' headers;
78
# the mirror dump always has them -- ### Why?
79
svnsync_headers_always = re.compile("Text-content-sha1: ")
80
dumpfile_a_n_cmp = [l for l in expected_dumpfile
81
if not svnsync_headers_always.match(l)]
82
dumpfile_s_n_cmp = [l for l in dumpfile_s_n
83
if not svnsync_headers_always.match(l)]
84
svntest.verify.compare_dump_files(None, None,
36
90
"""Manages a sandbox (one or more repository/working copy pairs) for
37
91
a test to operate within."""
54
110
self.read_only = read_only
55
111
self.wc_dir = os.path.join(svntest.main.general_wc_dir, self.name)
56
112
self.add_test_path(self.wc_dir)
113
if empty or not read_only: # use a local repo
58
114
self.repo_dir = os.path.join(svntest.main.general_repo_dir, self.name)
59
115
self.repo_url = (svntest.main.options.test_area_url + '/'
60
+ urllib.pathname2url(self.repo_dir))
116
+ svntest.wc.svn_uri_quote(
117
self.repo_dir.replace(os.path.sep, '/')))
61
118
self.add_test_path(self.repo_dir)
63
120
self.repo_dir = svntest.main.pristine_greek_repos_dir
64
121
self.repo_url = svntest.main.pristine_greek_repos_url
66
### TODO: Move this into to the build() method
67
# For dav tests we need a single authz file which must be present,
68
# so we recreate it each time a sandbox is created with some default
69
# contents, making sure that an empty file is never present
70
123
if self.repo_url.startswith("http"):
71
# this dir doesn't exist out of the box, so we may have to make it
72
if not os.path.exists(svntest.main.work_dir):
73
os.makedirs(svntest.main.work_dir)
74
124
self.authz_file = os.path.join(svntest.main.work_dir, "authz")
75
tmp_authz_file = os.path.join(svntest.main.work_dir, "authz-" + self.name)
76
open(tmp_authz_file, 'w').write("[/]\n* = rw\n")
77
shutil.move(tmp_authz_file, self.authz_file)
78
125
self.groups_file = os.path.join(svntest.main.work_dir, "groups")
80
# For svnserve tests we have a per-repository authz file, and it
81
# doesn't need to be there in order for things to work, so we don't
82
# have any default contents.
83
126
elif self.repo_url.startswith("svn"):
84
127
self.authz_file = os.path.join(self.repo_dir, "conf", "authz")
85
128
self.groups_file = os.path.join(self.repo_dir, "conf", "groups")
102
145
shutil.copytree(self.wc_dir, clone.wc_dir, symlinks=True)
105
def build(self, name=None, create_wc=True, read_only=False,
148
def build(self, name=None, create_wc=True, read_only=False, empty=False,
106
149
minor_version=None):
107
150
"""Make a 'Greek Tree' repo (or refer to the central one if READ_ONLY),
151
or make an empty repo if EMPTY is true,
108
152
and check out a WC from it (unless CREATE_WC is false). Change the
109
153
sandbox's name to NAME. See actions.make_repo_and_wc() for details."""
110
self._set_name(name, read_only)
111
svntest.actions.make_repo_and_wc(self, create_wc, read_only, minor_version)
154
self._set_name(name, read_only, empty)
156
svntest.actions.make_repo_and_wc(self, create_wc, read_only, empty,
112
158
self._is_built = True
160
def _ensure_authz(self):
161
"make sure the repository is accessible"
163
if self.repo_url.startswith("http"):
164
default_authz = "[/]\n* = rw\n"
166
if (svntest.main.options.parallel == 0
167
and (not os.path.isfile(self.authz_file)
168
or open(self.authz_file,'r').read() != default_authz)):
170
tmp_authz_file = os.path.join(svntest.main.work_dir, "authz-" + self.name)
171
open(tmp_authz_file, 'w').write(default_authz)
172
shutil.move(tmp_authz_file, self.authz_file)
114
174
def authz_name(self, repo_dir=None):
115
175
"return this sandbox's name for use in an authz file"
116
176
repo_dir = repo_dir or self.repo_dir
317
388
raise Exception("Unexpected line '" + line + "' in proplist output" + str(out))
320
def simple_add_symlink(self, dest, target):
321
"""Create a symlink TARGET pointing to DEST and add it to subversion"""
391
def simple_symlink(self, dest, target):
392
"""Create a symlink TARGET pointing to DEST"""
322
393
if svntest.main.is_posix_os():
323
394
os.symlink(dest, self.ospath(target))
325
396
svntest.main.file_write(self.ospath(target), "link %s" % dest)
398
def simple_add_symlink(self, dest, target, add=True):
399
"""Create a symlink TARGET pointing to DEST and add it to subversion"""
400
self.simple_symlink(dest, target)
326
401
self.simple_add(target)
327
if not svntest.main.is_posix_os():
328
# '*' is evaluated on Windows
402
if not svntest.main.is_posix_os(): # '*' is evaluated on Windows
329
403
self.simple_propset('svn:special', 'X', target)
331
405
def simple_add_text(self, text, *targets):
369
443
targets = self.ospaths(targets)
370
444
svntest.main.run_svn(False, 'lock', *targets)
447
_, output, _ = svntest.actions.run_and_verify_svnlook(
448
svntest.verify.AnyOutput, [],
449
'youngest', self.repo_dir)
450
youngest = int(output[0])
453
def verify_repo(self):
456
svnrdump_headers_missing = re.compile(
457
"Text-content-sha1: .*|Text-copy-source-md5: .*|"
458
"Text-copy-source-sha1: .*|Text-delta-base-sha1: .*"
460
svnrdump_headers_always = re.compile(
464
dumpfile_a_n = svntest.actions.run_and_verify_dump(self.repo_dir,
466
dumpfile_a_d = svntest.actions.run_and_verify_dump(self.repo_dir,
468
dumpfile_r_d = svntest.actions.run_and_verify_svnrdump(
469
None, svntest.verify.AnyOutput, [], 0, 'dump', '-q', self.repo_url,
470
svntest.main.svnrdump_crosscheck_authentication)
472
# Compare the two deltas dumpfiles, ignoring expected differences
473
dumpfile_a_d_cmp = [l for l in dumpfile_a_d
474
if not svnrdump_headers_missing.match(l)
475
and not svnrdump_headers_always.match(l)]
476
dumpfile_r_d_cmp = [l for l in dumpfile_r_d
477
if not svnrdump_headers_always.match(l)]
478
# Ignore differences in number of blank lines between node records,
479
# as svnrdump puts 3 whereas svnadmin puts 2 after a replace-with-copy.
480
svntest.verify.compare_dump_files(None, None,
483
ignore_number_of_blank_lines=True)
485
# Try loading the dump files.
486
# For extra points, load each with the other tool:
487
# svnadmin dump | svnrdump load
488
# svnrdump dump | svnadmin load
489
repo_dir_a_n, repo_url_a_n = self.add_repo_path('load_a_n')
490
svntest.main.create_repos(repo_dir_a_n)
491
svntest.actions.enable_revprop_changes(repo_dir_a_n)
492
svntest.actions.run_and_verify_svnrdump(
493
dumpfile_a_n, svntest.verify.AnyOutput, [], 0, 'load', repo_url_a_n,
494
svntest.main.svnrdump_crosscheck_authentication)
496
repo_dir_a_d, repo_url_a_d = self.add_repo_path('load_a_d')
497
svntest.main.create_repos(repo_dir_a_d)
498
svntest.actions.enable_revprop_changes(repo_dir_a_d)
499
svntest.actions.run_and_verify_svnrdump(
500
dumpfile_a_d, svntest.verify.AnyOutput, [], 0, 'load', repo_url_a_d,
501
svntest.main.svnrdump_crosscheck_authentication)
503
repo_dir_r_d, repo_url_r_d = self.add_repo_path('load_r_d')
504
svntest.main.create_repos(repo_dir_r_d)
505
svntest.actions.run_and_verify_load(repo_dir_r_d, dumpfile_r_d)
507
# Dump the loaded repositories in the same way; expect exact equality
508
reloaded_dumpfile_a_n = svntest.actions.run_and_verify_dump(repo_dir_a_n)
509
reloaded_dumpfile_a_d = svntest.actions.run_and_verify_dump(repo_dir_a_d)
510
reloaded_dumpfile_r_d = svntest.actions.run_and_verify_dump(repo_dir_r_d)
511
svntest.verify.compare_dump_files(None, None,
512
reloaded_dumpfile_a_n,
513
reloaded_dumpfile_a_d,
515
svntest.verify.compare_dump_files(None, None,
516
reloaded_dumpfile_a_d,
517
reloaded_dumpfile_r_d,
520
# Run each dump through svndumpfilter and check for no further change.
521
for dumpfile in [dumpfile_a_n,
525
### No buffer size seems to work for update_tests-2. So skip that test?
526
### (Its dumpfile size is ~360 KB non-delta, ~180 KB delta.)
527
if len(''.join(dumpfile)) > 100000:
530
exit_code, dumpfile2, errput = svntest.main.run_command_stdin(
531
svntest.main.svndumpfilter_binary, None, -1, True,
532
dumpfile, '--quiet', 'include', '/')
533
assert not exit_code and not errput
534
# Ignore empty prop sections in the input file during comparison, as
535
# svndumpfilter strips them.
536
# Ignore differences in number of blank lines between node records,
537
# as svndumpfilter puts 3 instead of 2 after an add or delete record.
538
svntest.verify.compare_dump_files(None, None, dumpfile, dumpfile2,
539
expect_content_length_always=True,
540
ignore_empty_prop_sections=True,
541
ignore_number_of_blank_lines=True)
543
# Run the repository through 'svnsync' and check that this does not
544
# change the repository content. (Don't bother if it's already been
545
# created by svnsync.)
546
if "svn:sync-from-url\n" not in dumpfile_a_n:
547
dest_sbox = make_mirror(self)
548
verify_mirror(dest_sbox.repo_url, dest_sbox.repo_dir, dumpfile_a_n)
550
def verify(self, skip_cross_check=False):
551
"""Do additional testing that should hold for any sandbox, such as
552
verifying that the repository can be dumped.
554
if (not skip_cross_check
555
and svntest.main.tests_verify_dump_load_cross_check()):
556
if self.is_built() and not self.read_only:
557
# verify that we can in fact dump the repo
558
# (except for the few tests that deliberately corrupt the repo)
559
os.chdir(self.was_cwd)
560
if os.path.exists(self.repo_dir):
561
logger.info("VERIFY: running dump/load cross-check")
564
logger.info("VERIFY: WARNING: skipping dump/load cross-check:"
565
" is-built=%s, read-only=%s"
566
% (self.is_built() and "true" or "false",
567
self.read_only and "true" or "false"))
373
570
def is_url(target):
374
571
return (target.startswith('^/')