3 # run_tests.py: test suite for cvs2svn
5 # Usage: run_tests.py [-v | --verbose] [list | <num>]
9 # enable verbose output
11 # Arguments (at most one argument is allowed):
13 # If the word "list" is passed as an argument, the list of
14 # available tests is printed (but no tests are run).
17 # If a number is passed as an argument, then only the test
18 # with that number is run.
20 # If no argument is specified, then all tests are run.
22 # Subversion is a tool for revision control.
23 # See http://subversion.tigris.org for more information.
25 # ====================================================================
26 # Copyright (c) 2000-2007 CollabNet. All rights reserved.
28 # This software is licensed as described in the file COPYING, which
29 # you should have received as part of this distribution. The terms
30 # are also available at http://subversion.tigris.org/license-1.html.
31 # If newer versions of this license are posted there, you may use a
32 # newer version instead, at your option.
34 ######################################################################
46 from difflib
import Differ
48 # Make sure that a supported version of Python is being used:
49 if not (0x02040000 <= sys
.hexversion
< 0x03000000):
51 'error: Python 2, version 2.4 or higher required.\n'
55 # This script needs to run in the correct directory. Make sure we're there.
56 if not (os
.path
.exists('cvs2svn') and os
.path
.exists('test-data')):
57 sys
.stderr
.write("error: I need to be run in the directory containing "
58 "'cvs2svn' and 'test-data'.\n")
61 # Load the Subversion test framework.
63 from svntest
import Failure
64 from svntest
.main
import run_command
65 from svntest
.main
import run_tests
66 from svntest
.main
import safe_rmtree
67 from svntest
.testcase
import TestCase
68 from svntest
.testcase
import Skip
69 from svntest
.testcase
import XFail
70 from svntest
.tree
import build_tree_from_wc
71 from svntest
.tree
import get_child
73 cvs2svn
= os
.path
.abspath('cvs2svn')
75 # We use the installed svn and svnlook binaries, instead of using
76 # svntest.main.run_svn() and svntest.main.run_svnlook(), because the
77 # behavior -- or even existence -- of local builds shouldn't affect
78 # the cvs2svn test suite.
82 test_data_dir
= 'test-data'
83 tmp_dir
= 'cvs2svn-tmp'
86 #----------------------------------------------------------------------
88 #----------------------------------------------------------------------
91 # The value to expect for svn:keywords if it is set:
92 KEYWORDS
= 'Author Date Id Revision'
95 class RunProgramException(Failure
):
99 class MissingErrorException(Failure
):
100 def __init__(self
, error_re
):
102 self
, "Test failed because no error matched '%s'" % (error_re
,)
106 def run_program(program
, error_re
, *varargs
):
107 """Run PROGRAM with VARARGS, return stdout as a list of lines.
109 If there is any stderr and ERROR_RE is None, raise
110 RunProgramException, and print the stderr lines if
111 svntest.main.verbose_mode is true.
113 If ERROR_RE is not None, it is a string regular expression that must
114 match some line of stderr. If it fails to match, raise
115 MissingErrorExpection."""
117 # FIXME: exit_code is currently ignored.
118 exit_code
, out
, err
= run_command(program
, 1, 0, *varargs
)
121 # Specified error expected on stderr.
123 raise MissingErrorException(error_re
)
126 if re
.match(error_re
, line
):
128 raise MissingErrorException(error_re
)
132 if svntest
.main
.verbose_mode
:
133 print '\n%s said:\n' % program
137 raise RunProgramException()
142 def run_cvs2svn(error_re
, *varargs
):
143 """Run cvs2svn with VARARGS, return stdout as a list of lines.
145 If there is any stderr and ERROR_RE is None, raise
146 RunProgramException, and print the stderr lines if
147 svntest.main.verbose_mode is true.
149 If ERROR_RE is not None, it is a string regular expression that must
150 match some line of stderr. If it fails to match, raise
151 MissingErrorException."""
153 # Use the same python that is running this script
154 return run_program(sys
.executable
, error_re
, cvs2svn
, *varargs
)
155 # On Windows, for an unknown reason, the cmd.exe process invoked by
156 # os.system('sort ...') in cvs2svn receives invalid stdio handles, if
157 # cvs2svn is started as "cvs2svn ...". "python cvs2svn ..." avoids
158 # this. Therefore, the redirection of the output to the .s-revs file fails.
159 # We no longer use the problematic invocation on any system, but this
160 # comment remains to warn about this problem.
163 def run_svn(*varargs
):
164 """Run svn with VARARGS; return stdout as a list of lines.
165 If there is any stderr, raise RunProgramException, and print the
166 stderr lines if svntest.main.verbose_mode is true."""
167 return run_program(svn
, None, *varargs
)
170 def repos_to_url(path_to_svn_repos
):
171 """This does what you think it does."""
172 rpath
= os
.path
.abspath(path_to_svn_repos
)
175 return 'file://%s' % rpath
.replace(os
.sep
, '/')
178 def svn_strptime(timestr
):
179 return time
.strptime(timestr
, '%Y-%m-%d %H:%M:%S')
183 def __init__(self
, revision
, author
, date
, symbols
):
184 self
.revision
= revision
187 # Internally, we represent the date as seconds since epoch (UTC).
188 # Since standard subversion log output shows dates in localtime
190 # "1993-06-18 00:46:07 -0500 (Fri, 18 Jun 1993)"
192 # and time.mktime() converts from localtime, it all works out very
194 self
.date
= time
.mktime(svn_strptime(date
[0:19]))
196 # The following symbols are used for string interpolation when
198 self
.symbols
= symbols
200 # The changed paths will be accumulated later, as log data is read.
201 # Keys here are paths such as '/trunk/foo/bar', values are letter
202 # codes such as 'M', 'A', and 'D'.
203 self
.changed_paths
= { }
205 # The msg will be accumulated later, as log data is read.
208 def absorb_changed_paths(self
, out
):
209 'Read changed paths from OUT into self, until no more.'
211 line
= out
.readline()
212 if len(line
) == 1: return
214 op_portion
= line
[3:4]
215 path_portion
= line
[5:]
216 # If we're running on Windows we get backslashes instead of
218 path_portion
= path_portion
.replace('\\', '/')
219 # # We could parse out history information, but currently we
220 # # just leave it in the path portion because that's how some
223 # m = re.match("(.*) \(from /.*:[0-9]+\)", path_portion)
225 # path_portion = m.group(1)
226 self
.changed_paths
[path_portion
] = op_portion
228 def __cmp__(self
, other
):
229 return cmp(self
.revision
, other
.revision
) or \
230 cmp(self
.author
, other
.author
) or cmp(self
.date
, other
.date
) or \
231 cmp(self
.changed_paths
, other
.changed_paths
) or \
232 cmp(self
.msg
, other
.msg
)
234 def get_path_op(self
, path
):
235 """Return the operator for the change involving PATH.
237 PATH is allowed to include string interpolation directives (e.g.,
238 '%(trunk)s'), which are interpolated against self.symbols. Return
239 None if there is no record for PATH."""
240 return self
.changed_paths
.get(path
% self
.symbols
)
242 def check_msg(self
, msg
):
243 """Verify that this Log's message starts with the specified MSG."""
244 if self
.msg
.find(msg
) != 0:
246 "Revision %d log message was:\n%s\n\n"
247 "It should have begun with:\n%s\n\n"
248 % (self
.revision
, self
.msg
, msg
,)
251 def check_change(self
, path
, op
):
252 """Verify that this Log includes a change for PATH with operator OP.
254 PATH is allowed to include string interpolation directives (e.g.,
255 '%(trunk)s'), which are interpolated against self.symbols."""
257 path
= path
% self
.symbols
258 found_op
= self
.changed_paths
.get(path
, None)
261 "Revision %d does not include change for path %s "
262 "(it should have been %s).\n"
263 % (self
.revision
, path
, op
,)
267 "Revision %d path %s had op %s (it should have been %s)\n"
268 % (self
.revision
, path
, found_op
, op
,)
271 def check_changes(self
, changed_paths
):
272 """Verify that this Log has precisely the CHANGED_PATHS specified.
274 CHANGED_PATHS is a sequence of tuples (path, op), where the paths
275 strings are allowed to include string interpolation directives
276 (e.g., '%(trunk)s'), which are interpolated against self.symbols."""
279 for (path
, op
) in changed_paths
:
280 cp
[path
% self
.symbols
] = op
282 if self
.changed_paths
!= cp
:
284 "Revision %d changed paths list was:\n%s\n\n"
285 "It should have been:\n%s\n\n"
286 % (self
.revision
, self
.changed_paths
, cp
,)
289 def check(self
, msg
, changed_paths
):
290 """Verify that this Log has the MSG and CHANGED_PATHS specified.
292 Convenience function to check two things at once. MSG is passed
293 to check_msg(); CHANGED_PATHS is passed to check_changes()."""
296 self
.check_changes(changed_paths
)
299 def parse_log(svn_repos
, symbols
):
300 """Return a dictionary of Logs, keyed on revision number, for SVN_REPOS.
302 Initialize the Logs' symbols with SYMBOLS."""
305 'Make a list of lines behave like an open file handle.'
306 def __init__(self
, lines
):
309 if len(self
.lines
) > 0:
310 return self
.lines
.pop(0)
314 def absorb_message_body(out
, num_lines
, log
):
315 """Read NUM_LINES of log message body from OUT into Log item LOG."""
317 for i
in range(num_lines
):
318 log
.msg
+= out
.readline()
320 log_start_re
= re
.compile('^r(?P<rev>[0-9]+) \| '
321 '(?P<author>[^\|]+) \| '
323 '\| (?P<lines>[0-9]+) (line|lines)$')
325 log_separator
= '-' * 72
329 out
= LineFeeder(run_svn('log', '-v', repos_to_url(svn_repos
)))
333 line
= out
.readline()
337 if line
.find(log_separator
) == 0:
338 line
= out
.readline()
341 m
= log_start_re
.match(line
)
344 int(m
.group('rev')), m
.group('author'), m
.group('date'), symbols
)
345 line
= out
.readline()
346 if not line
.find('Changed paths:') == 0:
347 print 'unexpected log output (missing changed paths)'
348 print "Line: '%s'" % line
350 this_log
.absorb_changed_paths(out
)
351 absorb_message_body(out
, int(m
.group('lines')), this_log
)
352 logs
[this_log
.revision
] = this_log
354 break # We've reached the end of the log output.
356 print 'unexpected log output (missing revision line)'
357 print "Line: '%s'" % line
360 print 'unexpected log output (missing log separator)'
361 print "Line: '%s'" % line
368 """Unconditionally remove PATH and its subtree, if any. PATH may be
369 non-existent, a file or symlink, or a directory."""
370 if os
.path
.isdir(path
):
372 elif os
.path
.exists(path
):
376 log_msg_text_wrapper
= textwrap
.TextWrapper(width
=76)
378 def sym_log_msg(symbolic_name
, is_tag
=None):
379 """Return the expected log message for a cvs2svn-synthesized revision
380 creating branch or tag SYMBOLIC_NAME."""
382 # This reproduces the logic in SVNSymbolCommit.get_log_msg().
388 return log_msg_text_wrapper
.fill(
389 "This commit was manufactured by cvs2svn to create %s '%s'."
390 % (type, symbolic_name
)
394 def make_conversion_id(
395 name
, args
, passbypass
, options_file
=None, symbol_hints_file
=None
397 """Create an identifying tag for a conversion.
399 The return value can also be used as part of a filesystem path.
401 NAME is the name of the CVS repository.
403 ARGS are the extra arguments to be passed to cvs2svn.
405 PASSBYPASS is a boolean indicating whether the conversion is to be
406 run one pass at a time.
408 If OPTIONS_FILE is specified, it is an options file that will be
409 used for the conversion.
411 If SYMBOL_HINTS_FILE is specified, it is a symbol hints file that
412 will be used for the conversion.
414 The 1-to-1 mapping between cvs2svn command parameters and
415 conversion_ids allows us to avoid running the same conversion more
416 than once, when multiple tests use exactly the same conversion."""
420 _win32_fname_mapping
= { '/': '_sl_', '\\': '_bs_', ':': '_co_',
421 '*': '_st_', '?': '_qm_', '"': '_qq_',
422 '<': '_lt_', '>': '_gt_', '|': '_pi_', }
424 # Replace some characters that Win32 isn't happy about having in a
425 # filename (which was causing the eol_mime test to fail).
427 for a
, b
in _win32_fname_mapping
.items():
428 sanitized_arg
= sanitized_arg
.replace(a
, b
)
429 conv_id
+= sanitized_arg
432 conv_id
+= '-passbypass'
434 if options_file
is not None:
435 conv_id
+= '--options=%s' % options_file
437 if symbol_hints_file
is not None:
438 conv_id
+= '--symbol-hints=%s' % symbol_hints_file
444 """A record of a cvs2svn conversion.
448 conv_id -- the conversion id for this Conversion.
450 name -- a one-word name indicating the involved repositories.
452 dumpfile -- the name of the SVN dumpfile created by the conversion
453 (if the DUMPFILE constructor argument was used); otherwise,
456 repos -- the path to the svn repository. Unset if DUMPFILE was
459 logs -- a dictionary of Log instances, as returned by parse_log().
460 Unset if DUMPFILE was specified.
462 symbols -- a dictionary of symbols used for string interpolation
465 stdout -- a list of lines written by cvs2svn to stdout
467 _wc -- the basename of the svn working copy (within tmp_dir).
468 Unset if DUMPFILE was specified.
470 _wc_path -- the path to the svn working copy, if it has already
471 been created; otherwise, None. (The working copy is created
472 lazily when get_wc() is called.) Unset if DUMPFILE was
475 _wc_tree -- the tree built from the svn working copy, if it has
476 already been created; otherwise, None. The tree is created
477 lazily when get_wc_tree() is called.) Unset if DUMPFILE was
480 _svnrepos -- the basename of the svn repository (within tmp_dir).
481 Unset if DUMPFILE was specified."""
483 # The number of the last cvs2svn pass (determined lazily by
488 def get_last_pass(cls
):
489 """Return the number of cvs2svn's last pass."""
491 if cls
.last_pass
is None:
492 out
= run_cvs2svn(None, '--help-passes')
493 cls
.last_pass
= int(out
[-1].split()[0])
497 self
, conv_id
, name
, error_re
, passbypass
, symbols
, args
,
498 options_file
=None, symbol_hints_file
=None, dumpfile
=None,
500 self
.conv_id
= conv_id
502 self
.symbols
= symbols
503 if not os
.path
.isdir(tmp_dir
):
506 cvsrepos
= os
.path
.join(test_data_dir
, '%s-cvsrepos' % self
.name
)
509 self
.dumpfile
= os
.path
.join(tmp_dir
, dumpfile
)
510 # Clean up from any previous invocations of this script.
514 self
.repos
= os
.path
.join(tmp_dir
, '%s-svnrepos' % self
.conv_id
)
515 self
._wc
= os
.path
.join(tmp_dir
, '%s-wc' % self
.conv_id
)
519 # Clean up from any previous invocations of this script.
525 self
.options_file
= os
.path
.join(cvsrepos
, options_file
)
527 '--options=%s' % self
.options_file
,
529 assert not symbol_hints_file
531 self
.options_file
= None
532 if tmp_dir
!= 'cvs2svn-tmp':
533 # Only include this argument if it differs from cvs2svn's default:
535 '--tmpdir=%s' % tmp_dir
,
538 if symbol_hints_file
:
539 self
.symbol_hints_file
= os
.path
.join(cvsrepos
, symbol_hints_file
)
541 '--symbol-hints=%s' % self
.symbol_hints_file
,
545 args
.extend(['--dumpfile=%s' % (self
.dumpfile
,)])
547 args
.extend(['-s', self
.repos
])
548 args
.extend([cvsrepos
])
552 for p
in range(1, self
.get_last_pass() + 1):
553 self
.stdout
+= run_cvs2svn(error_re
, '-p', str(p
), *args
)
555 self
.stdout
= run_cvs2svn(error_re
, *args
)
558 if not os
.path
.isfile(self
.dumpfile
):
560 "Dumpfile not created: '%s'"
561 % os
.path
.join(os
.getcwd(), self
.dumpfile
)
564 if os
.path
.isdir(self
.repos
):
565 self
.logs
= parse_log(self
.repos
, self
.symbols
)
566 elif error_re
is None:
568 "Repository not created: '%s'"
569 % os
.path
.join(os
.getcwd(), self
.repos
)
572 def output_found(self
, pattern
):
573 """Return True if PATTERN matches any line in self.stdout.
575 PATTERN is a regular expression pattern as a string.
578 pattern_re
= re
.compile(pattern
)
580 for line
in self
.stdout
:
581 if pattern_re
.match(line
):
582 # We found the pattern that we were looking for.
587 def find_tag_log(self
, tagname
):
588 """Search LOGS for a log message containing 'TAGNAME' and return the
589 log in which it was found."""
590 for i
in xrange(len(self
.logs
), 0, -1):
591 if self
.logs
[i
].msg
.find("'"+tagname
+"'") != -1:
593 raise ValueError("Tag %s not found in logs" % tagname
)
595 def get_wc(self
, *args
):
596 """Return the path to the svn working copy, or a path within the WC.
598 If a working copy has not been created yet, create it now.
600 If ARGS are specified, then they should be strings that form
601 fragments of a path within the WC. They are joined using
602 os.path.join() and appended to the WC path."""
604 if self
._wc
_path
is None:
605 run_svn('co', repos_to_url(self
.repos
), self
._wc
)
606 self
._wc
_path
= self
._wc
607 return os
.path
.join(self
._wc
_path
, *args
)
609 def get_wc_tree(self
):
610 if self
._wc
_tree
is None:
611 self
._wc
_tree
= build_tree_from_wc(self
.get_wc(), 1)
614 def path_exists(self
, *args
):
615 """Return True if the specified path exists within the repository.
617 (The strings in ARGS are first joined into a path using
620 return os
.path
.exists(self
.get_wc(*args
))
622 def check_props(self
, keys
, checks
):
623 """Helper function for checking lots of properties. For a list of
624 files in the conversion, check that the values of the properties
625 listed in KEYS agree with those listed in CHECKS. CHECKS is a
626 list of tuples: [ (filename, [value, value, ...]), ...], where the
627 values are listed in the same order as the key names are listed in
630 for (file, values
) in checks
:
631 assert len(values
) == len(keys
)
632 props
= props_for_path(self
.get_wc_tree(), file)
633 for i
in range(len(keys
)):
634 if props
.get(keys
[i
]) != values
[i
]:
636 "File %s has property %s set to \"%s\" "
637 "(it should have been \"%s\").\n"
638 % (file, keys
[i
], props
.get(keys
[i
]), values
[i
],)
642 # Cache of conversions that have already been done. Keys are conv_id;
643 # values are Conversion instances.
644 already_converted
= { }
646 def ensure_conversion(
647 name
, error_re
=None, passbypass
=None,
648 trunk
=None, branches
=None, tags
=None,
649 args
=None, options_file
=None, symbol_hints_file
=None, dumpfile
=None,
651 """Convert CVS repository NAME to Subversion, but only if it has not
652 been converted before by this invocation of this script. If it has
653 been converted before, return the Conversion object from the
656 If no error, return a Conversion instance.
658 If ERROR_RE is a string, it is a regular expression expected to
659 match some line of stderr printed by the conversion. If there is an
660 error and ERROR_RE is not set, then raise Failure.
662 If PASSBYPASS is set, then cvs2svn is run multiple times, each time
663 with a -p option starting at 1 and increasing to a (hardcoded) maximum.
665 NAME is just one word. For example, 'main' would mean to convert
666 './test-data/main-cvsrepos', and after the conversion, the resulting
667 Subversion repository would be in './cvs2svn-tmp/main-svnrepos', and
668 a checked out head working copy in './cvs2svn-tmp/main-wc'.
670 Any other options to pass to cvs2svn should be in ARGS, each element
671 being one option, e.g., '--trunk-only'. If the option takes an
672 argument, include it directly, e.g., '--mime-types=PATH'. Arguments
673 are passed to cvs2svn in the order that they appear in ARGS.
675 If OPTIONS_FILE is specified, then it should be the name of a file
676 within the main directory of the cvs repository associated with this
677 test. It is passed to cvs2svn using the --options option (which
678 suppresses some other options that are incompatible with --options).
680 If SYMBOL_HINTS_FILE is specified, then it should be the name of a
681 file within the main directory of the cvs repository associated with
682 this test. It is passed to cvs2svn using the --symbol-hints option.
684 If DUMPFILE is specified, then it is the name of a dumpfile within
685 the temporary directory to which the conversion output should be
696 args
.append('--trunk=%s' % (trunk
,))
699 branches
= 'branches'
701 args
.append('--branches=%s' % (branches
,))
706 args
.append('--tags=%s' % (tags
,))
708 conv_id
= make_conversion_id(
709 name
, args
, passbypass
, options_file
, symbol_hints_file
712 if conv_id
not in already_converted
:
714 # Run the conversion and store the result for the rest of this
716 already_converted
[conv_id
] = Conversion(
717 conv_id
, name
, error_re
, passbypass
,
718 {'trunk' : trunk
, 'branches' : branches
, 'tags' : tags
},
719 args
, options_file
, symbol_hints_file
, dumpfile
,
722 # Remember the failure so that a future attempt to run this conversion
723 # does not bother to retry, but fails immediately.
724 already_converted
[conv_id
] = None
727 conv
= already_converted
[conv_id
]
733 class Cvs2SvnTestCase(TestCase
):
735 self
, name
, description
=None, variant
=None,
736 error_re
=None, passbypass
=None,
737 trunk
=None, branches
=None, tags
=None,
739 options_file
=None, symbol_hints_file
=None, dumpfile
=None,
741 TestCase
.__init
__(self
)
744 if description
is not None:
745 self
._description
= description
747 # By default, use the first line of the class docstring as the
749 self
._description
= self
.__doc
__.splitlines()[0]
751 # Check that the original description is OK before we tinker with
753 self
.check_description()
755 if variant
is not None:
756 # Modify description to show the variant. Trim description
757 # first if necessary to stay within the 50-character limit.
758 suffix
= '...variant %s' % (variant
,)
759 self
._description
= self
._description
[:50 - len(suffix
)] + suffix
760 # Check that the description is still OK:
761 self
.check_description()
763 self
.error_re
= error_re
764 self
.passbypass
= passbypass
766 self
.branches
= branches
769 self
.options_file
= options_file
770 self
.symbol_hints_file
= symbol_hints_file
771 self
.dumpfile
= dumpfile
773 def get_description(self
):
774 return self
._description
776 def ensure_conversion(self
):
777 return ensure_conversion(
779 error_re
=self
.error_re
, passbypass
=self
.passbypass
,
780 trunk
=self
.trunk
, branches
=self
.branches
, tags
=self
.tags
,
782 options_file
=self
.options_file
,
783 symbol_hints_file
=self
.symbol_hints_file
,
784 dumpfile
=self
.dumpfile
,
788 class Cvs2SvnPropertiesTestCase(Cvs2SvnTestCase
):
789 """Test properties resulting from a conversion."""
791 def __init__(self
, name
, props_to_test
, expected_props
, **kw
):
792 """Initialize an instance of Cvs2SvnPropertiesTestCase.
794 NAME is the name of the test, passed to Cvs2SvnTestCase.
795 PROPS_TO_TEST is a list of the names of svn properties that should
796 be tested. EXPECTED_PROPS is a list of tuples [(filename,
797 [value,...])], where the second item in each tuple is a list of
798 values expected for the properties listed in PROPS_TO_TEST for the
799 specified filename. If a property must *not* be set, then its
800 value should be listed as None."""
802 Cvs2SvnTestCase
.__init
__(self
, name
, **kw
)
803 self
.props_to_test
= props_to_test
804 self
.expected_props
= expected_props
807 conv
= self
.ensure_conversion()
808 conv
.check_props(self
.props_to_test
, self
.expected_props
)
811 #----------------------------------------------------------------------
813 #----------------------------------------------------------------------
817 "cvs2svn with no arguments shows usage"
818 out
= run_cvs2svn(None)
819 if (len(out
) > 2 and out
[0].find('ERROR:') == 0
820 and out
[1].find('DBM module')):
821 print 'cvs2svn cannot execute due to lack of proper DBM module.'
822 print 'Exiting without running any further tests.'
824 if out
[0].find('Usage:') < 0:
825 raise Failure('Basic cvs2svn invocation failed.')
828 def show_help_passes():
829 "cvs2svn --help-passes shows pass information"
830 out
= run_cvs2svn(None, '--help-passes')
831 if out
[0].find('PASSES') < 0:
832 raise Failure('cvs2svn --help-passes failed.')
836 "detection of the executable flag"
837 if sys
.platform
== 'win32':
839 conv
= ensure_conversion('main')
840 st
= os
.stat(conv
.get_wc('trunk', 'single-files', 'attr-exec'))
841 if not st
[0] & stat
.S_IXUSR
:
846 "conversion of filename with a space"
847 conv
= ensure_conversion('main')
848 if not conv
.path_exists('trunk', 'single-files', 'space fname'):
853 "two commits in quick succession"
854 conv
= ensure_conversion('main')
856 os
.path
.join(conv
.repos
, 'trunk', 'single-files', 'twoquick'), {})
861 class PruneWithCare(Cvs2SvnTestCase
):
862 "prune, but never too much"
864 def __init__(self
, **kw
):
865 Cvs2SvnTestCase
.__init
__(self
, 'main', **kw
)
868 # Robert Pluim encountered this lovely one while converting the
869 # directory src/gnu/usr.bin/cvs/contrib/pcl-cvs/ in FreeBSD's CVS
870 # repository (see issue #1302). Step 4 is the doozy:
872 # revision 1: adds trunk/blah/, adds trunk/blah/cookie
873 # revision 2: adds trunk/blah/NEWS
874 # revision 3: deletes trunk/blah/cookie
875 # revision 4: deletes blah [re-deleting trunk/blah/cookie pruned blah!]
876 # revision 5: does nothing
878 # After fixing cvs2svn, the sequence (correctly) looks like this:
880 # revision 1: adds trunk/blah/, adds trunk/blah/cookie
881 # revision 2: adds trunk/blah/NEWS
882 # revision 3: deletes trunk/blah/cookie
883 # revision 4: does nothing [because trunk/blah/cookie already deleted]
884 # revision 5: deletes blah
886 # The difference is in 4 and 5. In revision 4, it's not correct to
887 # prune blah/, because NEWS is still in there, so revision 4 does
888 # nothing now. But when we delete NEWS in 5, that should bubble up
889 # and prune blah/ instead.
891 # ### Note that empty revisions like 4 are probably going to become
892 # ### at least optional, if not banished entirely from cvs2svn's
893 # ### output. Hmmm, or they may stick around, with an extra
894 # ### revision property explaining what happened. Need to think
895 # ### about that. In some sense, it's a bug in Subversion itself,
896 # ### that such revisions don't show up in 'svn log' output.
898 # In the test below, 'trunk/full-prune/first' represents
899 # cookie, and 'trunk/full-prune/second' represents NEWS.
901 conv
= self
.ensure_conversion()
903 # Confirm that revision 4 removes '/trunk/full-prune/first',
904 # and that revision 6 removes '/trunk/full-prune'.
906 # Also confirm similar things about '/full-prune-reappear/...',
907 # which is similar, except that later on it reappears, restored
908 # from pruneland, because a file gets added to it.
910 # And finally, a similar thing for '/partial-prune/...', except that
911 # in its case, a permanent file on the top level prevents the
912 # pruning from going farther than the subdirectory containing first
915 for path
in ('full-prune/first',
916 'full-prune-reappear/sub/first',
917 'partial-prune/sub/first'):
918 conv
.logs
[5].check_change('/%(trunk)s/' + path
, 'D')
920 for path
in ('full-prune',
921 'full-prune-reappear',
922 'partial-prune/sub'):
923 conv
.logs
[7].check_change('/%(trunk)s/' + path
, 'D')
925 for path
in ('full-prune-reappear',
926 'full-prune-reappear/appears-later'):
927 conv
.logs
[33].check_change('/%(trunk)s/' + path
, 'A')
930 def interleaved_commits():
931 "two interleaved trunk commits, different log msgs"
932 # See test-data/main-cvsrepos/proj/README.
933 conv
= ensure_conversion('main')
935 # The initial import.
937 conv
.logs
[rev
].check('Initial import.', (
938 ('/%(trunk)s/interleaved', 'A'),
939 ('/%(trunk)s/interleaved/1', 'A'),
940 ('/%(trunk)s/interleaved/2', 'A'),
941 ('/%(trunk)s/interleaved/3', 'A'),
942 ('/%(trunk)s/interleaved/4', 'A'),
943 ('/%(trunk)s/interleaved/5', 'A'),
944 ('/%(trunk)s/interleaved/a', 'A'),
945 ('/%(trunk)s/interleaved/b', 'A'),
946 ('/%(trunk)s/interleaved/c', 'A'),
947 ('/%(trunk)s/interleaved/d', 'A'),
948 ('/%(trunk)s/interleaved/e', 'A'),
951 def check_letters(rev
):
952 """Check if REV is the rev where only letters were committed."""
954 conv
.logs
[rev
].check('Committing letters only.', (
955 ('/%(trunk)s/interleaved/a', 'M'),
956 ('/%(trunk)s/interleaved/b', 'M'),
957 ('/%(trunk)s/interleaved/c', 'M'),
958 ('/%(trunk)s/interleaved/d', 'M'),
959 ('/%(trunk)s/interleaved/e', 'M'),
962 def check_numbers(rev
):
963 """Check if REV is the rev where only numbers were committed."""
965 conv
.logs
[rev
].check('Committing numbers only.', (
966 ('/%(trunk)s/interleaved/1', 'M'),
967 ('/%(trunk)s/interleaved/2', 'M'),
968 ('/%(trunk)s/interleaved/3', 'M'),
969 ('/%(trunk)s/interleaved/4', 'M'),
970 ('/%(trunk)s/interleaved/5', 'M'),
973 # One of the commits was letters only, the other was numbers only.
974 # But they happened "simultaneously", so we don't assume anything
975 # about which commit appeared first, so we just try both ways.
979 check_numbers(rev
+ 1)
982 check_letters(rev
+ 1)
985 def simple_commits():
986 "simple trunk commits"
987 # See test-data/main-cvsrepos/proj/README.
988 conv
= ensure_conversion('main')
990 # The initial import.
991 conv
.logs
[13].check('Initial import.', (
992 ('/%(trunk)s/proj', 'A'),
993 ('/%(trunk)s/proj/default', 'A'),
994 ('/%(trunk)s/proj/sub1', 'A'),
995 ('/%(trunk)s/proj/sub1/default', 'A'),
996 ('/%(trunk)s/proj/sub1/subsubA', 'A'),
997 ('/%(trunk)s/proj/sub1/subsubA/default', 'A'),
998 ('/%(trunk)s/proj/sub1/subsubB', 'A'),
999 ('/%(trunk)s/proj/sub1/subsubB/default', 'A'),
1000 ('/%(trunk)s/proj/sub2', 'A'),
1001 ('/%(trunk)s/proj/sub2/default', 'A'),
1002 ('/%(trunk)s/proj/sub2/subsubA', 'A'),
1003 ('/%(trunk)s/proj/sub2/subsubA/default', 'A'),
1004 ('/%(trunk)s/proj/sub3', 'A'),
1005 ('/%(trunk)s/proj/sub3/default', 'A'),
1009 conv
.logs
[18].check('First commit to proj, affecting two files.', (
1010 ('/%(trunk)s/proj/sub1/subsubA/default', 'M'),
1011 ('/%(trunk)s/proj/sub3/default', 'M'),
1014 # The second commit.
1015 conv
.logs
[19].check('Second commit to proj, affecting all 7 files.', (
1016 ('/%(trunk)s/proj/default', 'M'),
1017 ('/%(trunk)s/proj/sub1/default', 'M'),
1018 ('/%(trunk)s/proj/sub1/subsubA/default', 'M'),
1019 ('/%(trunk)s/proj/sub1/subsubB/default', 'M'),
1020 ('/%(trunk)s/proj/sub2/default', 'M'),
1021 ('/%(trunk)s/proj/sub2/subsubA/default', 'M'),
1022 ('/%(trunk)s/proj/sub3/default', 'M')
1026 class SimpleTags(Cvs2SvnTestCase
):
1027 "simple tags and branches, no commits"
1029 def __init__(self
, **kw
):
1030 # See test-data/main-cvsrepos/proj/README.
1031 Cvs2SvnTestCase
.__init
__(self
, 'main', **kw
)
1034 conv
= self
.ensure_conversion()
1036 # Verify the copy source for the tags we are about to check
1037 # No need to verify the copyfrom revision, as simple_commits did that
1038 conv
.logs
[13].check('Initial import.', (
1039 ('/%(trunk)s/proj', 'A'),
1040 ('/%(trunk)s/proj/default', 'A'),
1041 ('/%(trunk)s/proj/sub1', 'A'),
1042 ('/%(trunk)s/proj/sub1/default', 'A'),
1043 ('/%(trunk)s/proj/sub1/subsubA', 'A'),
1044 ('/%(trunk)s/proj/sub1/subsubA/default', 'A'),
1045 ('/%(trunk)s/proj/sub1/subsubB', 'A'),
1046 ('/%(trunk)s/proj/sub1/subsubB/default', 'A'),
1047 ('/%(trunk)s/proj/sub2', 'A'),
1048 ('/%(trunk)s/proj/sub2/default', 'A'),
1049 ('/%(trunk)s/proj/sub2/subsubA', 'A'),
1050 ('/%(trunk)s/proj/sub2/subsubA/default', 'A'),
1051 ('/%(trunk)s/proj/sub3', 'A'),
1052 ('/%(trunk)s/proj/sub3/default', 'A'),
1055 fromstr
= ' (from /%(branches)s/B_FROM_INITIALS:14)'
1057 # Tag on rev 1.1.1.1 of all files in proj
1058 conv
.logs
[14].check(sym_log_msg('B_FROM_INITIALS'), (
1059 ('/%(branches)s/B_FROM_INITIALS (from /%(trunk)s:13)', 'A'),
1060 ('/%(branches)s/B_FROM_INITIALS/single-files', 'D'),
1061 ('/%(branches)s/B_FROM_INITIALS/partial-prune', 'D'),
1064 # The same, as a tag
1065 log
= conv
.find_tag_log('T_ALL_INITIAL_FILES')
1066 log
.check(sym_log_msg('T_ALL_INITIAL_FILES',1), (
1067 ('/%(tags)s/T_ALL_INITIAL_FILES'+fromstr
, 'A'),
1070 # Tag on rev 1.1.1.1 of all files in proj, except one
1071 log
= conv
.find_tag_log('T_ALL_INITIAL_FILES_BUT_ONE')
1072 log
.check(sym_log_msg('T_ALL_INITIAL_FILES_BUT_ONE',1), (
1073 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE'+fromstr
, 'A'),
1074 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE/proj/sub1/subsubB', 'D'),
1077 # The same, as a branch
1078 conv
.logs
[17].check(sym_log_msg('B_FROM_INITIALS_BUT_ONE'), (
1079 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE'+fromstr
, 'A'),
1080 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE/proj/sub1/subsubB', 'D'),
1084 def simple_branch_commits():
1085 "simple branch commits"
1086 # See test-data/main-cvsrepos/proj/README.
1087 conv
= ensure_conversion('main')
1089 conv
.logs
[23].check('Modify three files, on branch B_MIXED.', (
1090 ('/%(branches)s/B_MIXED/proj/default', 'M'),
1091 ('/%(branches)s/B_MIXED/proj/sub1/default', 'M'),
1092 ('/%(branches)s/B_MIXED/proj/sub2/subsubA/default', 'M'),
1096 def mixed_time_tag():
1098 # See test-data/main-cvsrepos/proj/README.
1099 conv
= ensure_conversion('main')
1101 log
= conv
.find_tag_log('T_MIXED')
1103 ('/%(tags)s/T_MIXED (from /%(branches)s/B_MIXED:20)', 'A'),
1107 def mixed_time_branch_with_added_file():
1108 "mixed-time branch, and a file added to the branch"
1109 # See test-data/main-cvsrepos/proj/README.
1110 conv
= ensure_conversion('main')
1112 # A branch from the same place as T_MIXED in the previous test,
1113 # plus a file added directly to the branch
1114 conv
.logs
[20].check(sym_log_msg('B_MIXED'), (
1115 ('/%(branches)s/B_MIXED (from /%(trunk)s:19)', 'A'),
1116 ('/%(branches)s/B_MIXED/partial-prune', 'D'),
1117 ('/%(branches)s/B_MIXED/single-files', 'D'),
1118 ('/%(branches)s/B_MIXED/proj/sub2/subsubA '
1119 '(from /%(trunk)s/proj/sub2/subsubA:13)', 'R'),
1120 ('/%(branches)s/B_MIXED/proj/sub3 (from /%(trunk)s/proj/sub3:18)', 'R'),
1123 conv
.logs
[22].check('Add a file on branch B_MIXED.', (
1124 ('/%(branches)s/B_MIXED/proj/sub2/branch_B_MIXED_only', 'A'),
1129 "a commit affecting both trunk and a branch"
1130 # See test-data/main-cvsrepos/proj/README.
1131 conv
= ensure_conversion('main')
1133 conv
.logs
[24].check(
1134 'A single commit affecting one file on branch B_MIXED '
1135 'and one on trunk.', (
1136 ('/%(trunk)s/proj/sub2/default', 'M'),
1137 ('/%(branches)s/B_MIXED/proj/sub2/branch_B_MIXED_only', 'M'),
1141 def split_time_branch():
1142 "branch some trunk files, and later branch the rest"
1143 # See test-data/main-cvsrepos/proj/README.
1144 conv
= ensure_conversion('main')
1146 # First change on the branch, creating it
1147 conv
.logs
[25].check(sym_log_msg('B_SPLIT'), (
1148 ('/%(branches)s/B_SPLIT (from /%(trunk)s:24)', 'A'),
1149 ('/%(branches)s/B_SPLIT/partial-prune', 'D'),
1150 ('/%(branches)s/B_SPLIT/single-files', 'D'),
1151 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB', 'D'),
1154 conv
.logs
[29].check('First change on branch B_SPLIT.', (
1155 ('/%(branches)s/B_SPLIT/proj/default', 'M'),
1156 ('/%(branches)s/B_SPLIT/proj/sub1/default', 'M'),
1157 ('/%(branches)s/B_SPLIT/proj/sub1/subsubA/default', 'M'),
1158 ('/%(branches)s/B_SPLIT/proj/sub2/default', 'M'),
1159 ('/%(branches)s/B_SPLIT/proj/sub2/subsubA/default', 'M'),
1162 # A trunk commit for the file which was not branched
1163 conv
.logs
[30].check('A trunk change to sub1/subsubB/default. '
1164 'This was committed about an', (
1165 ('/%(trunk)s/proj/sub1/subsubB/default', 'M'),
1168 # Add the file not already branched to the branch, with modification:w
1169 conv
.logs
[31].check(sym_log_msg('B_SPLIT'), (
1170 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB '
1171 '(from /%(trunk)s/proj/sub1/subsubB:30)', 'A'),
1174 conv
.logs
[32].check('This change affects sub3/default and '
1175 'sub1/subsubB/default, on branch', (
1176 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB/default', 'M'),
1177 ('/%(branches)s/B_SPLIT/proj/sub3/default', 'M'),
1181 def multiple_tags():
1182 "multiple tags referring to same revision"
1183 conv
= ensure_conversion('main')
1184 if not conv
.path_exists('tags', 'T_ALL_INITIAL_FILES', 'proj', 'default'):
1186 if not conv
.path_exists(
1187 'tags', 'T_ALL_INITIAL_FILES_BUT_ONE', 'proj', 'default'):
1191 def multiply_defined_symbols():
1192 "multiple definitions of symbol names"
1194 # We can only check one line of the error output at a time, so test
1195 # twice. (The conversion only have to be done once because the
1196 # results are cached.)
1197 conv
= ensure_conversion(
1198 'multiply-defined-symbols',
1200 r
"ERROR\: Multiple definitions of the symbol \'BRANCH\' .*\: "
1204 conv
= ensure_conversion(
1205 'multiply-defined-symbols',
1207 r
"ERROR\: Multiple definitions of the symbol \'TAG\' .*\: "
1213 def multiply_defined_symbols_renamed():
1214 "rename multiply defined symbols"
1216 conv
= ensure_conversion(
1217 'multiply-defined-symbols',
1218 options_file
='cvs2svn-rename.options',
1222 def multiply_defined_symbols_ignored():
1223 "ignore multiply defined symbols"
1225 conv
= ensure_conversion(
1226 'multiply-defined-symbols',
1227 options_file
='cvs2svn-ignore.options',
1231 def repeatedly_defined_symbols():
1232 "multiple identical definitions of symbol names"
1234 # If a symbol is defined multiple times but has the same value each
1235 # time, that should not be an error.
1237 conv
= ensure_conversion('repeatedly-defined-symbols')
1241 "conversion of invalid symbolic names"
1242 conv
= ensure_conversion('bogus-tag')
1245 def overlapping_branch():
1246 "ignore a file with a branch with two names"
1247 conv
= ensure_conversion('overlapping-branch')
1249 if not conv
.output_found('.*cannot also have name \'vendorB\''):
1252 conv
.logs
[2].check('imported', (
1253 ('/%(trunk)s/nonoverlapping-branch', 'A'),
1254 ('/%(trunk)s/overlapping-branch', 'A'),
1257 if len(conv
.logs
) != 2:
1261 class PhoenixBranch(Cvs2SvnTestCase
):
1262 "convert a branch file rooted in a 'dead' revision"
1264 def __init__(self
, **kw
):
1265 Cvs2SvnTestCase
.__init
__(self
, 'phoenix', **kw
)
1268 conv
= self
.ensure_conversion()
1269 conv
.logs
[8].check('This file was supplied by Jack Moffitt', (
1270 ('/%(branches)s/volsung_20010721', 'A'),
1271 ('/%(branches)s/volsung_20010721/phoenix', 'A'),
1273 conv
.logs
[9].check('This file was supplied by Jack Moffitt', (
1274 ('/%(branches)s/volsung_20010721/phoenix', 'M'),
1278 ###TODO: We check for 4 changed paths here to accomodate creating tags
1279 ###and branches in rev 1, but that will change, so this will
1280 ###eventually change back.
1281 def ctrl_char_in_log():
1282 "handle a control char in a log message"
1283 # This was issue #1106.
1285 conv
= ensure_conversion('ctrl-char-in-log')
1286 conv
.logs
[rev
].check_changes((
1287 ('/%(trunk)s/ctrl-char-in-log', 'A'),
1289 if conv
.logs
[rev
].msg
.find('\x04') < 0:
1291 "Log message of 'ctrl-char-in-log,v' (rev 2) is wrong.")
1295 "handle tags rooted in a redeleted revision"
1296 conv
= ensure_conversion('overdead')
1299 class NoTrunkPrune(Cvs2SvnTestCase
):
1300 "ensure that trunk doesn't get pruned"
1302 def __init__(self
, **kw
):
1303 Cvs2SvnTestCase
.__init
__(self
, 'overdead', **kw
)
1306 conv
= self
.ensure_conversion()
1307 for rev
in conv
.logs
.keys():
1308 rev_logs
= conv
.logs
[rev
]
1309 if rev_logs
.get_path_op('/%(trunk)s') == 'D':
1313 def double_delete():
1314 "file deleted twice, in the root of the repository"
1315 # This really tests several things: how we handle a file that's
1316 # removed (state 'dead') in two successive revisions; how we
1317 # handle a file in the root of the repository (there were some
1318 # bugs in cvs2svn's svn path construction for top-level files); and
1319 # the --no-prune option.
1320 conv
= ensure_conversion(
1321 'double-delete', args
=['--trunk-only', '--no-prune'])
1323 path
= '/%(trunk)s/twice-removed'
1325 conv
.logs
[rev
].check('Updated CVS', (
1328 conv
.logs
[rev
+ 1].check('Remove this file for the first time.', (
1331 conv
.logs
[rev
+ 2].check('Remove this file for the second time,', (
1336 "branch created from both trunk and another branch"
1337 # See test-data/split-branch-cvsrepos/README.
1339 # The conversion will fail if the bug is present, and
1340 # ensure_conversion will raise Failure.
1341 conv
= ensure_conversion('split-branch')
1344 def resync_misgroups():
1345 "resyncing should not misorder commit groups"
1346 # See test-data/resync-misgroups-cvsrepos/README.
1348 # The conversion will fail if the bug is present, and
1349 # ensure_conversion will raise Failure.
1350 conv
= ensure_conversion('resync-misgroups')
1353 class TaggedBranchAndTrunk(Cvs2SvnTestCase
):
1354 "allow tags with mixed trunk and branch sources"
1356 def __init__(self
, **kw
):
1357 Cvs2SvnTestCase
.__init
__(self
, 'tagged-branch-n-trunk', **kw
)
1360 conv
= self
.ensure_conversion()
1362 tags
= conv
.symbols
.get('tags', 'tags')
1364 a_path
= conv
.get_wc(tags
, 'some-tag', 'a.txt')
1365 b_path
= conv
.get_wc(tags
, 'some-tag', 'b.txt')
1366 if not (os
.path
.exists(a_path
) and os
.path
.exists(b_path
)):
1368 if (open(a_path
, 'r').read().find('1.24') == -1) \
1369 or (open(b_path
, 'r').read().find('1.5') == -1):
1374 "never use the rev-in-progress as a copy source"
1376 # See issue #1427 and r8544.
1377 conv
= ensure_conversion('enroot-race')
1379 conv
.logs
[rev
].check_changes((
1380 ('/%(branches)s/mybranch (from /%(trunk)s:5)', 'A'),
1381 ('/%(branches)s/mybranch/proj/a.txt', 'D'),
1382 ('/%(branches)s/mybranch/proj/b.txt', 'D'),
1384 conv
.logs
[rev
+ 1].check_changes((
1385 ('/%(branches)s/mybranch/proj/c.txt', 'M'),
1386 ('/%(trunk)s/proj/a.txt', 'M'),
1387 ('/%(trunk)s/proj/b.txt', 'M'),
1391 def enroot_race_obo():
1392 "do use the last completed rev as a copy source"
1393 conv
= ensure_conversion('enroot-race-obo')
1394 conv
.logs
[3].check_change('/%(branches)s/BRANCH (from /%(trunk)s:2)', 'A')
1395 if not len(conv
.logs
) == 3:
1399 class BranchDeleteFirst(Cvs2SvnTestCase
):
1400 "correctly handle deletion as initial branch action"
1402 def __init__(self
, **kw
):
1403 Cvs2SvnTestCase
.__init
__(self
, 'branch-delete-first', **kw
)
1406 # See test-data/branch-delete-first-cvsrepos/README.
1408 # The conversion will fail if the bug is present, and
1409 # ensure_conversion would raise Failure.
1410 conv
= self
.ensure_conversion()
1412 branches
= conv
.symbols
.get('branches', 'branches')
1414 # 'file' was deleted from branch-1 and branch-2, but not branch-3
1415 if conv
.path_exists(branches
, 'branch-1', 'file'):
1417 if conv
.path_exists(branches
, 'branch-2', 'file'):
1419 if not conv
.path_exists(branches
, 'branch-3', 'file'):
1423 def nonascii_filenames():
1424 "non ascii files converted incorrectly"
1427 # on a en_US.iso-8859-1 machine this test fails with
1428 # svn: Can't recode ...
1430 # as described in the issue
1432 # on a en_US.UTF-8 machine this test fails with
1433 # svn: Malformed XML ...
1435 # which means at least it fails. Unfortunately it won't fail
1436 # with the same error...
1438 # mangle current locale settings so we know we're not running
1439 # a UTF-8 locale (which does not exhibit this problem)
1440 current_locale
= locale
.getlocale()
1441 new_locale
= 'en_US.ISO8859-1'
1442 locale_changed
= None
1444 # From http://docs.python.org/lib/module-sys.html
1446 # getfilesystemencoding():
1448 # Return the name of the encoding used to convert Unicode filenames
1449 # into system file names, or None if the system default encoding is
1450 # used. The result value depends on the operating system:
1452 # - On Windows 9x, the encoding is ``mbcs''.
1453 # - On Mac OS X, the encoding is ``utf-8''.
1454 # - On Unix, the encoding is the user's preference according to the
1455 # result of nl_langinfo(CODESET), or None if the
1456 # nl_langinfo(CODESET) failed.
1457 # - On Windows NT+, file names are Unicode natively, so no conversion is
1460 # So we're going to skip this test on Mac OS X for now.
1461 if sys
.platform
== "darwin":
1462 raise svntest
.Skip()
1465 # change locale to non-UTF-8 locale to generate latin1 names
1466 locale
.setlocale(locale
.LC_ALL
, # this might be too broad?
1469 except locale
.Error
:
1470 raise svntest
.Skip()
1473 srcrepos_path
= os
.path
.join(test_data_dir
,'main-cvsrepos')
1474 dstrepos_path
= os
.path
.join(test_data_dir
,'non-ascii-cvsrepos')
1475 if not os
.path
.exists(dstrepos_path
):
1476 # create repos from existing main repos
1477 shutil
.copytree(srcrepos_path
, dstrepos_path
)
1478 base_path
= os
.path
.join(dstrepos_path
, 'single-files')
1479 shutil
.copyfile(os
.path
.join(base_path
, 'twoquick,v'),
1480 os
.path
.join(base_path
, 'two\366uick,v'))
1481 new_path
= os
.path
.join(dstrepos_path
, 'single\366files')
1482 os
.rename(base_path
, new_path
)
1484 conv
= ensure_conversion('non-ascii', args
=['--encoding=latin1'])
1487 locale
.setlocale(locale
.LC_ALL
, current_locale
)
1488 safe_rmtree(dstrepos_path
)
1491 class UnicodeTest(Cvs2SvnTestCase
):
1492 "metadata contains unicode"
1494 warning_pattern
= r
'ERROR\: There were warnings converting .* messages'
1496 def __init__(self
, name
, warning_expected
, **kw
):
1497 if warning_expected
:
1498 error_re
= self
.warning_pattern
1502 Cvs2SvnTestCase
.__init
__(self
, name
, error_re
=error_re
, **kw
)
1503 self
.warning_expected
= warning_expected
1507 # ensure the availability of the "utf_8" encoding:
1508 u
'a'.encode('utf_8').decode('utf_8')
1510 raise svntest
.Skip()
1512 self
.ensure_conversion()
1515 class UnicodeAuthor(UnicodeTest
):
1516 "author name contains unicode"
1518 def __init__(self
, warning_expected
, **kw
):
1519 UnicodeTest
.__init
__(self
, 'unicode-author', warning_expected
, **kw
)
1522 class UnicodeLog(UnicodeTest
):
1523 "log message contains unicode"
1525 def __init__(self
, warning_expected
, **kw
):
1526 UnicodeTest
.__init
__(self
, 'unicode-log', warning_expected
, **kw
)
1529 def vendor_branch_sameness():
1530 "avoid spurious changes for initial revs"
1531 conv
= ensure_conversion(
1532 'vendor-branch-sameness', args
=['--keep-trivial-imports']
1535 # The following files are in this repository:
1537 # a.txt: Imported in the traditional way; 1.1 and 1.1.1.1 have
1538 # the same contents, the file's default branch is 1.1.1,
1539 # and both revisions are in state 'Exp'.
1541 # b.txt: Like a.txt, except that 1.1.1.1 has a real change from
1542 # 1.1 (the addition of a line of text).
1544 # c.txt: Like a.txt, except that 1.1.1.1 is in state 'dead'.
1546 # d.txt: This file was created by 'cvs add' instead of import, so
1547 # it has only 1.1 -- no 1.1.1.1, and no default branch.
1548 # The timestamp on the add is exactly the same as for the
1549 # imports of the other files.
1551 # e.txt: Like a.txt, except that the log message for revision 1.1
1552 # is not the standard import log message.
1554 # (Aside from e.txt, the log messages for the same revisions are the
1555 # same in all files.)
1557 # We expect that only a.txt is recognized as an import whose 1.1
1558 # revision can be omitted. The other files should be added on trunk
1559 # then filled to vbranchA, whereas a.txt should be added to vbranchA
1560 # then copied to trunk. In the copy of 1.1.1.1 back to trunk, a.txt
1561 # and e.txt should be copied untouched; b.txt should be 'M'odified,
1562 # and c.txt should be 'D'eleted.
1565 conv
.logs
[rev
].check('Initial revision', (
1566 ('/%(trunk)s/proj', 'A'),
1567 ('/%(trunk)s/proj/b.txt', 'A'),
1568 ('/%(trunk)s/proj/c.txt', 'A'),
1569 ('/%(trunk)s/proj/d.txt', 'A'),
1572 conv
.logs
[rev
+ 1].check(sym_log_msg('vbranchA'), (
1573 ('/%(branches)s/vbranchA (from /%(trunk)s:2)', 'A'),
1574 ('/%(branches)s/vbranchA/proj/d.txt', 'D'),
1577 conv
.logs
[rev
+ 2].check('First vendor branch revision.', (
1578 ('/%(branches)s/vbranchA/proj/a.txt', 'A'),
1579 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1580 ('/%(branches)s/vbranchA/proj/c.txt', 'D'),
1583 conv
.logs
[rev
+ 3].check('This commit was generated by cvs2svn '
1584 'to compensate for changes in r4,', (
1585 ('/%(trunk)s/proj/a.txt (from /%(branches)s/vbranchA/proj/a.txt:4)', 'A'),
1586 ('/%(trunk)s/proj/b.txt (from /%(branches)s/vbranchA/proj/b.txt:4)', 'R'),
1587 ('/%(trunk)s/proj/c.txt', 'D'),
1591 conv
.logs
[rev
].check('This log message is not the standard', (
1592 ('/%(trunk)s/proj/e.txt', 'A'),
1595 conv
.logs
[rev
+ 2].check('First vendor branch revision', (
1596 ('/%(branches)s/vbranchB/proj/e.txt', 'M'),
1599 conv
.logs
[rev
+ 3].check('This commit was generated by cvs2svn '
1600 'to compensate for changes in r9,', (
1601 ('/%(trunk)s/proj/e.txt (from /%(branches)s/vbranchB/proj/e.txt:9)', 'R'),
1605 def vendor_branch_trunk_only():
1606 "handle vendor branches with --trunk-only"
1607 conv
= ensure_conversion('vendor-branch-sameness', args
=['--trunk-only'])
1610 conv
.logs
[rev
].check('Initial revision', (
1611 ('/%(trunk)s/proj', 'A'),
1612 ('/%(trunk)s/proj/b.txt', 'A'),
1613 ('/%(trunk)s/proj/c.txt', 'A'),
1614 ('/%(trunk)s/proj/d.txt', 'A'),
1617 conv
.logs
[rev
+ 1].check('First vendor branch revision', (
1618 ('/%(trunk)s/proj/a.txt', 'A'),
1619 ('/%(trunk)s/proj/b.txt', 'M'),
1620 ('/%(trunk)s/proj/c.txt', 'D'),
1623 conv
.logs
[rev
+ 2].check('This log message is not the standard', (
1624 ('/%(trunk)s/proj/e.txt', 'A'),
1627 conv
.logs
[rev
+ 3].check('First vendor branch revision', (
1628 ('/%(trunk)s/proj/e.txt', 'M'),
1632 def default_branches():
1633 "handle default branches correctly"
1634 conv
= ensure_conversion('default-branches')
1636 # There are seven files in the repository:
1639 # Imported in the traditional way, so 1.1 and 1.1.1.1 are the
1640 # same. Then 1.1.1.2 and 1.1.1.3 were imported, then 1.2
1641 # committed (thus losing the default branch "1.1.1"), then
1642 # 1.1.1.4 was imported. All vendor import release tags are
1646 # Like a.txt, but without rev 1.2.
1649 # Exactly like b.txt, just s/b.txt/c.txt/ in content.
1652 # Same as the previous two, but 1.1.1 branch is unlabeled.
1655 # Same, but missing 1.1.1 label and all tags but 1.1.1.3.
1657 # deleted-on-vendor-branch.txt,v:
1658 # Like b.txt and c.txt, except that 1.1.1.3 is state 'dead'.
1660 # added-then-imported.txt,v:
1661 # Added with 'cvs add' to create 1.1, then imported with
1662 # completely different contents to create 1.1.1.1, therefore
1663 # never had a default branch.
1666 conv
.logs
[2].check("Import (vbranchA, vtag-1).", (
1667 ('/%(branches)s/unlabeled-1.1.1', 'A'),
1668 ('/%(branches)s/unlabeled-1.1.1/proj', 'A'),
1669 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'A'),
1670 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'A'),
1671 ('/%(branches)s/vbranchA', 'A'),
1672 ('/%(branches)s/vbranchA/proj', 'A'),
1673 ('/%(branches)s/vbranchA/proj/a.txt', 'A'),
1674 ('/%(branches)s/vbranchA/proj/b.txt', 'A'),
1675 ('/%(branches)s/vbranchA/proj/c.txt', 'A'),
1676 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'A'),
1679 conv
.logs
[3].check("This commit was generated by cvs2svn "
1680 "to compensate for changes in r2,", (
1681 ('/%(trunk)s/proj', 'A'),
1682 ('/%(trunk)s/proj/a.txt (from /%(branches)s/vbranchA/proj/a.txt:2)', 'A'),
1683 ('/%(trunk)s/proj/b.txt (from /%(branches)s/vbranchA/proj/b.txt:2)', 'A'),
1684 ('/%(trunk)s/proj/c.txt (from /%(branches)s/vbranchA/proj/c.txt:2)', 'A'),
1685 ('/%(trunk)s/proj/d.txt '
1686 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:2)', 'A'),
1687 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1688 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:2)', 'A'),
1689 ('/%(trunk)s/proj/e.txt '
1690 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:2)', 'A'),
1693 conv
.logs
[4].check(sym_log_msg('vtag-1',1), (
1694 ('/%(tags)s/vtag-1 (from /%(branches)s/vbranchA:2)', 'A'),
1695 ('/%(tags)s/vtag-1/proj/d.txt '
1696 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:2)', 'A'),
1699 conv
.logs
[5].check("Import (vbranchA, vtag-2).", (
1700 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1701 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1702 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1703 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1704 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1705 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'M'),
1708 conv
.logs
[6].check("This commit was generated by cvs2svn "
1709 "to compensate for changes in r5,", (
1710 ('/%(trunk)s/proj/a.txt '
1711 '(from /%(branches)s/vbranchA/proj/a.txt:5)', 'R'),
1712 ('/%(trunk)s/proj/b.txt '
1713 '(from /%(branches)s/vbranchA/proj/b.txt:5)', 'R'),
1714 ('/%(trunk)s/proj/c.txt '
1715 '(from /%(branches)s/vbranchA/proj/c.txt:5)', 'R'),
1716 ('/%(trunk)s/proj/d.txt '
1717 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:5)', 'R'),
1718 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1719 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:5)',
1721 ('/%(trunk)s/proj/e.txt '
1722 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:5)', 'R'),
1725 conv
.logs
[7].check(sym_log_msg('vtag-2',1), (
1726 ('/%(tags)s/vtag-2 (from /%(branches)s/vbranchA:5)', 'A'),
1727 ('/%(tags)s/vtag-2/proj/d.txt '
1728 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:5)', 'A'),
1731 conv
.logs
[8].check("Import (vbranchA, vtag-3).", (
1732 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1733 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1734 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1735 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1736 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1737 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'D'),
1740 conv
.logs
[9].check("This commit was generated by cvs2svn "
1741 "to compensate for changes in r8,", (
1742 ('/%(trunk)s/proj/a.txt '
1743 '(from /%(branches)s/vbranchA/proj/a.txt:8)', 'R'),
1744 ('/%(trunk)s/proj/b.txt '
1745 '(from /%(branches)s/vbranchA/proj/b.txt:8)', 'R'),
1746 ('/%(trunk)s/proj/c.txt '
1747 '(from /%(branches)s/vbranchA/proj/c.txt:8)', 'R'),
1748 ('/%(trunk)s/proj/d.txt '
1749 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:8)', 'R'),
1750 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'D'),
1751 ('/%(trunk)s/proj/e.txt '
1752 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:8)', 'R'),
1755 conv
.logs
[10].check(sym_log_msg('vtag-3',1), (
1756 ('/%(tags)s/vtag-3 (from /%(branches)s/vbranchA:8)', 'A'),
1757 ('/%(tags)s/vtag-3/proj/d.txt '
1758 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:8)', 'A'),
1759 ('/%(tags)s/vtag-3/proj/e.txt '
1760 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:8)', 'A'),
1763 conv
.logs
[11].check("First regular commit, to a.txt, on vtag-3.", (
1764 ('/%(trunk)s/proj/a.txt', 'M'),
1767 conv
.logs
[12].check("Add a file to the working copy.", (
1768 ('/%(trunk)s/proj/added-then-imported.txt', 'A'),
1771 conv
.logs
[13].check(sym_log_msg('vbranchA'), (
1772 ('/%(branches)s/vbranchA/proj/added-then-imported.txt '
1773 '(from /%(trunk)s/proj/added-then-imported.txt:12)', 'A'),
1776 conv
.logs
[14].check("Import (vbranchA, vtag-4).", (
1777 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1778 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1779 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1780 ('/%(branches)s/vbranchA/proj/added-then-imported.txt', 'M'), # CHECK!!!
1781 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1782 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1783 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'A'),
1786 conv
.logs
[15].check("This commit was generated by cvs2svn "
1787 "to compensate for changes in r14,", (
1788 ('/%(trunk)s/proj/b.txt '
1789 '(from /%(branches)s/vbranchA/proj/b.txt:14)', 'R'),
1790 ('/%(trunk)s/proj/c.txt '
1791 '(from /%(branches)s/vbranchA/proj/c.txt:14)', 'R'),
1792 ('/%(trunk)s/proj/d.txt '
1793 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:14)', 'R'),
1794 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1795 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:14)',
1797 ('/%(trunk)s/proj/e.txt '
1798 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:14)', 'R'),
1801 conv
.logs
[16].check(sym_log_msg('vtag-4',1), (
1802 ('/%(tags)s/vtag-4 (from /%(branches)s/vbranchA:14)', 'A'),
1803 ('/%(tags)s/vtag-4/proj/d.txt '
1804 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:14)', 'A'),
1808 def default_branches_trunk_only():
1809 "handle default branches with --trunk-only"
1811 conv
= ensure_conversion('default-branches', args
=['--trunk-only'])
1813 conv
.logs
[2].check("Import (vbranchA, vtag-1).", (
1814 ('/%(trunk)s/proj', 'A'),
1815 ('/%(trunk)s/proj/a.txt', 'A'),
1816 ('/%(trunk)s/proj/b.txt', 'A'),
1817 ('/%(trunk)s/proj/c.txt', 'A'),
1818 ('/%(trunk)s/proj/d.txt', 'A'),
1819 ('/%(trunk)s/proj/e.txt', 'A'),
1820 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'A'),
1823 conv
.logs
[3].check("Import (vbranchA, vtag-2).", (
1824 ('/%(trunk)s/proj/a.txt', 'M'),
1825 ('/%(trunk)s/proj/b.txt', 'M'),
1826 ('/%(trunk)s/proj/c.txt', 'M'),
1827 ('/%(trunk)s/proj/d.txt', 'M'),
1828 ('/%(trunk)s/proj/e.txt', 'M'),
1829 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'M'),
1832 conv
.logs
[4].check("Import (vbranchA, vtag-3).", (
1833 ('/%(trunk)s/proj/a.txt', 'M'),
1834 ('/%(trunk)s/proj/b.txt', 'M'),
1835 ('/%(trunk)s/proj/c.txt', 'M'),
1836 ('/%(trunk)s/proj/d.txt', 'M'),
1837 ('/%(trunk)s/proj/e.txt', 'M'),
1838 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'D'),
1841 conv
.logs
[5].check("First regular commit, to a.txt, on vtag-3.", (
1842 ('/%(trunk)s/proj/a.txt', 'M'),
1845 conv
.logs
[6].check("Add a file to the working copy.", (
1846 ('/%(trunk)s/proj/added-then-imported.txt', 'A'),
1849 conv
.logs
[7].check("Import (vbranchA, vtag-4).", (
1850 ('/%(trunk)s/proj/b.txt', 'M'),
1851 ('/%(trunk)s/proj/c.txt', 'M'),
1852 ('/%(trunk)s/proj/d.txt', 'M'),
1853 ('/%(trunk)s/proj/e.txt', 'M'),
1854 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'A'),
1858 def default_branch_and_1_2():
1859 "do not allow 1.2 revision with default branch"
1861 conv
= ensure_conversion(
1862 'default-branch-and-1-2',
1864 r
'.*File \'.*\' has default branch
=1\
.1\
.1 but also a revision
1\
.2'
1869 def compose_tag_three_sources():
1870 "compose a tag from three sources"
1871 conv = ensure_conversion('compose
-tag
-three
-sources
')
1873 conv.logs[2].check("Add on trunk", (
1874 ('/%(trunk)s/tagged
-on
-trunk
-1.1', 'A
'),
1875 ('/%(trunk)s/tagged
-on
-trunk
-1.2-a
', 'A
'),
1876 ('/%(trunk)s/tagged
-on
-trunk
-1.2-b
', 'A
'),
1877 ('/%(trunk)s/tagged
-on
-b1
', 'A
'),
1878 ('/%(trunk)s/tagged
-on
-b2
', 'A
'),
1881 conv.logs[3].check(sym_log_msg('b1
'), (
1882 ('/%(branches)s/b1 (from /%(trunk)s:2)', 'A
'),
1885 conv.logs[4].check(sym_log_msg('b2
'), (
1886 ('/%(branches)s/b2 (from /%(trunk)s:2)', 'A
'),
1889 conv.logs[5].check("Commit on branch b1", (
1890 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.1', 'M
'),
1891 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.2-a
', 'M
'),
1892 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.2-b
', 'M
'),
1893 ('/%(branches)s/b1
/tagged
-on
-b1
', 'M
'),
1894 ('/%(branches)s/b1
/tagged
-on
-b2
', 'M
'),
1897 conv.logs[6].check("Commit on branch b2", (
1898 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.1', 'M
'),
1899 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.2-a
', 'M
'),
1900 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.2-b
', 'M
'),
1901 ('/%(branches)s/b2
/tagged
-on
-b1
', 'M
'),
1902 ('/%(branches)s/b2
/tagged
-on
-b2
', 'M
'),
1905 conv.logs[7].check("Commit again on trunk", (
1906 ('/%(trunk)s/tagged
-on
-trunk
-1.2-a
', 'M
'),
1907 ('/%(trunk)s/tagged
-on
-trunk
-1.2-b
', 'M
'),
1908 ('/%(trunk)s/tagged
-on
-trunk
-1.1', 'M
'),
1909 ('/%(trunk)s/tagged
-on
-b1
', 'M
'),
1910 ('/%(trunk)s/tagged
-on
-b2
', 'M
'),
1913 conv.logs[8].check(sym_log_msg('T
',1), (
1914 ('/%(tags)s/T (from /%(trunk)s:7)', 'A
'),
1915 ('/%(tags)s/T
/tagged
-on
-trunk
-1.1 '
1916 '(from /%(trunk)s/tagged
-on
-trunk
-1.1:2)', 'R
'),
1917 ('/%(tags)s/T
/tagged
-on
-b1 (from /%(branches)s/b1
/tagged
-on
-b1
:5)', 'R
'),
1918 ('/%(tags)s/T
/tagged
-on
-b2 (from /%(branches)s/b2
/tagged
-on
-b2
:6)', 'R
'),
1922 def pass5_when_to_fill():
1923 "reserve a svn revnum for a fill only when required"
1924 # The conversion will fail if the bug is present, and
1925 # ensure_conversion would raise Failure.
1926 conv = ensure_conversion('pass5
-when
-to
-fill
')
1929 class EmptyTrunk(Cvs2SvnTestCase):
1930 "don't
break when the trunk
is empty
"
1932 def __init__(self, **kw):
1933 Cvs2SvnTestCase.__init__(self, 'empty-trunk', **kw)
1936 # The conversion will fail if the bug is present, and
1937 # ensure_conversion would raise Failure.
1938 conv = self.ensure_conversion()
1941 def no_spurious_svn_commits():
1942 "ensure that we don
't create any spurious commits"
1943 conv = ensure_conversion('phoenix
')
1945 # Check spurious commit that could be created in
1946 # SVNCommitCreator._pre_commit()
1948 # (When you add a file on a branch, CVS creates a trunk revision
1949 # in state 'dead
'. If the log message of that commit is equal to
1950 # the one that CVS generates, we do not ever create a 'fill
'
1951 # SVNCommit for it.)
1953 # and spurious commit that could be created in
1954 # SVNCommitCreator._commit()
1956 # (When you add a file on a branch, CVS creates a trunk revision
1957 # in state 'dead
'. If the log message of that commit is equal to
1958 # the one that CVS generates, we do not create a primary SVNCommit
1960 conv.logs[17].check('File added on branch xiphophorus
', (
1961 ('/%(branches)s/xiphophorus
/added
-on
-branch
.txt
', 'A
'),
1964 # Check to make sure that a commit *is* generated:
1965 # (When you add a file on a branch, CVS creates a trunk revision
1966 # in state 'dead
'. If the log message of that commit is NOT equal
1967 # to the one that CVS generates, we create a primary SVNCommit to
1968 # serve as a home for the log message in question.
1969 conv.logs[18].check('file added
-on
-branch2
.txt was initially added on
'
1970 + 'branch xiphophorus
,\nand this log message was tweaked
', ())
1972 # Check spurious commit that could be created in
1973 # SVNCommitCreator._commit_symbols().
1974 conv.logs[19].check('This
file was also added on branch xiphophorus
,', (
1975 ('/%(branches)s/xiphophorus
/added
-on
-branch2
.txt
', 'A
'),
1979 class PeerPathPruning(Cvs2SvnTestCase):
1980 "make sure that filling prunes paths correctly"
1982 def __init__(self, **kw):
1983 Cvs2SvnTestCase.__init__(self, 'peer
-path
-pruning
', **kw)
1986 conv = self.ensure_conversion()
1987 conv.logs[6].check(sym_log_msg('BRANCH
'), (
1988 ('/%(branches)s/BRANCH (from /%(trunk)s:4)', 'A
'),
1989 ('/%(branches)s/BRANCH
/bar
', 'D
'),
1990 ('/%(branches)s/BRANCH
/foo (from /%(trunk)s/foo
:5)', 'R
'),
1994 def invalid_closings_on_trunk():
1995 "verify correct revs are copied to default branches"
1996 # The conversion will fail if the bug is present, and
1997 # ensure_conversion would raise Failure.
1998 conv = ensure_conversion('invalid
-closings
-on
-trunk
')
2001 def individual_passes():
2002 "run each pass individually"
2003 conv = ensure_conversion('main
')
2004 conv2 = ensure_conversion('main
', passbypass=1)
2006 if conv.logs != conv2.logs:
2011 "reveal a big bug in our resync algorithm"
2012 # This will fail if the bug is present
2013 conv = ensure_conversion('resync
-bug
')
2016 def branch_from_default_branch():
2017 "reveal a bug in our default branch detection code"
2018 conv = ensure_conversion('branch
-from-default
-branch
')
2020 # This revision will be a default branch synchronization only
2021 # if cvs2svn is correctly determining default branch revisions.
2023 # The bug was that cvs2svn was treating revisions on branches off of
2024 # default branches as default branch revisions, resulting in
2025 # incorrectly regarding the branch off of the default branch as a
2026 # non-trunk default branch. Crystal clear? I thought so. See
2027 # issue #42 for more incoherent blathering.
2028 conv.logs[5].check("This commit was generated by cvs2svn", (
2029 ('/%(trunk)s/proj
/file.txt
'
2030 '(from /%(branches)s/upstream
/proj
/file.txt
:4)', 'R
'),
2034 def file_in_attic_too():
2035 "die if a file exists in and out of the attic"
2037 'file-in-attic
-too
',
2039 r'.*A CVS repository cannot contain both
'
2040 r'(.*)' + re.escape(os.sep) + r'(.*) '
2042 r'\
1' + re.escape(os.sep) + r'Attic
' + re.escape(os.sep) + r'\
2'
2047 def retain_file_in_attic_too():
2048 "test --retain-conflicting-attic-files option"
2049 conv = ensure_conversion(
2050 'file-in-attic
-too
', args=['--retain
-conflicting
-attic
-files
'])
2051 if not conv.path_exists('trunk
', 'file.txt
'):
2053 if not conv.path_exists('trunk
', 'Attic
', 'file.txt
'):
2057 def symbolic_name_filling_guide():
2058 "reveal a big bug in our SymbolFillingGuide"
2059 # This will fail if the bug is present
2060 conv = ensure_conversion('symbolic
-name
-overfill
')
2063 # Helpers for tests involving file contents and properties.
2065 class NodeTreeWalkException:
2066 "Exception class for node tree traversals."
2069 def node_for_path(node, path):
2070 "In the tree rooted under SVNTree NODE, return the node at PATH."
2071 if node.name != '__SVN_ROOT_NODE
':
2072 raise NodeTreeWalkException()
2073 path = path.strip('/')
2074 components = path.split('/')
2075 for component in components:
2076 node = get_child(node, component)
2079 # Helper for tests involving properties.
2080 def props_for_path(node, path):
2081 "In the tree rooted under SVNTree NODE, return the prop dict for PATH."
2082 return node_for_path(node, path).props
2085 class EOLMime(Cvs2SvnPropertiesTestCase):
2086 """eol settings and mime types together
2088 The files are as follows:
2090 trunk/foo.txt: no -kb, mime file says nothing.
2091 trunk/foo.xml: no -kb, mime file says text.
2092 trunk/foo.zip: no -kb, mime file says non-text.
2093 trunk/foo.bin: has -kb, mime file says nothing.
2094 trunk/foo.csv: has -kb, mime file says text.
2095 trunk/foo.dbf: has -kb, mime file says non-text.
2098 def __init__(self, args, **kw):
2099 # TODO: It's a bit klugey to construct this path here
. But so far
2100 # there's only one test with a mime.types file. If we have more,
2101 # we should abstract this into some helper, which would be located
2102 # near ensure_conversion(). Note that it is a convention of this
2103 # test suite for a mime.types file to be located in the top level
2104 # of the CVS repository to which it applies.
2105 self
.mime_path
= os
.path
.join(
2106 test_data_dir
, 'eol-mime-cvsrepos', 'mime.types')
2108 Cvs2SvnPropertiesTestCase
.__init
__(
2110 props_to_test
=['svn:eol-style', 'svn:mime-type', 'svn:keywords'],
2111 args
=['--mime-types=%s' % self
.mime_path
] + args
,
2115 # We do four conversions. Each time, we pass --mime-types=FILE with
2116 # the same FILE, but vary --default-eol and --eol-from-mime-type.
2117 # Thus there's one conversion with neither flag, one with just the
2118 # former, one with just the latter, and one with both.
2121 # Neither --no-default-eol nor --eol-from-mime-type:
2122 eol_mime1
= EOLMime(
2126 ('trunk/foo.txt', [None, None, None]),
2127 ('trunk/foo.xml', [None, 'text/xml', None]),
2128 ('trunk/foo.zip', [None, 'application/zip', None]),
2129 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2130 ('trunk/foo.csv', [None, 'text/csv', None]),
2131 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2135 # Just --no-default-eol, not --eol-from-mime-type:
2136 eol_mime2
= EOLMime(
2138 args
=['--default-eol=native'],
2140 ('trunk/foo.txt', ['native', None, KEYWORDS
]),
2141 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2142 ('trunk/foo.zip', ['native', 'application/zip', KEYWORDS
]),
2143 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2144 ('trunk/foo.csv', [None, 'text/csv', None]),
2145 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2149 # Just --eol-from-mime-type, not --no-default-eol:
2150 eol_mime3
= EOLMime(
2152 args
=['--eol-from-mime-type'],
2154 ('trunk/foo.txt', [None, None, None]),
2155 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2156 ('trunk/foo.zip', [None, 'application/zip', None]),
2157 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2158 ('trunk/foo.csv', [None, 'text/csv', None]),
2159 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2163 # Both --no-default-eol and --eol-from-mime-type:
2164 eol_mime4
= EOLMime(
2166 args
=['--eol-from-mime-type', '--default-eol=native'],
2168 ('trunk/foo.txt', ['native', None, KEYWORDS
]),
2169 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2170 ('trunk/foo.zip', [None, 'application/zip', None]),
2171 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2172 ('trunk/foo.csv', [None, 'text/csv', None]),
2173 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2177 cvs_revnums_off
= Cvs2SvnPropertiesTestCase(
2179 description
='test non-setting of cvs2svn:cvs-rev property',
2181 props_to_test
=['cvs2svn:cvs-rev'],
2183 ('trunk/foo.txt', [None]),
2184 ('trunk/foo.xml', [None]),
2185 ('trunk/foo.zip', [None]),
2186 ('trunk/foo.bin', [None]),
2187 ('trunk/foo.csv', [None]),
2188 ('trunk/foo.dbf', [None]),
2192 cvs_revnums_on
= Cvs2SvnPropertiesTestCase(
2194 description
='test setting of cvs2svn:cvs-rev property',
2195 args
=['--cvs-revnums'],
2196 props_to_test
=['cvs2svn:cvs-rev'],
2198 ('trunk/foo.txt', ['1.2']),
2199 ('trunk/foo.xml', ['1.2']),
2200 ('trunk/foo.zip', ['1.2']),
2201 ('trunk/foo.bin', ['1.2']),
2202 ('trunk/foo.csv', ['1.2']),
2203 ('trunk/foo.dbf', ['1.2']),
2207 keywords
= Cvs2SvnPropertiesTestCase(
2209 description
='test setting of svn:keywords property among others',
2210 args
=['--default-eol=native'],
2211 props_to_test
=['svn:keywords', 'svn:eol-style', 'svn:mime-type'],
2213 ('trunk/foo.default', [KEYWORDS
, 'native', None]),
2214 ('trunk/foo.kkvl', [KEYWORDS
, 'native', None]),
2215 ('trunk/foo.kkv', [KEYWORDS
, 'native', None]),
2216 ('trunk/foo.kb', [None, None, 'application/octet-stream']),
2217 ('trunk/foo.kk', [None, 'native', None]),
2218 ('trunk/foo.ko', [None, 'native', None]),
2219 ('trunk/foo.kv', [None, 'native', None]),
2224 "test setting of svn:ignore property"
2225 conv
= ensure_conversion('cvsignore')
2226 wc_tree
= conv
.get_wc_tree()
2227 topdir_props
= props_for_path(wc_tree
, 'trunk/proj')
2228 subdir_props
= props_for_path(wc_tree
, '/trunk/proj/subdir')
2230 if topdir_props
['svn:ignore'] != \
2231 '*.idx\n*.aux\n*.dvi\n*.log\nfoo\nbar\nbaz\nqux\n\n':
2234 if subdir_props
['svn:ignore'] != \
2235 '*.idx\n*.aux\n*.dvi\n*.log\nfoo\nbar\nbaz\nqux\n\n':
2240 "test that CVS can still do what RCS can't"
2241 # See issues 4, 11, 29 for the bugs whose regression we're testing for.
2242 conv
= ensure_conversion('requires-cvs', args
=["--use-cvs"])
2244 atsign_contents
= file(conv
.get_wc("trunk", "atsign-add")).read()
2245 cl_contents
= file(conv
.get_wc("trunk", "client_lock.idl")).read()
2247 if atsign_contents
[-1:] == "@":
2249 if cl_contents
.find("gregh\n//\n//Integration for locks") < 0:
2252 if not (conv
.logs
[21].author
== "William Lyon Phelps III" and
2253 conv
.logs
[20].author
== "j random"):
2257 def questionable_branch_names():
2258 "test that we can handle weird branch names"
2259 conv
= ensure_conversion('questionable-symbols')
2260 # If the conversion succeeds, then we're okay. We could check the
2261 # actual branch paths, too, but the main thing is to know that the
2262 # conversion doesn't fail.
2265 def questionable_tag_names():
2266 "test that we can handle weird tag names"
2267 conv
= ensure_conversion('questionable-symbols')
2268 conv
.find_tag_log('Tag_A').check(sym_log_msg('Tag_A', 1), (
2269 ('/%(tags)s/Tag_A (from /trunk:8)', 'A'),
2271 conv
.find_tag_log('TagWith/Backslash_E').check(
2272 sym_log_msg('TagWith/Backslash_E',1),
2274 ('/%(tags)s/TagWith', 'A'),
2275 ('/%(tags)s/TagWith/Backslash_E (from /trunk:8)', 'A'),
2278 conv
.find_tag_log('TagWith/Slash_Z').check(
2279 sym_log_msg('TagWith/Slash_Z',1),
2281 ('/%(tags)s/TagWith/Slash_Z (from /trunk:8)', 'A'),
2286 def revision_reorder_bug():
2287 "reveal a bug that reorders file revisions"
2288 conv
= ensure_conversion('revision-reorder-bug')
2289 # If the conversion succeeds, then we're okay. We could check the
2290 # actual revisions, too, but the main thing is to know that the
2291 # conversion doesn't fail.
2295 "test that exclude really excludes everything"
2296 conv
= ensure_conversion('main', args
=['--exclude=.*'])
2297 for log
in conv
.logs
.values():
2298 for item
in log
.changed_paths
.keys():
2299 if item
.startswith('/branches/') or item
.startswith('/tags/'):
2303 def vendor_branch_delete_add():
2304 "add trunk file that was deleted on vendor branch"
2305 # This will error if the bug is present
2306 conv
= ensure_conversion('vendor-branch-delete-add')
2309 def resync_pass2_pull_forward():
2310 "ensure pass2 doesn't pull rev too far forward"
2311 conv
= ensure_conversion('resync-pass2-pull-forward')
2312 # If the conversion succeeds, then we're okay. We could check the
2313 # actual revisions, too, but the main thing is to know that the
2314 # conversion doesn't fail.
2318 "only LFs for svn:eol-style=native files"
2319 conv
= ensure_conversion('native-eol', args
=['--default-eol=native'])
2320 lines
= run_program(svntest
.main
.svnadmin_binary
, None, 'dump', '-q',
2322 # Verify that all files in the dump have LF EOLs. We're actually
2323 # testing the whole dump file, but the dump file itself only uses
2324 # LF EOLs, so we're safe.
2326 if line
[-1] != '\n' or line
[:-1].find('\r') != -1:
2331 "reveal a bug that created a branch twice"
2332 conv
= ensure_conversion('double-fill')
2333 # If the conversion succeeds, then we're okay. We could check the
2334 # actual revisions, too, but the main thing is to know that the
2335 # conversion doesn't fail.
2339 "reveal a second bug that created a branch twice"
2340 conv
= ensure_conversion('double-fill2')
2341 conv
.logs
[6].check_msg(sym_log_msg('BRANCH1'))
2342 conv
.logs
[7].check_msg(sym_log_msg('BRANCH2'))
2344 # This check should fail:
2345 conv
.logs
[8].check_msg(sym_log_msg('BRANCH2'))
2349 raise Failure('Symbol filled twice in a row')
2352 def resync_pass2_push_backward():
2353 "ensure pass2 doesn't push rev too far backward"
2354 conv
= ensure_conversion('resync-pass2-push-backward')
2355 # If the conversion succeeds, then we're okay. We could check the
2356 # actual revisions, too, but the main thing is to know that the
2357 # conversion doesn't fail.
2361 "reveal a bug that added a branch file twice"
2362 conv
= ensure_conversion('double-add')
2363 # If the conversion succeeds, then we're okay. We could check the
2364 # actual revisions, too, but the main thing is to know that the
2365 # conversion doesn't fail.
2368 def bogus_branch_copy():
2369 "reveal a bug that copies a branch file wrongly"
2370 conv
= ensure_conversion('bogus-branch-copy')
2371 # If the conversion succeeds, then we're okay. We could check the
2372 # actual revisions, too, but the main thing is to know that the
2373 # conversion doesn't fail.
2376 def nested_ttb_directories():
2377 "require error if ttb directories are not disjoint"
2379 {'trunk' : 'a', 'branches' : 'a',},
2380 {'trunk' : 'a', 'tags' : 'a',},
2381 {'branches' : 'a', 'tags' : 'a',},
2382 # This option conflicts with the default trunk path:
2383 {'branches' : 'trunk',},
2384 # Try some nested directories:
2385 {'trunk' : 'a', 'branches' : 'a/b',},
2386 {'trunk' : 'a/b', 'tags' : 'a/b/c/d',},
2387 {'branches' : 'a', 'tags' : 'a/b',},
2390 for opts
in opts_list
:
2392 'main', error_re
=r
'The following paths are not disjoint\:', **opts
2396 class AutoProps(Cvs2SvnPropertiesTestCase
):
2399 The files are as follows:
2401 trunk/foo.txt: no -kb, mime auto-prop says nothing.
2402 trunk/foo.xml: no -kb, mime auto-prop says text and eol-style=CRLF.
2403 trunk/foo.zip: no -kb, mime auto-prop says non-text.
2404 trunk/foo.asc: no -kb, mime auto-prop says text and eol-style=<unset>.
2405 trunk/foo.bin: has -kb, mime auto-prop says nothing.
2406 trunk/foo.csv: has -kb, mime auto-prop says text and eol-style=CRLF.
2407 trunk/foo.dbf: has -kb, mime auto-prop says non-text.
2408 trunk/foo.UPCASE1: no -kb, no mime type.
2409 trunk/foo.UPCASE2: no -kb, no mime type.
2412 def __init__(self
, args
, **kw
):
2413 ### TODO: It's a bit klugey to construct this path here. See also
2414 ### the comment in eol_mime().
2415 auto_props_path
= os
.path
.join(
2416 test_data_dir
, 'eol-mime-cvsrepos', 'auto-props')
2418 Cvs2SvnPropertiesTestCase
.__init
__(
2428 '--auto-props=%s' % auto_props_path
,
2429 '--eol-from-mime-type'
2434 auto_props_ignore_case
= AutoProps(
2435 description
="test auto-props",
2436 args
=['--default-eol=native'],
2438 ('trunk/foo.txt', ['txt', 'native', None, KEYWORDS
, None]),
2439 ('trunk/foo.xml', ['xml', 'CRLF', 'text/xml', KEYWORDS
, None]),
2440 ('trunk/foo.zip', ['zip', None, 'application/zip', None, None]),
2441 ('trunk/foo.asc', ['asc', None, 'text/plain', None, None]),
2443 ['bin', None, 'application/octet-stream', None, '']),
2444 ('trunk/foo.csv', ['csv', 'CRLF', 'text/csv', None, None]),
2446 ['dbf', None, 'application/what-is-dbf', None, None]),
2447 ('trunk/foo.UPCASE1', ['UPCASE1', 'native', None, KEYWORDS
, None]),
2448 ('trunk/foo.UPCASE2', ['UPCASE2', 'native', None, KEYWORDS
, None]),
2452 def ctrl_char_in_filename():
2453 "do not allow control characters in filenames"
2456 srcrepos_path
= os
.path
.join(test_data_dir
,'main-cvsrepos')
2457 dstrepos_path
= os
.path
.join(test_data_dir
,'ctrl-char-filename-cvsrepos')
2458 if os
.path
.exists(dstrepos_path
):
2459 safe_rmtree(dstrepos_path
)
2461 # create repos from existing main repos
2462 shutil
.copytree(srcrepos_path
, dstrepos_path
)
2463 base_path
= os
.path
.join(dstrepos_path
, 'single-files')
2465 shutil
.copyfile(os
.path
.join(base_path
, 'twoquick,v'),
2466 os
.path
.join(base_path
, 'two\rquick,v'))
2468 # Operating systems that don't allow control characters in
2469 # filenames will hopefully have thrown an exception; in that
2470 # case, just skip this test.
2471 raise svntest
.Skip()
2473 conv
= ensure_conversion(
2474 'ctrl-char-filename',
2475 error_re
=(r
'.*Character .* in filename .* '
2476 r
'is not supported by Subversion\.'),
2479 safe_rmtree(dstrepos_path
)
2482 def commit_dependencies():
2483 "interleaved and multi-branch commits to same files"
2484 conv
= ensure_conversion("commit-dependencies")
2485 conv
.logs
[2].check('adding', (
2486 ('/%(trunk)s/interleaved', 'A'),
2487 ('/%(trunk)s/interleaved/file1', 'A'),
2488 ('/%(trunk)s/interleaved/file2', 'A'),
2490 conv
.logs
[3].check('big commit', (
2491 ('/%(trunk)s/interleaved/file1', 'M'),
2492 ('/%(trunk)s/interleaved/file2', 'M'),
2494 conv
.logs
[4].check('dependant small commit', (
2495 ('/%(trunk)s/interleaved/file1', 'M'),
2497 conv
.logs
[5].check('adding', (
2498 ('/%(trunk)s/multi-branch', 'A'),
2499 ('/%(trunk)s/multi-branch/file1', 'A'),
2500 ('/%(trunk)s/multi-branch/file2', 'A'),
2502 conv
.logs
[6].check(sym_log_msg("branch"), (
2503 ('/%(branches)s/branch (from /%(trunk)s:5)', 'A'),
2504 ('/%(branches)s/branch/interleaved', 'D'),
2506 conv
.logs
[7].check('multi-branch-commit', (
2507 ('/%(trunk)s/multi-branch/file1', 'M'),
2508 ('/%(trunk)s/multi-branch/file2', 'M'),
2509 ('/%(branches)s/branch/multi-branch/file1', 'M'),
2510 ('/%(branches)s/branch/multi-branch/file2', 'M'),
2514 def double_branch_delete():
2515 "fill branches before modifying files on them"
2516 conv
= ensure_conversion('double-branch-delete')
2518 # Test for issue #102. The file IMarshalledValue.java is branched,
2519 # deleted, readded on the branch, and then deleted again. If the
2520 # fill for the file on the branch is postponed until after the
2521 # modification, the file will end up live on the branch instead of
2522 # dead! Make sure it happens at the right time.
2524 conv
.logs
[6].check('JBAS-2436 - Adding LGPL Header2', (
2525 ('/%(branches)s/Branch_4_0/IMarshalledValue.java', 'A'),
2528 conv
.logs
[7].check('JBAS-3025 - Removing dependency', (
2529 ('/%(branches)s/Branch_4_0/IMarshalledValue.java', 'D'),
2533 def symbol_mismatches():
2534 "error for conflicting tag/branch"
2538 args
=['--symbol-default=strict'],
2539 error_re
=r
'.*Problems determining how symbols should be converted',
2543 def overlook_symbol_mismatches():
2544 "overlook conflicting tag/branch when --trunk-only"
2546 # This is a test for issue #85.
2548 ensure_conversion('symbol-mess', args
=['--trunk-only'])
2551 def force_symbols():
2552 "force symbols to be tags/branches"
2554 conv
= ensure_conversion(
2556 args
=['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG'])
2557 if conv
.path_exists('tags', 'BRANCH') \
2558 or not conv
.path_exists('branches', 'BRANCH'):
2560 if not conv
.path_exists('tags', 'TAG') \
2561 or conv
.path_exists('branches', 'TAG'):
2563 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2564 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2566 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2567 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2571 def commit_blocks_tags():
2572 "commit prevents forced tag"
2574 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2577 args
=(basic_args
+ ['--force-tag=BRANCH_WITH_COMMIT']),
2579 r
'.*The following branches cannot be forced to be tags '
2580 r
'because they have commits'
2585 def blocked_excludes():
2586 "error for blocked excludes"
2588 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2589 for blocker
in ['BRANCH', 'COMMIT', 'UNNAMED']:
2593 args
=(basic_args
+ ['--exclude=BLOCKED_BY_%s' % blocker
]))
2594 raise MissingErrorException()
2599 def unblock_blocked_excludes():
2600 "excluding blocker removes blockage"
2602 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2603 for blocker
in ['BRANCH', 'COMMIT']:
2606 args
=(basic_args
+ ['--exclude=BLOCKED_BY_%s' % blocker
,
2607 '--exclude=BLOCKING_%s' % blocker
]))
2610 def regexp_force_symbols():
2611 "force symbols via regular expressions"
2613 conv
= ensure_conversion(
2615 args
=['--force-branch=MOST.*_BRANCH', '--force-tag=MOST.*_TAG'])
2616 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2617 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2619 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2620 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2624 def heuristic_symbol_default():
2625 "test 'heuristic' symbol default"
2627 conv
= ensure_conversion(
2628 'symbol-mess', args
=['--symbol-default=heuristic'])
2629 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2630 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2632 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2633 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2637 def branch_symbol_default():
2638 "test 'branch' symbol default"
2640 conv
= ensure_conversion(
2641 'symbol-mess', args
=['--symbol-default=branch'])
2642 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2643 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2645 if conv
.path_exists('tags', 'MOSTLY_TAG') \
2646 or not conv
.path_exists('branches', 'MOSTLY_TAG'):
2650 def tag_symbol_default():
2651 "test 'tag' symbol default"
2653 conv
= ensure_conversion(
2654 'symbol-mess', args
=['--symbol-default=tag'])
2655 if not conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2656 or conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2658 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2659 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2663 def symbol_transform():
2664 "test --symbol-transform"
2666 conv
= ensure_conversion(
2669 '--symbol-default=heuristic',
2670 '--symbol-transform=BRANCH:branch',
2671 '--symbol-transform=TAG:tag',
2672 '--symbol-transform=MOSTLY_(BRANCH|TAG):MOSTLY.\\1',
2674 if not conv
.path_exists('branches', 'branch'):
2676 if not conv
.path_exists('tags', 'tag'):
2678 if not conv
.path_exists('branches', 'MOSTLY.BRANCH'):
2680 if not conv
.path_exists('tags', 'MOSTLY.TAG'):
2684 def write_symbol_info():
2685 "test --write-symbol-info"
2689 'trunk', 'trunk', '.'],
2690 ['0', 'BLOCKED_BY_UNNAMED',
2691 'branch', 'branches/BLOCKED_BY_UNNAMED', '.trunk.'],
2692 ['0', 'BLOCKING_COMMIT',
2693 'branch', 'branches/BLOCKING_COMMIT', 'BLOCKED_BY_COMMIT'],
2694 ['0', 'BLOCKED_BY_COMMIT',
2695 'branch', 'branches/BLOCKED_BY_COMMIT', '.trunk.'],
2696 ['0', 'BLOCKING_BRANCH',
2697 'branch', 'branches/BLOCKING_BRANCH', 'BLOCKED_BY_BRANCH'],
2698 ['0', 'BLOCKED_BY_BRANCH',
2699 'branch', 'branches/BLOCKED_BY_BRANCH', '.trunk.'],
2700 ['0', 'MOSTLY_BRANCH',
2704 ['0', 'BRANCH_WITH_COMMIT',
2705 'branch', 'branches/BRANCH_WITH_COMMIT', '.trunk.'],
2707 'branch', 'branches/BRANCH', '.trunk.'],
2709 'tag', 'tags/TAG', '.trunk.'],
2710 ['0', 'unlabeled-1.1.12.1.2',
2711 'branch', 'branches/unlabeled-1.1.12.1.2', 'BLOCKED_BY_UNNAMED'],
2713 expected_lines
.sort()
2715 symbol_info_file
= os
.path
.join(tmp_dir
, 'symbol-mess-symbol-info.txt')
2720 '--symbol-default=strict',
2721 '--write-symbol-info=%s' % (symbol_info_file
,),
2722 '--passes=:CollateSymbolsPass',
2725 raise MissingErrorException()
2729 comment_re
= re
.compile(r
'^\s*\#')
2730 for l
in open(symbol_info_file
, 'r'):
2731 if comment_re
.match(l
):
2733 lines
.append(l
.strip().split())
2735 if lines
!= expected_lines
:
2736 s
= ['Symbol info incorrect\n']
2738 for diffline
in differ
.compare(
2739 [' '.join(line
) + '\n' for line
in expected_lines
],
2740 [' '.join(line
) + '\n' for line
in lines
],
2743 raise Failure(''.join(s
))
2747 "test --symbol-hints for setting branch/tag"
2749 conv
= ensure_conversion(
2750 'symbol-mess', symbol_hints_file
='symbol-mess-symbol-hints.txt',
2752 if not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2754 if not conv
.path_exists('tags', 'MOSTLY_TAG'):
2756 conv
.logs
[3].check(sym_log_msg('MOSTLY_TAG', 1), (
2757 ('/tags/MOSTLY_TAG (from /trunk:2)', 'A'),
2759 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2760 ('/branches/BRANCH_WITH_COMMIT (from /trunk:2)', 'A'),
2762 conv
.logs
[10].check(sym_log_msg('MOSTLY_BRANCH'), (
2763 ('/branches/MOSTLY_BRANCH (from /trunk:2)', 'A'),
2768 "test --symbol-hints for setting parent"
2770 conv
= ensure_conversion(
2771 'symbol-mess', symbol_hints_file
='symbol-mess-parent-hints.txt',
2773 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2774 ('/%(branches)s/BRANCH_WITH_COMMIT (from /branches/BRANCH:8)', 'A'),
2778 def parent_hints_invalid():
2779 "test --symbol-hints with an invalid parent"
2781 # BRANCH_WITH_COMMIT is usually determined to branch from .trunk.;
2782 # this symbol hints file sets the preferred parent to BRANCH
2784 conv
= ensure_conversion(
2785 'symbol-mess', symbol_hints_file
='symbol-mess-parent-hints-invalid.txt',
2787 r
"BLOCKED_BY_BRANCH is not a valid parent for BRANCH_WITH_COMMIT"
2792 def parent_hints_wildcards():
2793 "test --symbol-hints wildcards"
2795 # BRANCH_WITH_COMMIT is usually determined to branch from .trunk.;
2796 # this symbol hints file sets the preferred parent to BRANCH
2798 conv
= ensure_conversion(
2800 symbol_hints_file
='symbol-mess-parent-hints-wildcards.txt',
2802 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2803 ('/%(branches)s/BRANCH_WITH_COMMIT (from /branches/BRANCH:8)', 'A'),
2808 "test --symbol-hints for setting svn paths"
2810 conv
= ensure_conversion(
2811 'symbol-mess', symbol_hints_file
='symbol-mess-path-hints.txt',
2813 conv
.logs
[1].check('Standard project directories initialized by cvs2svn.', (
2816 ('/a/strange', 'A'),
2817 ('/a/strange/trunk', 'A'),
2818 ('/a/strange/trunk/path', 'A'),
2822 conv
.logs
[3].check(sym_log_msg('MOSTLY_TAG', 1), (
2824 ('/special/tag', 'A'),
2825 ('/special/tag/path (from /a/strange/trunk/path:2)', 'A'),
2827 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2828 ('/special/other', 'A'),
2829 ('/special/other/branch', 'A'),
2830 ('/special/other/branch/path (from /a/strange/trunk/path:2)', 'A'),
2832 conv
.logs
[10].check(sym_log_msg('MOSTLY_BRANCH'), (
2833 ('/special/branch', 'A'),
2834 ('/special/branch/path (from /a/strange/trunk/path:2)', 'A'),
2839 "test problem from issue 99"
2841 conv
= ensure_conversion('issue-99')
2845 "test problem from issue 100"
2847 conv
= ensure_conversion('issue-100')
2848 file1
= conv
.get_wc('trunk', 'file1.txt')
2849 if file(file1
).read() != 'file1.txt<1.2>\n':
2854 "test problem from issue 106"
2856 conv
= ensure_conversion('issue-106')
2859 def options_option():
2860 "use of the --options option"
2862 conv
= ensure_conversion('main', options_file
='cvs2svn.options')
2866 "multiproject conversion"
2868 conv
= ensure_conversion(
2869 'main', options_file
='cvs2svn-multiproject.options'
2871 conv
.logs
[1].check('Standard project directories initialized by cvs2svn.', (
2872 ('/partial-prune', 'A'),
2873 ('/partial-prune/trunk', 'A'),
2874 ('/partial-prune/branches', 'A'),
2875 ('/partial-prune/tags', 'A'),
2876 ('/partial-prune/releases', 'A'),
2881 "multiproject conversion with cross-project commits"
2883 conv
= ensure_conversion(
2884 'main', options_file
='cvs2svn-crossproject.options'
2888 def tag_with_no_revision():
2889 "tag defined but revision is deleted"
2891 conv
= ensure_conversion('tag-with-no-revision')
2894 def delete_cvsignore():
2895 "svn:ignore should vanish when .cvsignore does"
2897 # This is issue #81.
2899 conv
= ensure_conversion('delete-cvsignore')
2901 wc_tree
= conv
.get_wc_tree()
2902 props
= props_for_path(wc_tree
, 'trunk/proj')
2904 if props
.has_key('svn:ignore'):
2908 def repeated_deltatext():
2909 "ignore repeated deltatext blocks with warning"
2911 conv
= ensure_conversion('repeated-deltatext')
2912 warning_re
= r
'.*Deltatext block for revision 1.1 appeared twice'
2913 if not conv
.output_found(warning_re
):
2918 "process some nasty dependency graphs"
2920 # It's not how well the bear can dance, but that the bear can dance
2922 conv
= ensure_conversion('nasty-graphs')
2925 def tagging_after_delete():
2926 "optimal tag after deleting files"
2928 conv
= ensure_conversion('tagging-after-delete')
2930 # tag should be 'clean', no deletes
2931 log
= conv
.find_tag_log('tag1')
2933 ('/%(tags)s/tag1 (from /%(trunk)s:3)', 'A'),
2935 log
.check_changes(expected
)
2938 def crossed_branches():
2939 "branches created in inconsistent orders"
2941 conv
= ensure_conversion('crossed-branches')
2944 def file_directory_conflict():
2945 "error when filename conflicts with directory name"
2947 conv
= ensure_conversion(
2948 'file-directory-conflict',
2949 error_re
=r
'.*Directory name conflicts with filename',
2953 def attic_directory_conflict():
2954 "error when attic filename conflicts with dirname"
2956 # This tests the problem reported in issue #105.
2958 conv
= ensure_conversion(
2959 'attic-directory-conflict',
2960 error_re
=r
'.*Directory name conflicts with filename',
2965 "verify that --use-internal-co works"
2967 rcs_conv
= ensure_conversion(
2968 'main', args
=['--use-rcs', '--default-eol=native'],
2970 conv
= ensure_conversion(
2971 'main', args
=['--default-eol=native'],
2973 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
2975 rcs_lines
= run_program(
2976 svntest
.main
.svnadmin_binary
, None, 'dump', '-q', '-r', '1:HEAD',
2978 lines
= run_program(
2979 svntest
.main
.svnadmin_binary
, None, 'dump', '-q', '-r', '1:HEAD',
2981 # Compare all lines following the repository UUID:
2982 if lines
[3:] != rcs_lines
[3:]:
2986 def internal_co_exclude():
2987 "verify that --use-internal-co --exclude=... works"
2989 rcs_conv
= ensure_conversion(
2991 args
=['--use-rcs', '--exclude=BRANCH', '--default-eol=native'],
2993 conv
= ensure_conversion(
2995 args
=['--exclude=BRANCH', '--default-eol=native'],
2997 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
2999 rcs_lines
= run_program(
3000 svntest
.main
.svnadmin_binary
, None, 'dump', '-q', '-r', '1:HEAD',
3002 lines
= run_program(
3003 svntest
.main
.svnadmin_binary
, None, 'dump', '-q', '-r', '1:HEAD',
3005 # Compare all lines following the repository UUID:
3006 if lines
[3:] != rcs_lines
[3:]:
3010 def internal_co_trunk_only():
3011 "verify that --use-internal-co --trunk-only works"
3013 conv
= ensure_conversion(
3015 args
=['--trunk-only', '--default-eol=native'],
3017 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3021 def leftover_revs():
3022 "check for leftover checked-out revisions"
3024 conv
= ensure_conversion(
3026 args
=['--exclude=BRANCH', '--default-eol=native'],
3028 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3032 def requires_internal_co():
3033 "test that internal co can do more than RCS"
3034 # See issues 4, 11 for the bugs whose regression we're testing for.
3035 # Unlike in requires_cvs above, issue 29 is not covered.
3036 conv
= ensure_conversion('requires-cvs')
3038 atsign_contents
= file(conv
.get_wc("trunk", "atsign-add")).read()
3040 if atsign_contents
[-1:] == "@":
3043 if not (conv
.logs
[21].author
== "William Lyon Phelps III" and
3044 conv
.logs
[20].author
== "j random"):
3048 def internal_co_keywords():
3049 "test that internal co handles keywords correctly"
3050 conv_ic
= ensure_conversion('internal-co-keywords',
3051 args
=["--keywords-off"])
3052 conv_cvs
= ensure_conversion('internal-co-keywords',
3053 args
=["--use-cvs", "--keywords-off"])
3055 ko_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'ko.txt')).read()
3056 ko_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'ko.txt')).read()
3057 kk_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'kk.txt')).read()
3058 kk_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'kk.txt')).read()
3059 kv_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'kv.txt')).read()
3060 kv_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'kv.txt')).read()
3067 # The date format changed between cvs and co ('/' instead of '-').
3068 # Accept either one:
3069 date_substitution_re
= re
.compile(r
' ([0-9]*)-([0-9]*)-([0-9]*) ')
3070 if kv_ic
!= kv_cvs \
3071 and date_substitution_re
.sub(r
' \1/\2/\3 ', kv_ic
) != kv_cvs
:
3075 def timestamp_chaos():
3076 "test timestamp adjustments"
3078 conv
= ensure_conversion('timestamp-chaos', args
=["-v"])
3081 '2007-01-01 21:00:00', # Initial commit
3082 '2007-01-01 21:00:00', # revision 1.1 of both files
3083 '2007-01-01 21:00:01', # revision 1.2 of file1.txt, adjusted forwards
3084 '2007-01-01 21:00:02', # revision 1.2 of file1.txt, adjusted backwards
3085 '2007-01-01 22:00:00', # revision 1.3 of both files
3087 for i
in range(len(times
)):
3088 if abs(conv
.logs
[i
+ 1].date
- time
.mktime(svn_strptime(times
[i
]))) > 0.1:
3093 "convert a repository that contains symlinks"
3095 # This is a test for issue #97.
3097 proj
= os
.path
.join(test_data_dir
, 'symlinks-cvsrepos', 'proj')
3100 os
.path
.join('..', 'file.txt,v'),
3101 os
.path
.join(proj
, 'dir1', 'file.txt,v'),
3105 os
.path
.join(proj
, 'dir2'),
3111 except AttributeError:
3112 # Apparently this OS doesn't support symlinks, so skip test.
3113 raise svntest
.Skip()
3116 for (src
,dst
) in links
:
3117 os
.symlink(src
, dst
)
3119 conv
= ensure_conversion('symlinks')
3120 conv
.logs
[2].check('', (
3121 ('/%(trunk)s/proj', 'A'),
3122 ('/%(trunk)s/proj/file.txt', 'A'),
3123 ('/%(trunk)s/proj/dir1', 'A'),
3124 ('/%(trunk)s/proj/dir1/file.txt', 'A'),
3125 ('/%(trunk)s/proj/dir2', 'A'),
3126 ('/%(trunk)s/proj/dir2/file.txt', 'A'),
3129 for (src
,dst
) in links
:
3133 def empty_trunk_path():
3134 "allow --trunk to be empty if --trunk-only"
3136 # This is a test for issue #53.
3138 conv
= ensure_conversion(
3139 'main', args
=['--trunk-only', '--trunk='],
3143 def preferred_parent_cycle():
3144 "handle a cycle in branch parent preferences"
3146 conv
= ensure_conversion('preferred-parent-cycle')
3149 def branch_from_empty_dir():
3150 "branch from an empty directory"
3152 conv
= ensure_conversion('branch-from-empty-dir')
3156 "add a file on a branch then on trunk"
3158 conv
= ensure_conversion('trunk-readd')
3161 def branch_from_deleted_1_1():
3162 "branch from a 1.1 revision that will be deleted"
3164 conv
= ensure_conversion('branch-from-deleted-1-1')
3165 conv
.logs
[5].check('Adding b.txt:1.1.2.1', (
3166 ('/%(branches)s/BRANCH1/proj/b.txt', 'A'),
3168 conv
.logs
[6].check('Adding b.txt:1.1.4.1', (
3169 ('/%(branches)s/BRANCH2/proj/b.txt', 'A'),
3171 conv
.logs
[7].check('Adding b.txt:1.2', (
3172 ('/%(trunk)s/proj/b.txt', 'A'),
3175 conv
.logs
[8].check('Adding c.txt:1.1.2.1', (
3176 ('/%(branches)s/BRANCH1/proj/c.txt', 'A'),
3178 conv
.logs
[9].check('Adding c.txt:1.1.4.1', (
3179 ('/%(branches)s/BRANCH2/proj/c.txt', 'A'),
3183 def add_on_branch():
3184 "add a file on a branch using newer CVS"
3186 conv
= ensure_conversion('add-on-branch')
3187 conv
.logs
[6].check('Adding b.txt:1.1', (
3188 ('/%(trunk)s/proj/b.txt', 'A'),
3190 conv
.logs
[7].check('Adding b.txt:1.1.2.2', (
3191 ('/%(branches)s/BRANCH1/proj/b.txt', 'A'),
3193 conv
.logs
[8].check('Adding c.txt:1.1', (
3194 ('/%(trunk)s/proj/c.txt', 'A'),
3196 conv
.logs
[9].check('Removing c.txt:1.2', (
3197 ('/%(trunk)s/proj/c.txt', 'D'),
3199 conv
.logs
[10].check('Adding c.txt:1.2.2.2', (
3200 ('/%(branches)s/BRANCH2/proj/c.txt', 'A'),
3202 conv
.logs
[11].check('Adding d.txt:1.1', (
3203 ('/%(trunk)s/proj/d.txt', 'A'),
3205 conv
.logs
[12].check('Adding d.txt:1.1.2.2', (
3206 ('/%(branches)s/BRANCH3/proj/d.txt', 'A'),
3211 "test output in git-fast-import format"
3213 # Note: To test importing into git, do
3215 # ./run-tests <test-number>
3218 # cat cvs2svn-tmp/git-{blob,dump}.dat | git-fast-import
3220 # Or, to load the dumpfiles separately:
3222 # cat cvs2svn-tmp/git-blob.dat \
3223 # | git-fast-import --export-marks=cvs2svn-tmp/git-marks.dat
3224 # cat cvs2svn-tmp/git-dump.dat \
3225 # | git-fast-import --import-marks=cvs2svn-tmp/git-marks.dat
3227 # Then use "gitk --all", "git log", etc. to test the contents of the
3230 conv
= ensure_conversion('main', options_file
='cvs2svn-git.options')
3233 def main_git_inline():
3234 "output in git-fast-import format with inline data"
3236 # Note: To test importing into git, do
3238 # ./run-tests <test-number>
3241 # cat cvs2svn-tmp/git-dump.dat | git-fast-import
3243 # Then use "gitk --all", "git log", etc. to test the contents of the
3246 conv
= ensure_conversion('main', options_file
='cvs2svn-git-inline.options')
3249 def invalid_symbol():
3250 "a symbol with the incorrect format"
3252 conv
= ensure_conversion('invalid-symbol')
3253 if not conv
.output_found(
3254 r
".*branch 'SYMBOL' references invalid revision 1$"
3259 def invalid_symbol_ignore():
3260 "ignore a symbol with the incorrect format"
3262 conv
= ensure_conversion(
3263 'invalid-symbol', options_file
='cvs2svn-ignore.options'
3267 class EOLVariants(Cvs2SvnTestCase
):
3268 "handle various --eol-style options"
3270 eol_style_strings
= {
3277 def __init__(self
, eol_style
):
3278 self
.eol_style
= eol_style
3279 self
.dumpfile
= 'eol-variants-%s.dump' % (self
.eol_style
,)
3280 Cvs2SvnTestCase
.__init
__(
3281 self
, 'eol-variants', variant
=self
.eol_style
,
3282 dumpfile
=self
.dumpfile
,
3284 '--default-eol=%s' % (self
.eol_style
,),
3289 conv
= self
.ensure_conversion()
3290 dump_contents
= open(conv
.dumpfile
, 'rb').read()
3291 expected_text
= self
.eol_style_strings
[self
.eol_style
].join(
3292 ['line 1', 'line 2', '\n\n']
3294 if not dump_contents
.endswith(expected_text
):
3299 "handle a file with no revisions (issue #80)"
3301 conv
= ensure_conversion('no-revs-file')
3304 def mirror_keyerror_test():
3305 "a case that gave KeyError in SVNRepositoryMirror"
3307 conv
= ensure_conversion('mirror-keyerror')
3310 def exclude_ntdb_test():
3311 "exclude a non-trunk default branch"
3313 symbol_info_file
= os
.path
.join(tmp_dir
, 'exclude-ntdb-symbol-info.txt')
3314 conv
= ensure_conversion(
3317 '--write-symbol-info=%s' % (symbol_info_file
,),
3318 '--exclude=branch3',
3320 '--exclude=vendortag3',
3321 '--exclude=vendorbranch',
3326 def mirror_keyerror2_test():
3327 "a case that gave KeyError in RepositoryMirror"
3329 conv
= ensure_conversion('mirror-keyerror2')
3332 def mirror_keyerror3_test():
3333 "a case that gave KeyError in RepositoryMirror"
3335 conv
= ensure_conversion('mirror-keyerror3')
3338 def add_cvsignore_to_branch_test():
3339 "check adding .cvsignore to an existing branch"
3341 # This a test for issue #122.
3343 conv
= ensure_conversion('add-cvsignore-to-branch')
3344 wc_tree
= conv
.get_wc_tree()
3345 trunk_props
= props_for_path(wc_tree
, 'trunk/dir')
3346 if trunk_props
['svn:ignore'] != '*.o\n\n':
3349 branch_props
= props_for_path(wc_tree
, 'branches/BRANCH/dir')
3350 if branch_props
['svn:ignore'] != '*.o\n\n':
3354 ########################################################################
3357 # list all tests here, starting with None:
3366 PruneWithCare(variant
=1, trunk
='a', branches
='b', tags
='c'),
3367 PruneWithCare(variant
=2, trunk
='a/1', branches
='b/1', tags
='c/1'),
3368 PruneWithCare(variant
=3, trunk
='a/1', branches
='a/2', tags
='a/3'),
3369 interleaved_commits
,
3373 SimpleTags(variant
=1, trunk
='a', branches
='b', tags
='c'),
3374 SimpleTags(variant
=2, trunk
='a/1', branches
='b/1', tags
='c/1'),
3375 SimpleTags(variant
=3, trunk
='a/1', branches
='a/2', tags
='a/3'),
3376 simple_branch_commits
,
3378 mixed_time_branch_with_added_file
,
3385 PhoenixBranch(variant
=1, trunk
='a/1', branches
='b/1', tags
='c/1'),
3389 NoTrunkPrune(variant
=1, trunk
='a', branches
='b', tags
='c'),
3390 NoTrunkPrune(variant
=2, trunk
='a/1', branches
='b/1', tags
='c/1'),
3391 NoTrunkPrune(variant
=3, trunk
='a/1', branches
='a/2', tags
='a/3'),
3396 TaggedBranchAndTrunk(),
3397 TaggedBranchAndTrunk(variant
=1, trunk
='a/1', branches
='a/2', tags
='a/3'),
3400 BranchDeleteFirst(),
3401 BranchDeleteFirst(variant
=1, trunk
='a/1', branches
='a/2', tags
='a/3'),
3405 warning_expected
=1),
3408 variant
='encoding', args
=['--encoding=utf_8']),
3411 variant
='fallback-encoding', args
=['--fallback-encoding=utf_8']),
3413 warning_expected
=1),
3416 variant
='encoding', args
=['--encoding=utf_8']),
3419 variant
='fallback-encoding', args
=['--fallback-encoding=utf_8']),
3420 vendor_branch_sameness
,
3421 vendor_branch_trunk_only
,
3423 default_branches_trunk_only
,
3425 default_branch_and_1_2
,
3426 compose_tag_three_sources
,
3429 PeerPathPruning(variant
=1, trunk
='a/1', branches
='a/2', tags
='a/3'),
3431 EmptyTrunk(variant
=1, trunk
='a', branches
='b', tags
='c'),
3432 EmptyTrunk(variant
=2, trunk
='a/1', branches
='a/2', tags
='a/3'),
3433 no_spurious_svn_commits
,
3434 invalid_closings_on_trunk
,
3438 branch_from_default_branch
,
3440 retain_file_in_attic_too
,
3441 symbolic_name_filling_guide
,
3452 questionable_branch_names
,
3453 questionable_tag_names
,
3454 revision_reorder_bug
,
3456 vendor_branch_delete_add
,
3458 resync_pass2_pull_forward
,
3461 XFail(double_fill2
),
3462 resync_pass2_push_backward
,
3465 nested_ttb_directories
,
3466 auto_props_ignore_case
,
3467 ctrl_char_in_filename
,
3469 commit_dependencies
,
3472 multiply_defined_symbols
,
3473 multiply_defined_symbols_renamed
,
3474 multiply_defined_symbols_ignored
,
3475 repeatedly_defined_symbols
,
3476 double_branch_delete
,
3478 overlook_symbol_mismatches
,
3483 unblock_blocked_excludes
,
3484 regexp_force_symbols
,
3485 heuristic_symbol_default
,
3486 branch_symbol_default
,
3493 parent_hints_invalid
,
3494 parent_hints_wildcards
,
3503 tag_with_no_revision
,
3507 XFail(tagging_after_delete
),
3509 file_directory_conflict
,
3510 attic_directory_conflict
,
3513 internal_co_exclude
,
3514 internal_co_trunk_only
,
3515 internal_co_keywords
,
3517 requires_internal_co
,
3521 preferred_parent_cycle
,
3522 branch_from_empty_dir
,
3525 branch_from_deleted_1_1
,
3528 XFail(main_git_inline
),
3530 invalid_symbol_ignore
,
3533 EOLVariants('CRLF'),
3535 EOLVariants('native'),
3537 mirror_keyerror_test
,
3539 mirror_keyerror2_test
,
3540 mirror_keyerror3_test
,
3541 XFail(add_cvsignore_to_branch_test
),
3544 if __name__
== '__main__':
3546 # Configure the environment for reproducable output from svn, etc.
3547 # I have no idea if this works on Windows too.
3548 os
.environ
["LC_ALL"] = "C"
3549 os
.environ
["TZ"] = "UTC"
3551 # The Subversion test suite code assumes it's being invoked from
3552 # within a working copy of the Subversion sources, and tries to use
3553 # the binaries in that tree. Since the cvs2svn tree never contains
3554 # a Subversion build, we just use the system's installed binaries.
3555 svntest
.main
.svn_binary
= 'svn'
3556 svntest
.main
.svnlook_binary
= 'svnlook'
3557 svntest
.main
.svnadmin_binary
= 'svnadmin'
3558 svntest
.main
.svnversion_binary
= 'svnversion'
3560 run_tests(test_list
)