1#!/usr/bin/env python
2#
3#  svnadmin_tests.py:  testing the 'svnadmin' tool.
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import os
29import logging
30import re
31import shutil
32import sys
33import threading
34import time
35import gzip
36
37logger = logging.getLogger()
38
39# Our testing module
40import svntest
41from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
42from svntest.verify import SVNUnexpectedStderr
43from svntest.verify import UnorderedOutput
44from svntest.main import SVN_PROP_MERGEINFO
45
46# (abbreviation)
47Skip = svntest.testcase.Skip_deco
48SkipUnless = svntest.testcase.SkipUnless_deco
49XFail = svntest.testcase.XFail_deco
50Issues = svntest.testcase.Issues_deco
51Issue = svntest.testcase.Issue_deco
52Wimp = svntest.testcase.Wimp_deco
53SkipDumpLoadCrossCheck = svntest.testcase.SkipDumpLoadCrossCheck_deco
54Item = svntest.wc.StateItem
55
56def read_rep_cache(repo_dir):
57  """Return the rep-cache contents as a dict {hash: (rev, index, ...)}.
58  """
59  db_path = os.path.join(repo_dir, 'db', 'rep-cache.db')
60  db1 = svntest.sqlite3.connect(db_path)
61  schema1 = db1.execute("pragma user_version").fetchone()[0]
62  # Can't test newer rep-cache schemas with an old built-in SQLite; see the
63  # documentation of STMT_CREATE_SCHEMA_V2 in ../../libsvn_fs_fs/rep-cache-db.sql
64  if schema1 >= 2 and svntest.sqlite3.sqlite_version_info < (3, 8, 2):
65    raise svntest.Failure("Can't read rep-cache schema %d using old "
66                          "Python-SQLite version %s < (3,8,2)" %
67                           (schema1,
68                            svntest.sqlite3.sqlite_version_info))
69
70  content = { row[0]: row[1:] for row in
71              db1.execute("select * from rep_cache") }
72  return content
73
74def check_hotcopy_bdb(src, dst):
75  "Verify that the SRC BDB repository has been correctly copied to DST."
76  ### TODO: This function should be extended to verify all hotcopied files,
77  ### not just compare the output of 'svnadmin dump'. See check_hotcopy_fsfs().
78  exit_code, origout, origerr = svntest.main.run_svnadmin("dump", src,
79                                                          '--quiet')
80  exit_code, backout, backerr = svntest.main.run_svnadmin("dump", dst,
81                                                          '--quiet')
82  if origerr or backerr or origout != backout:
83    raise svntest.Failure
84
85def check_hotcopy_fsfs_fsx(src, dst):
86    # Walk the source and compare all files to the destination
87    for src_dirpath, src_dirs, src_files in os.walk(src):
88      # Verify that the current directory exists in the destination
89      dst_dirpath = src_dirpath.replace(src, dst)
90      if not os.path.isdir(dst_dirpath):
91        raise svntest.Failure("%s does not exist in hotcopy "
92                              "destination" % dst_dirpath)
93      # Verify that all dirents in the current directory also exist in source
94      for dst_dirent in os.listdir(dst_dirpath):
95        # Ignore auto-created empty lock files as they may or may not
96        # be present and are neither required by nor do they harm to
97        # the destination repository.
98        if dst_dirent == 'pack-lock':
99          continue
100        if dst_dirent == 'write-lock':
101          continue
102
103        # Ignore auto-created rep-cache.db-journal file
104        if dst_dirent == 'rep-cache.db-journal':
105          continue
106
107        src_dirent = os.path.join(src_dirpath, dst_dirent)
108        if not os.path.exists(src_dirent):
109          raise svntest.Failure("%s does not exist in hotcopy "
110                                "source" % src_dirent)
111      # Compare all files in this directory
112      for src_file in src_files:
113        # Ignore auto-created empty lock files as they may or may not
114        # be present and are neither required by nor do they harm to
115        # the destination repository.
116        if src_file == 'pack-lock':
117          continue
118        if src_file == 'write-lock':
119          continue
120
121        # Ignore auto-created rep-cache.db-journal file
122        if src_file == 'rep-cache.db-journal':
123          continue
124
125        src_path = os.path.join(src_dirpath, src_file)
126        dst_path = os.path.join(dst_dirpath, src_file)
127        if not os.path.isfile(dst_path):
128          raise svntest.Failure("%s does not exist in hotcopy "
129                                "destination" % dst_path)
130
131        # Special case for db/uuid: Only the UUID in the first line needs
132        # to match. Source and target must have the same number of lines
133        # (due to having the same format).
134        if src_path == os.path.join(src, 'db', 'uuid'):
135          lines1 = open(src_path, 'rb').read().split(b"\n")
136          lines2 = open(dst_path, 'rb').read().split(b"\n")
137          if len(lines1) != len(lines2):
138            raise svntest.Failure("%s differs in number of lines"
139                                  % dst_path)
140          if lines1[0] != lines2[0]:
141            raise svntest.Failure("%s contains different uuid: '%s' vs. '%s'"
142                                   % (dst_path, lines1[0], lines2[0]))
143          continue
144
145        # Special case for rep-cache: It will always differ in a byte-by-byte
146        # comparison, so compare db tables instead.
147        if src_file == 'rep-cache.db':
148          db1 = svntest.sqlite3.connect(src_path)
149          db2 = svntest.sqlite3.connect(dst_path)
150          schema1 = db1.execute("pragma user_version").fetchone()[0]
151          schema2 = db2.execute("pragma user_version").fetchone()[0]
152          if schema1 != schema2:
153            raise svntest.Failure("rep-cache schema differs: '%s' vs. '%s'"
154                                  % (schema1, schema2))
155          # Can't test newer rep-cache schemas with an old built-in SQLite.
156          if schema1 >= 2 and svntest.sqlite3.sqlite_version_info < (3, 8, 2):
157            continue
158
159          rows1 = []
160          rows2 = []
161          for row in db1.execute("select * from rep_cache order by hash"):
162            rows1.append(row)
163          for row in db2.execute("select * from rep_cache order by hash"):
164            rows2.append(row)
165          if len(rows1) != len(rows2):
166            raise svntest.Failure("number of rows in rep-cache differs")
167          for i in range(len(rows1)):
168            if rows1[i] != rows2[i]:
169              raise svntest.Failure("rep-cache row %i differs: '%s' vs. '%s'"
170                                    % (i, rows1[i], rows2[i]))
171          continue
172
173        # Special case for revprop-generation: It will always be zero in
174        # the hotcopy destination (i.e. a fresh cache generation)
175        if src_file == 'revprop-generation':
176          f2 = open(dst_path, 'r')
177          revprop_gen = int(f2.read().strip())
178          if revprop_gen != 0:
179              raise svntest.Failure("Hotcopy destination has non-zero " +
180                                    "revprop generation")
181          continue
182
183        f1 = open(src_path, 'rb')
184        f2 = open(dst_path, 'rb')
185        while True:
186          offset = 0
187          BUFSIZE = 1024
188          buf1 = f1.read(BUFSIZE)
189          buf2 = f2.read(BUFSIZE)
190          if not buf1 or not buf2:
191            if not buf1 and not buf2:
192              # both at EOF
193              break
194            elif buf1:
195              raise svntest.Failure("%s differs at offset %i" %
196                                    (dst_path, offset))
197            elif buf2:
198              raise svntest.Failure("%s differs at offset %i" %
199                                    (dst_path, offset))
200          if len(buf1) != len(buf2):
201            raise svntest.Failure("%s differs in length" % dst_path)
202          for i in range(len(buf1)):
203            if buf1[i] != buf2[i]:
204              raise svntest.Failure("%s differs at offset %i"
205                                    % (dst_path, offset))
206            offset += 1
207        f1.close()
208        f2.close()
209
210def check_hotcopy_fsfs(src, dst):
211    "Verify that the SRC FSFS repository has been correctly copied to DST."
212    check_hotcopy_fsfs_fsx(src, dst)
213
214def check_hotcopy_fsx(src, dst):
215    "Verify that the SRC FSX repository has been correctly copied to DST."
216    check_hotcopy_fsfs_fsx(src, dst)
217
218#----------------------------------------------------------------------
219
220# How we currently test 'svnadmin' --
221#
222#   'svnadmin create':   Create an empty repository, test that the
223#                        root node has a proper created-revision,
224#                        because there was once a bug where it
225#                        didn't.
226#
227#                        Note also that "svnadmin create" is tested
228#                        implicitly every time we run a python test
229#                        script.  (An empty repository is always
230#                        created and then imported into;  if this
231#                        subcommand failed catastrophically, every
232#                        test would fail and we would know instantly.)
233#
234#   'svnadmin createtxn'
235#   'svnadmin rmtxn':    See below.
236#
237#   'svnadmin lstxns':   We don't care about the contents of transactions;
238#                        we only care that they exist or not.
239#                        Therefore, we can simply parse transaction headers.
240#
241#   'svnadmin dump':     A couple regression tests that ensure dump doesn't
242#                        error out, and one to check that the --quiet option
243#                        really does what it's meant to do. The actual
244#                        contents of the dump aren't verified at all.
245#
246#  ### TODO:  someday maybe we could parse the contents of trees too.
247#
248######################################################################
249# Helper routines
250
251
252def get_txns(repo_dir):
253  "Get the txn names using 'svnadmin lstxns'."
254
255  exit_code, output_lines, error_lines = svntest.main.run_svnadmin('lstxns',
256                                                                   repo_dir)
257  txns = sorted([output_lines.strip(x) for x in output_lines])
258
259  return txns
260
261def patch_format(repo_dir, shard_size):
262  """Rewrite the format of the FSFS or FSX repository REPO_DIR so
263  that it would use sharding with SHARDS revisions per shard."""
264
265  format_path = os.path.join(repo_dir, "db", "format")
266  contents = open(format_path, 'rb').read()
267  processed_lines = []
268
269  for line in contents.split(b"\n"):
270    if line.startswith(b"layout "):
271      processed_lines.append(("layout sharded %d" % shard_size).encode())
272    else:
273      processed_lines.append(line)
274
275  new_contents = b"\n".join(processed_lines)
276  os.chmod(format_path, svntest.main.S_ALL_RW)
277  with open(format_path, 'wb') as f:
278    f.write(new_contents)
279
280def is_sharded(repo_dir):
281  """Return whether the FSFS repository REPO_DIR is sharded."""
282
283  format_path = os.path.join(repo_dir, "db", "format")
284  contents = open(format_path, 'rb').read()
285
286  for line in contents.split(b"\n"):
287    if line.startswith(b"layout sharded"):
288      return True
289
290  return False
291
292def load_and_verify_dumpstream(sbox, expected_stdout, expected_stderr,
293                               revs, check_props, dump, *varargs):
294  """Load the array of lines passed in DUMP into the current tests'
295  repository and verify the repository content using the array of
296  wc.States passed in REVS.  If CHECK_PROPS is True, check properties
297  of each rev's items.  VARARGS are optional arguments passed to the
298  'load' command."""
299
300  dump = svntest.main.ensure_list(dump)
301
302  exit_code, output, errput = svntest.main.run_command_stdin(
303    svntest.main.svnadmin_binary, expected_stderr, 0, True, dump,
304    'load', '--quiet', sbox.repo_dir, *varargs)
305
306  if expected_stdout:
307    if expected_stdout is svntest.verify.AnyOutput:
308      if len(output) == 0:
309        raise SVNExpectedStdout
310    else:
311      svntest.verify.compare_and_display_lines(
312        "Standard output", "STDOUT:", expected_stdout, output)
313
314  if expected_stderr:
315    if expected_stderr is svntest.verify.AnyOutput:
316      if len(errput) == 0:
317        raise SVNExpectedStderr
318    else:
319      svntest.verify.compare_and_display_lines(
320        "Standard error output", "STDERR:", expected_stderr, errput)
321    # The expected error occurred, so don't try to verify the result
322    return
323
324  if revs:
325    # verify revs as wc states
326    for rev in range(len(revs)):
327      svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
328                                         "update", "-r%s" % (rev+1),
329                                         sbox.wc_dir)
330
331      rev_tree = revs[rev]
332      svntest.actions.verify_disk(sbox.wc_dir, rev_tree, check_props)
333
334def load_dumpstream(sbox, dump, *varargs):
335  "Load dump text without verification."
336  return load_and_verify_dumpstream(sbox, None, None, None, False, dump,
337                                    *varargs)
338
339class FSFS_Index:
340  """Manages indexes of a rev file in a FSFS format 7 repository.
341  The interface returns P2L information and allows for item offsets
342  and lengths to be modified. """
343
344  def __init__(self, sbox, revision):
345    self.by_item = { }
346    self.revision = revision
347    self.repo_dir = sbox.repo_dir
348
349    self._read()
350
351  def _read(self):
352    """ Read P2L index using svnfsfs. """
353    exit_code, output, errput = svntest.main.run_svnfsfs('dump-index',
354                                                  '-r' + str(self.revision),
355                                                  self.repo_dir)
356    svntest.verify.verify_outputs("Error while dumping index",
357                                  [], errput, [], [])
358    svntest.verify.verify_exit_code(None, exit_code, 0)
359
360    self.by_item.clear()
361    for line in output:
362      values = line.split()
363      if len(values) >= 4 and values[0] != 'Start':
364        item = int(values[4])
365        self.by_item[item] = values
366
367  def _write(self):
368    """ Rewrite indexes using svnfsfs. """
369    by_offset = {}
370    for key in self.by_item:
371      values = self.by_item[key]
372      by_offset[int(values[0], 16)] = values
373
374    lines = []
375    for (offset, values) in sorted(by_offset.items()):
376      values = by_offset[offset]
377      line = values[0] + ' ' + values[1] + ' ' + values[2] + ' ' + \
378             values[3] + ' ' + values[4] + '\n';
379      lines.append(line.encode())
380
381    exit_code, output, errput = svntest.main.run_command_stdin(
382      svntest.main.svnfsfs_binary, 0, 0, False, lines,
383      'load-index', self.repo_dir)
384
385    svntest.verify.verify_outputs("Error while rewriting index",
386                                  output, errput, [], [])
387    svntest.verify.verify_exit_code(None, exit_code, 0)
388
389  def get_item(self, item):
390    """ Return offset, length and type of ITEM. """
391    values = self.by_item[item]
392
393    offset = int(values[0], 16)
394    len = int(values[1], 16)
395    type = values[2]
396
397    return (offset, len, type)
398
399  def modify_item(self, item, offset, len):
400    """ Modify offset and length of ITEM. """
401    values = self.by_item[item]
402
403    values[0] = '%x' % offset
404    values[1] = '%x' % len
405
406    self._write()
407
408def repo_format(sbox):
409  """ Return the repository format number for SBOX."""
410
411  format_file = open(os.path.join(sbox.repo_dir, "db", "format"))
412  format = int(format_file.read()[:1])
413  format_file.close()
414
415  return format
416
417def set_changed_path_list(sbox, revision, changes):
418  """ Replace the changed paths list in the revision file REVISION in SBOX
419      with the text CHANGES."""
420
421  idx = None
422
423  # read full file
424  fp = open(fsfs_file(sbox.repo_dir, 'revs', str(revision)), 'r+b')
425  contents = fp.read()
426  length = len(contents)
427
428  if repo_format(sbox) < 7:
429    # replace the changed paths list
430    header = contents[contents.rfind(b'\n', length - 64, length - 1):]
431    body_len = int(header.split(b' ')[1])
432
433  else:
434    # read & parse revision file footer
435    footer_length = contents[length-1];
436    if isinstance(footer_length, str):
437      footer_length = ord(footer_length)
438
439    footer = contents[length - footer_length - 1:length-1]
440    l2p_offset = int(footer.split(b' ')[0])
441    l2p_checksum = footer.split(b' ')[1]
442    p2l_offset = int(footer.split(b' ')[2])
443    p2l_checksum = footer.split(b' ')[3]
444
445    idx = FSFS_Index(sbox, revision)
446    (offset, item_len, item_type) = idx.get_item(1)
447
448    # split file contents
449    body_len = offset
450    indexes = contents[l2p_offset:length - footer_length - 1]
451
452    # construct new footer, include indexes as are
453    file_len = body_len + len(changes) + 1
454    p2l_offset += file_len - l2p_offset
455
456    header = str(file_len).encode() + b' ' + l2p_checksum + b' ' \
457           + str(p2l_offset).encode() + b' ' + p2l_checksum
458    header += bytes([len(header)])
459    header = b'\n' + indexes + header
460
461  contents = contents[:body_len] + changes + header
462
463  # set new contents
464  fp.seek(0)
465  fp.write(contents)
466  fp.truncate()
467  fp.close()
468
469  if repo_format(sbox) >= 7:
470    idx.modify_item(1, offset, len(changes) + 1)
471
472######################################################################
473# Tests
474
475
476#----------------------------------------------------------------------
477
478# dump stream tests need a dump file
479
480def clean_dumpfile():
481  return \
482  [ b"SVN-fs-dump-format-version: 2\n\n",
483    b"UUID: 668cc64a-31ed-0310-8ccb-b75d75bb44e3\n\n",
484    b"Revision-number: 0\n",
485    b"Prop-content-length: 56\n",
486    b"Content-length: 56\n\n",
487    b"K 8\nsvn:date\nV 27\n2005-01-08T21:48:13.838745Z\nPROPS-END\n\n\n",
488    b"Revision-number: 1\n",
489    b"Prop-content-length: 98\n",
490    b"Content-length: 98\n\n",
491    b"K 7\nsvn:log\nV 0\n\nK 10\nsvn:author\nV 4\nerik\n",
492    b"K 8\nsvn:date\nV 27\n2005-01-08T21:51:16.313791Z\nPROPS-END\n\n\n",
493    b"Node-path: A\n",
494    b"Node-kind: file\n",
495    b"Node-action: add\n",
496    b"Prop-content-length: 35\n",
497    b"Text-content-length: 5\n",
498    b"Text-content-md5: e1cbb0c3879af8347246f12c559a86b5\n",
499    b"Content-length: 40\n\n",
500    b"K 12\nsvn:keywords\nV 2\nId\nPROPS-END\ntext\n\n\n"]
501
502dumpfile_revisions = \
503  [ svntest.wc.State('', { 'A' : svntest.wc.StateItem(contents="text\n") }) ]
504
505#----------------------------------------------------------------------
506def extra_headers(sbox):
507  "loading of dumpstream with extra headers"
508
509  sbox.build(empty=True)
510
511  dumpfile = clean_dumpfile()
512
513  dumpfile[3:3] = \
514       [ b"X-Comment-Header: Ignored header normally not in dump stream\n" ]
515
516  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, False, dumpfile,
517                             '--ignore-uuid')
518
519#----------------------------------------------------------------------
520# Ensure loading continues after skipping a bit of unknown extra content.
521def extra_blockcontent(sbox):
522  "load success on oversized Content-length"
523
524  sbox.build(empty=True)
525
526  dumpfile = clean_dumpfile()
527
528  # Replace "Content-length" line with two lines
529  dumpfile[8:9] = \
530       [ b"Extra-content-length: 10\n",
531         b"Content-length: 108\n\n" ]
532  # Insert the extra content after "PROPS-END\n"
533  dumpfile[11] = dumpfile[11][:-2] + b"extra text\n\n\n"
534
535  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, False, dumpfile,
536                             '--ignore-uuid')
537
538#----------------------------------------------------------------------
539def inconsistent_headers(sbox):
540  "load failure on undersized Content-length"
541
542  sbox.build(empty=True)
543
544  dumpfile = clean_dumpfile()
545
546  dumpfile[-2] = b"Content-length: 30\n\n"
547
548  load_and_verify_dumpstream(sbox, [], svntest.verify.AnyOutput,
549                             dumpfile_revisions, False, dumpfile)
550
551#----------------------------------------------------------------------
552# Test for issue #2729: Datestamp-less revisions in dump streams do
553# not remain so after load
554@Issue(2729)
555def empty_date(sbox):
556  "preserve date-less revisions in load"
557
558  sbox.build(empty=True)
559
560  dumpfile = clean_dumpfile()
561
562  # Replace portions of the revision data to drop the svn:date revprop.
563  dumpfile[7:11] = \
564       [ b"Prop-content-length: 52\n",
565         b"Content-length: 52\n\n",
566         b"K 7\nsvn:log\nV 0\n\nK 10\nsvn:author\nV 4\nerik\nPROPS-END\n\n\n"
567         ]
568
569  load_and_verify_dumpstream(sbox,[],[], dumpfile_revisions, False, dumpfile,
570                             '--ignore-uuid')
571
572  # Verify that the revision still lacks the svn:date property.
573  svntest.actions.run_and_verify_svn([], '.*(E195011|E200017).*svn:date',
574                                     "propget", "--revprop", "-r1", "svn:date",
575                                     sbox.wc_dir)
576
577#----------------------------------------------------------------------
578
579def dump_copied_dir(sbox):
580  "'svnadmin dump' on copied directory"
581
582  sbox.build()
583  wc_dir = sbox.wc_dir
584  repo_dir = sbox.repo_dir
585
586  old_C_path = os.path.join(wc_dir, 'A', 'C')
587  new_C_path = os.path.join(wc_dir, 'A', 'B', 'C')
588  svntest.main.run_svn(None, 'cp', old_C_path, new_C_path)
589  sbox.simple_commit(message='log msg')
590
591  exit_code, output, errput = svntest.main.run_svnadmin("dump", repo_dir)
592  if svntest.verify.compare_and_display_lines(
593    "Output of 'svnadmin dump' is unexpected.",
594    'STDERR', ["* Dumped revision 0.\n",
595               "* Dumped revision 1.\n",
596               "* Dumped revision 2.\n"], errput):
597    raise svntest.Failure
598
599#----------------------------------------------------------------------
600
601def dump_move_dir_modify_child(sbox):
602  "'svnadmin dump' on modified child of copied dir"
603
604  sbox.build()
605  wc_dir = sbox.wc_dir
606  repo_dir = sbox.repo_dir
607
608  B_path = os.path.join(wc_dir, 'A', 'B')
609  Q_path = os.path.join(wc_dir, 'A', 'Q')
610  svntest.main.run_svn(None, 'cp', B_path, Q_path)
611  svntest.main.file_append(os.path.join(Q_path, 'lambda'), 'hello')
612  sbox.simple_commit(message='log msg')
613  exit_code, output, errput = svntest.main.run_svnadmin("dump", repo_dir)
614  svntest.verify.compare_and_display_lines(
615    "Output of 'svnadmin dump' is unexpected.",
616    'STDERR', ["* Dumped revision 0.\n",
617               "* Dumped revision 1.\n",
618               "* Dumped revision 2.\n"], errput)
619
620  exit_code, output, errput = svntest.main.run_svnadmin("dump", "-r",
621                                                        "0:HEAD", repo_dir)
622  svntest.verify.compare_and_display_lines(
623    "Output of 'svnadmin dump' is unexpected.",
624    'STDERR', ["* Dumped revision 0.\n",
625               "* Dumped revision 1.\n",
626               "* Dumped revision 2.\n"], errput)
627
628#----------------------------------------------------------------------
629
630def dump_quiet(sbox):
631  "'svnadmin dump --quiet'"
632
633  sbox.build(create_wc = False)
634
635  exit_code, dump, errput = svntest.main.run_svnadmin("dump", sbox.repo_dir,
636                                                      '--quiet')
637  svntest.verify.compare_and_display_lines(
638    "Output of 'svnadmin dump --quiet' is unexpected.",
639    'STDERR', [], errput)
640
641#----------------------------------------------------------------------
642
643def hotcopy_dot(sbox):
644  "'svnadmin hotcopy PATH .'"
645  sbox.build()
646
647  backup_dir, backup_url = sbox.add_repo_path('backup')
648  os.mkdir(backup_dir)
649  cwd = os.getcwd()
650
651  os.chdir(backup_dir)
652  svntest.actions.run_and_verify_svnadmin(
653    None, [],
654    "hotcopy", os.path.join(cwd, sbox.repo_dir), '.')
655
656  os.chdir(cwd)
657
658  if svntest.main.is_fs_type_fsfs():
659    check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
660  if svntest.main.is_fs_type_bdb():
661    check_hotcopy_bdb(sbox.repo_dir, backup_dir)
662  if svntest.main.is_fs_type_fsx():
663    check_hotcopy_fsx(sbox.repo_dir, backup_dir)
664
665#----------------------------------------------------------------------
666
667# This test is redundant for FSFS. The hotcopy_dot and hotcopy_incremental
668# tests cover this check for FSFS already.
669@SkipUnless(svntest.main.is_fs_type_bdb)
670def hotcopy_format(sbox):
671  "'svnadmin hotcopy' checking db/format file"
672  sbox.build()
673
674  backup_dir, backup_url = sbox.add_repo_path('backup')
675  exit_code, output, errput = svntest.main.run_svnadmin("hotcopy",
676                                                        sbox.repo_dir,
677                                                        backup_dir)
678  if errput:
679    logger.warn("Error: hotcopy failed")
680    raise svntest.Failure
681
682  # verify that the db/format files are the same
683  fp = open(os.path.join(sbox.repo_dir, "db", "format"))
684  contents1 = fp.read()
685  fp.close()
686
687  fp2 = open(os.path.join(backup_dir, "db", "format"))
688  contents2 = fp2.read()
689  fp2.close()
690
691  if contents1 != contents2:
692    logger.warn("Error: db/format file contents do not match after hotcopy")
693    raise svntest.Failure
694
695#----------------------------------------------------------------------
696
697def setrevprop(sbox):
698  "setlog, setrevprop, delrevprop; bypass hooks"
699  sbox.build()
700
701  # Try a simple log property modification.
702  iota_path = os.path.join(sbox.wc_dir, "iota")
703  mu_path = sbox.ospath('A/mu')
704  svntest.actions.run_and_verify_svnadmin([], [],
705                                          "setlog", sbox.repo_dir, "-r0",
706                                          "--bypass-hooks",
707                                          iota_path)
708
709  # Make sure it fails without --bypass-hooks.  (We haven't called
710  # svntest.actions.enable_revprop_changes().)
711  #
712  # Note that we attempt to set the log message to a different value than the
713  # successful call.
714  svntest.actions.run_and_verify_svnadmin([], svntest.verify.AnyOutput,
715                                          "setlog", sbox.repo_dir, "-r0",
716                                          mu_path)
717
718  # Verify that the revprop value matches what we set when retrieved
719  # through the client.
720  svntest.actions.run_and_verify_svn([ "This is the file 'iota'.\n", "\n" ],
721                                     [], "propget", "--revprop", "-r0",
722                                     "svn:log", sbox.wc_dir)
723
724  # Try an author property modification.
725  foo_path = os.path.join(sbox.wc_dir, "foo")
726  svntest.main.file_write(foo_path, "foo")
727
728  exit_code, output, errput = svntest.main.run_svnadmin("setrevprop",
729                                                        sbox.repo_dir,
730                                                        "-r0", "svn:author",
731                                                        foo_path)
732  if errput:
733    logger.warn("Error: 'setrevprop' failed")
734    raise svntest.Failure
735
736  # Verify that the revprop value matches what we set when retrieved
737  # through the client.
738  svntest.actions.run_and_verify_svn([ "foo\n" ], [], "propget",
739                                     "--revprop", "-r0", "svn:author",
740                                     sbox.wc_dir)
741
742  # Delete the property.
743  svntest.actions.run_and_verify_svnadmin([], [],
744                                          "delrevprop", "-r0", sbox.repo_dir,
745                                          "svn:author")
746  svntest.actions.run_and_verify_svnlook([], ".*E200017.*svn:author.*",
747                                         "propget", "--revprop", "-r0",
748                                         sbox.repo_dir, "svn:author")
749
750def verify_windows_paths_in_repos(sbox):
751  "verify a repository containing paths like 'c:hi'"
752
753  # setup a repo with a directory 'c:hi'
754  sbox.build(create_wc = False)
755  repo_url       = sbox.repo_url
756  chi_url = sbox.repo_url + '/c:hi'
757
758  svntest.actions.run_and_verify_svn(None, [],
759                                     'mkdir', '-m', 'log_msg',
760                                     chi_url)
761
762  exit_code, output, errput = svntest.main.run_svnadmin("verify",
763                                                        sbox.repo_dir)
764  if errput:
765    raise SVNUnexpectedStderr(errput)
766
767  # unfortunately, some backends needs to do more checks than other
768  # resulting in different progress output
769  if svntest.main.is_fs_log_addressing():
770    svntest.verify.compare_and_display_lines(
771      "Error while running 'svnadmin verify'.",
772      'STDOUT', ["* Verifying metadata at revision 0 ...\n",
773                 "* Verifying repository metadata ...\n",
774                 "* Verified revision 0.\n",
775                 "* Verified revision 1.\n",
776                 "* Verified revision 2.\n"], output)
777  elif svntest.main.fs_has_rep_sharing() and not svntest.main.is_fs_type_bdb():
778    svntest.verify.compare_and_display_lines(
779      "Error while running 'svnadmin verify'.",
780      'STDOUT', ["* Verifying repository metadata ...\n",
781                 "* Verified revision 0.\n",
782                 "* Verified revision 1.\n",
783                 "* Verified revision 2.\n"], output)
784  else:
785    svntest.verify.compare_and_display_lines(
786      "Error while running 'svnadmin verify'.",
787      'STDOUT', ["* Verified revision 0.\n",
788                 "* Verified revision 1.\n",
789                 "* Verified revision 2.\n"], output)
790
791#----------------------------------------------------------------------
792
793# Returns the filename of the rev or revprop file (according to KIND)
794# numbered REV in REPO_DIR, which must be in the first shard if we're
795# using a sharded repository.
796def fsfs_file(repo_dir, kind, rev):
797  if svntest.main.options.server_minor_version >= 5:
798    if svntest.main.options.fsfs_sharding is None:
799      if svntest.main.is_fs_type_fsx():
800        rev = 'r' + rev
801      return os.path.join(repo_dir, 'db', kind, '0', rev)
802    else:
803      shard = int(rev) // svntest.main.options.fsfs_sharding
804      if svntest.main.is_fs_type_fsx():
805        rev = 'r' + rev
806      path = os.path.join(repo_dir, 'db', kind, str(shard), rev)
807
808      if svntest.main.options.fsfs_packing is None or kind == 'revprops':
809        # we don't pack revprops
810        return path
811      elif os.path.exists(path):
812        # rev exists outside a pack file.
813        return path
814      else:
815        # didn't find the plain file; assume it's in a pack file
816        return os.path.join(repo_dir, 'db', kind, ('%d.pack' % shard), 'pack')
817  else:
818    return os.path.join(repo_dir, 'db', kind, rev)
819
820
821@SkipUnless(svntest.main.is_fs_type_fsfs)
822def verify_incremental_fsfs(sbox):
823  """svnadmin verify detects corruption dump can't"""
824
825  if svntest.main.options.fsfs_version is not None and \
826     svntest.main.options.fsfs_version not in [4, 6]:
827    raise svntest.Skip("Unsupported prepackaged repository version")
828
829  # setup a repo with a directory 'c:hi'
830  # use physical addressing as this is hard to provoke with logical addressing
831  sbox.build(create_wc = False,
832             minor_version = min(svntest.main.options.server_minor_version,8))
833  repo_url = sbox.repo_url
834  E_url = sbox.repo_url + '/A/B/E'
835
836  # Create A/B/E/bravo in r2.
837  svntest.actions.run_and_verify_svn(None, [],
838                                     'mkdir', '-m', 'log_msg',
839                                     E_url + '/bravo')
840  # Corrupt r2's reference to A/C by replacing "dir 7-1.0.r1/1568" with
841  # "dir 7-1.0.r1/1569" (increment offset) and updating the checksum for
842  # this directory listing to "c9b5a2d26473a4e28088673dda9df804" so that
843  # the listing itself is valid.
844  r2 = fsfs_file(sbox.repo_dir, 'revs', '2')
845  if r2.endswith('pack'):
846    raise svntest.Skip("Test doesn't handle packed revisions")
847
848  fp = open(r2, 'wb')
849  fp.write(b"""id: 0-2.0.r2/0
850type: dir
851count: 0
852cpath: /A/B/E/bravo
853copyroot: 0 /
854
855PLAIN
856K 5
857alpha
858V 17
859file 3-1.0.r1/719
860K 4
861beta
862V 17
863file 4-1.0.r1/840
864K 5
865bravo
866V 14
867dir 0-2.0.r2/0
868END
869ENDREP
870id: 2-1.0.r2/181
871type: dir
872pred: 2-1.0.r1/1043
873count: 1
874text: 2 69 99 99 f63001f7fddd1842d8891474d0982111
875cpath: /A/B/E
876copyroot: 0 /
877
878PLAIN
879K 1
880E
881V 16
882dir 2-1.0.r2/181
883K 1
884F
885V 17
886dir 5-1.0.r1/1160
887K 6
888lambda
889V 17
890file 6-1.0.r1/597
891END
892ENDREP
893id: 1-1.0.r2/424
894type: dir
895pred: 1-1.0.r1/1335
896count: 1
897text: 2 316 95 95 bccb66379b4f825dac12b50d80211bae
898cpath: /A/B
899copyroot: 0 /
900
901PLAIN
902K 1
903B
904V 16
905dir 1-1.0.r2/424
906K 1
907C
908V 17
909dir 7-1.0.r1/1569
910K 1
911D
912V 17
913dir 8-1.0.r1/3061
914K 2
915mu
916V 18
917file i-1.0.r1/1451
918END
919ENDREP
920id: 0-1.0.r2/692
921type: dir
922pred: 0-1.0.r1/3312
923count: 1
924text: 2 558 121 121 c9b5a2d26473a4e28088673dda9df804
925cpath: /A
926copyroot: 0 /
927
928PLAIN
929K 1
930A
931V 16
932dir 0-1.0.r2/692
933K 4
934iota
935V 18
936file j-1.0.r1/3428
937END
938ENDREP
939id: 0.0.r2/904
940type: dir
941pred: 0.0.r1/3624
942count: 2
943text: 2 826 65 65 e44e4151d0d124533338619f082c8c9a
944cpath: /
945copyroot: 0 /
946
947_0.0.t1-1 add false false /A/B/E/bravo
948
949
950904 1031
951""")
952  fp.close()
953
954  exit_code, output, errput = svntest.main.run_svnadmin("verify", "-r2",
955                                                        sbox.repo_dir)
956  svntest.verify.verify_outputs(
957    message=None, actual_stdout=output, actual_stderr=errput,
958    expected_stdout=None,
959    expected_stderr=".*Found malformed header '[^']*' in revision file"
960                    "|.*Missing id field in node-rev.*")
961
962#----------------------------------------------------------------------
963
964# Helper for two test functions.
965def corrupt_and_recover_db_current(sbox, minor_version=None):
966  """Build up a MINOR_VERSION sandbox and test different recovery scenarios
967  with missing, out-of-date or even corrupt db/current files.  Recovery should
968  behave the same way with all values of MINOR_VERSION, hence this helper
969  containing the common code that allows us to check it."""
970
971  sbox.build(minor_version=minor_version)
972  current_path = os.path.join(sbox.repo_dir, 'db', 'current')
973
974  # Commit up to r3, so we can test various recovery scenarios.
975  svntest.main.file_append(os.path.join(sbox.wc_dir, 'iota'), 'newer line\n')
976  sbox.simple_commit(message='log msg')
977
978  svntest.main.file_append(os.path.join(sbox.wc_dir, 'iota'), 'newest line\n')
979  sbox.simple_commit(message='log msg')
980
981  # Remember the contents of the db/current file.
982  expected_current_contents = open(current_path).read()
983
984  # Move aside the current file for r3.
985  os.rename(os.path.join(sbox.repo_dir, 'db','current'),
986            os.path.join(sbox.repo_dir, 'db','was_current'))
987
988  # Run 'svnadmin recover' and check that the current file is recreated.
989  exit_code, output, errput = svntest.main.run_svnadmin("recover",
990                                                        sbox.repo_dir)
991  if errput:
992    raise SVNUnexpectedStderr(errput)
993
994  actual_current_contents = open(current_path).read()
995  svntest.verify.compare_and_display_lines(
996    "Contents of db/current is unexpected.",
997    'db/current', expected_current_contents, actual_current_contents)
998
999  # Now try writing db/current to be one rev lower than it should be.
1000  svntest.main.file_write(current_path, '2\n')
1001
1002  # Run 'svnadmin recover' and check that the current file is fixed.
1003  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1004                                                        sbox.repo_dir)
1005  if errput:
1006    raise SVNUnexpectedStderr(errput)
1007
1008  actual_current_contents = open(current_path).read()
1009  svntest.verify.compare_and_display_lines(
1010    "Contents of db/current is unexpected.",
1011    'db/current', expected_current_contents, actual_current_contents)
1012
1013  # Now try writing db/current to be *two* revs lower than it should be.
1014  svntest.main.file_write(current_path, '1\n')
1015
1016  # Run 'svnadmin recover' and check that the current file is fixed.
1017  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1018                                                        sbox.repo_dir)
1019  if errput:
1020    raise SVNUnexpectedStderr(errput)
1021
1022  actual_current_contents = open(current_path).read()
1023  svntest.verify.compare_and_display_lines(
1024    "Contents of db/current is unexpected.",
1025    'db/current', expected_current_contents, actual_current_contents)
1026
1027  # Now try writing db/current to be fish revs lower than it should be.
1028  #
1029  # Note: I'm not actually sure it's wise to recover from this, but
1030  # detecting it would require rewriting fs_fs.c:get_youngest() to
1031  # check the actual contents of its buffer, since atol() will happily
1032  # convert "fish" to 0.
1033  svntest.main.file_write(current_path, 'fish\n')
1034
1035  # Run 'svnadmin recover' and check that the current file is fixed.
1036  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1037                                                        sbox.repo_dir)
1038  if errput:
1039    raise SVNUnexpectedStderr(errput)
1040
1041  actual_current_contents = open(current_path).read()
1042  svntest.verify.compare_and_display_lines(
1043    "Contents of db/current is unexpected.",
1044    'db/current', expected_current_contents, actual_current_contents)
1045
1046
1047@SkipUnless(svntest.main.is_fs_type_fsfs)
1048def fsfs_recover_db_current(sbox):
1049  "fsfs recover db/current"
1050  corrupt_and_recover_db_current(sbox)
1051
1052
1053@SkipUnless(svntest.main.is_fs_type_fsfs)
1054def fsfs_recover_old_db_current(sbox):
1055  "fsfs recover db/current --compatible-version=1.3"
1056
1057  # Around trunk@1573728, 'svnadmin recover' wrongly errored out
1058  # for the --compatible-version=1.3 repositories with missing or
1059  # invalid db/current file:
1060  # svnadmin: E160006: No such revision 1
1061
1062  corrupt_and_recover_db_current(sbox, minor_version=3)
1063
1064#----------------------------------------------------------------------
1065@Issue(2983)
1066def load_with_parent_dir(sbox):
1067  "'svnadmin load --parent-dir' reparents mergeinfo"
1068
1069  ## See https://issues.apache.org/jira/browse/SVN-2983. ##
1070  sbox.build(empty=True)
1071
1072  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
1073                                   'svnadmin_tests_data',
1074                                   'mergeinfo_included.dump')
1075  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
1076
1077  # Create 'sample' dir in sbox.repo_url, and load the dump stream there.
1078  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
1079                                      'Committed revision 1.\n'],
1080                                     [], "mkdir", sbox.repo_url + "/sample",
1081                                     "-m", "Create sample dir")
1082  load_dumpstream(sbox, dumpfile, '--parent-dir', '/sample')
1083
1084  # Verify the svn:mergeinfo properties for '--parent-dir'
1085  svntest.actions.run_and_verify_svn([sbox.repo_url +
1086                                      "/sample/branch - /sample/trunk:5-7\n"],
1087                                     [], 'propget', 'svn:mergeinfo', '-R',
1088                                     sbox.repo_url + '/sample/branch')
1089  svntest.actions.run_and_verify_svn([sbox.repo_url +
1090                                      "/sample/branch1 - " +
1091                                      "/sample/branch:6-9\n"],
1092                                     [], 'propget', 'svn:mergeinfo', '-R',
1093                                     sbox.repo_url + '/sample/branch1')
1094
1095  # Create 'sample-2' dir in sbox.repo_url, and load the dump stream again.
1096  # This time, don't include a leading slash on the --parent-dir argument.
1097  # See issue #3547.
1098  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
1099                                      'Committed revision 11.\n'],
1100                                     [], "mkdir", sbox.repo_url + "/sample-2",
1101                                     "-m", "Create sample-2 dir")
1102  load_dumpstream(sbox, dumpfile, '--parent-dir', 'sample-2')
1103
1104  # Verify the svn:mergeinfo properties for '--parent-dir'.
1105  svntest.actions.run_and_verify_svn([sbox.repo_url +
1106                                      "/sample-2/branch - " +
1107                                      "/sample-2/trunk:15-17\n"],
1108                                     [], 'propget', 'svn:mergeinfo', '-R',
1109                                     sbox.repo_url + '/sample-2/branch')
1110  svntest.actions.run_and_verify_svn([sbox.repo_url +
1111                                      "/sample-2/branch1 - " +
1112                                      "/sample-2/branch:16-19\n"],
1113                                     [], 'propget', 'svn:mergeinfo', '-R',
1114                                     sbox.repo_url + '/sample-2/branch1')
1115
1116#----------------------------------------------------------------------
1117
1118def set_uuid(sbox):
1119  "test 'svnadmin setuuid'"
1120
1121  sbox.build(create_wc=False)
1122
1123  # Squirrel away the original repository UUID.
1124  exit_code, output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
1125  if errput:
1126    raise SVNUnexpectedStderr(errput)
1127  orig_uuid = output[0].rstrip()
1128
1129  # Try setting a new, bogus UUID.
1130  svntest.actions.run_and_verify_svnadmin(None, '^.*Malformed UUID.*$',
1131                                          'setuuid', sbox.repo_dir, 'abcdef')
1132
1133  # Try generating a brand new UUID.
1134  svntest.actions.run_and_verify_svnadmin([], None,
1135                                          'setuuid', sbox.repo_dir)
1136  exit_code, output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
1137  if errput:
1138    raise SVNUnexpectedStderr(errput)
1139  new_uuid = output[0].rstrip()
1140  if new_uuid == orig_uuid:
1141    logger.warn("Error: new UUID matches the original one")
1142    raise svntest.Failure
1143
1144  # Now, try setting the UUID back to the original value.
1145  svntest.actions.run_and_verify_svnadmin([], None,
1146                                          'setuuid', sbox.repo_dir, orig_uuid)
1147  exit_code, output, errput = svntest.main.run_svnlook('uuid', sbox.repo_dir)
1148  if errput:
1149    raise SVNUnexpectedStderr(errput)
1150  new_uuid = output[0].rstrip()
1151  if new_uuid != orig_uuid:
1152    logger.warn("Error: new UUID doesn't match the original one")
1153    raise svntest.Failure
1154
1155#----------------------------------------------------------------------
1156@Issue(3020)
1157def reflect_dropped_renumbered_revs(sbox):
1158  "reflect dropped renumbered revs in svn:mergeinfo"
1159
1160  ## See https://issues.apache.org/jira/browse/SVN-3020. ##
1161
1162  sbox.build(empty=True)
1163
1164  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
1165                                   'svndumpfilter_tests_data',
1166                                   'with_merges.dump')
1167  dumpfile = svntest.actions.load_dumpfile(dumpfile_location)
1168
1169  # Create 'toplevel' dir in sbox.repo_url
1170  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
1171                                            'Committed revision 1.\n'],
1172                                     [], "mkdir", sbox.repo_url + "/toplevel",
1173                                     "-m", "Create toplevel dir")
1174
1175  # Load the dump stream in sbox.repo_url
1176  load_dumpstream(sbox, dumpfile)
1177
1178  # Load the dump stream in toplevel dir
1179  load_dumpstream(sbox, dumpfile, '--parent-dir', '/toplevel')
1180
1181  # Verify the svn:mergeinfo properties
1182  url = sbox.repo_url
1183  expected_output = svntest.verify.UnorderedOutput([
1184    url + "/trunk - /branch1:5-9\n",
1185    url + "/toplevel/trunk - /toplevel/branch1:14-18\n",
1186    ])
1187  svntest.actions.run_and_verify_svn(expected_output, [],
1188                                     'propget', 'svn:mergeinfo', '-R',
1189                                     sbox.repo_url)
1190
1191#----------------------------------------------------------------------
1192
1193@SkipUnless(svntest.main.is_fs_type_fsfs)
1194@Issue(2992)
1195def fsfs_recover_handle_missing_revs_or_revprops_file(sbox):
1196  """fsfs recovery checks missing revs / revprops files"""
1197  # Set up a repository containing the greek tree.
1198  sbox.build()
1199
1200  # Commit up to r3, so we can test various recovery scenarios.
1201  svntest.main.file_append(os.path.join(sbox.wc_dir, 'iota'), 'newer line\n')
1202  sbox.simple_commit(message='log msg')
1203
1204  svntest.main.file_append(os.path.join(sbox.wc_dir, 'iota'), 'newest line\n')
1205  sbox.simple_commit(message='log msg')
1206
1207  rev_3 = fsfs_file(sbox.repo_dir, 'revs', '3')
1208  rev_was_3 = rev_3 + '.was'
1209
1210  # Move aside the revs file for r3.
1211  os.rename(rev_3, rev_was_3)
1212
1213  # Verify 'svnadmin recover' fails when youngest has a revprops
1214  # file but no revs file.
1215  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1216                                                        sbox.repo_dir)
1217
1218  if svntest.verify.verify_outputs(
1219    "Output of 'svnadmin recover' is unexpected.", None, errput, None,
1220    ".*Expected current rev to be <= %s but found 3"
1221    # For example, if svntest.main.fsfs_sharding == 2, then rev_3 would
1222    # be the pack file for r2:r3, and the error message would report "<= 1".
1223    % (rev_3.endswith('pack') and '[012]' or '2')):
1224    raise svntest.Failure
1225
1226  # Restore the r3 revs file, thus repairing the repository.
1227  os.rename(rev_was_3, rev_3)
1228
1229  revprop_3 = fsfs_file(sbox.repo_dir, 'revprops', '3')
1230  revprop_was_3 = revprop_3 + '.was'
1231
1232  # Move aside the revprops file for r3.
1233  os.rename(revprop_3, revprop_was_3)
1234
1235  # Verify 'svnadmin recover' fails when youngest has a revs file
1236  # but no revprops file (issue #2992).
1237  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1238                                                        sbox.repo_dir)
1239
1240  if svntest.verify.verify_outputs(
1241    "Output of 'svnadmin recover' is unexpected.", None, errput, None,
1242    ".*Revision 3 has a revs file but no revprops file"):
1243    raise svntest.Failure
1244
1245  # Restore the r3 revprops file, thus repairing the repository.
1246  os.rename(revprop_was_3, revprop_3)
1247
1248  # Change revprops file to a directory for revision 3
1249  os.rename(revprop_3, revprop_was_3)
1250  os.mkdir(revprop_3)
1251
1252  # Verify 'svnadmin recover' fails when youngest has a revs file
1253  # but revprops file is not a file (another aspect of issue #2992).
1254  exit_code, output, errput = svntest.main.run_svnadmin("recover",
1255                                                        sbox.repo_dir)
1256
1257  if svntest.verify.verify_outputs(
1258    "Output of 'svnadmin recover' is unexpected.", None, errput, None,
1259    ".*Revision 3 has a non-file where its revprops file should be.*"):
1260    raise svntest.Failure
1261
1262  # Restore the r3 revprops file, thus repairing the repository.
1263  os.rmdir(revprop_3)
1264  os.rename(revprop_was_3, revprop_3)
1265
1266
1267#----------------------------------------------------------------------
1268
1269@Skip(svntest.main.tests_use_prepackaged_repository)
1270def create_in_repo_subdir(sbox):
1271  "'svnadmin create /path/to/repo/subdir'"
1272
1273  sbox.build(create_wc=False, empty=True)
1274  repo_dir = sbox.repo_dir
1275
1276  success = False
1277  try:
1278    # This should fail
1279    subdir = os.path.join(repo_dir, 'Z')
1280    svntest.main.create_repos(subdir)
1281  except svntest.main.SVNRepositoryCreateFailure:
1282    success = True
1283  if not success:
1284    raise svntest.Failure
1285
1286  cwd = os.getcwd()
1287  success = False
1288  try:
1289    # This should fail, too
1290    subdir = os.path.join(repo_dir, 'conf')
1291    os.chdir(subdir)
1292    svntest.main.create_repos('Z')
1293    os.chdir(cwd)
1294  except svntest.main.SVNRepositoryCreateFailure:
1295    success = True
1296    os.chdir(cwd)
1297  if not success:
1298    raise svntest.Failure
1299
1300
1301@SkipUnless(svntest.main.is_fs_type_fsfs)
1302@SkipDumpLoadCrossCheck()
1303def verify_with_invalid_revprops(sbox):
1304  "svnadmin verify detects invalid revprops file"
1305
1306  sbox.build(create_wc=False, empty=True)
1307  repo_dir = sbox.repo_dir
1308
1309  # Run a test verify
1310  exit_code, output, errput = svntest.main.run_svnadmin("verify",
1311                                                        sbox.repo_dir)
1312
1313  if errput:
1314    raise SVNUnexpectedStderr(errput)
1315  if svntest.verify.verify_outputs(
1316    "Output of 'svnadmin verify' is unexpected.", None, output, None,
1317    ".*Verified revision 0*"):
1318    raise svntest.Failure
1319
1320  # Empty the revprops file
1321  rp_file = open(os.path.join(repo_dir, 'db', 'revprops', '0', '0'), 'w')
1322
1323  rp_file.write('')
1324  rp_file.close()
1325
1326  exit_code, output, errput = svntest.main.run_svnadmin("verify",
1327                                                        sbox.repo_dir)
1328
1329  if svntest.verify.verify_outputs(
1330    "Output of 'svnadmin verify' is unexpected.", None, errput, None,
1331    ".*svnadmin: E200002:.*"):
1332    raise svntest.Failure
1333
1334#----------------------------------------------------------------------
1335# Even *more* testing for issue #3020 'Reflect dropped/renumbered
1336# revisions in svn:mergeinfo data during svnadmin load'
1337#
1338# Full or incremental dump-load cycles should result in the same
1339# mergeinfo in the loaded repository.
1340#
1341# Given a repository 'SOURCE-REPOS' with mergeinfo, and a repository
1342# 'TARGET-REPOS' (which may or may not be empty), either of the following
1343# methods to move 'SOURCE-REPOS' to 'TARGET-REPOS' should result in
1344# the same mergeinfo on 'TARGET-REPOS':
1345#
1346#   1) Dump -r1:HEAD from 'SOURCE-REPOS' and load it in one shot to
1347#      'TARGET-REPOS'.
1348#
1349#   2) Dump 'SOURCE-REPOS' in a series of incremental dumps and load
1350#      each of them to 'TARGET-REPOS'.
1351#
1352# See https://issues.apache.org/jira/browse/SVN-3020#desc13
1353@Issue(3020)
1354def dont_drop_valid_mergeinfo_during_incremental_loads(sbox):
1355  "don't filter mergeinfo revs from incremental dump"
1356
1357  # Create an empty repos.
1358  sbox.build(empty=True)
1359
1360  # PART 1: Load a full dump to an empty repository.
1361  #
1362  # The test repository used here, 'mergeinfo_included_full.dump', is
1363  # this repos:
1364  #                       __________________________________________
1365  #                      |                                         |
1366  #                      |             ____________________________|_____
1367  #                      |            |                            |     |
1368  # trunk---r2---r3-----r5---r6-------r8---r9--------------->      |     |
1369  #   r1             |        |     |       |                      |     |
1370  # initial          |        |     |       |______                |     |
1371  # import         copy       |   copy             |            merge   merge
1372  #                  |        |     |            merge           (r5)   (r8)
1373  #                  |        |     |            (r9)              |     |
1374  #                  |        |     |              |               |     |
1375  #                  |        |     V              V               |     |
1376  #                  |        | branches/B2-------r11---r12---->   |     |
1377  #                  |        |     r7              |____|         |     |
1378  #                  |        |                        |           |     |
1379  #                  |      merge                      |___        |     |
1380  #                  |      (r6)                           |       |     |
1381  #                  |        |_________________           |       |     |
1382  #                  |                          |        merge     |     |
1383  #                  |                          |      (r11-12)    |     |
1384  #                  |                          |          |       |     |
1385  #                  V                          V          V       |     |
1386  #              branches/B1-------------------r10--------r13-->   |     |
1387  #                  r4                                            |     |
1388  #                   |                                            V     V
1389  #                  branches/B1/B/E------------------------------r14---r15->
1390  #
1391  #
1392  # The mergeinfo on this repos@15 is:
1393  #
1394  #   Properties on 'branches/B1':
1395  #     svn:mergeinfo
1396  #       /branches/B2:11-12
1397  #       /trunk:6,9
1398  #   Properties on 'branches/B1/B/E':
1399  #     svn:mergeinfo
1400  #       /branches/B2/B/E:11-12
1401  #       /trunk/B/E:5-6,8-9
1402  #   Properties on 'branches/B2':
1403  #     svn:mergeinfo
1404  #       /trunk:9
1405  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
1406                                   'svnadmin_tests_data',
1407                                   'mergeinfo_included_full.dump')
1408  dumpfile_full = svntest.actions.load_dumpfile(dumpfile_location)
1409  load_dumpstream(sbox, dumpfile_full, '--ignore-uuid')
1410
1411  # Check that the mergeinfo is as expected.
1412  url = sbox.repo_url + '/branches/'
1413  expected_output = svntest.verify.UnorderedOutput([
1414    url + "B1 - /branches/B2:11-12\n",
1415    "/trunk:6,9\n",
1416    url + "B2 - /trunk:9\n",
1417    url + "B1/B/E - /branches/B2/B/E:11-12\n",
1418    "/trunk/B/E:5-6,8-9\n"])
1419  svntest.actions.run_and_verify_svn(expected_output, [],
1420                                     'propget', 'svn:mergeinfo', '-R',
1421                                     sbox.repo_url)
1422
1423  # PART 2: Load a a series of incremental dumps to an empty repository.
1424  #
1425  # Incrementally dump the repository into three dump files:
1426  dump_file_r1_10 = sbox.get_tempname("r1-10-dump")
1427  exit_code, output, errput = svntest.main.run_svnadmin(
1428    'dump', sbox.repo_dir, '-r1:10')
1429  dump_fp = open(dump_file_r1_10, 'wb')
1430  dump_fp.writelines(output)
1431  dump_fp.close()
1432
1433  dump_file_r11_13 = sbox.get_tempname("r11-13-dump")
1434  exit_code, output, errput = svntest.main.run_svnadmin(
1435    'dump', sbox.repo_dir, '--incremental', '-r11:13')
1436  dump_fp = open(dump_file_r11_13, 'wb')
1437  dump_fp.writelines(output)
1438  dump_fp.close()
1439
1440  dump_file_r14_15 = sbox.get_tempname("r14-15-dump")
1441  exit_code, output, errput = svntest.main.run_svnadmin(
1442    'dump', sbox.repo_dir, '--incremental', '-r14:15')
1443  dump_fp = open(dump_file_r14_15, 'wb')
1444  dump_fp.writelines(output)
1445  dump_fp.close()
1446
1447  # Blow away the current repos and create an empty one in its place.
1448  sbox.build(empty=True)
1449
1450  # Load the three incremental dump files in sequence.
1451  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r1_10),
1452                  '--ignore-uuid')
1453  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r11_13),
1454                  '--ignore-uuid')
1455  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r14_15),
1456                  '--ignore-uuid')
1457
1458  # Check the mergeinfo, we use the same expected output as before,
1459  # as it (duh!) should be exactly the same as when we loaded the
1460  # repos in one shot.
1461  svntest.actions.run_and_verify_svn(expected_output, [],
1462                                     'propget', 'svn:mergeinfo', '-R',
1463                                     sbox.repo_url)
1464
1465  # Now repeat the above two scenarios, but with an initially non-empty target
1466  # repository.  First, try the full dump-load in one shot.
1467  #
1468  # PART 3: Load a full dump to an non-empty repository.
1469  #
1470  # Reset our sandbox.
1471  sbox.build(empty=True)
1472
1473  # Load this skeleton repos into the empty target:
1474  #
1475  #   Projects/       (Added r1)
1476  #     README        (Added r2)
1477  #     Project-X     (Added r3)
1478  #     Project-Y     (Added r4)
1479  #     Project-Z     (Added r5)
1480  #     docs/         (Added r6)
1481  #       README      (Added r6)
1482  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
1483                                                   'svnadmin_tests_data',
1484                                                   'skeleton_repos.dump')
1485  dumpfile_skeleton = svntest.actions.load_dumpfile(dumpfile_location)
1486  load_dumpstream(sbox, dumpfile_skeleton, '--ignore-uuid')
1487
1488  # Load 'svnadmin_tests_data/mergeinfo_included_full.dump' in one shot:
1489  load_dumpstream(sbox, dumpfile_full, '--parent-dir', 'Projects/Project-X',
1490                  '--ignore-uuid')
1491
1492  # Check that the mergeinfo is as expected.  This is exactly the
1493  # same expected mergeinfo we previously checked, except that the
1494  # revisions are all offset +6 to reflect the revions already in
1495  # the skeleton target before we began loading and the leading source
1496  # paths are adjusted by the --parent-dir:
1497  #
1498  #   Properties on 'branches/B1':
1499  #     svn:mergeinfo
1500  #       /Projects/Project-X/branches/B2:17-18
1501  #       /Projects/Project-X/trunk:12,15
1502  #   Properties on 'branches/B1/B/E':
1503  #     svn:mergeinfo
1504  #       /Projects/Project-X/branches/B2/B/E:17-18
1505  #       /Projects/Project-X/trunk/B/E:11-12,14-15
1506  #   Properties on 'branches/B2':
1507  #     svn:mergeinfo
1508  #       /Projects/Project-X/trunk:15
1509  url = sbox.repo_url + '/Projects/Project-X/branches/'
1510  expected_output = svntest.verify.UnorderedOutput([
1511    url + "B1 - /Projects/Project-X/branches/B2:17-18\n",
1512    "/Projects/Project-X/trunk:12,15\n",
1513    url + "B2 - /Projects/Project-X/trunk:15\n",
1514    url + "B1/B/E - /Projects/Project-X/branches/B2/B/E:17-18\n",
1515    "/Projects/Project-X/trunk/B/E:11-12,14-15\n"])
1516  svntest.actions.run_and_verify_svn(expected_output, [],
1517                                     'propget', 'svn:mergeinfo', '-R',
1518                                     sbox.repo_url)
1519
1520  # PART 4: Load a a series of incremental dumps to an non-empty repository.
1521  #
1522  # Reset our sandbox.
1523  sbox.build(empty=True)
1524
1525  # Load this skeleton repos into the empty target:
1526  load_dumpstream(sbox, dumpfile_skeleton, '--ignore-uuid')
1527
1528  # Load the three incremental dump files in sequence.
1529  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r1_10),
1530                  '--parent-dir', 'Projects/Project-X', '--ignore-uuid')
1531  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r11_13),
1532                  '--parent-dir', 'Projects/Project-X', '--ignore-uuid')
1533  load_dumpstream(sbox, svntest.actions.load_dumpfile(dump_file_r14_15),
1534                  '--parent-dir', 'Projects/Project-X', '--ignore-uuid')
1535
1536  # Check the resulting mergeinfo.  We expect the exact same results
1537  # as Part 3.
1538  # See https://issues.apache.org/jira/browse/SVN-3020#desc16.
1539  svntest.actions.run_and_verify_svn(expected_output, [],
1540                                     'propget', 'svn:mergeinfo', '-R',
1541                                     sbox.repo_url)
1542
1543
1544@SkipUnless(svntest.main.is_posix_os)
1545@Issue(2591)
1546def hotcopy_symlink(sbox):
1547  "'svnadmin hotcopy' replicates symlink"
1548
1549  ## See https://issues.apache.org/jira/browse/SVN-2591. ##
1550
1551  # Create a repository.
1552  sbox.build(create_wc=False, empty=True)
1553  original_repo = sbox.repo_dir
1554
1555  hotcopy_repo, hotcopy_url = sbox.add_repo_path('hotcopy')
1556
1557  # Create a file, a dir and a missing path outside the repoitory.
1558  svntest.main.safe_rmtree(sbox.wc_dir, 1)
1559  os.mkdir(sbox.wc_dir)
1560  external_file_path = os.path.join(sbox.wc_dir, "file")
1561  svntest.main.file_write(external_file_path, "An existing file")
1562  external_dir_path = os.path.join(sbox.wc_dir, "dir")
1563  os.mkdir(external_dir_path)
1564  external_missing_path = os.path.join(sbox.wc_dir, "missing")
1565
1566  # Symlink definitions: base name -> target relpath.
1567  # Check both existing and nonexistent targets.
1568  # Check targets both within and outside the source repository.
1569  symlinks = [
1570    ('in_repos_file',    'format'),
1571    ('in_repos_dir',     'conf'),
1572    ('in_repos_missing', 'missing'),
1573    ('external_file',    os.path.join('..', '..', '..', external_file_path)),
1574    ('external_dir',     os.path.join('..', '..', '..', external_dir_path)),
1575    ('external_missing', os.path.join('..', '..', '..', external_missing_path)),
1576  ]
1577
1578  # Create symlinks within the repository directory.
1579  for name, target_relpath in symlinks:
1580    target_path = os.path.join(original_repo, target_relpath)
1581    target_abspath = os.path.abspath(target_path)
1582
1583    # Create two symlinks to each target - one relative, one absolute.
1584    symlink_path = os.path.join(original_repo, name)
1585    os.symlink(target_relpath, symlink_path + '_rel')
1586    os.symlink(target_abspath, symlink_path + '_abs')
1587
1588  svntest.actions.run_and_verify_svnadmin(
1589    None, [],
1590    "hotcopy", original_repo, hotcopy_repo)
1591
1592  # Check if the symlinks were copied correctly.
1593  for name, target_relpath in symlinks:
1594    target_path = os.path.join(original_repo, target_relpath)
1595    target_abspath = os.path.abspath(target_path)
1596
1597    # Check two symlinks to each target - one relative, one absolute.
1598    symlink_path = os.path.join(hotcopy_repo, name)
1599    if os.readlink(symlink_path + '_rel') != target_relpath:
1600      raise svntest.Failure
1601    if os.readlink(symlink_path + '_abs') != target_abspath:
1602      raise svntest.Failure
1603
1604def load_bad_props(sbox):
1605  "svnadmin load with invalid svn: props"
1606
1607  dump_str = b"""SVN-fs-dump-format-version: 2
1608
1609UUID: dc40867b-38f6-0310-9f5f-f81aa277e06f
1610
1611Revision-number: 0
1612Prop-content-length: 56
1613Content-length: 56
1614
1615K 8
1616svn:date
1617V 27
16182005-05-03T19:09:41.129900Z
1619PROPS-END
1620
1621Revision-number: 1
1622Prop-content-length: 99
1623Content-length: 99
1624
1625K 7
1626svn:log
1627V 3
1628\n\r\n
1629K 10
1630svn:author
1631V 2
1632pl
1633K 8
1634svn:date
1635V 27
16362005-05-03T19:10:19.975578Z
1637PROPS-END
1638
1639Node-path: file
1640Node-kind: file
1641Node-action: add
1642Prop-content-length: 10
1643Text-content-length: 5
1644Text-content-md5: e1cbb0c3879af8347246f12c559a86b5
1645Content-length: 15
1646
1647PROPS-END
1648text
1649
1650
1651"""
1652  sbox.build(empty=True)
1653
1654  # Try to load the dumpstream, expecting a failure (because of mixed EOLs).
1655  exp_err = svntest.verify.RegexListOutput(['svnadmin: E125005:.*',
1656                                            'svnadmin: E125005:.*',
1657                                            'svnadmin: E125017:.*'],
1658                                           match_all=False)
1659  load_and_verify_dumpstream(sbox, [], exp_err, dumpfile_revisions,
1660                             False, dump_str, '--ignore-uuid')
1661
1662  # Now try it again bypassing prop validation.  (This interface takes
1663  # care of the removal and recreation of the original repository.)
1664  svntest.actions.load_repo(sbox, dump_str=dump_str,
1665                            bypass_prop_validation=True)
1666  # Getting the property should fail.
1667  svntest.actions.run_and_verify_svn(None, 'svn: E135000: ',
1668                                     'pg', 'svn:log', '--revprop', '-r1',
1669                                     sbox.repo_url)
1670
1671  # Now try it again with prop normalization.
1672  svntest.actions.load_repo(sbox, dump_str=dump_str,
1673                            bypass_prop_validation=False,
1674                            normalize_props=True)
1675  # We should get the expected property value.
1676  exit_code, output, _ = svntest.main.run_svn(None, 'pg', 'svn:log',
1677                                              '--revprop', '-r1',
1678                                              '--no-newline',
1679                                              sbox.repo_url)
1680  svntest.verify.verify_exit_code(None, exit_code, 0)
1681  if output != ['\n', '\n']:
1682    raise svntest.Failure("Unexpected property value %s" % output)
1683
1684# This test intentionally corrupts a revision and assumes an FSFS
1685# repository. If you can make it work with BDB please do so.
1686# However, the verification triggered by this test is in the repos layer
1687# so it will trigger with either backend anyway.
1688@SkipUnless(svntest.main.is_fs_type_fsfs)
1689@SkipUnless(svntest.main.server_enforces_UTF8_fspaths_in_verify)
1690def verify_non_utf8_paths(sbox):
1691  "svnadmin verify with non-UTF-8 paths"
1692
1693  if svntest.main.options.fsfs_version is not None and \
1694     svntest.main.options.fsfs_version not in [4, 6]:
1695    raise svntest.Skip("Unsupported prepackaged repository version")
1696
1697  dumpfile = clean_dumpfile()
1698
1699  # Corruption only possible in physically addressed revisions created
1700  # with pre-1.6 servers.
1701  sbox.build(empty=True,
1702             minor_version=min(svntest.main.options.server_minor_version,8))
1703
1704  # Load the dumpstream
1705  load_and_verify_dumpstream(sbox, [], [], dumpfile_revisions, False,
1706                             dumpfile, '--ignore-uuid')
1707
1708  # Replace the path 'A' in revision 1 with a non-UTF-8 sequence.
1709  # This has been observed in repositories in the wild, though Subversion
1710  # 1.6 and greater should prevent such filenames from entering the repository.
1711  path1 = os.path.join(sbox.repo_dir, "db", "revs", "0", "1")
1712  path_new = os.path.join(sbox.repo_dir, "db", "revs", "0", "1.new")
1713  fp1 = open(path1, 'rb')
1714  fp_new = open(path_new, 'wb')
1715  for line in fp1.readlines():
1716    if line == b"A\n":
1717      # replace 'A' with a latin1 character -- the new path is not valid UTF-8
1718      fp_new.write(b"\xE6\n")
1719    elif line == b"text: 1 340 32 32 a6be7b4cf075fd39e6a99eb69a31232b\n":
1720      # phys, PLAIN directories: fix up the representation checksum
1721      fp_new.write(b"text: 1 340 32 32 f2e93e73272cac0f18fccf16f224eb93\n")
1722    elif line == b"text: 1 340 44 32 a6be7b4cf075fd39e6a99eb69a31232b\n":
1723      # phys, deltified directories: fix up the representation checksum
1724      fp_new.write(b"text: 1 340 44 32 f2e93e73272cac0f18fccf16f224eb93\n")
1725    elif line == b"cpath: /A\n":
1726      # also fix up the 'created path' field
1727      fp_new.write(b"cpath: /\xE6\n")
1728    elif line == b"_0.0.t0-0 add-file true true /A\n":
1729      # and another occurrance
1730      fp_new.write(b"_0.0.t0-0 add-file true true /\xE6\n")
1731    else:
1732      fp_new.write(line)
1733  fp1.close()
1734  fp_new.close()
1735  os.remove(path1)
1736  os.rename(path_new, path1)
1737
1738  # Verify the repository, expecting failure
1739  exit_code, output, errput = svntest.main.run_svnadmin("verify",
1740                                                        sbox.repo_dir)
1741  svntest.verify.verify_outputs(
1742    "Unexpected error while running 'svnadmin verify'.",
1743    [], errput, None, ".*Path '.*' is not in UTF-8.*")
1744
1745  # Make sure the repository can still be dumped so that the
1746  # encoding problem can be fixed in a dump/edit/load cycle.
1747  expected_stderr = [
1748    "* Dumped revision 0.\n",
1749    "WARNING 0x0002: E160005: "
1750      "While validating fspath '?\\E6': "
1751      "Path '?\\E6' is not in UTF-8"
1752      "\n",
1753    "* Dumped revision 1.\n",
1754    ]
1755  exit_code, output, errput = svntest.main.run_svnadmin("dump", sbox.repo_dir)
1756  if svntest.verify.compare_and_display_lines(
1757    "Output of 'svnadmin dump' is unexpected.",
1758    'STDERR', expected_stderr, errput):
1759    raise svntest.Failure
1760
1761def test_lslocks_and_rmlocks(sbox):
1762  "test 'svnadmin lslocks' and 'svnadmin rmlocks'"
1763
1764  sbox.build(create_wc=False)
1765  iota_url = sbox.repo_url + '/iota'
1766  lambda_url = sbox.repo_url + '/A/B/lambda'
1767
1768  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
1769                                                        sbox.repo_dir)
1770
1771  if exit_code or errput or output:
1772    raise svntest.Failure("Error: 'lslocks' failed")
1773
1774  expected_output = svntest.verify.UnorderedRegexListOutput(
1775    ["'.*lambda' locked by user 'jrandom'.\n",
1776     "'.*iota' locked by user 'jrandom'.\n"])
1777
1778  # Lock iota and A/B/lambda using svn client
1779  svntest.actions.run_and_verify_svn(expected_output,
1780                                     [], "lock", "-m", "Locking files",
1781                                     iota_url, lambda_url)
1782
1783  def expected_output_list(path):
1784    return [
1785      "Path: " + path,
1786      "UUID Token: opaquelocktoken:.*",
1787      "Owner: jrandom",
1788      "Created:.*",
1789      "Expires:.*",
1790      "Comment \(1 line\):",
1791      "Locking files",
1792      "\n", # empty line
1793      ]
1794
1795  # List all locks
1796  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
1797                                                        sbox.repo_dir)
1798  if errput:
1799    raise SVNUnexpectedStderr(errput)
1800  svntest.verify.verify_exit_code(None, exit_code, 0)
1801
1802  expected_output = svntest.verify.UnorderedRegexListOutput(
1803                                     expected_output_list('/A/B/lambda') +
1804                                     expected_output_list('/iota'))
1805  svntest.verify.compare_and_display_lines('lslocks output mismatch',
1806                                           'output',
1807                                           expected_output, output)
1808
1809  # List lock in path /A
1810  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
1811                                                        sbox.repo_dir,
1812                                                        "A")
1813  if errput:
1814    raise SVNUnexpectedStderr(errput)
1815
1816  expected_output = svntest.verify.RegexListOutput(
1817                                     expected_output_list('/A/B/lambda'))
1818  svntest.verify.compare_and_display_lines('lslocks output mismatch',
1819                                           'output',
1820                                           expected_output, output)
1821  svntest.verify.verify_exit_code(None, exit_code, 0)
1822
1823  # Remove locks
1824  exit_code, output, errput = svntest.main.run_svnadmin("rmlocks",
1825                                                        sbox.repo_dir,
1826                                                        "iota",
1827                                                        "A/B/lambda")
1828  expected_output = UnorderedOutput(["Removed lock on '/iota'.\n",
1829                                     "Removed lock on '/A/B/lambda'.\n"])
1830
1831  svntest.verify.verify_outputs(
1832    "Unexpected output while running 'svnadmin rmlocks'.",
1833    output, [], expected_output, None)
1834
1835#----------------------------------------------------------------------
1836@Issue(3734)
1837def load_ranges(sbox):
1838  "'svnadmin load --revision X:Y'"
1839
1840  ## See https://issues.apache.org/jira/browse/SVN-3734. ##
1841  sbox.build(empty=True)
1842
1843  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
1844                                   'svnadmin_tests_data',
1845                                   'skeleton_repos.dump')
1846  dumplines = svntest.actions.load_dumpfile(dumpfile_location)
1847
1848  # Load our dumpfile, 2 revisions at a time, verifying that we have
1849  # the correct youngest revision after each load.
1850  load_dumpstream(sbox, dumplines, '-r0:2')
1851  svntest.actions.run_and_verify_svnlook(['2\n'],
1852                                         None, 'youngest', sbox.repo_dir)
1853  load_dumpstream(sbox, dumplines, '-r3:4')
1854  svntest.actions.run_and_verify_svnlook(['4\n'],
1855                                         None, 'youngest', sbox.repo_dir)
1856  load_dumpstream(sbox, dumplines, '-r5:6')
1857  svntest.actions.run_and_verify_svnlook(['6\n'],
1858                                         None, 'youngest', sbox.repo_dir)
1859
1860  # There are ordering differences in the property blocks.
1861  if (svntest.main.options.server_minor_version < 6):
1862    temp = []
1863
1864    for line in dumplines:
1865      if not "Text-content-sha1:" in line:
1866        temp.append(line)
1867
1868    expected_dump = UnorderedOutput(temp)
1869  else:
1870    expected_dump = UnorderedOutput(dumplines)
1871
1872  new_dumpdata = svntest.actions.run_and_verify_dump(sbox.repo_dir)
1873  svntest.verify.compare_and_display_lines("Dump files", "DUMP",
1874                                           expected_dump, new_dumpdata)
1875
1876@SkipUnless(svntest.main.is_fs_type_fsfs)
1877def hotcopy_incremental(sbox):
1878  "'svnadmin hotcopy --incremental PATH .'"
1879  sbox.build()
1880
1881  backup_dir, backup_url = sbox.add_repo_path('backup')
1882  os.mkdir(backup_dir)
1883  cwd = os.getcwd()
1884
1885  for i in [1, 2, 3]:
1886    os.chdir(backup_dir)
1887    svntest.actions.run_and_verify_svnadmin(
1888      None, [],
1889      "hotcopy", "--incremental", os.path.join(cwd, sbox.repo_dir), '.')
1890
1891    os.chdir(cwd)
1892
1893    check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
1894
1895    if i < 3:
1896      sbox.simple_mkdir("newdir-%i" % i)
1897      sbox.simple_commit()
1898
1899@SkipUnless(svntest.main.is_fs_type_fsfs)
1900@SkipUnless(svntest.main.fs_has_pack)
1901def hotcopy_incremental_packed(sbox):
1902  "'svnadmin hotcopy --incremental' with packing"
1903
1904  # Configure two files per shard to trigger packing.
1905  sbox.build()
1906  patch_format(sbox.repo_dir, shard_size=2)
1907
1908  backup_dir, backup_url = sbox.add_repo_path('backup')
1909  os.mkdir(backup_dir)
1910  cwd = os.getcwd()
1911
1912  # Pack revisions 0 and 1 if not already packed.
1913  if not (svntest.main.is_fs_type_fsfs and svntest.main.options.fsfs_packing
1914          and svntest.main.options.fsfs_sharding == 2):
1915    svntest.actions.run_and_verify_svnadmin(
1916      ['Packing revisions in shard 0...done.\n'], [], "pack",
1917      os.path.join(cwd, sbox.repo_dir))
1918
1919  # Commit 5 more revs, hotcopy and pack after each commit.
1920  for i in [1, 2, 3, 4, 5]:
1921    os.chdir(backup_dir)
1922    svntest.actions.run_and_verify_svnadmin(
1923      None, [],
1924      "hotcopy", "--incremental", os.path.join(cwd, sbox.repo_dir), '.')
1925
1926    os.chdir(cwd)
1927
1928    check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
1929
1930    if i < 5:
1931      sbox.simple_mkdir("newdir-%i" % i)
1932      sbox.simple_commit()
1933      if (svntest.main.is_fs_type_fsfs and not svntest.main.options.fsfs_packing
1934          and not i % 2):
1935        expected_output = ['Packing revisions in shard %d...done.\n' % (i/2)]
1936      else:
1937        expected_output = []
1938      svntest.actions.run_and_verify_svnadmin(
1939        expected_output, [], "pack", os.path.join(cwd, sbox.repo_dir))
1940
1941
1942def locking(sbox):
1943  "svnadmin lock tests"
1944  sbox.build(create_wc=False)
1945
1946  comment_path = os.path.join(svntest.main.temp_dir, "comment")
1947  svntest.main.file_write(comment_path, "dummy comment")
1948
1949  invalid_comment_path = os.path.join(svntest.main.temp_dir, "invalid_comment")
1950  svntest.main.file_write(invalid_comment_path, "character  is invalid")
1951
1952  # Test illegal character in comment file.
1953  expected_error = ".*svnadmin: E130004:.*"
1954  svntest.actions.run_and_verify_svnadmin(None,
1955                                          expected_error, "lock",
1956                                          sbox.repo_dir,
1957                                          "iota", "jrandom",
1958                                          invalid_comment_path)
1959
1960  # Test locking path with --bypass-hooks
1961  expected_output = "'/iota' locked by user 'jrandom'."
1962  svntest.actions.run_and_verify_svnadmin(expected_output,
1963                                          None, "lock",
1964                                          sbox.repo_dir,
1965                                          "iota", "jrandom",
1966                                          comment_path,
1967                                          "--bypass-hooks")
1968
1969  # Remove lock
1970  svntest.actions.run_and_verify_svnadmin(None,
1971                                          None, "rmlocks",
1972                                          sbox.repo_dir, "iota")
1973
1974  # Test locking path without --bypass-hooks
1975  expected_output = "'/iota' locked by user 'jrandom'."
1976  svntest.actions.run_and_verify_svnadmin(expected_output,
1977                                          None, "lock",
1978                                          sbox.repo_dir,
1979                                          "iota", "jrandom",
1980                                          comment_path)
1981
1982  # Test locking already locked path.
1983  expected_error = ".*svnadmin: E160035:.*"
1984  svntest.actions.run_and_verify_svnadmin(None,
1985                                          expected_error, "lock",
1986                                          sbox.repo_dir,
1987                                          "iota", "jrandom",
1988                                          comment_path)
1989
1990  # Test locking non-existent path.
1991  expected_error = ".*svnadmin: E160013:.*"
1992  svntest.actions.run_and_verify_svnadmin(None,
1993                                          expected_error, "lock",
1994                                          sbox.repo_dir,
1995                                          "non-existent", "jrandom",
1996                                          comment_path)
1997
1998  # Test locking a path while specifying a lock token.
1999  expected_output = "'/A/D/G/rho' locked by user 'jrandom'."
2000  lock_token = "opaquelocktoken:01234567-89ab-cdef-89ab-cdef01234567"
2001  svntest.actions.run_and_verify_svnadmin(expected_output,
2002                                          None, "lock",
2003                                          sbox.repo_dir,
2004                                          "A/D/G/rho", "jrandom",
2005                                          comment_path, lock_token)
2006
2007  # Test unlocking a path, but provide the wrong lock token.
2008  expected_error = ".*svnadmin: E160040:.*"
2009  wrong_lock_token = "opaquelocktoken:12345670-9ab8-defc-9ab8-def01234567c"
2010  svntest.actions.run_and_verify_svnadmin(None,
2011                                          expected_error, "unlock",
2012                                          sbox.repo_dir,
2013                                          "A/D/G/rho", "jrandom",
2014                                          wrong_lock_token)
2015
2016  # Test unlocking the path again, but this time provide the correct
2017  # lock token.
2018  expected_output = "'/A/D/G/rho' unlocked by user 'jrandom'."
2019  svntest.actions.run_and_verify_svnadmin(expected_output,
2020                                          None, "unlock",
2021                                          sbox.repo_dir,
2022                                          "A/D/G/rho", "jrandom",
2023                                          lock_token)
2024
2025  # Install lock/unlock prevention hooks.
2026  hook_path = svntest.main.get_pre_lock_hook_path(sbox.repo_dir)
2027  svntest.main.create_python_hook_script(hook_path, 'import sys; sys.exit(1)')
2028  hook_path = svntest.main.get_pre_unlock_hook_path(sbox.repo_dir)
2029  svntest.main.create_python_hook_script(hook_path, 'import sys; sys.exit(1)')
2030
2031  # Test locking a path.  Don't use --bypass-hooks, though, as we wish
2032  # to verify that hook script is really getting executed.
2033  expected_error = ".*svnadmin: E165001:.*"
2034  svntest.actions.run_and_verify_svnadmin(None,
2035                                          expected_error, "lock",
2036                                          sbox.repo_dir,
2037                                          "iota", "jrandom",
2038                                          comment_path)
2039
2040  # Fetch the lock token for our remaining locked path.  (We didn't
2041  # explicitly set it, so it will vary from test run to test run.)
2042  exit_code, output, errput = svntest.main.run_svnadmin("lslocks",
2043                                                        sbox.repo_dir,
2044                                                        "iota")
2045  iota_token = None
2046  for line in output:
2047    if line.startswith("UUID Token: opaquelocktoken:"):
2048      iota_token = line[12:].rstrip()
2049      break
2050  if iota_token is None:
2051    raise svntest.Failure("Unable to lookup lock token for 'iota'")
2052
2053  # Try to unlock a path while providing the correct lock token but
2054  # with a preventative hook in place.
2055  expected_error = ".*svnadmin: E165001:.*"
2056  svntest.actions.run_and_verify_svnadmin(None,
2057                                          expected_error, "unlock",
2058                                          sbox.repo_dir,
2059                                          "iota", "jrandom",
2060                                          iota_token)
2061
2062  # Finally, use --bypass-hooks to unlock the path (again using the
2063  # correct lock token).
2064  expected_output = "'/iota' unlocked by user 'jrandom'."
2065  svntest.actions.run_and_verify_svnadmin(expected_output,
2066                                          None, "unlock",
2067                                          "--bypass-hooks",
2068                                          sbox.repo_dir,
2069                                          "iota", "jrandom",
2070                                          iota_token)
2071
2072
2073@SkipUnless(svntest.main.is_threaded_python)
2074@Issue(4129)
2075def mergeinfo_race(sbox):
2076  "concurrent mergeinfo commits invalidate pred-count"
2077  sbox.build()
2078
2079  # This test exercises two commit-time race condition bugs:
2080  #
2081  # (a) metadata corruption when concurrent commits change svn:mergeinfo (issue #4129)
2082  # (b) false positive SVN_ERR_FS_CONFLICT error with httpv1 commits
2083  #     https://mail-archives.apache.org/mod_mbox/subversion-dev/201507.mbox/%3C20150731234536.GA5395@tarsus.local2%3E
2084  #
2085  # Both bugs are timing-dependent and might not reproduce 100% of the time.
2086
2087  wc_dir = sbox.wc_dir
2088  wc2_dir = sbox.add_wc_path('2')
2089
2090  ## Create wc2.
2091  svntest.main.run_svn(None, 'checkout', '-q', sbox.repo_url, wc2_dir)
2092
2093  ## Some random edits.
2094  svntest.main.run_svn(None, 'mkdir', sbox.ospath('d1', wc_dir))
2095  svntest.main.run_svn(None, 'mkdir', sbox.ospath('d2', wc2_dir))
2096
2097  ## Set random mergeinfo properties.
2098  svntest.main.run_svn(None, 'ps', 'svn:mergeinfo', '/P:42', sbox.ospath('A', wc_dir))
2099  svntest.main.run_svn(None, 'ps', 'svn:mergeinfo', '/Q:42', sbox.ospath('iota', wc2_dir))
2100
2101  def makethread(some_wc_dir):
2102    def worker():
2103      svntest.main.run_svn(None, 'commit', '-mm', some_wc_dir)
2104    return worker
2105
2106  t1 = threading.Thread(None, makethread(wc_dir))
2107  t2 = threading.Thread(None, makethread(wc2_dir))
2108
2109  # t2 will trigger the issue #4129 sanity check in fs_fs.c
2110  t1.start(); t2.start()
2111
2112  t1.join(); t2.join()
2113
2114  # Crude attempt to make sure everything worked.
2115  # TODO: better way to catch exceptions in the thread
2116  if svntest.actions.run_and_parse_info(sbox.repo_url)[0]['Revision'] != '3':
2117    raise svntest.Failure("one or both commits failed")
2118
2119
2120@Issue(4213)
2121@Skip(svntest.main.is_fs_type_fsx)
2122def recover_old_empty(sbox):
2123  "recover empty --compatible-version=1.3"
2124  sbox.build(create_wc=False, empty=True, minor_version=3)
2125  svntest.actions.run_and_verify_svnadmin(None, [],
2126                                          "recover", sbox.repo_dir)
2127
2128
2129@SkipUnless(svntest.main.is_fs_type_fsfs)
2130def verify_keep_going(sbox):
2131  "svnadmin verify --keep-going test"
2132
2133  # No support for modifying pack files
2134  if svntest.main.options.fsfs_packing:
2135    raise svntest.Skip('fsfs packing set')
2136
2137  sbox.build(create_wc = False)
2138  repo_url = sbox.repo_url
2139  B_url = sbox.repo_url + '/B'
2140  C_url = sbox.repo_url + '/C'
2141
2142  # Create A/B/E/bravo in r2.
2143  svntest.actions.run_and_verify_svn(None, [],
2144                                     'mkdir', '-m', 'log_msg',
2145                                     B_url)
2146
2147  svntest.actions.run_and_verify_svn(None, [],
2148                                     'mkdir', '-m', 'log_msg',
2149                                     C_url)
2150
2151  r2 = fsfs_file(sbox.repo_dir, 'revs', '2')
2152  fp = open(r2, 'r+b')
2153  fp.write(b"inserting junk to corrupt the rev")
2154  fp.close()
2155  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2156                                                        "--keep-going",
2157                                                        sbox.repo_dir)
2158
2159  exp_out = svntest.verify.RegexListOutput([".*Verified revision 0.",
2160                                            ".*Verified revision 1.",
2161                                            ".*",
2162                                            ".*Summary.*",
2163                                            ".*r2: E160004:.*",
2164                                            ".*r2: E160004:.*",
2165                                            ".*r3: E160004:.*",
2166                                            ".*r3: E160004:.*"])
2167
2168  if (svntest.main.fs_has_rep_sharing()):
2169    exp_out.insert(0, ".*Verifying.*metadata.*")
2170
2171  exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2172                                            "svnadmin: E160004:.*",
2173                                            "svnadmin: E160004:.*",
2174                                            ".*Error verifying revision 3.",
2175                                            "svnadmin: E160004:.*",
2176                                            "svnadmin: E160004:.*",
2177                                            "svnadmin: E205012:.*"], False)
2178
2179  if (svntest.main.is_fs_log_addressing()):
2180    exp_err.insert(0, ".*Error verifying repository metadata.")
2181    exp_err.insert(1, "svnadmin: E160004:.*")
2182
2183  if svntest.verify.verify_outputs("Unexpected error while running 'svnadmin verify'.",
2184                                   output, errput, exp_out, exp_err):
2185    raise svntest.Failure
2186
2187  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2188                                                        sbox.repo_dir)
2189
2190  if (svntest.main.is_fs_log_addressing()):
2191    exp_out = svntest.verify.RegexListOutput([".*Verifying metadata at revision 0.*"])
2192  else:
2193    exp_out = svntest.verify.RegexListOutput([".*Verified revision 0.",
2194                                              ".*Verified revision 1."])
2195    if (svntest.main.fs_has_rep_sharing()):
2196      exp_out.insert(0, ".*Verifying repository metadata.*")
2197
2198  if (svntest.main.is_fs_log_addressing()):
2199    exp_err = svntest.verify.RegexListOutput([
2200                                     ".*Error verifying repository metadata.",
2201                                     "svnadmin: E160004:.*"], False)
2202  else:
2203    exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2204                                              "svnadmin: E160004:.*",
2205                                              "svnadmin: E160004:.*"], False)
2206
2207  if svntest.verify.verify_outputs("Unexpected error while running 'svnadmin verify'.",
2208                                   output, errput, exp_out, exp_err):
2209    raise svntest.Failure
2210
2211
2212  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2213                                                        "--quiet",
2214                                                        sbox.repo_dir)
2215
2216  if (svntest.main.is_fs_log_addressing()):
2217    exp_err = svntest.verify.RegexListOutput([
2218                                      ".*Error verifying repository metadata.",
2219                                      "svnadmin: E160004:.*"], False)
2220  else:
2221    exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2222                                              "svnadmin: E160004:.*",
2223                                              "svnadmin: E160004:.*"], False)
2224
2225  if svntest.verify.verify_outputs("Output of 'svnadmin verify' is unexpected.",
2226                                   None, errput, None, exp_err):
2227    raise svntest.Failure
2228
2229  # Don't leave a corrupt repository
2230  svntest.main.safe_rmtree(sbox.repo_dir, True)
2231
2232
2233@SkipUnless(svntest.main.is_fs_type_fsfs)
2234def verify_keep_going_quiet(sbox):
2235  "svnadmin verify --keep-going --quiet test"
2236
2237  # No support for modifying pack files
2238  if svntest.main.options.fsfs_packing:
2239    raise svntest.Skip('fsfs packing set')
2240
2241  sbox.build(create_wc = False)
2242  repo_url = sbox.repo_url
2243  B_url = sbox.repo_url + '/B'
2244  C_url = sbox.repo_url + '/C'
2245
2246  # Create A/B/E/bravo in r2.
2247  svntest.actions.run_and_verify_svn(None, [],
2248                                     'mkdir', '-m', 'log_msg',
2249                                     B_url)
2250
2251  svntest.actions.run_and_verify_svn(None, [],
2252                                     'mkdir', '-m', 'log_msg',
2253                                     C_url)
2254
2255  r2 = fsfs_file(sbox.repo_dir, 'revs', '2')
2256  fp = open(r2, 'r+b')
2257  fp.write(b"inserting junk to corrupt the rev")
2258  fp.close()
2259
2260  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2261                                                        "--keep-going",
2262                                                        "--quiet",
2263                                                        sbox.repo_dir)
2264
2265  exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2266                                            "svnadmin: E160004:.*",
2267                                            "svnadmin: E160004:.*",
2268                                            ".*Error verifying revision 3.",
2269                                            "svnadmin: E160004:.*",
2270                                            "svnadmin: E160004:.*",
2271                                            "svnadmin: E205012:.*"], False)
2272
2273  # Insert another expected error from checksum verification
2274  if (svntest.main.is_fs_log_addressing()):
2275    exp_err.insert(0, ".*Error verifying repository metadata.")
2276    exp_err.insert(1, "svnadmin: E160004:.*")
2277
2278  if svntest.verify.verify_outputs(
2279          "Unexpected error while running 'svnadmin verify'.",
2280          output, errput, None, exp_err):
2281    raise svntest.Failure
2282
2283  # Don't leave a corrupt repository
2284  svntest.main.safe_rmtree(sbox.repo_dir, True)
2285
2286
2287@SkipUnless(svntest.main.is_fs_type_fsfs)
2288def verify_invalid_path_changes(sbox):
2289  "detect invalid changed path list entries"
2290
2291  # No support for modifying pack files
2292  if svntest.main.options.fsfs_packing:
2293    raise svntest.Skip('fsfs packing set')
2294
2295  sbox.build(create_wc = False)
2296  repo_url = sbox.repo_url
2297
2298  # Create a number of revisions each adding a single path
2299  for r in range(2,20):
2300    svntest.actions.run_and_verify_svn(None, [],
2301                                       'mkdir', '-m', 'log_msg',
2302                                       sbox.repo_url + '/B' + str(r))
2303
2304  # modify every other revision to make sure that errors are not simply
2305  # "carried over" but that all corrupts we get detected independently
2306
2307  # add existing node
2308  set_changed_path_list(sbox, 2,
2309                        b"_0.0.t1-1 add-dir false false /A\n\n")
2310
2311  # add into non-existent parent
2312  set_changed_path_list(sbox, 4,
2313                        b"_0.0.t3-2 add-dir false false /C/X\n\n")
2314
2315  # del non-existent node
2316  set_changed_path_list(sbox, 6,
2317                        b"_0.0.t5-2 delete-dir false false /C\n\n")
2318
2319  # del existent node of the wrong kind
2320  #
2321  # THIS WILL NOT BE DETECTED
2322  # since dump mechanism and file don't care about the types of deleted nodes
2323  set_changed_path_list(sbox, 8,
2324                        b"_0.0.t7-2 delete-file false false /B3\n\n")
2325
2326  # copy from non-existent node
2327  set_changed_path_list(sbox, 10,
2328                        b"_0.0.t9-2 add-dir false false /B10\n6 /B8\n")
2329
2330  # copy from existing node of the wrong kind
2331  set_changed_path_list(sbox, 12,
2332                        b"_0.0.t11-2 add-file false false /B12\n9 /B8\n")
2333
2334  # modify non-existent node
2335  set_changed_path_list(sbox, 14,
2336                        b"_0.0.t13-2 modify-file false false /A/D/H/foo\n\n")
2337
2338  # modify existent node of the wrong kind
2339  set_changed_path_list(sbox, 16,
2340                        b"_0.0.t15-2 modify-file false false /B12\n\n")
2341
2342  # replace non-existent node
2343  set_changed_path_list(sbox, 18,
2344                        b"_0.0.t17-2 replace-file false false /A/D/H/foo\n\n")
2345
2346  # find corruptions
2347  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2348                                                        "--keep-going",
2349                                                        sbox.repo_dir)
2350
2351  # Errors generated by FSFS when CHANGED_PATHS is not forced into emulation
2352  exp_out1 = svntest.verify.RegexListOutput([".*Verified revision 0.",
2353                                             ".*Verified revision 1.",
2354                                             ".*Verified revision 3.",
2355                                             ".*Verified revision 5.",
2356                                             ".*Verified revision 7.",
2357                                             ".*Verified revision 8.",
2358                                             ".*Verified revision 9.",
2359                                             ".*Verified revision 11.",
2360                                             ".*Verified revision 13.",
2361                                             ".*Verified revision 15.",
2362                                             ".*Verified revision 17.",
2363                                             ".*Verified revision 19.",
2364                                             ".*",
2365                                             ".*Summary.*",
2366                                             ".*r2: E160020:.*",
2367                                             ".*r2: E160020:.*",
2368                                             ".*r4: E160013:.*",
2369                                             ".*r6: E160013:.*",
2370                                             ".*r6: E160013:.*",
2371                                             ".*r10: E160013:.*",
2372                                             ".*r10: E160013:.*",
2373                                             ".*r12: E145001:.*",
2374                                             ".*r12: E145001:.*",
2375                                             ".*r14: E160013:.*",
2376                                             ".*r14: E160013:.*",
2377                                             ".*r16: E145001:.*",
2378                                             ".*r16: E145001:.*",
2379                                             ".*r18: E160013:.*",
2380                                             ".*r18: E160013:.*"])
2381
2382  exp_err1 = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2383                                             "svnadmin: E160020:.*",
2384                                             "svnadmin: E160020:.*",
2385                                             ".*Error verifying revision 4.",
2386                                             "svnadmin: E160013:.*",
2387                                             ".*Error verifying revision 6.",
2388                                             "svnadmin: E160013:.*",
2389                                             "svnadmin: E160013:.*",
2390                                             ".*Error verifying revision 10.",
2391                                             "svnadmin: E160013:.*",
2392                                             "svnadmin: E160013:.*",
2393                                             ".*Error verifying revision 12.",
2394                                             "svnadmin: E145001:.*",
2395                                             "svnadmin: E145001:.*",
2396                                             ".*Error verifying revision 14.",
2397                                             "svnadmin: E160013:.*",
2398                                             "svnadmin: E160013:.*",
2399                                             ".*Error verifying revision 16.",
2400                                             "svnadmin: E145001:.*",
2401                                             "svnadmin: E145001:.*",
2402                                             ".*Error verifying revision 18.",
2403                                             "svnadmin: E160013:.*",
2404                                             "svnadmin: E160013:.*",
2405                                             "svnadmin: E205012:.*"], False)
2406
2407  # If CHANGED_PATHS is emulated, FSFS fails earlier, generating fewer
2408  # of the same messages per revision.
2409  exp_out2 = svntest.verify.RegexListOutput([".*Verified revision 0.",
2410                                             ".*Verified revision 1.",
2411                                             ".*Verified revision 3.",
2412                                             ".*Verified revision 5.",
2413                                             ".*Verified revision 7.",
2414                                             ".*Verified revision 8.",
2415                                             ".*Verified revision 9.",
2416                                             ".*Verified revision 11.",
2417                                             ".*Verified revision 13.",
2418                                             ".*Verified revision 15.",
2419                                             ".*Verified revision 17.",
2420                                             ".*Verified revision 19.",
2421                                             ".*",
2422                                             ".*Summary.*",
2423                                             ".*r2: E160020:.*",
2424                                             ".*r2: E160020:.*",
2425                                             ".*r4: E160013:.*",
2426                                             ".*r6: E160013:.*",
2427                                             ".*r10: E160013:.*",
2428                                             ".*r10: E160013:.*",
2429                                             ".*r12: E145001:.*",
2430                                             ".*r12: E145001:.*",
2431                                             ".*r14: E160013:.*",
2432                                             ".*r16: E145001:.*",
2433                                             ".*r16: E145001:.*",
2434                                             ".*r18: E160013:.*"])
2435
2436  exp_err2 = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2437                                             "svnadmin: E160020:.*",
2438                                             "svnadmin: E160020:.*",
2439                                             ".*Error verifying revision 4.",
2440                                             "svnadmin: E160013:.*",
2441                                             ".*Error verifying revision 6.",
2442                                             "svnadmin: E160013:.*",
2443                                             ".*Error verifying revision 10.",
2444                                             "svnadmin: E160013:.*",
2445                                             "svnadmin: E160013:.*",
2446                                             ".*Error verifying revision 12.",
2447                                             "svnadmin: E145001:.*",
2448                                             "svnadmin: E145001:.*",
2449                                             ".*Error verifying revision 14.",
2450                                             "svnadmin: E160013:.*",
2451                                             ".*Error verifying revision 16.",
2452                                             "svnadmin: E145001:.*",
2453                                             "svnadmin: E145001:.*",
2454                                             ".*Error verifying revision 18.",
2455                                             "svnadmin: E160013:.*",
2456                                             "svnadmin: E205012:.*"], False)
2457
2458  # Determine which pattern to use.
2459  # Note that index() will throw an exception if the string can't be found.
2460  try:
2461    rev6_line = errput.index('* Error verifying revision 6.\n');
2462    rev10_line = errput.index('* Error verifying revision 10.\n');
2463
2464    error_count = 0
2465    for line in errput[rev6_line+1:rev10_line]:
2466      if "svnadmin: E" in line:
2467        error_count = error_count + 1
2468
2469    if error_count == 1:
2470      exp_out = exp_out2
2471      exp_err = exp_err2
2472    else:
2473      exp_out = exp_out1
2474      exp_err = exp_err1
2475  except ValueError:
2476    exp_out = exp_out1
2477    exp_err = exp_err1
2478
2479  if (svntest.main.fs_has_rep_sharing()):
2480    exp_out.insert(0, ".*Verifying.*metadata.*")
2481  if svntest.main.options.fsfs_sharding is not None:
2482    for x in range(0, 19 / svntest.main.options.fsfs_sharding):
2483      exp_out.insert(0, ".*Verifying.*metadata.*")
2484  if svntest.main.is_fs_log_addressing():
2485    exp_out.insert(0, ".*Verifying.*metadata.*")
2486
2487  if svntest.verify.verify_outputs("Unexpected error while running 'svnadmin verify'.",
2488                                   output, errput, exp_out, exp_err):
2489    raise svntest.Failure
2490
2491  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2492                                                        sbox.repo_dir)
2493
2494  exp_out = svntest.verify.RegexListOutput([".*Verified revision 0.",
2495                                            ".*Verified revision 1."])
2496  exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2497                                            "svnadmin: E160020:.*",
2498                                            "svnadmin: E160020:.*"], False)
2499
2500  if (svntest.main.fs_has_rep_sharing()):
2501    exp_out.insert(0, ".*Verifying.*metadata.*")
2502  if svntest.main.options.fsfs_sharding is not None:
2503    for x in range(0, 19 / svntest.main.options.fsfs_sharding):
2504      exp_out.insert(0, ".*Verifying.*metadata.*")
2505  if svntest.main.is_fs_log_addressing():
2506    exp_out.insert(0, ".*Verifying.*metadata.*")
2507
2508  if svntest.verify.verify_outputs("Unexpected error while running 'svnadmin verify'.",
2509                                   output, errput, exp_out, exp_err):
2510    raise svntest.Failure
2511
2512
2513  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2514                                                        "--quiet",
2515                                                        sbox.repo_dir)
2516
2517  exp_out = []
2518  exp_err = svntest.verify.RegexListOutput([".*Error verifying revision 2.",
2519                                            "svnadmin: E160020:.*",
2520                                            "svnadmin: E160020:.*"], False)
2521
2522  if svntest.verify.verify_outputs("Output of 'svnadmin verify' is unexpected.",
2523                                   output, errput, exp_out, exp_err):
2524    raise svntest.Failure
2525
2526  # Don't leave a corrupt repository
2527  svntest.main.safe_rmtree(sbox.repo_dir, True)
2528
2529
2530def verify_denormalized_names(sbox):
2531  "detect denormalized names and name collisions"
2532
2533  sbox.build(create_wc=False, empty=True)
2534
2535  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
2536                                   'svnadmin_tests_data',
2537                                   'normalization_check.dump')
2538  load_dumpstream(sbox, svntest.actions.load_dumpfile(dumpfile_location))
2539
2540  exit_code, output, errput = svntest.main.run_svnadmin(
2541    "verify", "--check-normalization", sbox.repo_dir)
2542
2543  expected_output_regex_list = [
2544    ".*Verified revision 0.",
2545    ".*Verified revision 1.",
2546    ".*Verified revision 2.",
2547    ".*Verified revision 3.",
2548                                           # A/{Eacute}/{aring}lpha
2549    "WARNING 0x0003: Duplicate representation of path 'A/.*/.*lpha'",
2550    ".*Verified revision 4.",
2551    ".*Verified revision 5.",
2552                                                      # Q/{aring}lpha
2553    "WARNING 0x0004: Duplicate representation of path '/Q/.*lpha'"
2554                                  # A/{Eacute}
2555    " in svn:mergeinfo property of 'A/.*'",
2556    ".*Verified revision 6.",
2557    ".*Verified revision 7."]
2558
2559  # The BDB backend doesn't do global metadata verification.
2560  if (svntest.main.fs_has_rep_sharing() and not svntest.main.is_fs_type_bdb()):
2561    expected_output_regex_list.insert(0, ".*Verifying repository metadata.*")
2562
2563  if svntest.main.options.fsfs_sharding is not None:
2564    for x in range(0, 7 / svntest.main.options.fsfs_sharding):
2565      expected_output_regex_list.insert(0, ".*Verifying.*metadata.*")
2566
2567  if svntest.main.is_fs_log_addressing():
2568    expected_output_regex_list.insert(0, ".* Verifying metadata at revision 0.*")
2569
2570  exp_out = svntest.verify.RegexListOutput(expected_output_regex_list)
2571  exp_err = svntest.verify.ExpectedOutput([])
2572
2573  svntest.verify.verify_outputs(
2574    "Unexpected error while running 'svnadmin verify'.",
2575    output, errput, exp_out, exp_err)
2576
2577
2578@SkipUnless(svntest.main.is_fs_type_fsfs)
2579def fsfs_recover_old_non_empty(sbox):
2580  "fsfs recover non-empty --compatible-version=1.3"
2581
2582  # Around trunk@1560210, 'svnadmin recover' wrongly errored out
2583  # for the --compatible-version=1.3 Greek tree repository:
2584  # svnadmin: E200002: Serialized hash missing terminator
2585
2586  sbox.build(create_wc=False, minor_version=3)
2587  svntest.actions.run_and_verify_svnadmin(None, [], "recover",
2588                                          sbox.repo_dir)
2589
2590
2591@SkipUnless(svntest.main.is_fs_type_fsfs)
2592def fsfs_hotcopy_old_non_empty(sbox):
2593  "fsfs hotcopy non-empty --compatible-version=1.3"
2594
2595  # Around trunk@1560210, 'svnadmin hotcopy' wrongly errored out
2596  # for the --compatible-version=1.3 Greek tree repository:
2597  # svnadmin: E160006: No such revision 1
2598
2599  sbox.build(create_wc=False, minor_version=3)
2600  backup_dir, backup_url = sbox.add_repo_path('backup')
2601  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2602                                          sbox.repo_dir, backup_dir)
2603
2604  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2605
2606
2607def load_ignore_dates(sbox):
2608  "svnadmin load --ignore-dates"
2609
2610  # All revisions in the loaded repository should come after this time.
2611  start_time = time.localtime()
2612  time.sleep(1)
2613
2614  sbox.build(create_wc=False, empty=True)
2615
2616  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
2617                                                   'svnadmin_tests_data',
2618                                                   'skeleton_repos.dump')
2619  dumpfile_skeleton = svntest.actions.load_dumpfile(dumpfile_location)
2620
2621  load_dumpstream(sbox, dumpfile_skeleton, '--ignore-dates')
2622  svntest.actions.run_and_verify_svnlook(['6\n'],
2623                                         None, 'youngest', sbox.repo_dir)
2624  for rev in range(1, 6):
2625    exit_code, output, errput = svntest.main.run_svnlook('date', '-r', rev,
2626                                                         sbox.repo_dir)
2627    if errput:
2628      raise SVNUnexpectedStderr(errput)
2629    rev_time = time.strptime(output[0].rstrip()[:19], '%Y-%m-%d %H:%M:%S')
2630    if rev_time < start_time:
2631      raise svntest.Failure("Revision time for r%d older than load start time\n"
2632                            "    rev_time: %s\n"
2633                            "  start_time: %s"
2634                            % (rev, str(rev_time), str(start_time)))
2635
2636
2637@SkipUnless(svntest.main.is_fs_type_fsfs)
2638def fsfs_hotcopy_old_with_id_changes(sbox):
2639  "fsfs hotcopy old with node-id and copy-id changes"
2640
2641  # Around trunk@1573728, running 'svnadmin hotcopy' for the
2642  # --compatible-version=1.3 repository with certain node-id and copy-id
2643  # changes ended with mismatching db/current in source and destination:
2644  #
2645  #   source: "2 l 1"  destination: "2 k 1",
2646  #           "3 l 2"               "3 4 2"
2647  #           (and so on...)
2648  #
2649  # We test this case by creating a --compatible-version=1.3 repository
2650  # and committing things that result in node-id and copy-id changes.
2651  # After every commit, we hotcopy the repository to a new destination
2652  # and check whether the source of the backup and the backup itself are
2653  # identical.  We also maintain a separate --incremental backup, which
2654  # is updated and checked after every commit.
2655  sbox.build(create_wc=True, minor_version=3)
2656
2657  inc_backup_dir, inc_backup_url = sbox.add_repo_path('incremental-backup')
2658
2659  # r1 = Initial greek tree sandbox.
2660  backup_dir, backup_url = sbox.add_repo_path('backup-after-r1')
2661  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2662                                          sbox.repo_dir, backup_dir)
2663  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2664                                          "--incremental",
2665                                          sbox.repo_dir, inc_backup_dir)
2666  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2667  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2668
2669  # r2 = Add a new property.
2670  sbox.simple_propset('foo', 'bar', 'A/mu')
2671  sbox.simple_commit(message='r2')
2672
2673  backup_dir, backup_url = sbox.add_repo_path('backup-after-r2')
2674  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2675                                          sbox.repo_dir, backup_dir)
2676  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2677                                          "--incremental",
2678                                          sbox.repo_dir, inc_backup_dir)
2679  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2680  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2681
2682  # r3 = Copy a file.
2683  sbox.simple_copy('A/B/E', 'A/B/E1')
2684  sbox.simple_commit(message='r3')
2685
2686  backup_dir, backup_url = sbox.add_repo_path('backup-after-r3')
2687  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2688                                          sbox.repo_dir, backup_dir)
2689  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2690                                          "--incremental",
2691                                          sbox.repo_dir, inc_backup_dir)
2692  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2693  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2694
2695  # r4 = Remove an existing file ...
2696  sbox.simple_rm('A/D/gamma')
2697  sbox.simple_commit(message='r4')
2698
2699  backup_dir, backup_url = sbox.add_repo_path('backup-after-r4')
2700  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2701                                          sbox.repo_dir, backup_dir)
2702  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2703                                          "--incremental",
2704                                          sbox.repo_dir, inc_backup_dir)
2705  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2706  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2707
2708  # r5 = ...and replace it with a new file here.
2709  sbox.simple_add_text("This is the replaced file.\n", 'A/D/gamma')
2710  sbox.simple_commit(message='r5')
2711
2712  backup_dir, backup_url = sbox.add_repo_path('backup-after-r5')
2713  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2714                                          sbox.repo_dir, backup_dir)
2715  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2716                                          "--incremental",
2717                                          sbox.repo_dir, inc_backup_dir)
2718  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2719  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2720
2721  # r6 = Add an entirely new file.
2722  sbox.simple_add_text('This is an entirely new file.\n', 'A/C/mu1')
2723  sbox.simple_commit(message='r6')
2724
2725  backup_dir, backup_url = sbox.add_repo_path('backup-after-r6')
2726  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2727                                          sbox.repo_dir, backup_dir)
2728  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2729                                          "--incremental",
2730                                          sbox.repo_dir, inc_backup_dir)
2731  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2732  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2733
2734  # r7 = Change the content of the existing file (this changeset does
2735  #      not bump the next-id and copy-id counters in the repository).
2736  sbox.simple_append('A/mu', 'This is change in the existing file.\n')
2737  sbox.simple_commit(message='r7')
2738
2739  backup_dir, backup_url = sbox.add_repo_path('backup-after-r7')
2740  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2741                                          sbox.repo_dir, backup_dir)
2742  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2743                                          "--incremental",
2744                                          sbox.repo_dir, inc_backup_dir)
2745  check_hotcopy_fsfs(sbox.repo_dir, backup_dir)
2746  check_hotcopy_fsfs(sbox.repo_dir, inc_backup_dir)
2747
2748
2749@SkipUnless(svntest.main.fs_has_pack)
2750def verify_packed(sbox):
2751  "verify packed with small shards"
2752
2753  # Configure two files per shard to trigger packing.
2754  sbox.build()
2755  patch_format(sbox.repo_dir, shard_size=2)
2756
2757  # Play with our greek tree.  These changesets fall into two
2758  # separate shards with r2 and r3 being in shard 1 ...
2759  sbox.simple_append('iota', "Line.\n")
2760  sbox.simple_append('A/D/gamma', "Another line.\n")
2761  sbox.simple_commit(message='r2')
2762  sbox.simple_propset('foo', 'bar', 'iota')
2763  sbox.simple_propset('foo', 'baz', 'A/mu')
2764  sbox.simple_commit(message='r3')
2765
2766  # ...and r4 and r5 being in shard 2.
2767  sbox.simple_rm('A/C')
2768  sbox.simple_copy('A/B/E', 'A/B/E1')
2769  sbox.simple_move('A/mu', 'A/B/mu')
2770  sbox.simple_commit(message='r4')
2771  sbox.simple_propdel('foo', 'A/B/mu')
2772  sbox.simple_commit(message='r5')
2773
2774  if svntest.main.is_fs_type_fsfs and svntest.main.options.fsfs_packing:
2775    # With --fsfs-packing, everything is already packed and we
2776    # can skip this part.
2777    pass
2778  else:
2779    expected_output = ["Packing revisions in shard 0...done.\n",
2780                       "Packing revisions in shard 1...done.\n",
2781                       "Packing revisions in shard 2...done.\n"]
2782    svntest.actions.run_and_verify_svnadmin(expected_output, [],
2783                                            "pack", sbox.repo_dir)
2784
2785  if svntest.main.is_fs_log_addressing():
2786    expected_output = ["* Verifying metadata at revision 0 ...\n",
2787                       "* Verifying metadata at revision 2 ...\n",
2788                       "* Verifying metadata at revision 4 ...\n",
2789                       "* Verifying repository metadata ...\n",
2790                       "* Verified revision 0.\n",
2791                       "* Verified revision 1.\n",
2792                       "* Verified revision 2.\n",
2793                       "* Verified revision 3.\n",
2794                       "* Verified revision 4.\n",
2795                       "* Verified revision 5.\n"]
2796  else:
2797    expected_output = ["* Verifying repository metadata ...\n",
2798                       "* Verified revision 0.\n",
2799                       "* Verified revision 1.\n",
2800                       "* Verified revision 2.\n",
2801                       "* Verified revision 3.\n",
2802                       "* Verified revision 4.\n",
2803                       "* Verified revision 5.\n"]
2804
2805  svntest.actions.run_and_verify_svnadmin(expected_output, [],
2806                                          "verify", sbox.repo_dir)
2807
2808# Test that 'svnadmin freeze' is nestable.  (For example, this ensures it
2809# won't take system-global locks, only repository-scoped ones.)
2810#
2811# This could be useful to easily freeze a small number of repositories at once.
2812#
2813# ### We don't actually test that freeze takes a write lock anywhere (not even
2814# ### in C tests.)
2815def freeze_freeze(sbox):
2816  "svnadmin freeze svnadmin freeze (some-cmd)"
2817
2818  sbox.build(create_wc=False, read_only=True)
2819  second_repo_dir, _ = sbox.add_repo_path('backup')
2820  svntest.actions.run_and_verify_svnadmin(None, [], "hotcopy",
2821                                          sbox.repo_dir, second_repo_dir)
2822
2823  if svntest.main.is_fs_type_fsx() or \
2824     (svntest.main.is_fs_type_fsfs() and \
2825      svntest.main.options.server_minor_version < 9):
2826    # FSFS repositories created with --compatible-version=1.8 and less
2827    # erroneously share the filesystem data (locks, shared transaction
2828    # data, ...) between hotcopy source and destination.  This is fixed
2829    # for new FS formats, but in order to avoid a deadlock for old formats,
2830    # we have to manually assign a new UUID for the hotcopy destination.
2831    # As of trunk@1618024, the same applies to FSX repositories.
2832    svntest.actions.run_and_verify_svnadmin([], None,
2833                                            'setuuid', second_repo_dir)
2834
2835  svntest.actions.run_and_verify_svnadmin(None, [],
2836                 'freeze', '--', sbox.repo_dir,
2837                 svntest.main.svnadmin_binary, 'freeze', '--', second_repo_dir,
2838                 sys.executable, '-c', 'True')
2839
2840  arg_file = sbox.get_tempname()
2841  svntest.main.file_write(arg_file,
2842                          "%s\n%s\n" % (sbox.repo_dir, second_repo_dir))
2843
2844  svntest.actions.run_and_verify_svnadmin(None, [],
2845                                          'freeze', '-F', arg_file, '--',
2846                                          sys.executable, '-c', 'True')
2847
2848def verify_metadata_only(sbox):
2849  "verify metadata only"
2850
2851  sbox.build(create_wc = False)
2852  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2853                                                        sbox.repo_dir,
2854                                                        "--metadata-only")
2855  if errput:
2856    raise SVNUnexpectedStderr(errput)
2857
2858  # Unfortunately, older formats won't test as thoroughly than newer ones
2859  # resulting in different progress output. BDB will do a full check but
2860  # not produce any output.
2861  if svntest.main.is_fs_log_addressing():
2862    svntest.verify.compare_and_display_lines(
2863      "Unexpected error while running 'svnadmin verify'.",
2864      'STDOUT', ["* Verifying metadata at revision 0 ...\n",
2865                 "* Verifying repository metadata ...\n"], output)
2866  elif svntest.main.fs_has_rep_sharing() \
2867       and not svntest.main.is_fs_type_bdb():
2868    svntest.verify.compare_and_display_lines(
2869      "Unexpected error while running 'svnadmin verify'.",
2870      'STDOUT', ["* Verifying repository metadata ...\n"], output)
2871  else:
2872    svntest.verify.compare_and_display_lines(
2873      "Unexpected error while running 'svnadmin verify'.",
2874      'STDOUT', [], output)
2875
2876
2877@Skip(svntest.main.is_fs_type_bdb)
2878def verify_quickly(sbox):
2879  "verify quickly using metadata"
2880
2881  sbox.build(create_wc = False)
2882  rev_file = open(fsfs_file(sbox.repo_dir, 'revs', '1'), 'r+b')
2883
2884  # set new contents
2885  rev_file.seek(8)
2886  rev_file.write(b'#')
2887  rev_file.close()
2888
2889  exit_code, output, errput = svntest.main.run_svnadmin("verify",
2890                                                        sbox.repo_dir,
2891                                                        "--metadata-only")
2892
2893  # unfortunately, some backends needs to do more checks than other
2894  # resulting in different progress output
2895  if svntest.main.is_fs_log_addressing():
2896    exp_out = svntest.verify.RegexListOutput([])
2897    exp_err = svntest.verify.RegexListOutput(["svnadmin: E160004:.*"], False)
2898  else:
2899    exp_out = svntest.verify.RegexListOutput([])
2900    exp_err = svntest.verify.RegexListOutput([])
2901
2902  if (svntest.main.fs_has_rep_sharing()):
2903    exp_out.insert(0, ".*Verifying.*metadata.*")
2904  if svntest.verify.verify_outputs("Unexpected error while running 'svnadmin verify'.",
2905                                   output, errput, exp_out, exp_err):
2906    raise svntest.Failure
2907
2908  # Don't leave a corrupt repository
2909  svntest.main.safe_rmtree(sbox.repo_dir, True)
2910
2911
2912@SkipUnless(svntest.main.is_fs_type_fsfs)
2913@SkipUnless(svntest.main.fs_has_pack)
2914def fsfs_hotcopy_progress(sbox):
2915  "hotcopy progress reporting"
2916
2917  # Check how 'svnadmin hotcopy' reports progress for non-incremental
2918  # and incremental scenarios.  The progress output can be affected by
2919  # the --fsfs-packing option, so skip the test if that is the case.
2920  if svntest.main.options.fsfs_packing:
2921    raise svntest.Skip('fsfs packing set')
2922
2923  # Create an empty repository, configure three files per shard.
2924  sbox.build(create_wc=False, empty=True)
2925  patch_format(sbox.repo_dir, shard_size=3)
2926
2927  inc_backup_dir, inc_backup_url = sbox.add_repo_path('incremental-backup')
2928
2929  # Nothing really exciting for the empty repository.
2930  expected_full = [
2931    "* Copied revision 0.\n"
2932    ]
2933  expected_incremental = [
2934    "* Copied revision 0.\n",
2935    ]
2936
2937  backup_dir, backup_url = sbox.add_repo_path('backup-0')
2938  svntest.actions.run_and_verify_svnadmin(expected_full, [],
2939                                          'hotcopy',
2940                                          sbox.repo_dir, backup_dir)
2941  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
2942                                          'hotcopy', '--incremental',
2943                                          sbox.repo_dir, inc_backup_dir)
2944
2945  # Commit three revisions.  After this step we have a full shard
2946  # (r0, r1, r2) and the second shard (r3) with a single revision.
2947  for i in range(3):
2948    svntest.actions.run_and_verify_svn(None, [], 'mkdir',
2949                                       '-m', svntest.main.make_log_msg(),
2950                                       sbox.repo_url + '/dir-%i' % i)
2951  expected_full = [
2952    "* Copied revision 0.\n",
2953    "* Copied revision 1.\n",
2954    "* Copied revision 2.\n",
2955    "* Copied revision 3.\n",
2956    ]
2957  expected_incremental = [
2958    "* Copied revision 1.\n",
2959    "* Copied revision 2.\n",
2960    "* Copied revision 3.\n",
2961    ]
2962
2963  backup_dir, backup_url = sbox.add_repo_path('backup-1')
2964  svntest.actions.run_and_verify_svnadmin(expected_full, [],
2965                                          'hotcopy',
2966                                          sbox.repo_dir, backup_dir)
2967  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
2968                                          'hotcopy', '--incremental',
2969                                          sbox.repo_dir, inc_backup_dir)
2970
2971  # Pack everything (r3 is still unpacked) and hotcopy again.  In this case,
2972  # the --incremental output should track the incoming (r0, r1, r2) pack and
2973  # should not mention r3, because it is already a part of the destination
2974  # and is *not* a part of the incoming pack.
2975  svntest.actions.run_and_verify_svnadmin(None, [], 'pack',
2976                                          sbox.repo_dir)
2977  expected_full = [
2978    "* Copied revisions from 0 to 2.\n",
2979    "* Copied revision 3.\n",
2980    ]
2981  expected_incremental = [
2982    "* Copied revisions from 0 to 2.\n",
2983    ]
2984
2985  backup_dir, backup_url = sbox.add_repo_path('backup-2')
2986  svntest.actions.run_and_verify_svnadmin(expected_full, [],
2987                                          'hotcopy',
2988                                          sbox.repo_dir, backup_dir)
2989  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
2990                                          'hotcopy', '--incremental',
2991                                          sbox.repo_dir, inc_backup_dir)
2992
2993  # Fill the second shard, pack again, commit several unpacked revisions
2994  # on top of it.  Rerun the hotcopy and check the progress output.
2995  for i in range(4, 6):
2996    svntest.actions.run_and_verify_svn(None, [], 'mkdir',
2997                                       '-m', svntest.main.make_log_msg(),
2998                                       sbox.repo_url + '/dir-%i' % i)
2999
3000  svntest.actions.run_and_verify_svnadmin(None, [], 'pack',
3001                                          sbox.repo_dir)
3002
3003  for i in range(6, 8):
3004    svntest.actions.run_and_verify_svn(None, [], 'mkdir',
3005                                       '-m', svntest.main.make_log_msg(),
3006                                       sbox.repo_url + '/dir-%i' % i)
3007  expected_full = [
3008    "* Copied revisions from 0 to 2.\n",
3009    "* Copied revisions from 3 to 5.\n",
3010    "* Copied revision 6.\n",
3011    "* Copied revision 7.\n",
3012    ]
3013  expected_incremental = [
3014    "* Copied revisions from 3 to 5.\n",
3015    "* Copied revision 6.\n",
3016    "* Copied revision 7.\n",
3017    ]
3018
3019  backup_dir, backup_url = sbox.add_repo_path('backup-3')
3020  svntest.actions.run_and_verify_svnadmin(expected_full, [],
3021                                          'hotcopy',
3022                                          sbox.repo_dir, backup_dir)
3023  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
3024                                          'hotcopy', '--incremental',
3025                                          sbox.repo_dir, inc_backup_dir)
3026
3027
3028@SkipUnless(svntest.main.is_fs_type_fsfs)
3029def fsfs_hotcopy_progress_with_revprop_changes(sbox):
3030  "incremental hotcopy progress with changed revprops"
3031
3032  # The progress output can be affected by the --fsfs-packing
3033  # option, so skip the test if that is the case.
3034  if svntest.main.options.fsfs_packing:
3035    raise svntest.Skip('fsfs packing set')
3036
3037  # Create an empty repository, commit several revisions and hotcopy it.
3038  sbox.build(create_wc=False, empty=True)
3039
3040  for i in range(6):
3041    svntest.actions.run_and_verify_svn(None, [], 'mkdir',
3042                                       '-m', svntest.main.make_log_msg(),
3043                                       sbox.repo_url + '/dir-%i' % i)
3044  expected_output = [
3045    "* Copied revision 0.\n",
3046    "* Copied revision 1.\n",
3047    "* Copied revision 2.\n",
3048    "* Copied revision 3.\n",
3049    "* Copied revision 4.\n",
3050    "* Copied revision 5.\n",
3051    "* Copied revision 6.\n",
3052    ]
3053
3054  backup_dir, backup_url = sbox.add_repo_path('backup')
3055  svntest.actions.run_and_verify_svnadmin(expected_output, [],
3056                                          'hotcopy',
3057                                          sbox.repo_dir, backup_dir)
3058
3059  # Amend a few log messages in the source, run the --incremental hotcopy.
3060  # The progress output should only mention the corresponding revisions.
3061  revprop_file = sbox.get_tempname()
3062  svntest.main.file_write(revprop_file, "Modified log message.")
3063
3064  for i in [1, 3, 6]:
3065    svntest.actions.run_and_verify_svnadmin(None, [],
3066                                            'setrevprop',
3067                                            sbox.repo_dir, '-r', i,
3068                                            'svn:log', revprop_file)
3069  expected_output = [
3070    "* Copied revision 1.\n",
3071    "* Copied revision 3.\n",
3072    "* Copied revision 6.\n",
3073    ]
3074  svntest.actions.run_and_verify_svnadmin(expected_output, [],
3075                                          'hotcopy', '--incremental',
3076                                          sbox.repo_dir, backup_dir)
3077
3078
3079@SkipUnless(svntest.main.is_fs_type_fsfs)
3080def fsfs_hotcopy_progress_old(sbox):
3081  "hotcopy --compatible-version=1.3 progress"
3082
3083  sbox.build(create_wc=False, empty=True, minor_version=3)
3084
3085  inc_backup_dir, inc_backup_url = sbox.add_repo_path('incremental-backup')
3086
3087  # Nothing really exciting for the empty repository.
3088  expected_full = [
3089    "* Copied revision 0.\n"
3090    ]
3091  expected_incremental = [
3092    "* Copied revision 0.\n",
3093    ]
3094
3095  backup_dir, backup_url = sbox.add_repo_path('backup-0')
3096  svntest.actions.run_and_verify_svnadmin(expected_full, [],
3097                                          'hotcopy',
3098                                          sbox.repo_dir, backup_dir)
3099  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
3100                                          'hotcopy', '--incremental',
3101                                          sbox.repo_dir, inc_backup_dir)
3102
3103  # Commit three revisions, hotcopy and check the progress output.
3104  for i in range(3):
3105    svntest.actions.run_and_verify_svn(None, [], 'mkdir',
3106                                       '-m', svntest.main.make_log_msg(),
3107                                       sbox.repo_url + '/dir-%i' % i)
3108
3109  expected_full = [
3110    "* Copied revision 0.\n",
3111    "* Copied revision 1.\n",
3112    "* Copied revision 2.\n",
3113    "* Copied revision 3.\n",
3114    ]
3115  expected_incremental = [
3116    "* Copied revision 1.\n",
3117    "* Copied revision 2.\n",
3118    "* Copied revision 3.\n",
3119    ]
3120
3121  backup_dir, backup_url = sbox.add_repo_path('backup-1')
3122  svntest.actions.run_and_verify_svnadmin(expected_full, [],
3123                                          'hotcopy',
3124                                          sbox.repo_dir, backup_dir)
3125  svntest.actions.run_and_verify_svnadmin(expected_incremental, [],
3126                                          'hotcopy', '--incremental',
3127                                          sbox.repo_dir, inc_backup_dir)
3128
3129
3130@SkipUnless(svntest.main.fs_has_unique_freeze)
3131def freeze_same_uuid(sbox):
3132  "freeze multiple repositories with same UUID"
3133
3134  sbox.build(create_wc=False)
3135
3136  first_repo_dir, _ = sbox.add_repo_path('first')
3137  second_repo_dir, _ = sbox.add_repo_path('second')
3138
3139  # Test that 'svnadmin freeze A (svnadmin freeze B)' does not deadlock for
3140  # new FSFS formats, even if 'A' and 'B' share the same UUID.  Create two
3141  # repositories by loading the same dump file, ...
3142  svntest.main.create_repos(first_repo_dir)
3143  svntest.main.create_repos(second_repo_dir)
3144
3145  dump_path = os.path.join(os.path.dirname(sys.argv[0]),
3146                                           'svnadmin_tests_data',
3147                                           'skeleton_repos.dump')
3148  dump_contents = open(dump_path, 'rb').readlines()
3149  svntest.actions.run_and_verify_load(first_repo_dir, dump_contents)
3150  svntest.actions.run_and_verify_load(second_repo_dir, dump_contents)
3151
3152  # ...and execute the 'svnadmin freeze -F' command.
3153  arg_file = sbox.get_tempname()
3154  svntest.main.file_write(arg_file,
3155                          "%s\n%s\n" % (first_repo_dir, second_repo_dir))
3156
3157  svntest.actions.run_and_verify_svnadmin(None, None,
3158                                          'freeze', '-F', arg_file, '--',
3159                                          sys.executable, '-c', 'True')
3160
3161
3162@Skip(svntest.main.is_fs_type_fsx)
3163def upgrade(sbox):
3164  "upgrade --compatible-version=1.3"
3165
3166  sbox.build(create_wc=False, minor_version=3)
3167  svntest.actions.run_and_verify_svnadmin(None, [], "upgrade",
3168                                          sbox.repo_dir)
3169  # Does the repository work after upgrade?
3170  svntest.actions.run_and_verify_svn(['Committing transaction...\n',
3171                                     'Committed revision 2.\n'], [], 'mkdir',
3172                                     '-m', svntest.main.make_log_msg(),
3173                                     sbox.repo_url + '/dir')
3174
3175def load_txdelta(sbox):
3176  "exercising svn_txdelta_target on BDB"
3177
3178  sbox.build(empty=True)
3179
3180  # This dumpfile produced a BDB repository that generated cheksum
3181  # mismatches on read caused by the improper handling of
3182  # svn_txdelta_target ops.  The bug was fixed by r1640832.
3183
3184  dumpfile_location = os.path.join(os.path.dirname(sys.argv[0]),
3185                                   'svnadmin_tests_data',
3186                                   'load_txdelta.dump.gz')
3187  dumpfile = gzip.open(dumpfile_location, "rb").readlines()
3188
3189  load_dumpstream(sbox, dumpfile)
3190
3191  # Verify would fail with a checksum mismatch:
3192  # * Error verifying revision 14.
3193  # svnadmin: E200014: MD5 checksum mismatch on representation 'r':
3194  #    expected:  5182e8876ed894dc7fe28f6ff5b2fee6
3195  #      actual:  5121f82875508863ad70daa8244e6947
3196
3197  exit_code, output, errput = svntest.main.run_svnadmin("verify", sbox.repo_dir)
3198  if errput:
3199    raise SVNUnexpectedStderr(errput)
3200  if svntest.verify.verify_outputs(
3201    "Output of 'svnadmin verify' is unexpected.", None, output, None,
3202    ".*Verified revision *"):
3203    raise svntest.Failure
3204
3205@Issues(4563)
3206def load_no_svndate_r0(sbox):
3207  "load without svn:date on r0"
3208
3209  sbox.build(create_wc=False, empty=True)
3210
3211  # svn:date exits
3212  svntest.actions.run_and_verify_svnlook(['  svn:date\n'], [],
3213                                         'proplist', '--revprop', '-r0',
3214                                         sbox.repo_dir)
3215
3216  dump_old = [b"SVN-fs-dump-format-version: 2\n", b"\n",
3217              b"UUID: bf52886d-358d-4493-a414-944a6e5ad4f5\n", b"\n",
3218              b"Revision-number: 0\n",
3219              b"Prop-content-length: 10\n",
3220              b"Content-length: 10\n", b"\n",
3221              b"PROPS-END\n", b"\n"]
3222  svntest.actions.run_and_verify_load(sbox.repo_dir, dump_old)
3223
3224  # svn:date should have been removed
3225  svntest.actions.run_and_verify_svnlook([], [],
3226                                         'proplist', '--revprop', '-r0',
3227                                         sbox.repo_dir)
3228
3229# This is only supported for FSFS
3230# The port to FSX is still pending, BDB won't support it.
3231@SkipUnless(svntest.main.is_fs_type_fsfs)
3232def hotcopy_read_only(sbox):
3233  "'svnadmin hotcopy' a read-only source repository"
3234  sbox.build()
3235  svntest.main.chmod_tree(sbox.repo_dir, 0, svntest.main.S_ALL_WRITE)
3236
3237  backup_dir, backup_url = sbox.add_repo_path('backup')
3238  exit_code, output, errput = svntest.main.run_svnadmin("hotcopy",
3239                                                        sbox.repo_dir,
3240                                                        backup_dir)
3241
3242  # r/o repos are hard to clean up. Make it writable again.
3243  svntest.main.chmod_tree(sbox.repo_dir, svntest.main.S_ALL_WRITE,
3244                          svntest.main.S_ALL_WRITE)
3245  if errput:
3246    logger.warn("Error: hotcopy failed")
3247    raise SVNUnexpectedStderr(errput)
3248
3249@SkipUnless(svntest.main.is_fs_type_fsfs)
3250@SkipUnless(svntest.main.fs_has_pack)
3251def fsfs_pack_non_sharded(sbox):
3252  "'svnadmin pack' on a non-sharded repository"
3253
3254  # Configure two files per shard to trigger packing.
3255  sbox.build(create_wc = False,
3256             minor_version = min(svntest.main.options.server_minor_version,3))
3257
3258  # Skip for pre-cooked sharded repositories
3259  if is_sharded(sbox.repo_dir):
3260    raise svntest.Skip('sharded pre-cooked repository')
3261
3262  svntest.actions.run_and_verify_svnadmin(
3263      None, [], "upgrade", sbox.repo_dir)
3264  svntest.actions.run_and_verify_svnadmin(
3265      ['svnadmin: Warning - this repository is not sharded. Packing has no effect.\n'],
3266      [], "pack", sbox.repo_dir)
3267
3268def load_revprops(sbox):
3269  "svnadmin load-revprops"
3270
3271  sbox.build(create_wc=False, empty=True)
3272
3273  dump_path = os.path.join(os.path.dirname(sys.argv[0]),
3274                                           'svnadmin_tests_data',
3275                                           'skeleton_repos.dump')
3276  dump_contents = open(dump_path, 'rb').readlines()
3277  load_and_verify_dumpstream(sbox, None, [], None, False, dump_contents)
3278
3279  svntest.actions.run_and_verify_svnlook(['Initial setup...\n', '\n'],
3280                                         [], 'log', '-r1', sbox.repo_dir)
3281
3282  # After loading the dump, amend one of the log message in the repository.
3283  input_file = sbox.get_tempname()
3284  svntest.main.file_write(input_file, 'Modified log message...\n')
3285
3286  svntest.actions.run_and_verify_svnadmin([], [], 'setlog', '--bypass-hooks',
3287                                          '-r1', sbox.repo_dir, input_file)
3288  svntest.actions.run_and_verify_svnlook(['Modified log message...\n', '\n'],
3289                                         [], 'log', '-r1', sbox.repo_dir)
3290
3291  # Load the same dump, but with 'svnadmin load-revprops'.  Doing so should
3292  # restore the log message to its original state.
3293  svntest.main.run_command_stdin(svntest.main.svnadmin_binary, None, 0,
3294                                 True, dump_contents, 'load-revprops',
3295                                 sbox.repo_dir)
3296
3297  svntest.actions.run_and_verify_svnlook(['Initial setup...\n', '\n'],
3298                                         [], 'log', '-r1', sbox.repo_dir)
3299
3300def dump_revprops(sbox):
3301  "svnadmin dump-revprops"
3302
3303  sbox.build(create_wc=False)
3304
3305  # Dump revprops only.
3306  exit_code, dump_contents, errput = \
3307    svntest.actions.run_and_verify_svnadmin(None, [], "dump-revprops", "-q",
3308                                            sbox.repo_dir)
3309
3310  # We expect the dump to contain no path changes
3311  for line in dump_contents:
3312    if line.find(b"Node-path: ") > -1:
3313      logger.warn("Error: path change found in revprops-only dump.")
3314      raise svntest.Failure
3315
3316  # Remember the current log message for r1
3317  exit_code, log_msg, errput = \
3318    svntest.actions.run_and_verify_svnlook(None, [], 'log', '-r1',
3319                                           sbox.repo_dir)
3320
3321  # Now, change the log message in the repository.
3322  input_file = sbox.get_tempname()
3323  svntest.main.file_write(input_file, 'Modified log message...\n')
3324
3325  svntest.actions.run_and_verify_svnadmin([], [], 'setlog', '--bypass-hooks',
3326                                          '-r1', sbox.repo_dir, input_file)
3327  svntest.actions.run_and_verify_svnlook(['Modified log message...\n', '\n'],
3328                                         [], 'log', '-r1', sbox.repo_dir)
3329
3330  # Load the same dump with 'svnadmin load-revprops'.  Doing so should
3331  # restore the log message to its original state.
3332  svntest.main.run_command_stdin(svntest.main.svnadmin_binary, None, 0,
3333                                 True, dump_contents, 'load-revprops',
3334                                 sbox.repo_dir)
3335
3336  svntest.actions.run_and_verify_svnlook(log_msg, [], 'log', '-r1',
3337                                         sbox.repo_dir)
3338
3339@XFail(svntest.main.is_fs_type_fsx)
3340@Issue(4598)
3341def dump_no_op_change(sbox):
3342  "svnadmin dump with no-op changes"
3343
3344  sbox.build(create_wc=False, empty=True)
3345  empty_file = sbox.get_tempname()
3346  svntest.main.file_write(empty_file, '')
3347
3348  svntest.actions.run_and_verify_svnmucc(None, [],
3349                                         '-U', sbox.repo_url,
3350                                         '-m', svntest.main.make_log_msg(),
3351                                         'put', empty_file, 'bar')
3352  # Commit a no-op change.
3353  svntest.actions.run_and_verify_svnmucc(None, [],
3354                                         '-U', sbox.repo_url,
3355                                         '-m', svntest.main.make_log_msg(),
3356                                         'put', empty_file, 'bar')
3357  # Dump and load the repository.
3358  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3359                                                       'dump', '-q',
3360                                                       sbox.repo_dir)
3361  sbox2 = sbox.clone_dependent()
3362  sbox2.build(create_wc=False, empty=True)
3363  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3364
3365  # We expect svn log -v to yield identical results for both original and
3366  # reconstructed repositories.  This used to fail as described in the
3367  # Issue 4598 (https://issues.apache.org/jira/browse/SVN-4598), at least
3368  # around r1706415.
3369  #
3370  # Test svn log -v for r2:
3371  _, expected, _ = svntest.actions.run_and_verify_svn(None, [], 'log', '-v',
3372                                                      '-r2', sbox.repo_url)
3373  found = [True for line in expected if line.find('M /bar\n') != -1]
3374  if not found:
3375    raise svntest.Failure
3376  svntest.actions.run_and_verify_svn(expected, [], 'log',  '-v',
3377                                     '-r2', sbox2.repo_url)
3378  # Test svn log -v for /bar:
3379  _, expected, _ = svntest.actions.run_and_verify_svn(None, [], 'log', '-v',
3380                                                      sbox.repo_url + '/bar')
3381  found = [True for line in expected if line.find('M /bar\n') != -1]
3382  if not found:
3383    raise svntest.Failure
3384  svntest.actions.run_and_verify_svn(expected, [], 'log',  '-v',
3385                                     sbox2.repo_url + '/bar')
3386
3387@XFail(svntest.main.is_fs_type_bdb)
3388@XFail(svntest.main.is_fs_type_fsx)
3389@Issue(4623)
3390def dump_no_op_prop_change(sbox):
3391  "svnadmin dump with no-op property change"
3392
3393  sbox.build(create_wc=False, empty=True)
3394  empty_file = sbox.get_tempname()
3395  svntest.main.file_write(empty_file, '')
3396
3397  svntest.actions.run_and_verify_svnmucc(None, [],
3398                                         '-U', sbox.repo_url,
3399                                         '-m', svntest.main.make_log_msg(),
3400                                         'put', empty_file, 'bar',
3401                                         'propset', 'pname', 'pval', 'bar')
3402  # Commit a no-op property change.
3403  svntest.actions.run_and_verify_svnmucc(None, [],
3404                                         '-U', sbox.repo_url,
3405                                         '-m', svntest.main.make_log_msg(),
3406                                         'propset', 'pname', 'pval', 'bar')
3407  # Dump and load the repository.
3408  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3409                                                       'dump', '-q',
3410                                                       sbox.repo_dir)
3411  sbox2 = sbox.clone_dependent()
3412  sbox2.build(create_wc=False, empty=True)
3413  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3414
3415  # Test svn log -v for r2:
3416  _, expected, _ = svntest.actions.run_and_verify_svn(None, [], 'log', '-v',
3417                                                      '-r2', sbox.repo_url)
3418  found = [True for line in expected if line.find('M /bar\n') != -1]
3419  if not found:
3420    raise svntest.Failure
3421  svntest.actions.run_and_verify_svn(expected, [], 'log',  '-v',
3422                                     '-r2', sbox2.repo_url)
3423  # Test svn log -v for /bar:
3424  _, expected, _ = svntest.actions.run_and_verify_svn(None, [], 'log', '-v',
3425                                                      sbox.repo_url + '/bar')
3426  found = [True for line in expected if line.find('M /bar\n') != -1]
3427  if not found:
3428    raise svntest.Failure
3429  svntest.actions.run_and_verify_svn(expected, [], 'log',  '-v',
3430                                     sbox2.repo_url + '/bar')
3431
3432def load_no_flush_to_disk(sbox):
3433  "svnadmin load --no-flush-to-disk"
3434
3435  sbox.build(empty=True)
3436
3437  # Can't test the "not flushing to disk part", but loading the
3438  # dump should work.
3439  dump = clean_dumpfile()
3440  expected = [
3441    svntest.wc.State('', {
3442      'A' : svntest.wc.StateItem(contents="text\n",
3443                                 props={'svn:keywords': 'Id'})
3444      })
3445    ]
3446  load_and_verify_dumpstream(sbox, [], [], expected, True, dump,
3447                             '--no-flush-to-disk', '--ignore-uuid')
3448
3449def dump_to_file(sbox):
3450  "svnadmin dump --file ARG"
3451
3452  sbox.build(create_wc=False, empty=False)
3453  expected_dump = svntest.actions.run_and_verify_dump(sbox.repo_dir)
3454
3455  file = sbox.get_tempname()
3456  svntest.actions.run_and_verify_svnadmin2([],
3457                                           ["* Dumped revision 0.\n",
3458                                            "* Dumped revision 1.\n"],
3459                                           0, 'dump', '--file', file,
3460                                           sbox.repo_dir)
3461  actual_dump = open(file, 'rb').readlines()
3462  svntest.verify.compare_dump_files(None, None, expected_dump, actual_dump)
3463
3464  # Test that svnadmin dump --file overwrites existing files.
3465  file = sbox.get_tempname()
3466  svntest.main.file_write(file, '')
3467  svntest.actions.run_and_verify_svnadmin2([],
3468                                           ["* Dumped revision 0.\n",
3469                                            "* Dumped revision 1.\n"],
3470                                           0, 'dump', '--file', file,
3471                                           sbox.repo_dir)
3472  actual_dump = open(file, 'rb').readlines()
3473  svntest.verify.compare_dump_files(None, None, expected_dump, actual_dump)
3474
3475def load_from_file(sbox):
3476  "svnadmin load --file ARG"
3477
3478  sbox.build(empty=True)
3479
3480  file = sbox.get_tempname()
3481  with open(file, 'wb') as f:
3482    f.writelines(clean_dumpfile())
3483  svntest.actions.run_and_verify_svnadmin2(None, [],
3484                                           0, 'load', '--file', file,
3485                                           '--ignore-uuid', sbox.repo_dir)
3486  expected_tree = \
3487    svntest.wc.State('', {
3488      'A' : svntest.wc.StateItem(contents="text\n",
3489                                 props={'svn:keywords': 'Id'})
3490      })
3491  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
3492                                     'update', sbox.wc_dir)
3493  svntest.actions.verify_disk(sbox.wc_dir, expected_tree, check_props=True)
3494
3495def dump_exclude(sbox):
3496  "svnadmin dump with excluded paths"
3497
3498  sbox.build(create_wc=False)
3499
3500  # Dump repository with /A/D/H and /A/B/E paths excluded.
3501  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3502                                                       'dump', '-q',
3503                                                       '--exclude', '/A/D/H',
3504                                                       '--exclude', '/A/B/E',
3505                                                       sbox.repo_dir)
3506
3507  # Load repository from dump.
3508  sbox2 = sbox.clone_dependent()
3509  sbox2.build(create_wc=False, empty=True)
3510  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3511
3512  # Check log.
3513  expected_output = svntest.verify.RegexListOutput([
3514    '-+\\n',
3515    'r1\ .*\n',
3516    # '/A/D/H' and '/A/B/E' is not added.
3517    re.escape('Changed paths:\n'),
3518    re.escape('   A /A\n'),
3519    re.escape('   A /A/B\n'),
3520    re.escape('   A /A/B/F\n'),
3521    re.escape('   A /A/B/lambda\n'),
3522    re.escape('   A /A/C\n'),
3523    re.escape('   A /A/D\n'),
3524    re.escape('   A /A/D/G\n'),
3525    re.escape('   A /A/D/G/pi\n'),
3526    re.escape('   A /A/D/G/rho\n'),
3527    re.escape('   A /A/D/G/tau\n'),
3528    re.escape('   A /A/D/gamma\n'),
3529    re.escape('   A /A/mu\n'),
3530    re.escape('   A /iota\n'),
3531    '-+\\n'
3532  ])
3533  svntest.actions.run_and_verify_svn(expected_output, [],
3534                                     'log', '-v', '-q', sbox2.repo_url)
3535
3536def dump_exclude_copysource(sbox):
3537  "svnadmin dump with excluded copysource"
3538
3539  sbox.build(create_wc=False, empty=True)
3540
3541  # Create default repository structure.
3542  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3543                                     sbox.repo_url + '/trunk',
3544                                     sbox.repo_url + '/branches',
3545                                     sbox.repo_url + '/tags',
3546                                     "-m", "Create repository structure.")
3547
3548  # Create a branch.
3549  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "copy",
3550                                     sbox.repo_url + '/trunk',
3551                                     sbox.repo_url + '/branches/branch1',
3552                                     "-m", "Create branch.")
3553
3554  # Dump repository with /trunk excluded.
3555  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3556                                                       'dump', '-q',
3557                                                       '--exclude', '/trunk',
3558                                                       sbox.repo_dir)
3559
3560  # Load repository from dump.
3561  sbox2 = sbox.clone_dependent()
3562  sbox2.build(create_wc=False, empty=True)
3563  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3564
3565  # Check log.
3566  expected_output = svntest.verify.RegexListOutput([
3567    '-+\\n',
3568    'r2\ .*\n',
3569    re.escape('Changed paths:\n'),
3570    # Simple add, not copy.
3571    re.escape('   A /branches/branch1\n'),
3572    '-+\\n',
3573    'r1\ .*\n',
3574    # '/trunk' is not added.
3575    re.escape('Changed paths:\n'),
3576    re.escape('   A /branches\n'),
3577    re.escape('   A /tags\n'),
3578    '-+\\n'
3579  ])
3580  svntest.actions.run_and_verify_svn(expected_output, [],
3581                                     'log', '-v', '-q', sbox2.repo_url)
3582
3583def dump_include(sbox):
3584  "svnadmin dump with included paths"
3585
3586  sbox.build(create_wc=False, empty=True)
3587
3588  # Create a couple of directories.
3589  # Note that we can't use greek tree as it contains only two top-level
3590  # nodes. Including non top-level nodes (e.g. '--include /A/B/E') will
3591  # produce unloadable dump for now.
3592  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3593                                     sbox.repo_url + '/A',
3594                                     sbox.repo_url + '/B',
3595                                     sbox.repo_url + '/C',
3596                                     "-m", "Create folder.")
3597
3598  # Dump repository with /A and /C paths included.
3599  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3600                                                       'dump', '-q',
3601                                                       '--include', '/A',
3602                                                       '--include', '/C',
3603                                                       sbox.repo_dir)
3604
3605  # Load repository from dump.
3606  sbox2 = sbox.clone_dependent()
3607  sbox2.build(create_wc=False, empty=True)
3608  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3609
3610  # Check log.
3611  expected_output = svntest.verify.RegexListOutput([
3612    '-+\\n',
3613    'r1\ .*\n',
3614    # '/B' is not added.
3615    re.escape('Changed paths:\n'),
3616    re.escape('   A /A\n'),
3617    re.escape('   A /C\n'),
3618    '-+\\n'
3619  ])
3620  svntest.actions.run_and_verify_svn(expected_output, [],
3621                                     'log', '-v', '-q', sbox2.repo_url)
3622
3623def dump_not_include_copysource(sbox):
3624  "svnadmin dump with not included copysource"
3625
3626  sbox.build(create_wc=False, empty=True)
3627
3628  # Create default repository structure.
3629  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3630                                     sbox.repo_url + '/trunk',
3631                                     sbox.repo_url + '/branches',
3632                                     sbox.repo_url + '/tags',
3633                                     "-m", "Create repository structure.")
3634
3635  # Create a branch.
3636  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "copy",
3637                                     sbox.repo_url + '/trunk',
3638                                     sbox.repo_url + '/branches/branch1',
3639                                     "-m", "Create branch.")
3640
3641  # Dump repository with only /branches included.
3642  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3643                                                       'dump', '-q',
3644                                                       '--include', '/branches',
3645                                                       sbox.repo_dir)
3646
3647  # Load repository from dump.
3648  sbox2 = sbox.clone_dependent()
3649  sbox2.build(create_wc=False, empty=True)
3650  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3651
3652  # Check log.
3653  expected_output = svntest.verify.RegexListOutput([
3654    '-+\\n',
3655    'r2\ .*\n',
3656    re.escape('Changed paths:\n'),
3657    # Simple add, not copy.
3658    re.escape('   A /branches/branch1\n'),
3659    '-+\\n',
3660    'r1\ .*\n',
3661    # Only '/branches' is added in r1.
3662    re.escape('Changed paths:\n'),
3663    re.escape('   A /branches\n'),
3664    '-+\\n'
3665  ])
3666  svntest.actions.run_and_verify_svn(expected_output, [],
3667                                     'log', '-v', '-q', sbox2.repo_url)
3668
3669def dump_exclude_by_pattern(sbox):
3670  "svnadmin dump with paths excluded by pattern"
3671
3672  sbox.build(create_wc=False, empty=True)
3673
3674  # Create a couple of directories.
3675  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3676                                     sbox.repo_url + '/aaa',
3677                                     sbox.repo_url + '/aab',
3678                                     sbox.repo_url + '/aac',
3679                                     sbox.repo_url + '/bbc',
3680                                     "-m", "Create repository structure.")
3681
3682  # Dump with paths excluded by pattern.
3683  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3684                                                       'dump', '-q',
3685                                                       '--exclude', '/aa?',
3686                                                       '--pattern',
3687                                                       sbox.repo_dir)
3688
3689  # Load repository from dump.
3690  sbox2 = sbox.clone_dependent()
3691  sbox2.build(create_wc=False, empty=True)
3692  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3693
3694  # Check log.
3695  expected_output = svntest.verify.RegexListOutput([
3696    '-+\\n',
3697    'r1\ .*\n',
3698    re.escape('Changed paths:\n'),
3699    # Only '/bbc' is added in r1.
3700    re.escape('   A /bbc\n'),
3701    '-+\\n'
3702  ])
3703  svntest.actions.run_and_verify_svn(expected_output, [],
3704                                     'log', '-v', '-q', sbox2.repo_url)
3705
3706def dump_include_by_pattern(sbox):
3707  "svnadmin dump with paths included by pattern"
3708
3709  sbox.build(create_wc=False, empty=True)
3710
3711  # Create a couple of directories.
3712  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3713                                     sbox.repo_url + '/aaa',
3714                                     sbox.repo_url + '/aab',
3715                                     sbox.repo_url + '/aac',
3716                                     sbox.repo_url + '/bbc',
3717                                     "-m", "Create repository structure.")
3718
3719  # Dump with paths included by pattern.
3720  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3721                                                       'dump', '-q',
3722                                                       '--include', '/aa?',
3723                                                       '--pattern',
3724                                                       sbox.repo_dir)
3725
3726  # Load repository from dump.
3727  sbox2 = sbox.clone_dependent()
3728  sbox2.build(create_wc=False, empty=True)
3729  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3730
3731  # Check log.
3732  expected_output = svntest.verify.RegexListOutput([
3733    '-+\\n',
3734    'r1\ .*\n',
3735    # '/bbc' is not added.
3736    re.escape('Changed paths:\n'),
3737    re.escape('   A /aaa\n'),
3738    re.escape('   A /aab\n'),
3739    re.escape('   A /aac\n'),
3740    '-+\\n'
3741  ])
3742  svntest.actions.run_and_verify_svn(expected_output, [],
3743                                     'log', '-v', '-q', sbox2.repo_url)
3744
3745def dump_exclude_all_rev_changes(sbox):
3746  "svnadmin dump with all revision changes excluded"
3747
3748  sbox.build(create_wc=False, empty=True)
3749
3750  # Create a couple of directories (r1).
3751  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3752                                     sbox.repo_url + '/r1a',
3753                                     sbox.repo_url + '/r1b',
3754                                     sbox.repo_url + '/r1c',
3755                                     "-m", "Revision 1.")
3756
3757  # Create a couple of directories (r2).
3758  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3759                                     sbox.repo_url + '/r2a',
3760                                     sbox.repo_url + '/r2b',
3761                                     sbox.repo_url + '/r2c',
3762                                     "-m", "Revision 2.")
3763
3764  # Create a couple of directories (r3).
3765  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "mkdir",
3766                                     sbox.repo_url + '/r3a',
3767                                     sbox.repo_url + '/r3b',
3768                                     sbox.repo_url + '/r3c',
3769                                     "-m", "Revision 3.")
3770
3771  # Dump with paths excluded by pattern.
3772  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3773                                                       'dump', '-q',
3774                                                       '--exclude', '/r2?',
3775                                                       '--pattern',
3776                                                       sbox.repo_dir)
3777
3778  # Load repository from dump.
3779  sbox2 = sbox.clone_dependent()
3780  sbox2.build(create_wc=False, empty=True)
3781  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3782
3783  # Check log. Revision properties ('svn:log' etc.) should be empty for r2.
3784  expected_output = svntest.verify.RegexListOutput([
3785    '-+\\n',
3786    'r3 | jrandom | .* | 1 line\\n',
3787    re.escape('Changed paths:'),
3788    re.escape('   A /r3a'),
3789    re.escape('   A /r3b'),
3790    re.escape('   A /r3c'),
3791    '',
3792    re.escape('Revision 3.'),
3793    '-+\\n',
3794    re.escape('r2 | (no author) | (no date) | 1 line'),
3795    '',
3796    '',
3797    '-+\\n',
3798    'r1 | jrandom | .* | 1 line\\n',
3799    re.escape('Changed paths:'),
3800    re.escape('   A /r1a'),
3801    re.escape('   A /r1b'),
3802    re.escape('   A /r1c'),
3803    '',
3804    re.escape('Revision 1.'),
3805    '-+\\n',
3806  ])
3807  svntest.actions.run_and_verify_svn(expected_output, [],
3808                                     'log', '-v',  sbox2.repo_url)
3809
3810def dump_invalid_filtering_option(sbox):
3811  "dump with --include and --exclude simultaneously"
3812
3813  sbox.build(create_wc=False, empty=False)
3814
3815  # Attempt to dump repository with '--include' and '--exclude' options
3816  # specified simultaneously.
3817  expected_error = ".*: '--exclude' and '--include' options cannot be used " \
3818                   "simultaneously"
3819  svntest.actions.run_and_verify_svnadmin(None, expected_error,
3820                                          'dump', '-q',
3821                                          '--exclude', '/A/D/H',
3822                                          '--include', '/A/B/E',
3823                                          sbox.repo_dir)
3824
3825@Issue(4725)
3826def load_issue4725(sbox):
3827  """load that triggers issue 4725"""
3828
3829  sbox.build(empty=True)
3830
3831  sbox.simple_mkdir('subversion')
3832  sbox.simple_commit()
3833  sbox.simple_mkdir('subversion/trunk')
3834  sbox.simple_mkdir('subversion/branches')
3835  sbox.simple_commit()
3836  sbox.simple_mkdir('subversion/trunk/src')
3837  sbox.simple_commit()
3838
3839  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3840                                                       'dump', '-q',
3841                                                       sbox.repo_dir)
3842
3843  sbox2 = sbox.clone_dependent()
3844  sbox2.build(create_wc=False, empty=True)
3845  load_and_verify_dumpstream(sbox2, None, [], None, False, dump, '-M100')
3846
3847@Issue(4767)
3848def dump_no_canonicalize_svndate(sbox):
3849  "svnadmin dump shouldn't canonicalize svn:date"
3850
3851  sbox.build(create_wc=False, empty=True)
3852  svntest.actions.enable_revprop_changes(sbox.repo_dir)
3853
3854  # set svn:date in a non-canonical format (not six decimal places)
3855  propval = "2015-01-01T00:00:00.0Z"
3856  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [],
3857                                     "propset", "--revprop", "-r0", "svn:date",
3858                                     propval,
3859                                     sbox.repo_url)
3860
3861  dump_lines = svntest.actions.run_and_verify_dump(sbox.repo_dir)
3862  assert propval.encode() + b'\n' in dump_lines
3863
3864def check_recover_prunes_rep_cache(sbox, enable_rep_sharing):
3865  """Check 'recover' prunes the rep-cache while enable-rep-sharing is
3866     true/false.
3867  """
3868  # Remember the initial rep cache content.
3869  rep_cache_r1 = read_rep_cache(sbox.repo_dir)
3870  #print '\n'.join([h + ": " + repr(ref) for h, ref in rep_cache_r1.items()])
3871
3872  # Commit one new rep and check the rep-cache is extended.
3873  sbox.simple_append('iota', 'New line.\n')
3874  sbox.simple_commit()
3875  rep_cache_r2 = read_rep_cache(sbox.repo_dir)
3876  if not (len(rep_cache_r2) == len(rep_cache_r1) + 1):
3877    raise svntest.Failure
3878
3879  fsfs_conf = svntest.main.get_fsfs_conf_file_path(sbox.repo_dir)
3880  svntest.main.file_append(fsfs_conf,
3881                           # Add a newline in case the existing file doesn't
3882                           # end with one.
3883                           "\n"
3884                           "[rep-sharing]\n"
3885                           "enable-rep-sharing = %s\n"
3886                           % (('true' if enable_rep_sharing else 'false'),))
3887
3888  # Break r2 in such a way that 'recover' will discard it
3889  head_rev_path = fsfs_file(sbox.repo_dir, 'revs', '2')
3890  os.remove(head_rev_path)
3891  current_path = os.path.join(sbox.repo_dir, 'db', 'current')
3892  svntest.main.file_write(current_path, '1\n')
3893
3894  # Recover back to r1.
3895  svntest.actions.run_and_verify_svnadmin(None, [],
3896                                          "recover", sbox.repo_dir)
3897  svntest.actions.run_and_verify_svnlook(['1\n'], [], 'youngest',
3898                                         sbox.repo_dir)
3899
3900  # Check the rep-cache is pruned.
3901  rep_cache_recovered = read_rep_cache(sbox.repo_dir)
3902  if not (rep_cache_recovered == rep_cache_r1):
3903    raise svntest.Failure
3904
3905@Issue(4077)
3906@SkipUnless(svntest.main.is_fs_type_fsfs)
3907@SkipUnless(svntest.main.python_sqlite_can_read_without_rowid)
3908def recover_prunes_rep_cache_when_enabled(sbox):
3909  "recover prunes rep cache when enabled"
3910  sbox.build()
3911
3912  check_recover_prunes_rep_cache(sbox, enable_rep_sharing=True)
3913
3914@Issue(4077)
3915@SkipUnless(svntest.main.is_fs_type_fsfs)
3916@SkipUnless(svntest.main.python_sqlite_can_read_without_rowid)
3917def recover_prunes_rep_cache_when_disabled(sbox):
3918  "recover prunes rep cache when disabled"
3919  sbox.build()
3920
3921  check_recover_prunes_rep_cache(sbox, enable_rep_sharing=False)
3922
3923@Issue(4760)
3924def dump_include_copied_directory(sbox):
3925  "include copied directory with nested nodes"
3926
3927  sbox.build(create_wc=False)
3928
3929  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "copy",
3930                                     sbox.repo_url + '/A/D',
3931                                     sbox.repo_url + '/COPY',
3932                                     "-m", "Create branch.")
3933
3934  # Dump repository with only /COPY path included.
3935  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
3936                                                       'dump', '-q',
3937                                                       '--include', '/COPY',
3938                                                       sbox.repo_dir)
3939
3940  # Load repository from dump.
3941  sbox2 = sbox.clone_dependent()
3942  sbox2.build(create_wc=False, empty=True)
3943  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
3944
3945  # Check log.
3946  expected_output = svntest.verify.RegexListOutput([
3947    '-+\\n',
3948    'r2\ .*\n',
3949    # Only '/COPY' is added
3950    re.escape('Changed paths:\n'),
3951    re.escape('   A /COPY'),
3952    re.escape('   A /COPY/G'),
3953    re.escape('   A /COPY/G/pi'),
3954    re.escape('   A /COPY/G/rho'),
3955    re.escape('   A /COPY/G/tau'),
3956    re.escape('   A /COPY/H'),
3957    re.escape('   A /COPY/H/chi'),
3958    re.escape('   A /COPY/H/omega'),
3959    re.escape('   A /COPY/H/psi'),
3960    re.escape('   A /COPY/gamma'),
3961    '-+\\n',
3962    'r1\ .*\n',
3963    '-+\\n'
3964  ])
3965  svntest.actions.run_and_verify_svn(expected_output, [],
3966                                     'log', '-v', '-q', sbox2.repo_url)
3967
3968def load_normalize_node_props(sbox):
3969  "svnadmin load --normalize node props"
3970
3971  dump_str = b"""SVN-fs-dump-format-version: 2
3972
3973UUID: dc40867b-38f6-0310-9f5f-f81aa277e06f
3974
3975Revision-number: 0
3976Prop-content-length: 56
3977Content-length: 56
3978
3979K 8
3980svn:date
3981V 27
39822005-05-03T19:09:41.129900Z
3983PROPS-END
3984
3985Revision-number: 1
3986Prop-content-length: 99
3987Content-length: 99
3988
3989K 7
3990svn:log
3991V 0
3992
3993K 10
3994svn:author
3995V 2
3996pl
3997K 8
3998svn:date
3999V 27
40002005-05-03T19:10:19.975578Z
4001PROPS-END
4002
4003Node-path:\x20
4004Node-kind: dir
4005Node-action: change
4006Prop-content-length: 32
4007Content-length: 32
4008
4009K 10
4010svn:ignore
4011V 3
4012\n\r\n
4013PROPS-END
4014
4015
4016"""
4017  sbox.build(empty=True)
4018
4019  # Try to load the dumpstream, expecting a failure (because of mixed
4020  # EOLs in the svn:ignore property value).
4021  exp_err = svntest.verify.RegexListOutput(['svnadmin: E125005:.*',
4022                                            'svnadmin: E125017:.*'],
4023                                           match_all=False)
4024  load_and_verify_dumpstream(sbox, [], exp_err, dumpfile_revisions,
4025                             False, dump_str, '--ignore-uuid')
4026
4027  # Now try it again with prop normalization.
4028  svntest.actions.load_repo(sbox, dump_str=dump_str,
4029                            bypass_prop_validation=False,
4030                            normalize_props=True)
4031  # We should get the normalized property value.
4032  exit_code, output, _ = svntest.main.run_svn(None, 'pg', 'svn:ignore',
4033                                              '--no-newline',
4034                                              sbox.repo_url)
4035  svntest.verify.verify_exit_code(None, exit_code, 0)
4036  if output != ['\n', '\n']:
4037    raise svntest.Failure("Unexpected property value %s" % output)
4038
4039@SkipUnless(svntest.main.is_fs_type_fsfs)
4040@SkipUnless(svntest.main.fs_has_rep_sharing)
4041@SkipUnless(svntest.main.python_sqlite_can_read_without_rowid)
4042def build_repcache(sbox):
4043  "svnadmin build-repcache"
4044
4045  sbox.build(create_wc = False)
4046
4047  # Remember and remove the existing rep-cache.
4048  rep_cache = read_rep_cache(sbox.repo_dir)
4049  rep_cache_path = os.path.join(sbox.repo_dir, 'db', 'rep-cache.db')
4050  os.remove(rep_cache_path)
4051
4052  # Build a new rep-cache and compare with the original one.
4053  expected_output = ["* Processed revision 1.\n"]
4054  svntest.actions.run_and_verify_svnadmin(expected_output, [],
4055                                          "build-repcache", sbox.repo_dir)
4056
4057  new_rep_cache = read_rep_cache(sbox.repo_dir)
4058  if new_rep_cache != rep_cache:
4059    raise svntest.Failure
4060
4061
4062########################################################################
4063# Run the tests
4064
4065
4066# list all tests here, starting with None:
4067test_list = [ None,
4068              extra_headers,
4069              extra_blockcontent,
4070              inconsistent_headers,
4071              empty_date,
4072              dump_copied_dir,
4073              dump_move_dir_modify_child,
4074              dump_quiet,
4075              hotcopy_dot,
4076              hotcopy_format,
4077              setrevprop,
4078              verify_windows_paths_in_repos,
4079              verify_incremental_fsfs,
4080              fsfs_recover_db_current,
4081              fsfs_recover_old_db_current,
4082              load_with_parent_dir,
4083              set_uuid,
4084              reflect_dropped_renumbered_revs,
4085              fsfs_recover_handle_missing_revs_or_revprops_file,
4086              create_in_repo_subdir,
4087              verify_with_invalid_revprops,
4088              dont_drop_valid_mergeinfo_during_incremental_loads,
4089              hotcopy_symlink,
4090              load_bad_props,
4091              verify_non_utf8_paths,
4092              test_lslocks_and_rmlocks,
4093              load_ranges,
4094              hotcopy_incremental,
4095              hotcopy_incremental_packed,
4096              locking,
4097              mergeinfo_race,
4098              recover_old_empty,
4099              verify_keep_going,
4100              verify_keep_going_quiet,
4101              verify_invalid_path_changes,
4102              verify_denormalized_names,
4103              fsfs_recover_old_non_empty,
4104              fsfs_hotcopy_old_non_empty,
4105              load_ignore_dates,
4106              fsfs_hotcopy_old_with_id_changes,
4107              verify_packed,
4108              freeze_freeze,
4109              verify_metadata_only,
4110              verify_quickly,
4111              fsfs_hotcopy_progress,
4112              fsfs_hotcopy_progress_with_revprop_changes,
4113              fsfs_hotcopy_progress_old,
4114              freeze_same_uuid,
4115              upgrade,
4116              load_txdelta,
4117              load_no_svndate_r0,
4118              hotcopy_read_only,
4119              fsfs_pack_non_sharded,
4120              load_revprops,
4121              dump_revprops,
4122              dump_no_op_change,
4123              dump_no_op_prop_change,
4124              load_no_flush_to_disk,
4125              dump_to_file,
4126              load_from_file,
4127              dump_exclude,
4128              dump_exclude_copysource,
4129              dump_include,
4130              dump_not_include_copysource,
4131              dump_exclude_by_pattern,
4132              dump_include_by_pattern,
4133              dump_exclude_all_rev_changes,
4134              dump_invalid_filtering_option,
4135              load_issue4725,
4136              dump_no_canonicalize_svndate,
4137              recover_prunes_rep_cache_when_enabled,
4138              recover_prunes_rep_cache_when_disabled,
4139              dump_include_copied_directory,
4140              load_normalize_node_props,
4141              build_repcache,
4142             ]
4143
4144if __name__ == '__main__':
4145  svntest.main.run_tests(test_list)
4146  # NOTREACHED
4147
4148
4149### End of file.
4150