1#!/usr/bin/env python
2#
3#  svnsync_tests.py:  Tests SVNSync's repository mirroring capabilities.
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 sys, os
29
30# Test suite-specific modules
31import re
32
33# Our testing module
34import svntest
35from svntest.verify import SVNUnexpectedStdout, SVNUnexpectedStderr
36from svntest.verify import SVNExpectedStderr
37from svntest.verify import AnyOutput
38from svntest.main import server_has_partial_replay
39
40# (abbreviation)
41Skip = svntest.testcase.Skip_deco
42SkipUnless = svntest.testcase.SkipUnless_deco
43XFail = svntest.testcase.XFail_deco
44Issues = svntest.testcase.Issues_deco
45Issue = svntest.testcase.Issue_deco
46Wimp = svntest.testcase.Wimp_deco
47Item = svntest.wc.StateItem
48
49######################################################################
50# Helper routines
51
52
53def run_sync(url, source_url=None,
54             source_prop_encoding=None,
55             expected_output=AnyOutput, expected_error=[]):
56  "Synchronize the mirror repository with the master"
57  if source_url is not None:
58    args = ["synchronize", url, source_url]
59  else: # Allow testing of old source-URL-less syntax
60    args = ["synchronize", url]
61  if source_prop_encoding:
62    args.append("--source-prop-encoding")
63    args.append(source_prop_encoding)
64
65  # Normal expected output is of the form:
66  #            ['Transmitting file data .......\n',  # optional
67  #             'Committed revision 1.\n',
68  #             'Copied properties for revision 1.\n', ...]
69  svntest.actions.run_and_verify_svnsync(expected_output, expected_error,
70                                         *args)
71
72def run_copy_revprops(url, source_url,
73                      source_prop_encoding=None,
74                      expected_output=AnyOutput, expected_error=[]):
75  "Copy revprops to the mirror repository from the master"
76  args = ["copy-revprops", url, source_url]
77  if source_prop_encoding:
78    args.append("--source-prop-encoding")
79    args.append(source_prop_encoding)
80
81  # Normal expected output is of the form:
82  #            ['Copied properties for revision 1.\n', ...]
83  svntest.actions.run_and_verify_svnsync(expected_output, expected_error,
84                                         *args)
85
86def run_init(dst_url, src_url, source_prop_encoding=None):
87  "Initialize the mirror repository from the master"
88  args = ["initialize", dst_url, src_url]
89  if source_prop_encoding:
90    args.append("--source-prop-encoding")
91    args.append(source_prop_encoding)
92
93  expected_output = ['Copied properties for revision 0.\n']
94  svntest.actions.run_and_verify_svnsync(expected_output, [], *args)
95
96def run_info(url, expected_output=AnyOutput, expected_error=[]):
97  "Print synchronization information of the repository"
98  # Normal expected output is of the form:
99  #            ['From URL: http://....\n',
100  #             'From UUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n',
101  #             'Last Merged Revision: XXX\n']
102  svntest.actions.run_and_verify_svnsync(expected_output, expected_error,
103                                         "info", url)
104
105
106def setup_and_sync(sbox, dump_file_contents, subdir=None,
107                   bypass_prop_validation=False, source_prop_encoding=None,
108                   is_src_ra_local=None, is_dest_ra_local=None):
109  """Create a repository for SBOX, load it with DUMP_FILE_CONTENTS, then create a mirror repository and sync it with SBOX. If is_src_ra_local or is_dest_ra_local is True, then run_init, run_sync, and run_copy_revprops will use the file:// scheme for the source and destination URLs.  Return the mirror sandbox."""
110
111  # Create the empty master repository.
112  sbox.build(create_wc=False, empty=True)
113
114  # Load the repository from DUMP_FILE_PATH.
115  svntest.actions.run_and_verify_load(sbox.repo_dir, dump_file_contents,
116                                      bypass_prop_validation)
117
118  # Create the empty destination repository.
119  dest_sbox = sbox.clone_dependent()
120  dest_sbox.build(create_wc=False, empty=True)
121
122  # Setup the mirror repository.  Feed it the UUID of the source repository.
123  exit_code, output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
124  svntest.actions.run_and_verify_svnadmin2(None, None, 0,
125                                           'setuuid', dest_sbox.repo_dir,
126                                           output[0][:-1])
127
128  # Create the revprop-change hook for this test
129  svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
130
131  repo_url = sbox.repo_url
132  cwd = os.getcwd()
133  if is_src_ra_local:
134    repo_url = sbox.file_protocol_repo_url()
135
136  if subdir:
137    repo_url = repo_url + subdir
138
139  dest_repo_url = dest_sbox.repo_url
140  if is_dest_ra_local:
141    dest_repo_url = dest_sbox.file_protocol_repo_url()
142  run_init(dest_repo_url, repo_url, source_prop_encoding)
143
144  run_sync(dest_repo_url, repo_url,
145           source_prop_encoding=source_prop_encoding)
146  run_copy_revprops(dest_repo_url, repo_url,
147                    source_prop_encoding=source_prop_encoding)
148
149  return dest_sbox
150
151def verify_mirror(dest_sbox, exp_dump_file_contents):
152  """Compare the contents of the mirror repository in DEST_SBOX with
153     EXP_DUMP_FILE_CONTENTS, by comparing the parsed dump stream content.
154
155     First remove svnsync rev-props from the DEST_SBOX repository.
156  """
157
158  # Remove some SVNSync-specific housekeeping properties from the
159  # mirror repository in preparation for the comparison dump.
160  for prop_name in ("svn:sync-from-url", "svn:sync-from-uuid",
161                    "svn:sync-last-merged-rev"):
162    svntest.actions.run_and_verify_svn(
163      None, [], "propdel", "--revprop", "-r", "0",
164      prop_name, dest_sbox.repo_url)
165
166  # Create a dump file from the mirror repository.
167  dest_dump = svntest.actions.run_and_verify_dump(dest_sbox.repo_dir)
168
169  svntest.verify.compare_dump_files(
170    None, None, exp_dump_file_contents, dest_dump)
171
172def run_test(sbox, dump_file_name, subdir=None, exp_dump_file_name=None,
173             bypass_prop_validation=False, source_prop_encoding=None,
174             is_src_ra_local=None, is_dest_ra_local=None):
175
176  """Load a dump file, sync repositories, and compare contents with the original
177or another dump file."""
178
179  # This directory contains all the dump files
180  svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
181                                   'svnsync_tests_data')
182
183  # Load the specified dump file into the master repository.
184  master_dumpfile_contents = open(os.path.join(svnsync_tests_dir,
185                                               dump_file_name),
186                                  'rb').readlines()
187
188  dest_sbox = setup_and_sync(sbox, master_dumpfile_contents, subdir,
189                             bypass_prop_validation, source_prop_encoding,
190                             is_src_ra_local, is_dest_ra_local)
191
192  # Compare the dump produced by the mirror repository with either the original
193  # dump file (used to create the master repository) or another specified dump
194  # file.
195  if exp_dump_file_name:
196    exp_dump_file_contents = open(os.path.join(svnsync_tests_dir,
197                                  exp_dump_file_name), 'rb').readlines()
198  else:
199    exp_dump_file_contents = master_dumpfile_contents
200
201  verify_mirror(dest_sbox, exp_dump_file_contents)
202
203
204
205######################################################################
206# Tests
207
208#----------------------------------------------------------------------
209
210def copy_and_modify(sbox):
211  "copy and modify"
212  run_test(sbox, "copy-and-modify.dump")
213
214#----------------------------------------------------------------------
215
216def copy_from_previous_version_and_modify(sbox):
217  "copy from previous version and modify"
218  run_test(sbox, "copy-from-previous-version-and-modify.dump")
219
220#----------------------------------------------------------------------
221
222def copy_from_previous_version(sbox):
223  "copy from previous version"
224  run_test(sbox, "copy-from-previous-version.dump")
225
226#----------------------------------------------------------------------
227
228def modified_in_place(sbox):
229  "modified in place"
230  run_test(sbox, "modified-in-place.dump")
231
232#----------------------------------------------------------------------
233
234def tag_empty_trunk(sbox):
235  "tag empty trunk"
236  run_test(sbox, "tag-empty-trunk.dump")
237
238#----------------------------------------------------------------------
239
240def tag_trunk_with_dir(sbox):
241  "tag trunk containing a sub-directory"
242  run_test(sbox, "tag-trunk-with-dir.dump")
243
244#----------------------------------------------------------------------
245
246def tag_trunk_with_file(sbox):
247  "tag trunk containing a file"
248  run_test(sbox, "tag-trunk-with-file.dump")
249
250#----------------------------------------------------------------------
251
252def tag_trunk_with_file2(sbox):
253  "tag trunk containing a file (#2)"
254  run_test(sbox, "tag-trunk-with-file2.dump")
255
256#----------------------------------------------------------------------
257
258def tag_with_modified_file(sbox):
259  "tag with a modified file"
260  run_test(sbox, "tag-with-modified-file.dump")
261
262#----------------------------------------------------------------------
263
264def dir_prop_change(sbox):
265  "directory property changes"
266  run_test(sbox, "dir-prop-change.dump")
267
268#----------------------------------------------------------------------
269
270def file_dir_file(sbox):
271  "files and dirs mixed together"
272  run_test(sbox, "file-dir-file.dump")
273
274#----------------------------------------------------------------------
275
276def copy_parent_modify_prop(sbox):
277  "copy parent and modify prop"
278  run_test(sbox, "copy-parent-modify-prop.dump")
279
280#----------------------------------------------------------------------
281
282def detect_meddling(sbox):
283  "detect non-svnsync commits in destination"
284
285  sbox.build("svnsync-meddling")
286
287  dest_sbox = sbox.clone_dependent()
288  dest_sbox.build(create_wc=False, empty=True)
289
290  # Make our own destination checkout (have to do it ourself because
291  # it is not greek).
292
293  svntest.main.safe_rmtree(dest_sbox.wc_dir)
294  svntest.actions.run_and_verify_svn(None,
295                                     [],
296                                     'co',
297                                     dest_sbox.repo_url,
298                                     dest_sbox.wc_dir)
299
300  svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
301
302  run_init(dest_sbox.repo_url, sbox.repo_url)
303  run_sync(dest_sbox.repo_url)
304
305  svntest.actions.run_and_verify_svn(None,
306                                     [],
307                                     'up',
308                                     dest_sbox.wc_dir)
309
310  # Commit some change to the destination, which should be detected by svnsync
311  svntest.main.file_append(os.path.join(dest_sbox.wc_dir, 'A', 'B', 'lambda'),
312                           'new lambda text')
313  svntest.actions.run_and_verify_svn(None,
314                                     [],
315                                     'ci',
316                                     '-m', 'msg',
317                                     dest_sbox.wc_dir)
318
319  expected_error = r".*Destination HEAD \(2\) is not the last merged revision \(1\).*"
320  run_sync(dest_sbox.repo_url, None,
321           expected_output=[], expected_error=expected_error)
322
323def url_encoding(sbox):
324  "test url encoding issues"
325  run_test(sbox, "url-encoding-bug.dump")
326
327
328# A test for copying revisions that lack a property that already exists
329# on the destination rev as part of the commit (i.e. svn:author in this
330# case, but svn:date would also work).
331def no_author(sbox):
332  "test copying revs with no svn:author revprops"
333  run_test(sbox, "no-author.dump")
334
335def copy_revprops(sbox):
336  "test copying revprops other than svn:*"
337  run_test(sbox, "revprops.dump")
338
339@SkipUnless(server_has_partial_replay)
340def only_trunk(sbox):
341  "test syncing subdirectories"
342  run_test(sbox, "svnsync-trunk-only.dump", "/trunk",
343           "svnsync-trunk-only.expected.dump")
344
345@SkipUnless(server_has_partial_replay)
346def only_trunk_A_with_changes(sbox):
347  "test syncing subdirectories with changes on root"
348  run_test(sbox, "svnsync-trunk-A-changes.dump", "/trunk/A",
349           "svnsync-trunk-A-changes.expected.dump")
350
351# test for issue #2904
352@Issue(2904)
353def move_and_modify_in_the_same_revision(sbox):
354  "test move parent and modify child file in same rev"
355  run_test(sbox, "svnsync-move-and-modify.dump")
356
357def info_synchronized(sbox):
358  "test info cmd on a synchronized repo"
359
360  sbox.build("svnsync-info-syncd", False)
361
362  # Get the UUID of the source repository.
363  exit_code, output, errput = svntest.main.run_svnlook("uuid", sbox.repo_dir)
364  src_uuid = output[0].strip()
365
366  dest_sbox = sbox.clone_dependent()
367  dest_sbox.build(create_wc=False, empty=True)
368
369  svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
370  run_init(dest_sbox.repo_url, sbox.repo_url)
371  run_sync(dest_sbox.repo_url)
372
373  expected_out = ['Source URL: %s\n' % sbox.repo_url,
374                  'Source Repository UUID: %s\n' % src_uuid,
375                  'Last Merged Revision: 1\n',
376                  ]
377  svntest.actions.run_and_verify_svnsync(expected_out, [],
378                                         "info", dest_sbox.repo_url)
379
380def info_not_synchronized(sbox):
381  "test info cmd on an un-synchronized repo"
382
383  sbox.build("svnsync-info-not-syncd", False)
384
385  run_info(sbox.repo_url,
386           [], ".*Repository '%s' is not initialized.*" % sbox.repo_url)
387
388#----------------------------------------------------------------------
389
390def copy_bad_line_endings(sbox):
391  "copy with inconsistent line endings in svn:* props"
392  run_test(sbox, "copy-bad-line-endings.dump",
393           exp_dump_file_name="copy-bad-line-endings.expected.dump",
394           bypass_prop_validation=True)
395
396def copy_bad_line_endings2(sbox):
397  "copy with non-LF line endings in svn:* props"
398  run_test(sbox, "copy-bad-line-endings2.dump",
399           exp_dump_file_name="copy-bad-line-endings2.expected.dump",
400           bypass_prop_validation=True)
401
402def copy_bad_encoding(sbox):
403  "copy and reencode non-UTF-8 svn:* props"
404  run_test(sbox, "copy-bad-encoding.dump",
405           exp_dump_file_name="copy-bad-encoding.expected.dump",
406           bypass_prop_validation=True, source_prop_encoding="ISO-8859-3")
407
408#----------------------------------------------------------------------
409
410def delete_svn_props(sbox):
411  "copy with svn:* prop deletions"
412  run_test(sbox, "delete-svn-props.dump")
413
414@Issue(3438)
415def commit_a_copy_of_root(sbox):
416  "commit a copy of root causes sync to fail"
417  #Testcase for issue 3438.
418  run_test(sbox, "repo-with-copy-of-root-dir.dump")
419
420
421# issue #3641 'svnsync fails to partially copy a repository'.
422# This currently fails because while replacements with history
423# within copies are handled, replacements without history inside
424# copies cause the sync to fail:
425#
426#   >svnsync synchronize %TEST_REPOS_ROOT_URL%/svnsync_tests-29-1
427#    %TEST_REPOS_ROOT_URL%/svnsync_tests-29/trunk/H
428#   Transmitting file data ...\..\..\subversion\svnsync\main.c:1444: (apr_err=160013)
429#   ..\..\..\subversion\svnsync\main.c:1391: (apr_err=160013)
430#   ..\..\..\subversion\libsvn_ra\ra_loader.c:1168: (apr_err=160013)
431#   ..\..\..\subversion\libsvn_delta\path_driver.c:254: (apr_err=160013)
432#   ..\..\..\subversion\libsvn_repos\replay.c:480: (apr_err=160013)
433#   ..\..\..\subversion\libsvn_repos\replay.c:276: (apr_err=160013)
434#   ..\..\..\subversion\libsvn_repos\replay.c:290: (apr_err=160013)
435#   ..\..\..\subversion\libsvn_fs_base\tree.c:1258: (apr_err=160013)
436#   ..\..\..\subversion\libsvn_fs_base\tree.c:1258: (apr_err=160013)
437#   ..\..\..\subversion\libsvn_fs_base\tree.c:1236: (apr_err=160013)
438#   ..\..\..\subversion\libsvn_fs_base\tree.c:931: (apr_err=160013)
439#   ..\..\..\subversion\libsvn_fs_base\tree.c:742: (apr_err=160013)
440#   svnsync: File not found: revision 4, path '/trunk/H/Z/B/lambda'
441#
442# See also http://svn.haxx.se/dev/archive-2010-11/0411.shtml and
443#
444#
445# Note: For those who may poke around this test in the future, r3 of
446# delete-revprops.dump was created with the following svnmucc command:
447#
448# svnmucc.exe -mm cp head %ROOT_URL%/trunk/A %ROOT_URL%/trunk/H
449#                 rm %ROOT_URL%/trunk/H/B
450#                 cp head %ROOT_URL%/trunk/X %ROOT_URL%/trunk/B
451#
452# r4 was created with this svnmucc command:
453#
454# svnmucc.exe -mm cp head %ROOT_URL%/trunk/A %ROOT_URL%/trunk/H/Z
455#                 rm %ROOT_URL%/trunk/H/Z/B
456#                 mkdir %ROOT_URL%/trunk/H/Z/B
457@Issue(3641)
458def descend_into_replace(sbox):
459  "descending into replaced dir looks in src"
460  run_test(sbox, "descend-into-replace.dump", subdir='/trunk/H',
461           exp_dump_file_name = "descend-into-replace.expected.dump")
462
463# issue #3728
464@Issue(3728)
465def delete_revprops(sbox):
466  "copy-revprops with removals"
467  svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
468                                   'svnsync_tests_data')
469  initial_contents  = open(os.path.join(svnsync_tests_dir,
470                                        "delete-revprops.dump"),
471                           'rb').readlines()
472  expected_contents = open(os.path.join(svnsync_tests_dir,
473                                        "delete-revprops.expected.dump"),
474                           'rb').readlines()
475
476  # Create the initial repos and mirror, and sync 'em.
477  dest_sbox = setup_and_sync(sbox, initial_contents)
478
479  # Now remove a revprop from r1 of the source, and run 'svnsync
480  # copy-revprops' to re-sync 'em.
481  svntest.actions.enable_revprop_changes(sbox.repo_dir)
482  exit_code, out, err = svntest.main.run_svn(None,
483                                             'pdel',
484                                             '-r', '1',
485                                             '--revprop',
486                                             'issue-id',
487                                             sbox.repo_url)
488  if err:
489    raise SVNUnexpectedStderr(err)
490  run_copy_revprops(dest_sbox.repo_url, sbox.repo_url)
491
492  # Does the result look as we expected?
493  verify_mirror(dest_sbox, expected_contents)
494
495@Issue(3870)
496@SkipUnless(svntest.main.is_posix_os)
497def fd_leak_sync_from_serf_to_local(sbox):
498  "fd leak during sync from serf to local"
499  import resource
500  resource.setrlimit(resource.RLIMIT_NOFILE, (128, 128))
501  run_test(sbox, "largemods.dump", is_src_ra_local=None, is_dest_ra_local=True)
502
503#----------------------------------------------------------------------
504
505@Issue(4476)
506def mergeinfo_contains_r0(sbox):
507  "mergeinfo contains r0"
508
509  def make_node_record(node_name, mi):
510    """Return a dumpfile node-record for adding a (directory) node named
511       NODE_NAME with mergeinfo MI. Return it as a list of newline-terminated
512       lines.
513    """
514    headers_tmpl = """\
515Node-path: %s
516Node-kind: dir
517Node-action: add
518Prop-content-length: %d
519Content-length: %d
520"""
521    content_tmpl = """\
522K 13
523svn:mergeinfo
524V %d
525%s
526PROPS-END
527"""
528    content = content_tmpl % (len(mi), mi)
529    headers = headers_tmpl % (node_name, len(content), len(content))
530    record = (headers + '\n' + content + '\n\n').encode()
531    return record.splitlines(True)
532
533  # The test case mergeinfo (before, after) syncing, separated here with
534  # spaces instead of newlines
535  test_mi = [
536    ("",            ""),  # unchanged
537    ("/a:1",        "/a:1"),
538    ("/a:1 /b:1*,2","/a:1 /b:1*,2"),
539    ("/:0:1",       "/:0:1"),  # unchanged; colon-zero in filename
540    ("/a:0",        ""),  # dropped entirely
541    ("/a:0*",       ""),
542    ("/a:0 /b:0*",  ""),
543    ("/a:1 /b:0",   "/a:1"),  # one kept, one dropped
544    ("/a:0 /b:1",   "/b:1"),
545    ("/a:0,1 /b:1", "/a:1 /b:1"),  # one kept, one changed
546    ("/a:1 /b:0,1", "/a:1 /b:1"),
547    ("/a:0,1 /b:0*,1 /c:0,2 /d:0-1 /e:0-1,3 /f:0-2 /g:0-3",
548     "/a:1 /b:1 /c:2 /d:1 /e:1,3 /f:1-2 /g:1-3"),  # all changed
549    ("/a:0:0-1",    "/a:0:1"),  # changed; colon-zero in filename
550    ]
551
552  # Get the constant prefix for each dumpfile
553  dump_file_name = "mergeinfo-contains-r0.dump"
554  svnsync_tests_dir = os.path.join(os.path.dirname(sys.argv[0]),
555                                   'svnsync_tests_data')
556  dump_in = open(os.path.join(svnsync_tests_dir, dump_file_name),
557                 'rb').readlines()
558  dump_out = list(dump_in)  # duplicate the list
559
560  # Add dumpfile node records containing the test mergeinfo
561  for n, mi in enumerate(test_mi):
562    node_name = "D" + str(n)
563
564    mi_in = mi[0].replace(' ', '\n')
565    mi_out = mi[1].replace(' ', '\n')
566    dump_in.extend(make_node_record(node_name, mi_in))
567    dump_out.extend(make_node_record(node_name, mi_out))
568
569  # Run the sync
570  dest_sbox = setup_and_sync(sbox, dump_in, bypass_prop_validation=True)
571
572  # Compare the dump produced by the mirror repository with expected
573  verify_mirror(dest_sbox, dump_out)
574
575def up_to_date_sync(sbox):
576  """sync that does nothing"""
577
578  # An up-to-date mirror.
579  sbox.build(create_wc=False)
580  dest_sbox = sbox.clone_dependent()
581  dest_sbox.build(create_wc=False, empty=True)
582  svntest.actions.enable_revprop_changes(dest_sbox.repo_dir)
583  run_init(dest_sbox.repo_url, sbox.repo_url)
584  run_sync(dest_sbox.repo_url)
585
586  # Another sync should be a no-op
587  svntest.actions.run_and_verify_svnsync([], [],
588                                         "synchronize", dest_sbox.repo_url)
589
590
591########################################################################
592# Run the tests
593
594
595# list all tests here, starting with None:
596test_list = [ None,
597              copy_and_modify,
598              copy_from_previous_version_and_modify,
599              copy_from_previous_version,
600              modified_in_place,
601              tag_empty_trunk,
602              tag_trunk_with_dir,
603              tag_trunk_with_file,
604              tag_trunk_with_file2,
605              tag_with_modified_file,
606              dir_prop_change,
607              file_dir_file,
608              copy_parent_modify_prop,
609              detect_meddling,
610              url_encoding,
611              no_author,
612              copy_revprops,
613              only_trunk,
614              only_trunk_A_with_changes,
615              move_and_modify_in_the_same_revision,
616              info_synchronized,
617              info_not_synchronized,
618              copy_bad_line_endings,
619              copy_bad_line_endings2,
620              copy_bad_encoding,
621              delete_svn_props,
622              commit_a_copy_of_root,
623              descend_into_replace,
624              delete_revprops,
625              fd_leak_sync_from_serf_to_local, # calls setrlimit
626              mergeinfo_contains_r0,
627              up_to_date_sync,
628             ]
629
630if __name__ == '__main__':
631  svntest.main.run_tests(test_list)
632  # NOTREACHED
633
634
635### End of file.
636