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-2009 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 ######################################################################
49 from hashlib
import md5
52 from difflib
import Differ
54 # Make sure that a supported version of Python is being used:
55 if not (0x02040000 <= sys
.hexversion
< 0x03000000):
57 'error: Python 2, version 2.4 or higher required.\n'
61 # This script needs to run in the correct directory. Make sure we're there.
62 if not (os
.path
.exists('cvs2svn') and os
.path
.exists('test-data')):
63 sys
.stderr
.write("error: I need to be run in the directory containing "
64 "'cvs2svn' and 'test-data'.\n")
67 # Load the Subversion test framework.
69 from svntest
import Failure
70 from svntest
.main
import safe_rmtree
71 from svntest
.testcase
import TestCase
72 from svntest
.testcase
import XFail_deco
74 # Test if Mercurial >= 1.1 is available.
76 from mercurial
import context
79 except (ImportError, AttributeError):
82 cvs2svn
= os
.path
.abspath('cvs2svn')
83 cvs2git
= os
.path
.abspath('cvs2git')
84 cvs2hg
= os
.path
.abspath('cvs2hg')
86 # We use the installed svn and svnlook binaries, instead of using
87 # svntest.main.run_svn() and svntest.main.run_svnlook(), because the
88 # behavior -- or even existence -- of local builds shouldn't affect
89 # the cvs2svn test suite.
91 svnlook_binary
= 'svnlook'
92 svnadmin_binary
= 'svnadmin'
93 svnversion_binary
= 'svnversion'
95 test_data_dir
= 'test-data'
96 tmp_dir
= 'cvs2svn-tmp'
99 #----------------------------------------------------------------------
101 #----------------------------------------------------------------------
104 # The value to expect for svn:keywords if it is set:
105 KEYWORDS
= 'Author Date Id Revision'
108 class RunProgramException(Failure
):
112 class MissingErrorException(Failure
):
113 def __init__(self
, error_re
):
115 self
, "Test failed because no error matched '%s'" % (error_re
,)
119 def run_program(program
, error_re
, *varargs
):
120 """Run PROGRAM with VARARGS, return stdout as a list of lines.
122 If there is any stderr and ERROR_RE is None, raise
123 RunProgramException, and log the stderr lines via
124 svntest.main.logger.info().
126 If ERROR_RE is not None, it is a string regular expression that must
127 match some line of stderr. If it fails to match, raise
128 MissingErrorExpection."""
130 # FIXME: exit_code is currently ignored.
131 exit_code
, out
, err
= svntest
.main
.run_command(program
, 1, 0, *varargs
)
134 # Specified error expected on stderr.
136 raise MissingErrorException(error_re
)
139 if re
.match(error_re
, line
):
141 raise MissingErrorException(error_re
)
145 log
= svntest
.main
.logger
.info
146 log('%s said:' % program
)
148 log(' ' + line
.rstrip())
149 raise RunProgramException()
154 def run_script(script
, error_re
, *varargs
):
155 """Run Python script SCRIPT with VARARGS, returning stdout as a list
158 If there is any stderr and ERROR_RE is None, raise
159 RunProgramException, and log the stderr lines via
160 svntest.main.logger.info().
162 If ERROR_RE is not None, it is a string regular expression that must
163 match some line of stderr. If it fails to match, raise
164 MissingErrorException."""
166 # Use the same python that is running this script
167 return run_program(sys
.executable
, error_re
, script
, *varargs
)
168 # On Windows, for an unknown reason, the cmd.exe process invoked by
169 # os.system('sort ...') in cvs2svn receives invalid stdio handles, if
170 # cvs2svn is started as "cvs2svn ...". "python cvs2svn ..." avoids
171 # this. Therefore, the redirection of the output to the .s-revs file fails.
172 # We no longer use the problematic invocation on any system, but this
173 # comment remains to warn about this problem.
176 def run_svn(*varargs
):
177 """Run svn with VARARGS; return stdout as a list of lines.
178 If there is any stderr, raise RunProgramException, and log the
179 stderr lines via svntest.main.logger.info()."""
180 return run_program(svn_binary
, None, *varargs
)
183 def repos_to_url(path_to_svn_repos
):
184 """This does what you think it does."""
185 rpath
= os
.path
.abspath(path_to_svn_repos
)
188 return 'file://%s' % rpath
.replace(os
.sep
, '/')
191 def svn_strptime(timestr
):
192 return time
.strptime(timestr
, '%Y-%m-%d %H:%M:%S')
196 def __init__(self
, revision
, author
, date
, symbols
):
197 self
.revision
= revision
200 # Internally, we represent the date as seconds since epoch (UTC).
201 # Since standard subversion log output shows dates in localtime
203 # "1993-06-18 00:46:07 -0500 (Fri, 18 Jun 1993)"
205 # and time.mktime() converts from localtime, it all works out very
207 self
.date
= time
.mktime(svn_strptime(date
[0:19]))
209 # The following symbols are used for string interpolation when
211 self
.symbols
= symbols
213 # The changed paths will be accumulated later, as log data is read.
214 # Keys here are paths such as '/trunk/foo/bar', values are letter
215 # codes such as 'M', 'A', and 'D'.
216 self
.changed_paths
= { }
218 # The msg will be accumulated later, as log data is read.
221 def absorb_changed_paths(self
, out
):
222 'Read changed paths from OUT into self, until no more.'
224 line
= out
.readline()
225 if len(line
) == 1: return
227 op_portion
= line
[3:4]
228 path_portion
= line
[5:]
229 # If we're running on Windows we get backslashes instead of
231 path_portion
= path_portion
.replace('\\', '/')
232 # # We could parse out history information, but currently we
233 # # just leave it in the path portion because that's how some
236 # m = re.match("(.*) \(from /.*:[0-9]+\)", path_portion)
238 # path_portion = m.group(1)
239 self
.changed_paths
[path_portion
] = op_portion
241 def __cmp__(self
, other
):
242 return cmp(self
.revision
, other
.revision
) or \
243 cmp(self
.author
, other
.author
) or cmp(self
.date
, other
.date
) or \
244 cmp(self
.changed_paths
, other
.changed_paths
) or \
245 cmp(self
.msg
, other
.msg
)
247 def get_path_op(self
, path
):
248 """Return the operator for the change involving PATH.
250 PATH is allowed to include string interpolation directives (e.g.,
251 '%(trunk)s'), which are interpolated against self.symbols. Return
252 None if there is no record for PATH."""
253 return self
.changed_paths
.get(path
% self
.symbols
)
255 def check_msg(self
, msg
):
256 """Verify that this Log's message starts with the specified MSG."""
257 if self
.msg
.find(msg
) != 0:
259 "Revision %d log message was:\n%s\n\n"
260 "It should have begun with:\n%s\n\n"
261 % (self
.revision
, self
.msg
, msg
,)
264 def check_change(self
, path
, op
):
265 """Verify that this Log includes a change for PATH with operator OP.
267 PATH is allowed to include string interpolation directives (e.g.,
268 '%(trunk)s'), which are interpolated against self.symbols."""
270 path
= path
% self
.symbols
271 found_op
= self
.changed_paths
.get(path
, None)
274 "Revision %d does not include change for path %s "
275 "(it should have been %s).\n"
276 % (self
.revision
, path
, op
,)
280 "Revision %d path %s had op %s (it should have been %s)\n"
281 % (self
.revision
, path
, found_op
, op
,)
284 def check_changes(self
, changed_paths
):
285 """Verify that this Log has precisely the CHANGED_PATHS specified.
287 CHANGED_PATHS is a sequence of tuples (path, op), where the paths
288 strings are allowed to include string interpolation directives
289 (e.g., '%(trunk)s'), which are interpolated against self.symbols."""
292 for (path
, op
) in changed_paths
:
293 cp
[path
% self
.symbols
] = op
295 if self
.changed_paths
!= cp
:
297 "Revision %d changed paths list was:\n%s\n\n"
298 "It should have been:\n%s\n\n"
299 % (self
.revision
, self
.changed_paths
, cp
,)
302 def check(self
, msg
, changed_paths
):
303 """Verify that this Log has the MSG and CHANGED_PATHS specified.
305 Convenience function to check two things at once. MSG is passed
306 to check_msg(); CHANGED_PATHS is passed to check_changes()."""
309 self
.check_changes(changed_paths
)
312 def parse_log(svn_repos
, symbols
):
313 """Return a dictionary of Logs, keyed on revision number, for SVN_REPOS.
315 Initialize the Logs' symbols with SYMBOLS."""
318 'Make a list of lines behave like an open file handle.'
319 def __init__(self
, lines
):
322 if len(self
.lines
) > 0:
323 return self
.lines
.pop(0)
327 def absorb_message_body(out
, num_lines
, log
):
328 """Read NUM_LINES of log message body from OUT into Log item LOG."""
330 for i
in range(num_lines
):
331 log
.msg
+= out
.readline()
333 log_start_re
= re
.compile('^r(?P<rev>[0-9]+) \| '
334 '(?P<author>[^\|]+) \| '
336 '\| (?P<lines>[0-9]+) (line|lines)$')
338 log_separator
= '-' * 72
342 out
= LineFeeder(run_svn('log', '-v', repos_to_url(svn_repos
)))
346 line
= out
.readline()
350 if line
.find(log_separator
) == 0:
351 line
= out
.readline()
354 m
= log_start_re
.match(line
)
357 int(m
.group('rev')), m
.group('author'), m
.group('date'), symbols
)
358 line
= out
.readline()
359 if not line
.find('Changed paths:') == 0:
360 print 'unexpected log output (missing changed paths)'
361 print "Line: '%s'" % line
363 this_log
.absorb_changed_paths(out
)
364 absorb_message_body(out
, int(m
.group('lines')), this_log
)
365 logs
[this_log
.revision
] = this_log
367 break # We've reached the end of the log output.
369 print 'unexpected log output (missing revision line)'
370 print "Line: '%s'" % line
373 print 'unexpected log output (missing log separator)'
374 print "Line: '%s'" % line
381 """Unconditionally remove PATH and its subtree, if any. PATH may be
382 non-existent, a file or symlink, or a directory."""
383 if os
.path
.isdir(path
):
385 elif os
.path
.exists(path
):
389 log_msg_text_wrapper
= textwrap
.TextWrapper(width
=76, break_long_words
=False)
391 def sym_log_msg(symbolic_name
, is_tag
=None):
392 """Return the expected log message for a cvs2svn-synthesized revision
393 creating branch or tag SYMBOLIC_NAME."""
395 # This reproduces the logic in SVNSymbolCommit.get_log_msg().
401 return log_msg_text_wrapper
.fill(
402 "This commit was manufactured by cvs2svn to create %s '%s'."
403 % (type, symbolic_name
)
407 def make_conversion_id(
408 name
, args
, passbypass
, options_file
=None, symbol_hints_file
=None
410 """Create an identifying tag for a conversion.
412 The return value can also be used as part of a filesystem path.
414 NAME is the name of the CVS repository.
416 ARGS are the extra arguments to be passed to cvs2svn.
418 PASSBYPASS is a boolean indicating whether the conversion is to be
419 run one pass at a time.
421 If OPTIONS_FILE is specified, it is an options file that will be
422 used for the conversion.
424 If SYMBOL_HINTS_FILE is specified, it is a symbol hints file that
425 will be used for the conversion.
427 The 1-to-1 mapping between cvs2svn command parameters and
428 conversion_ids allows us to avoid running the same conversion more
429 than once, when multiple tests use exactly the same conversion."""
436 args
.append('--passbypass')
438 if symbol_hints_file
is not None:
439 args
.append('--symbol-hints=%s' % (symbol_hints_file
,))
441 # There are some characters that are forbidden in filenames, and
442 # there is a limit on the total length of a path to a file. So use
443 # a hash of the parameters rather than concatenating the parameters
446 conv_id
+= "-" + md5('\0'.join(args
)).hexdigest()
448 # Some options-file based tests rely on knowing the paths to which
449 # the repository should be written, so we handle that option as a
450 # predictable string:
451 if options_file
is not None:
452 conv_id
+= '--options=%s' % (options_file
,)
458 """A record of a cvs2svn conversion.
462 conv_id -- the conversion id for this Conversion.
464 name -- a one-word name indicating the involved repositories.
466 dumpfile -- the name of the SVN dumpfile created by the conversion
467 (if the DUMPFILE constructor argument was used); otherwise,
470 repos -- the path to the svn repository. Unset if DUMPFILE was
473 logs -- a dictionary of Log instances, as returned by parse_log().
474 Unset if DUMPFILE was specified.
476 symbols -- a dictionary of symbols used for string interpolation
479 stdout -- a list of lines written by cvs2svn to stdout
481 _wc -- the basename of the svn working copy (within tmp_dir).
482 Unset if DUMPFILE was specified.
484 _wc_path -- the path to the svn working copy, if it has already
485 been created; otherwise, None. (The working copy is created
486 lazily when get_wc() is called.) Unset if DUMPFILE was
489 _wc_tree -- the tree built from the svn working copy, if it has
490 already been created; otherwise, None. The tree is created
491 lazily when get_wc_tree() is called.) Unset if DUMPFILE was
494 _svnrepos -- the basename of the svn repository (within tmp_dir).
495 Unset if DUMPFILE was specified."""
497 # The number of the last cvs2svn pass (determined lazily by
502 def get_last_pass(cls
):
503 """Return the number of cvs2svn's last pass."""
505 if cls
.last_pass
is None:
506 out
= run_script(cvs2svn
, None, '--help-passes')
507 cls
.last_pass
= int(out
[-1].split()[0])
511 self
, conv_id
, name
, error_re
, passbypass
, symbols
, args
,
512 options_file
=None, symbol_hints_file
=None, dumpfile
=None,
514 self
.conv_id
= conv_id
516 self
.symbols
= symbols
517 if not os
.path
.isdir(tmp_dir
):
520 cvsrepos
= os
.path
.join(test_data_dir
, '%s-cvsrepos' % self
.name
)
523 self
.dumpfile
= os
.path
.join(tmp_dir
, dumpfile
)
524 # Clean up from any previous invocations of this script.
528 self
.repos
= os
.path
.join(tmp_dir
, '%s-svnrepos' % self
.conv_id
)
529 self
._wc
= os
.path
.join(tmp_dir
, '%s-wc' % self
.conv_id
)
533 # Clean up from any previous invocations of this script.
539 '--svnadmin=%s' % (svntest
.main
.svnadmin_binary
,),
542 self
.options_file
= os
.path
.join(cvsrepos
, options_file
)
544 '--options=%s' % self
.options_file
,
546 assert not symbol_hints_file
548 self
.options_file
= None
549 if tmp_dir
!= 'cvs2svn-tmp':
550 # Only include this argument if it differs from cvs2svn's default:
552 '--tmpdir=%s' % tmp_dir
,
555 if symbol_hints_file
:
556 self
.symbol_hints_file
= os
.path
.join(cvsrepos
, symbol_hints_file
)
558 '--symbol-hints=%s' % self
.symbol_hints_file
,
562 args
.extend(['--dumpfile=%s' % (self
.dumpfile
,)])
564 args
.extend(['-s', self
.repos
])
565 args
.extend([cvsrepos
])
569 for p
in range(1, self
.get_last_pass() + 1):
570 self
.stdout
+= run_script(cvs2svn
, error_re
, '-p', str(p
), *args
)
572 self
.stdout
= run_script(cvs2svn
, error_re
, *args
)
575 if not os
.path
.isfile(self
.dumpfile
):
577 "Dumpfile not created: '%s'"
578 % os
.path
.join(os
.getcwd(), self
.dumpfile
)
581 if os
.path
.isdir(self
.repos
):
582 self
.logs
= parse_log(self
.repos
, self
.symbols
)
583 elif error_re
is None:
585 "Repository not created: '%s'"
586 % os
.path
.join(os
.getcwd(), self
.repos
)
589 def output_found(self
, pattern
):
590 """Return True if PATTERN matches any line in self.stdout.
592 PATTERN is a regular expression pattern as a string.
595 pattern_re
= re
.compile(pattern
)
597 for line
in self
.stdout
:
598 if pattern_re
.match(line
):
599 # We found the pattern that we were looking for.
604 def find_tag_log(self
, tagname
):
605 """Search LOGS for a log message containing 'TAGNAME' and return the
606 log in which it was found."""
607 for i
in xrange(len(self
.logs
), 0, -1):
608 if self
.logs
[i
].msg
.find("'"+tagname
+"'") != -1:
610 raise ValueError("Tag %s not found in logs" % tagname
)
612 def get_wc(self
, *args
):
613 """Return the path to the svn working copy, or a path within the WC.
615 If a working copy has not been created yet, create it now.
617 If ARGS are specified, then they should be strings that form
618 fragments of a path within the WC. They are joined using
619 os.path.join() and appended to the WC path."""
621 if self
._wc
_path
is None:
622 run_svn('co', repos_to_url(self
.repos
), self
._wc
)
623 self
._wc
_path
= self
._wc
624 return os
.path
.join(self
._wc
_path
, *args
)
626 def get_wc_tree(self
):
627 if self
._wc
_tree
is None:
628 self
._wc
_tree
= svntest
.tree
.build_tree_from_wc(self
.get_wc(), 1)
631 def path_exists(self
, *args
):
632 """Return True if the specified path exists within the repository.
634 (The strings in ARGS are first joined into a path using
637 return os
.path
.exists(self
.get_wc(*args
))
639 def check_props(self
, keys
, checks
):
640 """Helper function for checking lots of properties. For a list of
641 files in the conversion, check that the values of the properties
642 listed in KEYS agree with those listed in CHECKS. CHECKS is a
643 list of tuples: [ (filename, [value, value, ...]), ...], where the
644 values are listed in the same order as the key names are listed in
647 for (file, values
) in checks
:
648 assert len(values
) == len(keys
)
649 props
= props_for_path(self
.get_wc_tree(), file)
650 for i
in range(len(keys
)):
651 if props
.get(keys
[i
]) != values
[i
]:
653 "File %s has property %s set to \"%s\" "
654 "(it should have been \"%s\").\n"
655 % (file, keys
[i
], props
.get(keys
[i
]), values
[i
],)
660 """A record of a cvs2svn conversion.
664 name -- a one-word name indicating the CVS repository to be converted.
666 stdout -- a list of lines written by cvs2svn to stdout."""
668 def __init__(self
, name
, error_re
, args
, options_file
=None):
670 if not os
.path
.isdir(tmp_dir
):
673 cvsrepos
= os
.path
.join(test_data_dir
, '%s-cvsrepos' % self
.name
)
677 self
.options_file
= os
.path
.join(cvsrepos
, options_file
)
679 '--options=%s' % self
.options_file
,
682 self
.options_file
= None
684 self
.stdout
= run_script(cvs2git
, error_re
, *args
)
687 # Cache of conversions that have already been done. Keys are conv_id;
688 # values are Conversion instances.
689 already_converted
= { }
691 def ensure_conversion(
692 name
, error_re
=None, passbypass
=None,
693 trunk
=None, branches
=None, tags
=None,
694 args
=None, options_file
=None, symbol_hints_file
=None, dumpfile
=None,
696 """Convert CVS repository NAME to Subversion, but only if it has not
697 been converted before by this invocation of this script. If it has
698 been converted before, return the Conversion object from the
701 If no error, return a Conversion instance.
703 If ERROR_RE is a string, it is a regular expression expected to
704 match some line of stderr printed by the conversion. If there is an
705 error and ERROR_RE is not set, then raise Failure.
707 If PASSBYPASS is set, then cvs2svn is run multiple times, each time
708 with a -p option starting at 1 and increasing to a (hardcoded) maximum.
710 NAME is just one word. For example, 'main' would mean to convert
711 './test-data/main-cvsrepos', and after the conversion, the resulting
712 Subversion repository would be in './cvs2svn-tmp/main-svnrepos', and
713 a checked out head working copy in './cvs2svn-tmp/main-wc'.
715 Any other options to pass to cvs2svn should be in ARGS, each element
716 being one option, e.g., '--trunk-only'. If the option takes an
717 argument, include it directly, e.g., '--mime-types=PATH'. Arguments
718 are passed to cvs2svn in the order that they appear in ARGS.
720 If OPTIONS_FILE is specified, then it should be the name of a file
721 within the main directory of the cvs repository associated with this
722 test. It is passed to cvs2svn using the --options option (which
723 suppresses some other options that are incompatible with --options).
725 If SYMBOL_HINTS_FILE is specified, then it should be the name of a
726 file within the main directory of the cvs repository associated with
727 this test. It is passed to cvs2svn using the --symbol-hints option.
729 If DUMPFILE is specified, then it is the name of a dumpfile within
730 the temporary directory to which the conversion output should be
741 args
.append('--trunk=%s' % (trunk
,))
744 branches
= 'branches'
746 args
.append('--branches=%s' % (branches
,))
751 args
.append('--tags=%s' % (tags
,))
753 conv_id
= make_conversion_id(
754 name
, args
, passbypass
, options_file
, symbol_hints_file
757 if conv_id
not in already_converted
:
759 # Run the conversion and store the result for the rest of this
761 already_converted
[conv_id
] = Conversion(
762 conv_id
, name
, error_re
, passbypass
,
763 {'trunk' : trunk
, 'branches' : branches
, 'tags' : tags
},
764 args
, options_file
, symbol_hints_file
, dumpfile
,
767 # Remember the failure so that a future attempt to run this conversion
768 # does not bother to retry, but fails immediately.
769 already_converted
[conv_id
] = None
772 conv
= already_converted
[conv_id
]
778 class Cvs2SvnTestFunction(TestCase
):
779 """A TestCase based on a naked Python function object.
781 FUNC should be a function that returns None on success and throws an
782 svntest.Failure exception on failure. It should have a brief
783 docstring describing what it does (and fulfilling certain
784 conditions). FUNC must take no arguments.
786 This class is almost identical to svntest.testcase.FunctionTestCase,
787 except that the test function does not require a sandbox and does
788 not accept any parameter (not even sandbox=None).
790 This class can be used as an annotation on a Python function.
794 def __init__(self
, func
):
795 # it better be a function that accepts no parameters and has a
797 assert isinstance(func
, types
.FunctionType
)
799 name
= func
.func_name
801 assert func
.func_code
.co_argcount
== 0, \
802 '%s must not take any arguments' % name
804 doc
= func
.__doc
__.strip()
805 assert doc
, '%s must have a docstring' % name
807 # enforce stylistic guidelines for the function docstrings:
808 # - no longer than 50 characters
809 # - should not end in a period
810 # - should not be capitalized
811 assert len(doc
) <= 50, \
812 "%s's docstring must be 50 characters or less" % name
813 assert doc
[-1] != '.', \
814 "%s's docstring should not end in a period" % name
815 assert doc
[0].lower() == doc
[0], \
816 "%s's docstring should not be capitalized" % name
818 TestCase
.__init
__(self
, doc
=doc
)
821 def get_function_name(self
):
822 return self
.func
.func_name
824 def get_sandbox_name(self
):
827 def run(self
, sandbox
):
831 class Cvs2HgTestFunction(Cvs2SvnTestFunction
):
832 """Same as Cvs2SvnTestFunction, but for test cases that should be
833 skipped if Mercurial is not available.
835 def run(self
, sandbox
):
842 class Cvs2SvnTestCase(TestCase
):
844 self
, name
, doc
=None, variant
=None,
845 error_re
=None, passbypass
=None,
846 trunk
=None, branches
=None, tags
=None,
848 options_file
=None, symbol_hints_file
=None, dumpfile
=None,
853 # By default, use the first line of the class docstring as the
855 doc
= self
.__doc
__.splitlines()[0]
857 if variant
is not None:
858 # Modify doc to show the variant. Trim doc first if necessary
859 # to stay within the 50-character limit.
860 suffix
= '...variant %s' % (variant
,)
861 doc
= doc
[:50 - len(suffix
)] + suffix
863 TestCase
.__init
__(self
, doc
=doc
)
865 self
.error_re
= error_re
866 self
.passbypass
= passbypass
868 self
.branches
= branches
871 self
.options_file
= options_file
872 self
.symbol_hints_file
= symbol_hints_file
873 self
.dumpfile
= dumpfile
875 def ensure_conversion(self
):
876 return ensure_conversion(
878 error_re
=self
.error_re
, passbypass
=self
.passbypass
,
879 trunk
=self
.trunk
, branches
=self
.branches
, tags
=self
.tags
,
881 options_file
=self
.options_file
,
882 symbol_hints_file
=self
.symbol_hints_file
,
883 dumpfile
=self
.dumpfile
,
886 def get_sandbox_name(self
):
890 class Cvs2SvnPropertiesTestCase(Cvs2SvnTestCase
):
891 """Test properties resulting from a conversion."""
893 def __init__(self
, name
, props_to_test
, expected_props
, **kw
):
894 """Initialize an instance of Cvs2SvnPropertiesTestCase.
896 NAME is the name of the test, passed to Cvs2SvnTestCase.
897 PROPS_TO_TEST is a list of the names of svn properties that should
898 be tested. EXPECTED_PROPS is a list of tuples [(filename,
899 [value,...])], where the second item in each tuple is a list of
900 values expected for the properties listed in PROPS_TO_TEST for the
901 specified filename. If a property must *not* be set, then its
902 value should be listed as None."""
904 Cvs2SvnTestCase
.__init
__(self
, name
, **kw
)
905 self
.props_to_test
= props_to_test
906 self
.expected_props
= expected_props
909 conv
= self
.ensure_conversion()
910 conv
.check_props(self
.props_to_test
, self
.expected_props
)
913 #----------------------------------------------------------------------
915 #----------------------------------------------------------------------
920 "cvs2svn with no arguments shows usage"
921 out
= run_script(cvs2svn
, None)
922 if (len(out
) > 2 and out
[0].find('ERROR:') == 0
923 and out
[1].find('DBM module')):
924 print 'cvs2svn cannot execute due to lack of proper DBM module.'
925 print 'Exiting without running any further tests.'
927 if out
[0].find('Usage:') < 0:
928 raise Failure('Basic cvs2svn invocation failed.')
932 def cvs2svn_manpage():
933 "generate a manpage for cvs2svn"
934 out
= run_script(cvs2svn
, None, '--man')
938 def cvs2git_manpage():
939 "generate a manpage for cvs2git"
940 out
= run_script(cvs2git
, None, '--man')
945 def cvs2hg_manpage():
946 "generate a manpage for cvs2hg"
947 out
= run_script(cvs2hg
, None, '--man')
951 def show_help_passes():
952 "cvs2svn --help-passes shows pass information"
953 out
= run_script(cvs2svn
, None, '--help-passes')
954 if out
[0].find('PASSES') < 0:
955 raise Failure('cvs2svn --help-passes failed.')
960 "detection of the executable flag"
961 if sys
.platform
== 'win32':
963 conv
= ensure_conversion('main')
964 st
= os
.stat(conv
.get_wc('trunk', 'single-files', 'attr-exec'))
965 if not st
.st_mode
& stat
.S_IXUSR
:
971 "conversion of filename with a space"
972 conv
= ensure_conversion('main')
973 if not conv
.path_exists('trunk', 'single-files', 'space fname'):
979 "two commits in quick succession"
980 conv
= ensure_conversion('main')
982 os
.path
.join(conv
.repos
, 'trunk', 'single-files', 'twoquick'), {})
987 class PruneWithCare(Cvs2SvnTestCase
):
988 "prune, but never too much"
990 def __init__(self
, **kw
):
991 Cvs2SvnTestCase
.__init
__(self
, 'main', **kw
)
994 # Robert Pluim encountered this lovely one while converting the
995 # directory src/gnu/usr.bin/cvs/contrib/pcl-cvs/ in FreeBSD's CVS
996 # repository (see issue #1302). Step 4 is the doozy:
998 # revision 1: adds trunk/blah/, adds trunk/blah/first
999 # revision 2: adds trunk/blah/second
1000 # revision 3: deletes trunk/blah/first
1001 # revision 4: deletes blah [re-deleting trunk/blah/first pruned blah!]
1002 # revision 5: does nothing
1004 # After fixing cvs2svn, the sequence (correctly) looks like this:
1006 # revision 1: adds trunk/blah/, adds trunk/blah/first
1007 # revision 2: adds trunk/blah/second
1008 # revision 3: deletes trunk/blah/first
1009 # revision 4: does nothing [because trunk/blah/first already deleted]
1010 # revision 5: deletes blah
1012 # The difference is in 4 and 5. In revision 4, it's not correct
1013 # to prune blah/, because second is still in there, so revision 4
1014 # does nothing now. But when we delete second in 5, that should
1015 # bubble up and prune blah/ instead.
1017 # ### Note that empty revisions like 4 are probably going to become
1018 # ### at least optional, if not banished entirely from cvs2svn's
1019 # ### output. Hmmm, or they may stick around, with an extra
1020 # ### revision property explaining what happened. Need to think
1021 # ### about that. In some sense, it's a bug in Subversion itself,
1022 # ### that such revisions don't show up in 'svn log' output.
1024 conv
= self
.ensure_conversion()
1026 # Confirm that revision 4 removes '/trunk/full-prune/first',
1027 # and that revision 6 removes '/trunk/full-prune'.
1029 # Also confirm similar things about '/full-prune-reappear/...',
1030 # which is similar, except that later on it reappears, restored
1031 # from pruneland, because a file gets added to it.
1033 # And finally, a similar thing for '/partial-prune/...', except that
1034 # in its case, a permanent file on the top level prevents the
1035 # pruning from going farther than the subdirectory containing first
1038 for path
in ('full-prune/first',
1039 'full-prune-reappear/sub/first',
1040 'partial-prune/sub/first'):
1041 conv
.logs
[5].check_change('/%(trunk)s/' + path
, 'D')
1043 for path
in ('full-prune',
1044 'full-prune-reappear',
1045 'partial-prune/sub'):
1046 conv
.logs
[7].check_change('/%(trunk)s/' + path
, 'D')
1048 for path
in ('full-prune-reappear',
1049 'full-prune-reappear/appears-later'):
1050 conv
.logs
[33].check_change('/%(trunk)s/' + path
, 'A')
1053 @Cvs2SvnTestFunction
1054 def interleaved_commits():
1055 "two interleaved trunk commits, different log msgs"
1056 # See test-data/main-cvsrepos/proj/README.
1057 conv
= ensure_conversion('main')
1059 # The initial import.
1061 conv
.logs
[rev
].check('Initial import.', (
1062 ('/%(trunk)s/interleaved', 'A'),
1063 ('/%(trunk)s/interleaved/1', 'A'),
1064 ('/%(trunk)s/interleaved/2', 'A'),
1065 ('/%(trunk)s/interleaved/3', 'A'),
1066 ('/%(trunk)s/interleaved/4', 'A'),
1067 ('/%(trunk)s/interleaved/5', 'A'),
1068 ('/%(trunk)s/interleaved/a', 'A'),
1069 ('/%(trunk)s/interleaved/b', 'A'),
1070 ('/%(trunk)s/interleaved/c', 'A'),
1071 ('/%(trunk)s/interleaved/d', 'A'),
1072 ('/%(trunk)s/interleaved/e', 'A'),
1075 def check_letters(rev
):
1076 """Check if REV is the rev where only letters were committed."""
1078 conv
.logs
[rev
].check('Committing letters only.', (
1079 ('/%(trunk)s/interleaved/a', 'M'),
1080 ('/%(trunk)s/interleaved/b', 'M'),
1081 ('/%(trunk)s/interleaved/c', 'M'),
1082 ('/%(trunk)s/interleaved/d', 'M'),
1083 ('/%(trunk)s/interleaved/e', 'M'),
1086 def check_numbers(rev
):
1087 """Check if REV is the rev where only numbers were committed."""
1089 conv
.logs
[rev
].check('Committing numbers only.', (
1090 ('/%(trunk)s/interleaved/1', 'M'),
1091 ('/%(trunk)s/interleaved/2', 'M'),
1092 ('/%(trunk)s/interleaved/3', 'M'),
1093 ('/%(trunk)s/interleaved/4', 'M'),
1094 ('/%(trunk)s/interleaved/5', 'M'),
1097 # One of the commits was letters only, the other was numbers only.
1098 # But they happened "simultaneously", so we don't assume anything
1099 # about which commit appeared first, so we just try both ways.
1103 check_numbers(rev
+ 1)
1106 check_letters(rev
+ 1)
1109 @Cvs2SvnTestFunction
1110 def simple_commits():
1111 "simple trunk commits"
1112 # See test-data/main-cvsrepos/proj/README.
1113 conv
= ensure_conversion('main')
1115 # The initial import.
1116 conv
.logs
[13].check('Initial import.', (
1117 ('/%(trunk)s/proj', 'A'),
1118 ('/%(trunk)s/proj/default', 'A'),
1119 ('/%(trunk)s/proj/sub1', 'A'),
1120 ('/%(trunk)s/proj/sub1/default', 'A'),
1121 ('/%(trunk)s/proj/sub1/subsubA', 'A'),
1122 ('/%(trunk)s/proj/sub1/subsubA/default', 'A'),
1123 ('/%(trunk)s/proj/sub1/subsubB', 'A'),
1124 ('/%(trunk)s/proj/sub1/subsubB/default', 'A'),
1125 ('/%(trunk)s/proj/sub2', 'A'),
1126 ('/%(trunk)s/proj/sub2/default', 'A'),
1127 ('/%(trunk)s/proj/sub2/subsubA', 'A'),
1128 ('/%(trunk)s/proj/sub2/subsubA/default', 'A'),
1129 ('/%(trunk)s/proj/sub3', 'A'),
1130 ('/%(trunk)s/proj/sub3/default', 'A'),
1134 conv
.logs
[18].check('First commit to proj, affecting two files.', (
1135 ('/%(trunk)s/proj/sub1/subsubA/default', 'M'),
1136 ('/%(trunk)s/proj/sub3/default', 'M'),
1139 # The second commit.
1140 conv
.logs
[19].check('Second commit to proj, affecting all 7 files.', (
1141 ('/%(trunk)s/proj/default', 'M'),
1142 ('/%(trunk)s/proj/sub1/default', 'M'),
1143 ('/%(trunk)s/proj/sub1/subsubA/default', 'M'),
1144 ('/%(trunk)s/proj/sub1/subsubB/default', 'M'),
1145 ('/%(trunk)s/proj/sub2/default', 'M'),
1146 ('/%(trunk)s/proj/sub2/subsubA/default', 'M'),
1147 ('/%(trunk)s/proj/sub3/default', 'M')
1151 class SimpleTags(Cvs2SvnTestCase
):
1152 "simple tags and branches, no commits"
1154 def __init__(self
, **kw
):
1155 # See test-data/main-cvsrepos/proj/README.
1156 Cvs2SvnTestCase
.__init
__(self
, 'main', **kw
)
1158 def run(self
, sbox
):
1159 conv
= self
.ensure_conversion()
1161 # Verify the copy source for the tags we are about to check
1162 # No need to verify the copyfrom revision, as simple_commits did that
1163 conv
.logs
[13].check('Initial import.', (
1164 ('/%(trunk)s/proj', 'A'),
1165 ('/%(trunk)s/proj/default', 'A'),
1166 ('/%(trunk)s/proj/sub1', 'A'),
1167 ('/%(trunk)s/proj/sub1/default', 'A'),
1168 ('/%(trunk)s/proj/sub1/subsubA', 'A'),
1169 ('/%(trunk)s/proj/sub1/subsubA/default', 'A'),
1170 ('/%(trunk)s/proj/sub1/subsubB', 'A'),
1171 ('/%(trunk)s/proj/sub1/subsubB/default', 'A'),
1172 ('/%(trunk)s/proj/sub2', 'A'),
1173 ('/%(trunk)s/proj/sub2/default', 'A'),
1174 ('/%(trunk)s/proj/sub2/subsubA', 'A'),
1175 ('/%(trunk)s/proj/sub2/subsubA/default', 'A'),
1176 ('/%(trunk)s/proj/sub3', 'A'),
1177 ('/%(trunk)s/proj/sub3/default', 'A'),
1180 # Tag on rev 1.1.1.1 of all files in proj
1181 conv
.logs
[16].check(sym_log_msg('B_FROM_INITIALS'), (
1182 ('/%(branches)s/B_FROM_INITIALS (from /%(trunk)s:13)', 'A'),
1183 ('/%(branches)s/B_FROM_INITIALS/single-files', 'D'),
1184 ('/%(branches)s/B_FROM_INITIALS/partial-prune', 'D'),
1187 # The same, as a tag
1188 log
= conv
.find_tag_log('T_ALL_INITIAL_FILES')
1189 log
.check(sym_log_msg('T_ALL_INITIAL_FILES',1), (
1190 ('/%(tags)s/T_ALL_INITIAL_FILES (from /%(trunk)s:13)', 'A'),
1191 ('/%(tags)s/T_ALL_INITIAL_FILES/single-files', 'D'),
1192 ('/%(tags)s/T_ALL_INITIAL_FILES/partial-prune', 'D'),
1195 # Tag on rev 1.1.1.1 of all files in proj, except one
1196 log
= conv
.find_tag_log('T_ALL_INITIAL_FILES_BUT_ONE')
1197 log
.check(sym_log_msg('T_ALL_INITIAL_FILES_BUT_ONE',1), (
1198 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE (from /%(trunk)s:13)', 'A'),
1199 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE/single-files', 'D'),
1200 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE/partial-prune', 'D'),
1201 ('/%(tags)s/T_ALL_INITIAL_FILES_BUT_ONE/proj/sub1/subsubB', 'D'),
1204 # The same, as a branch
1205 conv
.logs
[17].check(sym_log_msg('B_FROM_INITIALS_BUT_ONE'), (
1206 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE (from /%(trunk)s:13)', 'A'),
1207 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE/proj/sub1/subsubB', 'D'),
1208 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE/single-files', 'D'),
1209 ('/%(branches)s/B_FROM_INITIALS_BUT_ONE/partial-prune', 'D'),
1213 @Cvs2SvnTestFunction
1214 def simple_branch_commits():
1215 "simple branch commits"
1216 # See test-data/main-cvsrepos/proj/README.
1217 conv
= ensure_conversion('main')
1219 conv
.logs
[23].check('Modify three files, on branch B_MIXED.', (
1220 ('/%(branches)s/B_MIXED/proj/default', 'M'),
1221 ('/%(branches)s/B_MIXED/proj/sub1/default', 'M'),
1222 ('/%(branches)s/B_MIXED/proj/sub2/subsubA/default', 'M'),
1226 @Cvs2SvnTestFunction
1227 def mixed_time_tag():
1229 # See test-data/main-cvsrepos/proj/README.
1230 conv
= ensure_conversion('main')
1232 log
= conv
.find_tag_log('T_MIXED')
1234 ('/%(tags)s/T_MIXED (from /%(trunk)s:19)', 'A'),
1235 ('/%(tags)s/T_MIXED/single-files', 'D'),
1236 ('/%(tags)s/T_MIXED/partial-prune', 'D'),
1237 ('/%(tags)s/T_MIXED/proj/sub2/subsubA '
1238 '(from /%(trunk)s/proj/sub2/subsubA:13)', 'R'),
1239 ('/%(tags)s/T_MIXED/proj/sub3 (from /%(trunk)s/proj/sub3:18)', 'R'),
1243 @Cvs2SvnTestFunction
1244 def mixed_time_branch_with_added_file():
1245 "mixed-time branch, and a file added to the branch"
1246 # See test-data/main-cvsrepos/proj/README.
1247 conv
= ensure_conversion('main')
1249 # A branch from the same place as T_MIXED in the previous test,
1250 # plus a file added directly to the branch
1251 conv
.logs
[21].check(sym_log_msg('B_MIXED'), (
1252 ('/%(branches)s/B_MIXED (from /%(trunk)s:19)', 'A'),
1253 ('/%(branches)s/B_MIXED/partial-prune', 'D'),
1254 ('/%(branches)s/B_MIXED/single-files', 'D'),
1255 ('/%(branches)s/B_MIXED/proj/sub2/subsubA '
1256 '(from /%(trunk)s/proj/sub2/subsubA:13)', 'R'),
1257 ('/%(branches)s/B_MIXED/proj/sub3 (from /%(trunk)s/proj/sub3:18)', 'R'),
1260 conv
.logs
[22].check('Add a file on branch B_MIXED.', (
1261 ('/%(branches)s/B_MIXED/proj/sub2/branch_B_MIXED_only', 'A'),
1265 @Cvs2SvnTestFunction
1267 "a commit affecting both trunk and a branch"
1268 # See test-data/main-cvsrepos/proj/README.
1269 conv
= ensure_conversion('main')
1271 conv
.logs
[24].check(
1272 'A single commit affecting one file on branch B_MIXED '
1273 'and one on trunk.', (
1274 ('/%(trunk)s/proj/sub2/default', 'M'),
1275 ('/%(branches)s/B_MIXED/proj/sub2/branch_B_MIXED_only', 'M'),
1279 @Cvs2SvnTestFunction
1280 def split_time_branch():
1281 "branch some trunk files, and later branch the rest"
1282 # See test-data/main-cvsrepos/proj/README.
1283 conv
= ensure_conversion('main')
1285 # First change on the branch, creating it
1286 conv
.logs
[25].check(sym_log_msg('B_SPLIT'), (
1287 ('/%(branches)s/B_SPLIT (from /%(trunk)s:24)', 'A'),
1288 ('/%(branches)s/B_SPLIT/partial-prune', 'D'),
1289 ('/%(branches)s/B_SPLIT/single-files', 'D'),
1290 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB', 'D'),
1293 conv
.logs
[29].check('First change on branch B_SPLIT.', (
1294 ('/%(branches)s/B_SPLIT/proj/default', 'M'),
1295 ('/%(branches)s/B_SPLIT/proj/sub1/default', 'M'),
1296 ('/%(branches)s/B_SPLIT/proj/sub1/subsubA/default', 'M'),
1297 ('/%(branches)s/B_SPLIT/proj/sub2/default', 'M'),
1298 ('/%(branches)s/B_SPLIT/proj/sub2/subsubA/default', 'M'),
1301 # A trunk commit for the file which was not branched
1302 conv
.logs
[30].check('A trunk change to sub1/subsubB/default. '
1303 'This was committed about an', (
1304 ('/%(trunk)s/proj/sub1/subsubB/default', 'M'),
1307 # Add the file not already branched to the branch, with modification:w
1308 conv
.logs
[31].check(sym_log_msg('B_SPLIT'), (
1309 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB '
1310 '(from /%(trunk)s/proj/sub1/subsubB:30)', 'A'),
1313 conv
.logs
[32].check('This change affects sub3/default and '
1314 'sub1/subsubB/default, on branch', (
1315 ('/%(branches)s/B_SPLIT/proj/sub1/subsubB/default', 'M'),
1316 ('/%(branches)s/B_SPLIT/proj/sub3/default', 'M'),
1320 @Cvs2SvnTestFunction
1321 def multiple_tags():
1322 "multiple tags referring to same revision"
1323 conv
= ensure_conversion('main')
1324 if not conv
.path_exists('tags', 'T_ALL_INITIAL_FILES', 'proj', 'default'):
1326 if not conv
.path_exists(
1327 'tags', 'T_ALL_INITIAL_FILES_BUT_ONE', 'proj', 'default'):
1331 @Cvs2SvnTestFunction
1332 def multiply_defined_symbols():
1333 "multiple definitions of symbol names"
1335 # We can only check one line of the error output at a time, so test
1336 # twice. (The conversion only have to be done once because the
1337 # results are cached.)
1338 conv
= ensure_conversion(
1339 'multiply-defined-symbols',
1341 r
"ERROR\: Multiple definitions of the symbol \'BRANCH\' .*\: "
1345 conv
= ensure_conversion(
1346 'multiply-defined-symbols',
1348 r
"ERROR\: Multiple definitions of the symbol \'TAG\' .*\: "
1354 @Cvs2SvnTestFunction
1355 def multiply_defined_symbols_renamed():
1356 "rename multiply defined symbols"
1358 conv
= ensure_conversion(
1359 'multiply-defined-symbols',
1360 options_file
='cvs2svn-rename.options',
1364 @Cvs2SvnTestFunction
1365 def multiply_defined_symbols_ignored():
1366 "ignore multiply defined symbols"
1368 conv
= ensure_conversion(
1369 'multiply-defined-symbols',
1370 options_file
='cvs2svn-ignore.options',
1374 @Cvs2SvnTestFunction
1375 def repeatedly_defined_symbols():
1376 "multiple identical definitions of symbol names"
1378 # If a symbol is defined multiple times but has the same value each
1379 # time, that should not be an error.
1381 conv
= ensure_conversion('repeatedly-defined-symbols')
1384 @Cvs2SvnTestFunction
1386 "conversion of invalid symbolic names"
1387 conv
= ensure_conversion('bogus-tag')
1390 @Cvs2SvnTestFunction
1391 def overlapping_branch():
1392 "ignore a file with a branch with two names"
1393 conv
= ensure_conversion('overlapping-branch')
1395 if not conv
.output_found('.*cannot also have name \'vendorB\''):
1398 conv
.logs
[2].check('imported', (
1399 ('/%(trunk)s/nonoverlapping-branch', 'A'),
1400 ('/%(trunk)s/overlapping-branch', 'A'),
1403 if len(conv
.logs
) != 2:
1407 class PhoenixBranch(Cvs2SvnTestCase
):
1408 "convert a branch file rooted in a 'dead' revision"
1410 def __init__(self
, **kw
):
1411 Cvs2SvnTestCase
.__init
__(self
, 'phoenix', **kw
)
1413 def run(self
, sbox
):
1414 conv
= self
.ensure_conversion()
1415 conv
.logs
[8].check('This file was supplied by Jack Moffitt', (
1416 ('/%(branches)s/volsung_20010721', 'A'),
1417 ('/%(branches)s/volsung_20010721/phoenix', 'A'),
1419 conv
.logs
[9].check('This file was supplied by Jack Moffitt', (
1420 ('/%(branches)s/volsung_20010721/phoenix', 'M'),
1424 ###TODO: We check for 4 changed paths here to accomodate creating tags
1425 ###and branches in rev 1, but that will change, so this will
1426 ###eventually change back.
1427 @Cvs2SvnTestFunction
1428 def ctrl_char_in_log():
1429 "handle a control char in a log message"
1430 # This was issue #1106.
1432 conv
= ensure_conversion('ctrl-char-in-log')
1433 conv
.logs
[rev
].check_changes((
1434 ('/%(trunk)s/ctrl-char-in-log', 'A'),
1436 if conv
.logs
[rev
].msg
.find('\x04') < 0:
1438 "Log message of 'ctrl-char-in-log,v' (rev 2) is wrong.")
1441 @Cvs2SvnTestFunction
1443 "handle tags rooted in a redeleted revision"
1444 conv
= ensure_conversion('overdead')
1447 class NoTrunkPrune(Cvs2SvnTestCase
):
1448 "ensure that trunk doesn't get pruned"
1450 def __init__(self
, **kw
):
1451 Cvs2SvnTestCase
.__init
__(self
, 'overdead', **kw
)
1453 def run(self
, sbox
):
1454 conv
= self
.ensure_conversion()
1455 for rev
in conv
.logs
.keys():
1456 rev_logs
= conv
.logs
[rev
]
1457 if rev_logs
.get_path_op('/%(trunk)s') == 'D':
1461 @Cvs2SvnTestFunction
1462 def double_delete():
1463 "file deleted twice, in the root of the repository"
1464 # This really tests several things: how we handle a file that's
1465 # removed (state 'dead') in two successive revisions; how we
1466 # handle a file in the root of the repository (there were some
1467 # bugs in cvs2svn's svn path construction for top-level files); and
1468 # the --no-prune option.
1469 conv
= ensure_conversion(
1470 'double-delete', args
=['--trunk-only', '--no-prune'])
1472 path
= '/%(trunk)s/twice-removed'
1474 conv
.logs
[rev
].check('Updated CVS', (
1477 conv
.logs
[rev
+ 1].check('Remove this file for the first time.', (
1480 conv
.logs
[rev
+ 2].check('Remove this file for the second time,', (
1484 @Cvs2SvnTestFunction
1486 "branch created from both trunk and another branch"
1487 # See test-data/split-branch-cvsrepos/README.
1489 # The conversion will fail if the bug is present, and
1490 # ensure_conversion will raise Failure.
1491 conv
= ensure_conversion('split-branch')
1494 @Cvs2SvnTestFunction
1495 def resync_misgroups():
1496 "resyncing should not misorder commit groups"
1497 # See test-data/resync-misgroups-cvsrepos/README.
1499 # The conversion will fail if the bug is present, and
1500 # ensure_conversion will raise Failure.
1501 conv
= ensure_conversion('resync-misgroups')
1504 class TaggedBranchAndTrunk(Cvs2SvnTestCase
):
1505 "allow tags with mixed trunk and branch sources"
1507 def __init__(self
, **kw
):
1508 Cvs2SvnTestCase
.__init
__(self
, 'tagged-branch-n-trunk', **kw
)
1510 def run(self
, sbox
):
1511 conv
= self
.ensure_conversion()
1513 tags
= conv
.symbols
.get('tags', 'tags')
1515 a_path
= conv
.get_wc(tags
, 'some-tag', 'a.txt')
1516 b_path
= conv
.get_wc(tags
, 'some-tag', 'b.txt')
1517 if not (os
.path
.exists(a_path
) and os
.path
.exists(b_path
)):
1519 if (open(a_path
, 'r').read().find('1.24') == -1) \
1520 or (open(b_path
, 'r').read().find('1.5') == -1):
1524 @Cvs2SvnTestFunction
1526 "never use the rev-in-progress as a copy source"
1528 # See issue #1427 and r8544.
1529 conv
= ensure_conversion('enroot-race')
1531 conv
.logs
[rev
].check_changes((
1532 ('/%(branches)s/mybranch (from /%(trunk)s:5)', 'A'),
1533 ('/%(branches)s/mybranch/proj/a.txt', 'D'),
1534 ('/%(branches)s/mybranch/proj/b.txt', 'D'),
1536 conv
.logs
[rev
+ 1].check_changes((
1537 ('/%(branches)s/mybranch/proj/c.txt', 'M'),
1538 ('/%(trunk)s/proj/a.txt', 'M'),
1539 ('/%(trunk)s/proj/b.txt', 'M'),
1543 @Cvs2SvnTestFunction
1544 def enroot_race_obo():
1545 "do use the last completed rev as a copy source"
1546 conv
= ensure_conversion('enroot-race-obo')
1547 conv
.logs
[3].check_change('/%(branches)s/BRANCH (from /%(trunk)s:2)', 'A')
1548 if not len(conv
.logs
) == 3:
1552 class BranchDeleteFirst(Cvs2SvnTestCase
):
1553 "correctly handle deletion as initial branch action"
1555 def __init__(self
, **kw
):
1556 Cvs2SvnTestCase
.__init
__(self
, 'branch-delete-first', **kw
)
1558 def run(self
, sbox
):
1559 # See test-data/branch-delete-first-cvsrepos/README.
1561 # The conversion will fail if the bug is present, and
1562 # ensure_conversion would raise Failure.
1563 conv
= self
.ensure_conversion()
1565 branches
= conv
.symbols
.get('branches', 'branches')
1567 # 'file' was deleted from branch-1 and branch-2, but not branch-3
1568 if conv
.path_exists(branches
, 'branch-1', 'file'):
1570 if conv
.path_exists(branches
, 'branch-2', 'file'):
1572 if not conv
.path_exists(branches
, 'branch-3', 'file'):
1576 @Cvs2SvnTestFunction
1577 def nonascii_filenames():
1578 "non ascii files converted incorrectly"
1581 # on a en_US.iso-8859-1 machine this test fails with
1582 # svn: Can't recode ...
1584 # as described in the issue
1586 # on a en_US.UTF-8 machine this test fails with
1587 # svn: Malformed XML ...
1589 # which means at least it fails. Unfortunately it won't fail
1590 # with the same error...
1592 # mangle current locale settings so we know we're not running
1593 # a UTF-8 locale (which does not exhibit this problem)
1594 current_locale
= locale
.getlocale()
1595 new_locale
= 'en_US.ISO8859-1'
1596 locale_changed
= None
1598 # From http://docs.python.org/lib/module-sys.html
1600 # getfilesystemencoding():
1602 # Return the name of the encoding used to convert Unicode filenames
1603 # into system file names, or None if the system default encoding is
1604 # used. The result value depends on the operating system:
1606 # - On Windows 9x, the encoding is ``mbcs''.
1607 # - On Mac OS X, the encoding is ``utf-8''.
1608 # - On Unix, the encoding is the user's preference according to the
1609 # result of nl_langinfo(CODESET), or None if the
1610 # nl_langinfo(CODESET) failed.
1611 # - On Windows NT+, file names are Unicode natively, so no conversion is
1614 # So we're going to skip this test on Mac OS X for now.
1615 if sys
.platform
== "darwin":
1616 raise svntest
.Skip()
1619 # change locale to non-UTF-8 locale to generate latin1 names
1620 locale
.setlocale(locale
.LC_ALL
, # this might be too broad?
1623 except locale
.Error
:
1624 raise svntest
.Skip()
1627 srcrepos_path
= os
.path
.join(test_data_dir
,'main-cvsrepos')
1628 dstrepos_path
= os
.path
.join(test_data_dir
,'non-ascii-cvsrepos')
1629 if not os
.path
.exists(dstrepos_path
):
1630 # create repos from existing main repos
1631 shutil
.copytree(srcrepos_path
, dstrepos_path
)
1632 base_path
= os
.path
.join(dstrepos_path
, 'single-files')
1633 shutil
.copyfile(os
.path
.join(base_path
, 'twoquick,v'),
1634 os
.path
.join(base_path
, 'two\366uick,v'))
1635 new_path
= os
.path
.join(dstrepos_path
, 'single\366files')
1636 os
.rename(base_path
, new_path
)
1638 conv
= ensure_conversion('non-ascii', args
=['--encoding=latin1'])
1641 locale
.setlocale(locale
.LC_ALL
, current_locale
)
1642 safe_rmtree(dstrepos_path
)
1645 class UnicodeTest(Cvs2SvnTestCase
):
1646 "metadata contains Unicode"
1648 warning_pattern
= r
'ERROR\: There were warnings converting .* messages'
1650 def __init__(self
, name
, warning_expected
, **kw
):
1651 if warning_expected
:
1652 error_re
= self
.warning_pattern
1656 Cvs2SvnTestCase
.__init
__(self
, name
, error_re
=error_re
, **kw
)
1657 self
.warning_expected
= warning_expected
1659 def run(self
, sbox
):
1661 # ensure the availability of the "utf_8" encoding:
1662 u
'a'.encode('utf_8').decode('utf_8')
1664 raise svntest
.Skip()
1666 self
.ensure_conversion()
1669 class UnicodeAuthor(UnicodeTest
):
1670 "author name contains Unicode"
1672 def __init__(self
, warning_expected
, **kw
):
1673 UnicodeTest
.__init
__(self
, 'unicode-author', warning_expected
, **kw
)
1676 class UnicodeLog(UnicodeTest
):
1677 "log message contains Unicode"
1679 def __init__(self
, warning_expected
, **kw
):
1680 UnicodeTest
.__init
__(self
, 'unicode-log', warning_expected
, **kw
)
1683 @Cvs2SvnTestFunction
1684 def vendor_branch_sameness():
1685 "avoid spurious changes for initial revs"
1686 conv
= ensure_conversion(
1687 'vendor-branch-sameness', args
=['--keep-trivial-imports']
1690 # The following files are in this repository:
1692 # a.txt: Imported in the traditional way; 1.1 and 1.1.1.1 have
1693 # the same contents, the file's default branch is 1.1.1,
1694 # and both revisions are in state 'Exp'.
1696 # b.txt: Like a.txt, except that 1.1.1.1 has a real change from
1697 # 1.1 (the addition of a line of text).
1699 # c.txt: Like a.txt, except that 1.1.1.1 is in state 'dead'.
1701 # d.txt: This file was created by 'cvs add' instead of import, so
1702 # it has only 1.1 -- no 1.1.1.1, and no default branch.
1703 # The timestamp on the add is exactly the same as for the
1704 # imports of the other files.
1706 # e.txt: Like a.txt, except that the log message for revision 1.1
1707 # is not the standard import log message.
1709 # (Aside from e.txt, the log messages for the same revisions are the
1710 # same in all files.)
1712 # We expect that only a.txt is recognized as an import whose 1.1
1713 # revision can be omitted. The other files should be added on trunk
1714 # then filled to vbranchA, whereas a.txt should be added to vbranchA
1715 # then copied to trunk. In the copy of 1.1.1.1 back to trunk, a.txt
1716 # and e.txt should be copied untouched; b.txt should be 'M'odified,
1717 # and c.txt should be 'D'eleted.
1720 conv
.logs
[rev
].check('Initial revision', (
1721 ('/%(trunk)s/proj', 'A'),
1722 ('/%(trunk)s/proj/b.txt', 'A'),
1723 ('/%(trunk)s/proj/c.txt', 'A'),
1724 ('/%(trunk)s/proj/d.txt', 'A'),
1727 conv
.logs
[rev
+ 1].check(sym_log_msg('vbranchA'), (
1728 ('/%(branches)s/vbranchA (from /%(trunk)s:2)', 'A'),
1729 ('/%(branches)s/vbranchA/proj/d.txt', 'D'),
1732 conv
.logs
[rev
+ 2].check('First vendor branch revision.', (
1733 ('/%(branches)s/vbranchA/proj/a.txt', 'A'),
1734 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1735 ('/%(branches)s/vbranchA/proj/c.txt', 'D'),
1738 conv
.logs
[rev
+ 3].check('This commit was generated by cvs2svn '
1739 'to compensate for changes in r4,', (
1740 ('/%(trunk)s/proj/a.txt (from /%(branches)s/vbranchA/proj/a.txt:4)', 'A'),
1741 ('/%(trunk)s/proj/b.txt (from /%(branches)s/vbranchA/proj/b.txt:4)', 'R'),
1742 ('/%(trunk)s/proj/c.txt', 'D'),
1746 conv
.logs
[rev
].check('This log message is not the standard', (
1747 ('/%(trunk)s/proj/e.txt', 'A'),
1750 conv
.logs
[rev
+ 2].check('First vendor branch revision', (
1751 ('/%(branches)s/vbranchB/proj/e.txt', 'M'),
1754 conv
.logs
[rev
+ 3].check('This commit was generated by cvs2svn '
1755 'to compensate for changes in r9,', (
1756 ('/%(trunk)s/proj/e.txt (from /%(branches)s/vbranchB/proj/e.txt:9)', 'R'),
1760 @Cvs2SvnTestFunction
1761 def vendor_branch_trunk_only():
1762 "handle vendor branches with --trunk-only"
1763 conv
= ensure_conversion('vendor-branch-sameness', args
=['--trunk-only'])
1766 conv
.logs
[rev
].check('Initial revision', (
1767 ('/%(trunk)s/proj', 'A'),
1768 ('/%(trunk)s/proj/b.txt', 'A'),
1769 ('/%(trunk)s/proj/c.txt', 'A'),
1770 ('/%(trunk)s/proj/d.txt', 'A'),
1773 conv
.logs
[rev
+ 1].check('First vendor branch revision', (
1774 ('/%(trunk)s/proj/a.txt', 'A'),
1775 ('/%(trunk)s/proj/b.txt', 'M'),
1776 ('/%(trunk)s/proj/c.txt', 'D'),
1779 conv
.logs
[rev
+ 2].check('This log message is not the standard', (
1780 ('/%(trunk)s/proj/e.txt', 'A'),
1783 conv
.logs
[rev
+ 3].check('First vendor branch revision', (
1784 ('/%(trunk)s/proj/e.txt', 'M'),
1788 @Cvs2SvnTestFunction
1789 def default_branches():
1790 "handle default branches correctly"
1791 conv
= ensure_conversion('default-branches')
1793 # There are seven files in the repository:
1796 # Imported in the traditional way, so 1.1 and 1.1.1.1 are the
1797 # same. Then 1.1.1.2 and 1.1.1.3 were imported, then 1.2
1798 # committed (thus losing the default branch "1.1.1"), then
1799 # 1.1.1.4 was imported. All vendor import release tags are
1803 # Like a.txt, but without rev 1.2.
1806 # Exactly like b.txt, just s/b.txt/c.txt/ in content.
1809 # Same as the previous two, but 1.1.1 branch is unlabeled.
1812 # Same, but missing 1.1.1 label and all tags but 1.1.1.3.
1814 # deleted-on-vendor-branch.txt,v:
1815 # Like b.txt and c.txt, except that 1.1.1.3 is state 'dead'.
1817 # added-then-imported.txt,v:
1818 # Added with 'cvs add' to create 1.1, then imported with
1819 # completely different contents to create 1.1.1.1, therefore
1820 # never had a default branch.
1823 conv
.logs
[2].check("Import (vbranchA, vtag-1).", (
1824 ('/%(branches)s/unlabeled-1.1.1', 'A'),
1825 ('/%(branches)s/unlabeled-1.1.1/proj', 'A'),
1826 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'A'),
1827 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'A'),
1828 ('/%(branches)s/vbranchA', 'A'),
1829 ('/%(branches)s/vbranchA/proj', 'A'),
1830 ('/%(branches)s/vbranchA/proj/a.txt', 'A'),
1831 ('/%(branches)s/vbranchA/proj/b.txt', 'A'),
1832 ('/%(branches)s/vbranchA/proj/c.txt', 'A'),
1833 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'A'),
1836 conv
.logs
[3].check("This commit was generated by cvs2svn "
1837 "to compensate for changes in r2,", (
1838 ('/%(trunk)s/proj', 'A'),
1839 ('/%(trunk)s/proj/a.txt (from /%(branches)s/vbranchA/proj/a.txt:2)', 'A'),
1840 ('/%(trunk)s/proj/b.txt (from /%(branches)s/vbranchA/proj/b.txt:2)', 'A'),
1841 ('/%(trunk)s/proj/c.txt (from /%(branches)s/vbranchA/proj/c.txt:2)', 'A'),
1842 ('/%(trunk)s/proj/d.txt '
1843 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:2)', 'A'),
1844 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1845 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:2)', 'A'),
1846 ('/%(trunk)s/proj/e.txt '
1847 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:2)', 'A'),
1850 conv
.logs
[4].check(sym_log_msg('vtag-1',1), (
1851 ('/%(tags)s/vtag-1 (from /%(branches)s/vbranchA:2)', 'A'),
1852 ('/%(tags)s/vtag-1/proj/d.txt '
1853 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:2)', 'A'),
1856 conv
.logs
[5].check("Import (vbranchA, vtag-2).", (
1857 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1858 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1859 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1860 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1861 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1862 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'M'),
1865 conv
.logs
[6].check("This commit was generated by cvs2svn "
1866 "to compensate for changes in r5,", (
1867 ('/%(trunk)s/proj/a.txt '
1868 '(from /%(branches)s/vbranchA/proj/a.txt:5)', 'R'),
1869 ('/%(trunk)s/proj/b.txt '
1870 '(from /%(branches)s/vbranchA/proj/b.txt:5)', 'R'),
1871 ('/%(trunk)s/proj/c.txt '
1872 '(from /%(branches)s/vbranchA/proj/c.txt:5)', 'R'),
1873 ('/%(trunk)s/proj/d.txt '
1874 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:5)', 'R'),
1875 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1876 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:5)',
1878 ('/%(trunk)s/proj/e.txt '
1879 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:5)', 'R'),
1882 conv
.logs
[7].check(sym_log_msg('vtag-2',1), (
1883 ('/%(tags)s/vtag-2 (from /%(branches)s/vbranchA:5)', 'A'),
1884 ('/%(tags)s/vtag-2/proj/d.txt '
1885 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:5)', 'A'),
1888 conv
.logs
[8].check("Import (vbranchA, vtag-3).", (
1889 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1890 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1891 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1892 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1893 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1894 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'D'),
1897 conv
.logs
[9].check("This commit was generated by cvs2svn "
1898 "to compensate for changes in r8,", (
1899 ('/%(trunk)s/proj/a.txt '
1900 '(from /%(branches)s/vbranchA/proj/a.txt:8)', 'R'),
1901 ('/%(trunk)s/proj/b.txt '
1902 '(from /%(branches)s/vbranchA/proj/b.txt:8)', 'R'),
1903 ('/%(trunk)s/proj/c.txt '
1904 '(from /%(branches)s/vbranchA/proj/c.txt:8)', 'R'),
1905 ('/%(trunk)s/proj/d.txt '
1906 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:8)', 'R'),
1907 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'D'),
1908 ('/%(trunk)s/proj/e.txt '
1909 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:8)', 'R'),
1912 conv
.logs
[10].check(sym_log_msg('vtag-3',1), (
1913 ('/%(tags)s/vtag-3 (from /%(branches)s/vbranchA:8)', 'A'),
1914 ('/%(tags)s/vtag-3/proj/d.txt '
1915 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:8)', 'A'),
1916 ('/%(tags)s/vtag-3/proj/e.txt '
1917 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:8)', 'A'),
1920 conv
.logs
[11].check("First regular commit, to a.txt, on vtag-3.", (
1921 ('/%(trunk)s/proj/a.txt', 'M'),
1924 conv
.logs
[12].check("Add a file to the working copy.", (
1925 ('/%(trunk)s/proj/added-then-imported.txt', 'A'),
1928 conv
.logs
[13].check(sym_log_msg('vbranchA'), (
1929 ('/%(branches)s/vbranchA/proj/added-then-imported.txt '
1930 '(from /%(trunk)s/proj/added-then-imported.txt:12)', 'A'),
1933 conv
.logs
[14].check("Import (vbranchA, vtag-4).", (
1934 ('/%(branches)s/unlabeled-1.1.1/proj/d.txt', 'M'),
1935 ('/%(branches)s/unlabeled-1.1.1/proj/e.txt', 'M'),
1936 ('/%(branches)s/vbranchA/proj/a.txt', 'M'),
1937 ('/%(branches)s/vbranchA/proj/added-then-imported.txt', 'M'), # CHECK!!!
1938 ('/%(branches)s/vbranchA/proj/b.txt', 'M'),
1939 ('/%(branches)s/vbranchA/proj/c.txt', 'M'),
1940 ('/%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt', 'A'),
1943 conv
.logs
[15].check("This commit was generated by cvs2svn "
1944 "to compensate for changes in r14,", (
1945 ('/%(trunk)s/proj/b.txt '
1946 '(from /%(branches)s/vbranchA/proj/b.txt:14)', 'R'),
1947 ('/%(trunk)s/proj/c.txt '
1948 '(from /%(branches)s/vbranchA/proj/c.txt:14)', 'R'),
1949 ('/%(trunk)s/proj/d.txt '
1950 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:14)', 'R'),
1951 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt '
1952 '(from /%(branches)s/vbranchA/proj/deleted-on-vendor-branch.txt:14)',
1954 ('/%(trunk)s/proj/e.txt '
1955 '(from /%(branches)s/unlabeled-1.1.1/proj/e.txt:14)', 'R'),
1958 conv
.logs
[16].check(sym_log_msg('vtag-4',1), (
1959 ('/%(tags)s/vtag-4 (from /%(branches)s/vbranchA:14)', 'A'),
1960 ('/%(tags)s/vtag-4/proj/d.txt '
1961 '(from /%(branches)s/unlabeled-1.1.1/proj/d.txt:14)', 'A'),
1965 @Cvs2SvnTestFunction
1966 def default_branches_trunk_only():
1967 "handle default branches with --trunk-only"
1969 conv
= ensure_conversion('default-branches', args
=['--trunk-only'])
1971 conv
.logs
[2].check("Import (vbranchA, vtag-1).", (
1972 ('/%(trunk)s/proj', 'A'),
1973 ('/%(trunk)s/proj/a.txt', 'A'),
1974 ('/%(trunk)s/proj/b.txt', 'A'),
1975 ('/%(trunk)s/proj/c.txt', 'A'),
1976 ('/%(trunk)s/proj/d.txt', 'A'),
1977 ('/%(trunk)s/proj/e.txt', 'A'),
1978 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'A'),
1981 conv
.logs
[3].check("Import (vbranchA, vtag-2).", (
1982 ('/%(trunk)s/proj/a.txt', 'M'),
1983 ('/%(trunk)s/proj/b.txt', 'M'),
1984 ('/%(trunk)s/proj/c.txt', 'M'),
1985 ('/%(trunk)s/proj/d.txt', 'M'),
1986 ('/%(trunk)s/proj/e.txt', 'M'),
1987 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'M'),
1990 conv
.logs
[4].check("Import (vbranchA, vtag-3).", (
1991 ('/%(trunk)s/proj/a.txt', 'M'),
1992 ('/%(trunk)s/proj/b.txt', 'M'),
1993 ('/%(trunk)s/proj/c.txt', 'M'),
1994 ('/%(trunk)s/proj/d.txt', 'M'),
1995 ('/%(trunk)s/proj/e.txt', 'M'),
1996 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'D'),
1999 conv
.logs
[5].check("First regular commit, to a.txt, on vtag-3.", (
2000 ('/%(trunk)s/proj/a.txt', 'M'),
2003 conv
.logs
[6].check("Add a file to the working copy.", (
2004 ('/%(trunk)s/proj/added-then-imported.txt', 'A'),
2007 conv
.logs
[7].check("Import (vbranchA, vtag-4).", (
2008 ('/%(trunk)s/proj/b.txt', 'M'),
2009 ('/%(trunk)s/proj/c.txt', 'M'),
2010 ('/%(trunk)s/proj/d.txt', 'M'),
2011 ('/%(trunk)s/proj/e.txt', 'M'),
2012 ('/%(trunk)s/proj/deleted-on-vendor-branch.txt', 'A'),
2016 @Cvs2SvnTestFunction
2017 def default_branch_and_1_2():
2018 "do not allow 1.2 revision with default branch"
2020 conv
= ensure_conversion(
2021 'default-branch-and-1-2',
2023 r
'.*File \'.*\' has default branch
=1\
.1\
.1 but also a revision
1\
.2'
2028 @Cvs2SvnTestFunction
2029 def compose_tag_three_sources():
2030 "compose a tag from three sources"
2031 conv = ensure_conversion('compose
-tag
-three
-sources
')
2033 conv.logs[2].check("Add on trunk", (
2034 ('/%(trunk)s/tagged
-on
-trunk
-1.1', 'A
'),
2035 ('/%(trunk)s/tagged
-on
-trunk
-1.2-a
', 'A
'),
2036 ('/%(trunk)s/tagged
-on
-trunk
-1.2-b
', 'A
'),
2037 ('/%(trunk)s/tagged
-on
-b1
', 'A
'),
2038 ('/%(trunk)s/tagged
-on
-b2
', 'A
'),
2041 conv.logs[3].check(sym_log_msg('b1
'), (
2042 ('/%(branches)s/b1 (from /%(trunk)s:2)', 'A
'),
2045 conv.logs[4].check(sym_log_msg('b2
'), (
2046 ('/%(branches)s/b2 (from /%(trunk)s:2)', 'A
'),
2049 conv.logs[5].check("Commit on branch b1", (
2050 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.1', 'M
'),
2051 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.2-a
', 'M
'),
2052 ('/%(branches)s/b1
/tagged
-on
-trunk
-1.2-b
', 'M
'),
2053 ('/%(branches)s/b1
/tagged
-on
-b1
', 'M
'),
2054 ('/%(branches)s/b1
/tagged
-on
-b2
', 'M
'),
2057 conv.logs[6].check("Commit on branch b2", (
2058 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.1', 'M
'),
2059 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.2-a
', 'M
'),
2060 ('/%(branches)s/b2
/tagged
-on
-trunk
-1.2-b
', 'M
'),
2061 ('/%(branches)s/b2
/tagged
-on
-b1
', 'M
'),
2062 ('/%(branches)s/b2
/tagged
-on
-b2
', 'M
'),
2065 conv.logs[7].check("Commit again on trunk", (
2066 ('/%(trunk)s/tagged
-on
-trunk
-1.2-a
', 'M
'),
2067 ('/%(trunk)s/tagged
-on
-trunk
-1.2-b
', 'M
'),
2068 ('/%(trunk)s/tagged
-on
-trunk
-1.1', 'M
'),
2069 ('/%(trunk)s/tagged
-on
-b1
', 'M
'),
2070 ('/%(trunk)s/tagged
-on
-b2
', 'M
'),
2073 conv.logs[8].check(sym_log_msg('T
',1), (
2074 ('/%(tags)s/T (from /%(trunk)s:7)', 'A
'),
2075 ('/%(tags)s/T
/tagged
-on
-trunk
-1.1 '
2076 '(from /%(trunk)s/tagged
-on
-trunk
-1.1:2)', 'R
'),
2077 ('/%(tags)s/T
/tagged
-on
-b1 (from /%(branches)s/b1
/tagged
-on
-b1
:5)', 'R
'),
2078 ('/%(tags)s/T
/tagged
-on
-b2 (from /%(branches)s/b2
/tagged
-on
-b2
:6)', 'R
'),
2082 @Cvs2SvnTestFunction
2083 def pass5_when_to_fill():
2084 "reserve a svn revnum for a fill only when required"
2085 # The conversion will fail if the bug is present, and
2086 # ensure_conversion would raise Failure.
2087 conv = ensure_conversion('pass5
-when
-to
-fill
')
2090 class EmptyTrunk(Cvs2SvnTestCase):
2091 "don't
break when the trunk
is empty
"
2093 def __init__(self, **kw):
2094 Cvs2SvnTestCase.__init__(self, 'empty-trunk', **kw)
2096 def run(self, sbox):
2097 # The conversion will fail if the bug is present, and
2098 # ensure_conversion would raise Failure.
2099 conv = self.ensure_conversion()
2102 @Cvs2SvnTestFunction
2103 def no_spurious_svn_commits():
2104 "ensure that we don
't create any spurious commits"
2105 conv = ensure_conversion('phoenix
')
2107 # Check spurious commit that could be created in
2108 # SVNCommitCreator._pre_commit()
2110 # (When you add a file on a branch, CVS creates a trunk revision
2111 # in state 'dead
'. If the log message of that commit is equal to
2112 # the one that CVS generates, we do not ever create a 'fill
'
2113 # SVNCommit for it.)
2115 # and spurious commit that could be created in
2116 # SVNCommitCreator._commit()
2118 # (When you add a file on a branch, CVS creates a trunk revision
2119 # in state 'dead
'. If the log message of that commit is equal to
2120 # the one that CVS generates, we do not create a primary SVNCommit
2122 conv.logs[17].check('File added on branch xiphophorus
', (
2123 ('/%(branches)s/xiphophorus
/added
-on
-branch
.txt
', 'A
'),
2126 # Check to make sure that a commit *is* generated:
2127 # (When you add a file on a branch, CVS creates a trunk revision
2128 # in state 'dead
'. If the log message of that commit is NOT equal
2129 # to the one that CVS generates, we create a primary SVNCommit to
2130 # serve as a home for the log message in question.
2131 conv.logs[18].check('file added
-on
-branch2
.txt was initially added on
'
2132 + 'branch xiphophorus
,\nand this log message was tweaked
', ())
2134 # Check spurious commit that could be created in
2135 # SVNCommitCreator._commit_symbols().
2136 conv.logs[19].check('This
file was also added on branch xiphophorus
,', (
2137 ('/%(branches)s/xiphophorus
/added
-on
-branch2
.txt
', 'A
'),
2141 class PeerPathPruning(Cvs2SvnTestCase):
2142 "make sure that filling prunes paths correctly"
2144 def __init__(self, **kw):
2145 Cvs2SvnTestCase.__init__(self, 'peer
-path
-pruning
', **kw)
2147 def run(self, sbox):
2148 conv = self.ensure_conversion()
2149 conv.logs[6].check(sym_log_msg('BRANCH
'), (
2150 ('/%(branches)s/BRANCH (from /%(trunk)s:4)', 'A
'),
2151 ('/%(branches)s/BRANCH
/bar
', 'D
'),
2152 ('/%(branches)s/BRANCH
/foo (from /%(trunk)s/foo
:5)', 'R
'),
2156 @Cvs2SvnTestFunction
2157 def invalid_closings_on_trunk():
2158 "verify correct revs are copied to default branches"
2159 # The conversion will fail if the bug is present, and
2160 # ensure_conversion would raise Failure.
2161 conv = ensure_conversion('invalid
-closings
-on
-trunk
')
2164 @Cvs2SvnTestFunction
2165 def individual_passes():
2166 "run each pass individually"
2167 conv = ensure_conversion('main
')
2168 conv2 = ensure_conversion('main
', passbypass=1)
2170 if conv.logs != conv2.logs:
2174 @Cvs2SvnTestFunction
2176 "reveal a big bug in our resync algorithm"
2177 # This will fail if the bug is present
2178 conv = ensure_conversion('resync
-bug
')
2181 @Cvs2SvnTestFunction
2182 def branch_from_default_branch():
2183 "reveal a bug in our default branch detection code"
2184 conv = ensure_conversion('branch
-from-default
-branch
')
2186 # This revision will be a default branch synchronization only
2187 # if cvs2svn is correctly determining default branch revisions.
2189 # The bug was that cvs2svn was treating revisions on branches off of
2190 # default branches as default branch revisions, resulting in
2191 # incorrectly regarding the branch off of the default branch as a
2192 # non-trunk default branch. Crystal clear? I thought so. See
2193 # issue #42 for more incoherent blathering.
2194 conv.logs[5].check("This commit was generated by cvs2svn", (
2195 ('/%(trunk)s/proj
/file.txt
'
2196 '(from /%(branches)s/upstream
/proj
/file.txt
:4)', 'R
'),
2200 @Cvs2SvnTestFunction
2201 def file_in_attic_too():
2202 "die if a file exists in and out of the attic"
2204 'file-in-attic
-too
',
2206 r'.*A CVS repository cannot contain both
'
2207 r'(.*)' + re.escape(os.sep) + r'(.*) '
2209 r'\
1' + re.escape(os.sep) + r'Attic
' + re.escape(os.sep) + r'\
2'
2214 @Cvs2SvnTestFunction
2215 def retain_file_in_attic_too():
2216 "test --retain-conflicting-attic-files option"
2217 conv = ensure_conversion(
2218 'file-in-attic
-too
', args=['--retain
-conflicting
-attic
-files
'])
2219 if not conv.path_exists('trunk
', 'file.txt
'):
2221 if not conv.path_exists('trunk
', 'Attic
', 'file.txt
'):
2225 @Cvs2SvnTestFunction
2226 def symbolic_name_filling_guide():
2227 "reveal a big bug in our SymbolFillingGuide"
2228 # This will fail if the bug is present
2229 conv = ensure_conversion('symbolic
-name
-overfill
')
2232 # Helpers for tests involving file contents and properties.
2234 class NodeTreeWalkException:
2235 "Exception class for node tree traversals."
2238 def node_for_path(node, path):
2239 "In the tree rooted under SVNTree NODE, return the node at PATH."
2240 if node.name != '__SVN_ROOT_NODE
':
2241 raise NodeTreeWalkException()
2242 path = path.strip('/')
2243 components = path.split('/')
2244 for component in components:
2245 node = svntest.tree.get_child(node, component)
2248 # Helper for tests involving properties.
2249 def props_for_path(node, path):
2250 "In the tree rooted under SVNTree NODE, return the prop dict for PATH."
2251 return node_for_path(node, path).props
2254 class EOLMime(Cvs2SvnPropertiesTestCase):
2255 """eol settings and mime types together
2257 The files are as follows:
2259 trunk/foo.txt: no -kb, mime file says nothing.
2260 trunk/foo.xml: no -kb, mime file says text.
2261 trunk/foo.zip: no -kb, mime file says non-text.
2262 trunk/foo.bin: has -kb, mime file says nothing.
2263 trunk/foo.csv: has -kb, mime file says text.
2264 trunk/foo.dbf: has -kb, mime file says non-text.
2267 def __init__(self, args, **kw):
2268 # TODO: It's a bit klugey to construct this path here
. But so far
2269 # there's only one test with a mime.types file. If we have more,
2270 # we should abstract this into some helper, which would be located
2271 # near ensure_conversion(). Note that it is a convention of this
2272 # test suite for a mime.types file to be located in the top level
2273 # of the CVS repository to which it applies.
2274 self
.mime_path
= os
.path
.join(
2275 test_data_dir
, 'eol-mime-cvsrepos', 'mime.types')
2277 Cvs2SvnPropertiesTestCase
.__init
__(
2279 props_to_test
=['svn:eol-style', 'svn:mime-type', 'svn:keywords'],
2280 args
=['--mime-types=%s' % self
.mime_path
] + args
,
2284 # We do four conversions. Each time, we pass --mime-types=FILE with
2285 # the same FILE, but vary --default-eol and --eol-from-mime-type.
2286 # Thus there's one conversion with neither flag, one with just the
2287 # former, one with just the latter, and one with both.
2290 # Neither --no-default-eol nor --eol-from-mime-type:
2291 eol_mime1
= EOLMime(
2295 ('trunk/foo.txt', [None, None, None]),
2296 ('trunk/foo.xml', [None, 'text/xml', None]),
2297 ('trunk/foo.zip', [None, 'application/zip', None]),
2298 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2299 ('trunk/foo.csv', [None, 'text/csv', None]),
2300 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2304 # Just --no-default-eol, not --eol-from-mime-type:
2305 eol_mime2
= EOLMime(
2307 args
=['--default-eol=native'],
2309 ('trunk/foo.txt', ['native', None, KEYWORDS
]),
2310 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2311 ('trunk/foo.zip', ['native', 'application/zip', KEYWORDS
]),
2312 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2313 ('trunk/foo.csv', [None, 'text/csv', None]),
2314 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2318 # Just --eol-from-mime-type, not --no-default-eol:
2319 eol_mime3
= EOLMime(
2321 args
=['--eol-from-mime-type'],
2323 ('trunk/foo.txt', [None, None, None]),
2324 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2325 ('trunk/foo.zip', [None, 'application/zip', None]),
2326 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2327 ('trunk/foo.csv', [None, 'text/csv', None]),
2328 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2332 # Both --no-default-eol and --eol-from-mime-type:
2333 eol_mime4
= EOLMime(
2335 args
=['--eol-from-mime-type', '--default-eol=native'],
2337 ('trunk/foo.txt', ['native', None, KEYWORDS
]),
2338 ('trunk/foo.xml', ['native', 'text/xml', KEYWORDS
]),
2339 ('trunk/foo.zip', [None, 'application/zip', None]),
2340 ('trunk/foo.bin', [None, 'application/octet-stream', None]),
2341 ('trunk/foo.csv', [None, 'text/csv', None]),
2342 ('trunk/foo.dbf', [None, 'application/what-is-dbf', None]),
2346 cvs_revnums_off
= Cvs2SvnPropertiesTestCase(
2348 doc
='test non-setting of cvs2svn:cvs-rev property',
2350 props_to_test
=['cvs2svn:cvs-rev'],
2352 ('trunk/foo.txt', [None]),
2353 ('trunk/foo.xml', [None]),
2354 ('trunk/foo.zip', [None]),
2355 ('trunk/foo.bin', [None]),
2356 ('trunk/foo.csv', [None]),
2357 ('trunk/foo.dbf', [None]),
2361 cvs_revnums_on
= Cvs2SvnPropertiesTestCase(
2363 doc
='test setting of cvs2svn:cvs-rev property',
2364 args
=['--cvs-revnums'],
2365 props_to_test
=['cvs2svn:cvs-rev'],
2367 ('trunk/foo.txt', ['1.2']),
2368 ('trunk/foo.xml', ['1.2']),
2369 ('trunk/foo.zip', ['1.2']),
2370 ('trunk/foo.bin', ['1.2']),
2371 ('trunk/foo.csv', ['1.2']),
2372 ('trunk/foo.dbf', ['1.2']),
2376 keywords
= Cvs2SvnPropertiesTestCase(
2378 doc
='test setting of svn:keywords property among others',
2379 args
=['--default-eol=native'],
2380 props_to_test
=['svn:keywords', 'svn:eol-style', 'svn:mime-type'],
2382 ('trunk/foo.default', [KEYWORDS
, 'native', None]),
2383 ('trunk/foo.kkvl', [KEYWORDS
, 'native', None]),
2384 ('trunk/foo.kkv', [KEYWORDS
, 'native', None]),
2385 ('trunk/foo.kb', [None, None, 'application/octet-stream']),
2386 ('trunk/foo.kk', [None, 'native', None]),
2387 ('trunk/foo.ko', [None, 'native', None]),
2388 ('trunk/foo.kv', [None, 'native', None]),
2392 @Cvs2SvnTestFunction
2394 "test setting of svn:ignore property"
2395 conv
= ensure_conversion('cvsignore')
2396 wc_tree
= conv
.get_wc_tree()
2397 topdir_props
= props_for_path(wc_tree
, 'trunk/proj')
2398 subdir_props
= props_for_path(wc_tree
, '/trunk/proj/subdir')
2400 if topdir_props
['svn:ignore'] != \
2401 '*.idx\n*.aux\n*.dvi\n*.log\nfoo\nbar\nbaz\nqux\n':
2404 if subdir_props
['svn:ignore'] != \
2405 '*.idx\n*.aux\n*.dvi\n*.log\nfoo\nbar\nbaz\nqux\n':
2409 @Cvs2SvnTestFunction
2411 "test that CVS can still do what RCS can't"
2412 # See issues 4, 11, 29 for the bugs whose regression we're testing for.
2413 conv
= ensure_conversion(
2414 'requires-cvs', args
=['--use-cvs', '--default-eol=native'],
2417 atsign_contents
= file(conv
.get_wc("trunk", "atsign-add")).read()
2418 cl_contents
= file(conv
.get_wc("trunk", "client_lock.idl")).read()
2420 if atsign_contents
[-1:] == "@":
2422 if cl_contents
.find("gregh\n//\n//Integration for locks") < 0:
2425 if not (conv
.logs
[6].author
== "William Lyon Phelps III" and
2426 conv
.logs
[5].author
== "j random"):
2430 @Cvs2SvnTestFunction
2431 def questionable_branch_names():
2432 "test that we can handle weird branch names"
2433 conv
= ensure_conversion('questionable-symbols')
2434 # If the conversion succeeds, then we're okay. We could check the
2435 # actual branch paths, too, but the main thing is to know that the
2436 # conversion doesn't fail.
2439 @Cvs2SvnTestFunction
2440 def questionable_tag_names():
2441 "test that we can handle weird tag names"
2442 conv
= ensure_conversion('questionable-symbols')
2443 conv
.find_tag_log('Tag_A').check(sym_log_msg('Tag_A', 1), (
2444 ('/%(tags)s/Tag_A (from /trunk:8)', 'A'),
2446 conv
.find_tag_log('TagWith/Backslash_E').check(
2447 sym_log_msg('TagWith/Backslash_E',1),
2449 ('/%(tags)s/TagWith', 'A'),
2450 ('/%(tags)s/TagWith/Backslash_E (from /trunk:8)', 'A'),
2453 conv
.find_tag_log('TagWith/Slash_Z').check(
2454 sym_log_msg('TagWith/Slash_Z',1),
2456 ('/%(tags)s/TagWith/Slash_Z (from /trunk:8)', 'A'),
2461 @Cvs2SvnTestFunction
2462 def revision_reorder_bug():
2463 "reveal a bug that reorders file revisions"
2464 conv
= ensure_conversion('revision-reorder-bug')
2465 # If the conversion succeeds, then we're okay. We could check the
2466 # actual revisions, too, but the main thing is to know that the
2467 # conversion doesn't fail.
2470 @Cvs2SvnTestFunction
2472 "test that exclude really excludes everything"
2473 conv
= ensure_conversion('main', args
=['--exclude=.*'])
2474 for log
in conv
.logs
.values():
2475 for item
in log
.changed_paths
.keys():
2476 if item
.startswith('/branches/') or item
.startswith('/tags/'):
2480 @Cvs2SvnTestFunction
2481 def vendor_branch_delete_add():
2482 "add trunk file that was deleted on vendor branch"
2483 # This will error if the bug is present
2484 conv
= ensure_conversion('vendor-branch-delete-add')
2487 @Cvs2SvnTestFunction
2488 def resync_pass2_pull_forward():
2489 "ensure pass2 doesn't pull rev too far forward"
2490 conv
= ensure_conversion('resync-pass2-pull-forward')
2491 # If the conversion succeeds, then we're okay. We could check the
2492 # actual revisions, too, but the main thing is to know that the
2493 # conversion doesn't fail.
2496 @Cvs2SvnTestFunction
2498 "only LFs for svn:eol-style=native files"
2499 conv
= ensure_conversion('native-eol', args
=['--default-eol=native'])
2500 lines
= run_program(svntest
.main
.svnadmin_binary
, None, 'dump', '-q',
2502 # Verify that all files in the dump have LF EOLs. We're actually
2503 # testing the whole dump file, but the dump file itself only uses
2504 # LF EOLs, so we're safe.
2506 if line
[-1] != '\n' or line
[:-1].find('\r') != -1:
2510 @Cvs2SvnTestFunction
2512 "reveal a bug that created a branch twice"
2513 conv
= ensure_conversion('double-fill')
2514 # If the conversion succeeds, then we're okay. We could check the
2515 # actual revisions, too, but the main thing is to know that the
2516 # conversion doesn't fail.
2520 @Cvs2SvnTestFunction
2522 "reveal a second bug that created a branch twice"
2523 conv
= ensure_conversion('double-fill2')
2524 conv
.logs
[6].check_msg(sym_log_msg('BRANCH1'))
2525 conv
.logs
[7].check_msg(sym_log_msg('BRANCH2'))
2527 # This check should fail:
2528 conv
.logs
[8].check_msg(sym_log_msg('BRANCH2'))
2532 raise Failure('Symbol filled twice in a row')
2535 @Cvs2SvnTestFunction
2536 def resync_pass2_push_backward():
2537 "ensure pass2 doesn't push rev too far backward"
2538 conv
= ensure_conversion('resync-pass2-push-backward')
2539 # If the conversion succeeds, then we're okay. We could check the
2540 # actual revisions, too, but the main thing is to know that the
2541 # conversion doesn't fail.
2544 @Cvs2SvnTestFunction
2546 "reveal a bug that added a branch file twice"
2547 conv
= ensure_conversion('double-add')
2548 # If the conversion succeeds, then we're okay. We could check the
2549 # actual revisions, too, but the main thing is to know that the
2550 # conversion doesn't fail.
2553 @Cvs2SvnTestFunction
2554 def bogus_branch_copy():
2555 "reveal a bug that copies a branch file wrongly"
2556 conv
= ensure_conversion('bogus-branch-copy')
2557 # If the conversion succeeds, then we're okay. We could check the
2558 # actual revisions, too, but the main thing is to know that the
2559 # conversion doesn't fail.
2562 @Cvs2SvnTestFunction
2563 def nested_ttb_directories():
2564 "require error if ttb directories are not disjoint"
2566 {'trunk' : 'a', 'branches' : 'a',},
2567 {'trunk' : 'a', 'tags' : 'a',},
2568 {'branches' : 'a', 'tags' : 'a',},
2569 # This option conflicts with the default trunk path:
2570 {'branches' : 'trunk',},
2571 # Try some nested directories:
2572 {'trunk' : 'a', 'branches' : 'a/b',},
2573 {'trunk' : 'a/b', 'tags' : 'a/b/c/d',},
2574 {'branches' : 'a', 'tags' : 'a/b',},
2577 for opts
in opts_list
:
2579 'main', error_re
=r
'The following paths are not disjoint\:', **opts
2583 class AutoProps(Cvs2SvnPropertiesTestCase
):
2586 The files are as follows:
2588 trunk/foo.txt: no -kb, mime auto-prop says nothing.
2589 trunk/foo.xml: no -kb, mime auto-prop says text and eol-style=CRLF.
2590 trunk/foo.zip: no -kb, mime auto-prop says non-text.
2591 trunk/foo.asc: no -kb, mime auto-prop says text and eol-style=<unset>.
2592 trunk/foo.bin: has -kb, mime auto-prop says nothing.
2593 trunk/foo.csv: has -kb, mime auto-prop says text and eol-style=CRLF.
2594 trunk/foo.dbf: has -kb, mime auto-prop says non-text.
2595 trunk/foo.UPCASE1: no -kb, no mime type.
2596 trunk/foo.UPCASE2: no -kb, no mime type.
2599 def __init__(self
, args
, **kw
):
2600 ### TODO: It's a bit klugey to construct this path here. See also
2601 ### the comment in eol_mime().
2602 auto_props_path
= os
.path
.join(
2603 test_data_dir
, 'eol-mime-cvsrepos', 'auto-props')
2605 Cvs2SvnPropertiesTestCase
.__init
__(
2615 '--auto-props=%s' % auto_props_path
,
2616 '--eol-from-mime-type'
2621 auto_props_ignore_case
= AutoProps(
2622 doc
="test auto-props",
2623 args
=['--default-eol=native'],
2625 ('trunk/foo.txt', ['txt', 'native', None, KEYWORDS
, None]),
2626 ('trunk/foo.xml', ['xml', 'CRLF', 'text/xml', KEYWORDS
, None]),
2627 ('trunk/foo.zip', ['zip', None, 'application/zip', None, None]),
2628 ('trunk/foo.asc', ['asc', None, 'text/plain', None, None]),
2630 ['bin', None, 'application/octet-stream', None, '']),
2631 ('trunk/foo.csv', ['csv', 'CRLF', 'text/csv', None, None]),
2633 ['dbf', None, 'application/what-is-dbf', None, None]),
2634 ('trunk/foo.UPCASE1', ['UPCASE1', 'native', None, KEYWORDS
, None]),
2635 ('trunk/foo.UPCASE2', ['UPCASE2', 'native', None, KEYWORDS
, None]),
2639 @Cvs2SvnTestFunction
2640 def ctrl_char_in_filename():
2641 "do not allow control characters in filenames"
2644 srcrepos_path
= os
.path
.join(test_data_dir
,'main-cvsrepos')
2645 dstrepos_path
= os
.path
.join(test_data_dir
,'ctrl-char-filename-cvsrepos')
2646 if os
.path
.exists(dstrepos_path
):
2647 safe_rmtree(dstrepos_path
)
2649 # create repos from existing main repos
2650 shutil
.copytree(srcrepos_path
, dstrepos_path
)
2651 base_path
= os
.path
.join(dstrepos_path
, 'single-files')
2653 shutil
.copyfile(os
.path
.join(base_path
, 'twoquick,v'),
2654 os
.path
.join(base_path
, 'two\rquick,v'))
2656 # Operating systems that don't allow control characters in
2657 # filenames will hopefully have thrown an exception; in that
2658 # case, just skip this test.
2659 raise svntest
.Skip()
2661 conv
= ensure_conversion(
2662 'ctrl-char-filename',
2663 error_re
=(r
'.*Subversion does not allow character .*.'),
2666 safe_rmtree(dstrepos_path
)
2669 @Cvs2SvnTestFunction
2670 def commit_dependencies():
2671 "interleaved and multi-branch commits to same files"
2672 conv
= ensure_conversion("commit-dependencies")
2673 conv
.logs
[2].check('adding', (
2674 ('/%(trunk)s/interleaved', 'A'),
2675 ('/%(trunk)s/interleaved/file1', 'A'),
2676 ('/%(trunk)s/interleaved/file2', 'A'),
2678 conv
.logs
[3].check('big commit', (
2679 ('/%(trunk)s/interleaved/file1', 'M'),
2680 ('/%(trunk)s/interleaved/file2', 'M'),
2682 conv
.logs
[4].check('dependant small commit', (
2683 ('/%(trunk)s/interleaved/file1', 'M'),
2685 conv
.logs
[5].check('adding', (
2686 ('/%(trunk)s/multi-branch', 'A'),
2687 ('/%(trunk)s/multi-branch/file1', 'A'),
2688 ('/%(trunk)s/multi-branch/file2', 'A'),
2690 conv
.logs
[6].check(sym_log_msg("branch"), (
2691 ('/%(branches)s/branch (from /%(trunk)s:5)', 'A'),
2692 ('/%(branches)s/branch/interleaved', 'D'),
2694 conv
.logs
[7].check('multi-branch-commit', (
2695 ('/%(trunk)s/multi-branch/file1', 'M'),
2696 ('/%(trunk)s/multi-branch/file2', 'M'),
2697 ('/%(branches)s/branch/multi-branch/file1', 'M'),
2698 ('/%(branches)s/branch/multi-branch/file2', 'M'),
2702 @Cvs2SvnTestFunction
2703 def double_branch_delete():
2704 "fill branches before modifying files on them"
2705 conv
= ensure_conversion('double-branch-delete')
2707 # Test for issue #102. The file IMarshalledValue.java is branched,
2708 # deleted, readded on the branch, and then deleted again. If the
2709 # fill for the file on the branch is postponed until after the
2710 # modification, the file will end up live on the branch instead of
2711 # dead! Make sure it happens at the right time.
2713 conv
.logs
[6].check('JBAS-2436 - Adding LGPL Header2', (
2714 ('/%(branches)s/Branch_4_0/IMarshalledValue.java', 'A'),
2717 conv
.logs
[7].check('JBAS-3025 - Removing dependency', (
2718 ('/%(branches)s/Branch_4_0/IMarshalledValue.java', 'D'),
2722 @Cvs2SvnTestFunction
2723 def symbol_mismatches():
2724 "error for conflicting tag/branch"
2728 args
=['--symbol-default=strict'],
2729 error_re
=r
'.*Problems determining how symbols should be converted',
2733 @Cvs2SvnTestFunction
2734 def overlook_symbol_mismatches():
2735 "overlook conflicting tag/branch when --trunk-only"
2737 # This is a test for issue #85.
2739 ensure_conversion('symbol-mess', args
=['--trunk-only'])
2742 @Cvs2SvnTestFunction
2743 def force_symbols():
2744 "force symbols to be tags/branches"
2746 conv
= ensure_conversion(
2748 args
=['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG'])
2749 if conv
.path_exists('tags', 'BRANCH') \
2750 or not conv
.path_exists('branches', 'BRANCH'):
2752 if not conv
.path_exists('tags', 'TAG') \
2753 or conv
.path_exists('branches', 'TAG'):
2755 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2756 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2758 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2759 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2763 @Cvs2SvnTestFunction
2764 def commit_blocks_tags():
2765 "commit prevents forced tag"
2767 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2770 args
=(basic_args
+ ['--force-tag=BRANCH_WITH_COMMIT']),
2772 r
'.*The following branches cannot be forced to be tags '
2773 r
'because they have commits'
2778 @Cvs2SvnTestFunction
2779 def blocked_excludes():
2780 "error for blocked excludes"
2782 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2783 for blocker
in ['BRANCH', 'COMMIT', 'UNNAMED']:
2787 args
=(basic_args
+ ['--exclude=BLOCKED_BY_%s' % blocker
]))
2788 raise MissingErrorException()
2793 @Cvs2SvnTestFunction
2794 def unblock_blocked_excludes():
2795 "excluding blocker removes blockage"
2797 basic_args
= ['--force-branch=MOSTLY_BRANCH', '--force-tag=MOSTLY_TAG']
2798 for blocker
in ['BRANCH', 'COMMIT']:
2801 args
=(basic_args
+ ['--exclude=BLOCKED_BY_%s' % blocker
,
2802 '--exclude=BLOCKING_%s' % blocker
]))
2805 @Cvs2SvnTestFunction
2806 def regexp_force_symbols():
2807 "force symbols via regular expressions"
2809 conv
= ensure_conversion(
2811 args
=['--force-branch=MOST.*_BRANCH', '--force-tag=MOST.*_TAG'])
2812 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2813 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2815 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2816 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2820 @Cvs2SvnTestFunction
2821 def heuristic_symbol_default():
2822 "test 'heuristic' symbol default"
2824 conv
= ensure_conversion(
2825 'symbol-mess', args
=['--symbol-default=heuristic'])
2826 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2827 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2829 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2830 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2834 @Cvs2SvnTestFunction
2835 def branch_symbol_default():
2836 "test 'branch' symbol default"
2838 conv
= ensure_conversion(
2839 'symbol-mess', args
=['--symbol-default=branch'])
2840 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2841 or not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2843 if conv
.path_exists('tags', 'MOSTLY_TAG') \
2844 or not conv
.path_exists('branches', 'MOSTLY_TAG'):
2848 @Cvs2SvnTestFunction
2849 def tag_symbol_default():
2850 "test 'tag' symbol default"
2852 conv
= ensure_conversion(
2853 'symbol-mess', args
=['--symbol-default=tag'])
2854 if not conv
.path_exists('tags', 'MOSTLY_BRANCH') \
2855 or conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2857 if not conv
.path_exists('tags', 'MOSTLY_TAG') \
2858 or conv
.path_exists('branches', 'MOSTLY_TAG'):
2862 @Cvs2SvnTestFunction
2863 def symbol_transform():
2864 "test --symbol-transform"
2866 conv
= ensure_conversion(
2869 '--symbol-default=heuristic',
2870 '--symbol-transform=BRANCH:branch',
2871 '--symbol-transform=TAG:tag',
2872 '--symbol-transform=MOSTLY_(BRANCH|TAG):MOSTLY.\\1',
2874 if not conv
.path_exists('branches', 'branch'):
2876 if not conv
.path_exists('tags', 'tag'):
2878 if not conv
.path_exists('branches', 'MOSTLY.BRANCH'):
2880 if not conv
.path_exists('tags', 'MOSTLY.TAG'):
2884 @Cvs2SvnTestFunction
2885 def write_symbol_info():
2886 "test --write-symbol-info"
2890 'trunk', 'trunk', '.'],
2891 ['0', 'BLOCKED_BY_UNNAMED',
2892 'branch', 'branches/BLOCKED_BY_UNNAMED', '.trunk.'],
2893 ['0', 'BLOCKING_COMMIT',
2894 'branch', 'branches/BLOCKING_COMMIT', 'BLOCKED_BY_COMMIT'],
2895 ['0', 'BLOCKED_BY_COMMIT',
2896 'branch', 'branches/BLOCKED_BY_COMMIT', '.trunk.'],
2897 ['0', 'BLOCKING_BRANCH',
2898 'branch', 'branches/BLOCKING_BRANCH', 'BLOCKED_BY_BRANCH'],
2899 ['0', 'BLOCKED_BY_BRANCH',
2900 'branch', 'branches/BLOCKED_BY_BRANCH', '.trunk.'],
2901 ['0', 'MOSTLY_BRANCH',
2905 ['0', 'BRANCH_WITH_COMMIT',
2906 'branch', 'branches/BRANCH_WITH_COMMIT', '.trunk.'],
2908 'branch', 'branches/BRANCH', '.trunk.'],
2910 'tag', 'tags/TAG', '.trunk.'],
2911 ['0', 'unlabeled-1.1.12.1.2',
2912 'branch', 'branches/unlabeled-1.1.12.1.2', 'BLOCKED_BY_UNNAMED'],
2914 expected_lines
.sort()
2916 symbol_info_file
= os
.path
.join(tmp_dir
, 'symbol-mess-symbol-info.txt')
2921 '--symbol-default=strict',
2922 '--write-symbol-info=%s' % (symbol_info_file
,),
2923 '--passes=:CollateSymbolsPass',
2926 raise MissingErrorException()
2930 comment_re
= re
.compile(r
'^\s*\#')
2931 for l
in open(symbol_info_file
, 'r'):
2932 if comment_re
.match(l
):
2934 lines
.append(l
.strip().split())
2936 if lines
!= expected_lines
:
2937 s
= ['Symbol info incorrect\n']
2939 for diffline
in differ
.compare(
2940 [' '.join(line
) + '\n' for line
in expected_lines
],
2941 [' '.join(line
) + '\n' for line
in lines
],
2944 raise Failure(''.join(s
))
2947 @Cvs2SvnTestFunction
2949 "test --symbol-hints for setting branch/tag"
2951 conv
= ensure_conversion(
2952 'symbol-mess', symbol_hints_file
='symbol-mess-symbol-hints.txt',
2954 if not conv
.path_exists('branches', 'MOSTLY_BRANCH'):
2956 if not conv
.path_exists('tags', 'MOSTLY_TAG'):
2958 conv
.logs
[3].check(sym_log_msg('MOSTLY_TAG', 1), (
2959 ('/tags/MOSTLY_TAG (from /trunk:2)', 'A'),
2961 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2962 ('/branches/BRANCH_WITH_COMMIT (from /trunk:2)', 'A'),
2964 conv
.logs
[10].check(sym_log_msg('MOSTLY_BRANCH'), (
2965 ('/branches/MOSTLY_BRANCH (from /trunk:2)', 'A'),
2969 @Cvs2SvnTestFunction
2971 "test --symbol-hints for setting parent"
2973 conv
= ensure_conversion(
2974 'symbol-mess', symbol_hints_file
='symbol-mess-parent-hints.txt',
2976 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
2977 ('/%(branches)s/BRANCH_WITH_COMMIT (from /branches/BRANCH:8)', 'A'),
2981 @Cvs2SvnTestFunction
2982 def parent_hints_invalid():
2983 "test --symbol-hints with an invalid parent"
2985 # BRANCH_WITH_COMMIT is usually determined to branch from .trunk.;
2986 # this symbol hints file sets the preferred parent to BRANCH
2988 conv
= ensure_conversion(
2989 'symbol-mess', symbol_hints_file
='symbol-mess-parent-hints-invalid.txt',
2991 r
"BLOCKED_BY_BRANCH is not a valid parent for BRANCH_WITH_COMMIT"
2996 @Cvs2SvnTestFunction
2997 def parent_hints_wildcards():
2998 "test --symbol-hints wildcards"
3000 # BRANCH_WITH_COMMIT is usually determined to branch from .trunk.;
3001 # this symbol hints file sets the preferred parent to BRANCH
3003 conv
= ensure_conversion(
3005 symbol_hints_file
='symbol-mess-parent-hints-wildcards.txt',
3007 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
3008 ('/%(branches)s/BRANCH_WITH_COMMIT (from /branches/BRANCH:8)', 'A'),
3012 @Cvs2SvnTestFunction
3014 "test --symbol-hints for setting svn paths"
3016 conv
= ensure_conversion(
3017 'symbol-mess', symbol_hints_file
='symbol-mess-path-hints.txt',
3019 conv
.logs
[1].check('Standard project directories initialized by cvs2svn.', (
3022 ('/a/strange', 'A'),
3023 ('/a/strange/trunk', 'A'),
3024 ('/a/strange/trunk/path', 'A'),
3028 conv
.logs
[3].check(sym_log_msg('MOSTLY_TAG', 1), (
3030 ('/special/tag', 'A'),
3031 ('/special/tag/path (from /a/strange/trunk/path:2)', 'A'),
3033 conv
.logs
[9].check(sym_log_msg('BRANCH_WITH_COMMIT'), (
3034 ('/special/other', 'A'),
3035 ('/special/other/branch', 'A'),
3036 ('/special/other/branch/path (from /a/strange/trunk/path:2)', 'A'),
3038 conv
.logs
[10].check(sym_log_msg('MOSTLY_BRANCH'), (
3039 ('/special/branch', 'A'),
3040 ('/special/branch/path (from /a/strange/trunk/path:2)', 'A'),
3044 @Cvs2SvnTestFunction
3046 "test problem from issue 99"
3048 conv
= ensure_conversion('issue-99')
3051 @Cvs2SvnTestFunction
3053 "test problem from issue 100"
3055 conv
= ensure_conversion('issue-100')
3056 file1
= conv
.get_wc('trunk', 'file1.txt')
3057 if file(file1
).read() != 'file1.txt<1.2>\n':
3061 @Cvs2SvnTestFunction
3063 "test problem from issue 106"
3065 conv
= ensure_conversion('issue-106')
3068 @Cvs2SvnTestFunction
3069 def options_option():
3070 "use of the --options option"
3072 conv
= ensure_conversion('main', options_file
='cvs2svn.options')
3075 @Cvs2SvnTestFunction
3077 "multiproject conversion"
3079 conv
= ensure_conversion(
3080 'main', options_file
='cvs2svn-multiproject.options'
3082 conv
.logs
[1].check('Standard project directories initialized by cvs2svn.', (
3083 ('/partial-prune', 'A'),
3084 ('/partial-prune/trunk', 'A'),
3085 ('/partial-prune/branches', 'A'),
3086 ('/partial-prune/tags', 'A'),
3087 ('/partial-prune/releases', 'A'),
3091 @Cvs2SvnTestFunction
3093 "multiproject conversion with cross-project commits"
3095 conv
= ensure_conversion(
3096 'main', options_file
='cvs2svn-crossproject.options'
3100 @Cvs2SvnTestFunction
3101 def tag_with_no_revision():
3102 "tag defined but revision is deleted"
3104 conv
= ensure_conversion('tag-with-no-revision')
3107 @Cvs2SvnTestFunction
3108 def delete_cvsignore():
3109 "svn:ignore should vanish when .cvsignore does"
3111 # This is issue #81.
3113 conv
= ensure_conversion('delete-cvsignore')
3115 wc_tree
= conv
.get_wc_tree()
3116 props
= props_for_path(wc_tree
, 'trunk/proj')
3118 if props
.has_key('svn:ignore'):
3122 @Cvs2SvnTestFunction
3123 def repeated_deltatext():
3124 "ignore repeated deltatext blocks with warning"
3126 conv
= ensure_conversion('repeated-deltatext')
3127 warning_re
= r
'.*Deltatext block for revision 1.1 appeared twice'
3128 if not conv
.output_found(warning_re
):
3132 @Cvs2SvnTestFunction
3134 "process some nasty dependency graphs"
3136 # It's not how well the bear can dance, but that the bear can dance
3138 conv
= ensure_conversion('nasty-graphs')
3142 @Cvs2SvnTestFunction
3143 def tagging_after_delete():
3144 "optimal tag after deleting files"
3146 conv
= ensure_conversion('tagging-after-delete')
3148 # tag should be 'clean', no deletes
3149 log
= conv
.find_tag_log('tag1')
3151 ('/%(tags)s/tag1 (from /%(trunk)s:3)', 'A'),
3153 log
.check_changes(expected
)
3156 @Cvs2SvnTestFunction
3157 def crossed_branches():
3158 "branches created in inconsistent orders"
3160 conv
= ensure_conversion('crossed-branches')
3163 @Cvs2SvnTestFunction
3164 def file_directory_conflict():
3165 "error when filename conflicts with directory name"
3167 conv
= ensure_conversion(
3168 'file-directory-conflict',
3169 error_re
=r
'.*Directory name conflicts with filename',
3173 @Cvs2SvnTestFunction
3174 def attic_directory_conflict():
3175 "error when attic filename conflicts with dirname"
3177 # This tests the problem reported in issue #105.
3179 conv
= ensure_conversion(
3180 'attic-directory-conflict',
3181 error_re
=r
'.*Directory name conflicts with filename',
3185 @Cvs2SvnTestFunction
3187 "verify that --use-rcs and --use-internal-co agree"
3189 rcs_conv
= ensure_conversion(
3190 'main', args
=['--use-rcs', '--default-eol=native'], dumpfile
='use-rcs-rcs.dump',
3192 conv
= ensure_conversion(
3193 'main', args
=['--default-eol=native'], dumpfile
='use-rcs-int.dump',
3195 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3197 rcs_lines
= list(open(rcs_conv
.dumpfile
, 'rb'))
3198 lines
= list(open(conv
.dumpfile
, 'rb'))
3199 # Compare all lines following the repository UUID:
3200 if lines
[3:] != rcs_lines
[3:]:
3204 @Cvs2SvnTestFunction
3205 def internal_co_exclude():
3206 "verify that --use-internal-co --exclude=... works"
3208 rcs_conv
= ensure_conversion(
3210 args
=['--use-rcs', '--exclude=BRANCH', '--default-eol=native'],
3211 dumpfile
='internal-co-exclude-rcs.dump',
3213 conv
= ensure_conversion(
3215 args
=['--exclude=BRANCH', '--default-eol=native'],
3216 dumpfile
='internal-co-exclude-int.dump',
3218 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3220 rcs_lines
= list(open(rcs_conv
.dumpfile
, 'rb'))
3221 lines
= list(open(conv
.dumpfile
, 'rb'))
3222 # Compare all lines following the repository UUID:
3223 if lines
[3:] != rcs_lines
[3:]:
3227 @Cvs2SvnTestFunction
3228 def internal_co_trunk_only():
3229 "verify that --use-internal-co --trunk-only works"
3231 conv
= ensure_conversion(
3233 args
=['--trunk-only', '--default-eol=native'],
3235 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3239 @Cvs2SvnTestFunction
3240 def leftover_revs():
3241 "check for leftover checked-out revisions"
3243 conv
= ensure_conversion(
3245 args
=['--exclude=BRANCH', '--default-eol=native'],
3247 if conv
.output_found(r
'WARNING\: internal problem\: leftover revisions'):
3251 @Cvs2SvnTestFunction
3252 def requires_internal_co():
3253 "test that internal co can do more than RCS"
3254 # See issues 4, 11 for the bugs whose regression we're testing for.
3255 # Unlike in requires_cvs above, issue 29 is not covered.
3256 conv
= ensure_conversion('requires-cvs')
3258 atsign_contents
= file(conv
.get_wc("trunk", "atsign-add")).read()
3260 if atsign_contents
[-1:] == "@":
3263 if not (conv
.logs
[6].author
== "William Lyon Phelps III" and
3264 conv
.logs
[5].author
== "j random"):
3268 @Cvs2SvnTestFunction
3269 def internal_co_keywords():
3270 "test that internal co handles keywords correctly"
3271 conv_ic
= ensure_conversion('internal-co-keywords',
3272 args
=["--keywords-off"])
3273 conv_cvs
= ensure_conversion('internal-co-keywords',
3274 args
=["--use-cvs", "--keywords-off"])
3276 ko_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'ko.txt')).read()
3277 ko_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'ko.txt')).read()
3278 kk_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'kk.txt')).read()
3279 kk_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'kk.txt')).read()
3280 kv_ic
= file(conv_ic
.get_wc('trunk', 'dir', 'kv.txt')).read()
3281 kv_cvs
= file(conv_cvs
.get_wc('trunk', 'dir', 'kv.txt')).read()
3282 # Ensure proper "/Attic" expansion of $Source$ keyword in files
3283 # which are in a deleted state in trunk
3284 del_ic
= file(conv_ic
.get_wc('branches/b', 'dir', 'kv-deleted.txt')).read()
3285 del_cvs
= file(conv_cvs
.get_wc('branches/b', 'dir', 'kv-deleted.txt')).read()
3292 if del_ic
!= del_cvs
:
3295 # The date format changed between cvs and co ('/' instead of '-').
3296 # Accept either one:
3297 date_substitution_re
= re
.compile(r
' ([0-9]*)-([0-9]*)-([0-9]*) ')
3298 if kv_ic
!= kv_cvs \
3299 and date_substitution_re
.sub(r
' \1/\2/\3 ', kv_ic
) != kv_cvs
:
3303 @Cvs2SvnTestFunction
3304 def timestamp_chaos():
3305 "test timestamp adjustments"
3307 conv
= ensure_conversion('timestamp-chaos', args
=["-v"])
3309 # The times are expressed here in UTC:
3311 '2007-01-01 21:00:00', # Initial commit
3312 '2007-01-01 21:00:00', # revision 1.1 of both files
3313 '2007-01-01 21:00:01', # revision 1.2 of file1.txt, adjusted forwards
3314 '2007-01-01 21:00:02', # revision 1.2 of file2.txt, adjusted backwards
3315 '2007-01-01 22:00:00', # revision 1.3 of both files
3318 # Convert the times to seconds since the epoch, in UTC:
3319 times
= [calendar
.timegm(svn_strptime(t
)) for t
in times
]
3321 for i
in range(len(times
)):
3322 if abs(conv
.logs
[i
+ 1].date
- times
[i
]) > 0.1:
3326 @Cvs2SvnTestFunction
3328 "convert a repository that contains symlinks"
3330 # This is a test for issue #97.
3332 proj
= os
.path
.join(test_data_dir
, 'symlinks-cvsrepos', 'proj')
3335 os
.path
.join('..', 'file.txt,v'),
3336 os
.path
.join(proj
, 'dir1', 'file.txt,v'),
3340 os
.path
.join(proj
, 'dir2'),
3346 except AttributeError:
3347 # Apparently this OS doesn't support symlinks, so skip test.
3348 raise svntest
.Skip()
3351 for (src
,dst
) in links
:
3352 os
.symlink(src
, dst
)
3354 conv
= ensure_conversion('symlinks')
3355 conv
.logs
[2].check('', (
3356 ('/%(trunk)s/proj', 'A'),
3357 ('/%(trunk)s/proj/file.txt', 'A'),
3358 ('/%(trunk)s/proj/dir1', 'A'),
3359 ('/%(trunk)s/proj/dir1/file.txt', 'A'),
3360 ('/%(trunk)s/proj/dir2', 'A'),
3361 ('/%(trunk)s/proj/dir2/file.txt', 'A'),
3364 for (src
,dst
) in links
:
3368 @Cvs2SvnTestFunction
3369 def empty_trunk_path():
3370 "allow --trunk to be empty if --trunk-only"
3372 # This is a test for issue #53.
3374 conv
= ensure_conversion(
3375 'main', args
=['--trunk-only', '--trunk='],
3379 @Cvs2SvnTestFunction
3380 def preferred_parent_cycle():
3381 "handle a cycle in branch parent preferences"
3383 conv
= ensure_conversion('preferred-parent-cycle')
3386 @Cvs2SvnTestFunction
3387 def branch_from_empty_dir():
3388 "branch from an empty directory"
3390 conv
= ensure_conversion('branch-from-empty-dir')
3393 @Cvs2SvnTestFunction
3395 "add a file on a branch then on trunk"
3397 conv
= ensure_conversion('trunk-readd')
3400 @Cvs2SvnTestFunction
3401 def branch_from_deleted_1_1():
3402 "branch from a 1.1 revision that will be deleted"
3404 conv
= ensure_conversion('branch-from-deleted-1-1')
3405 conv
.logs
[5].check('Adding b.txt:1.1.2.1', (
3406 ('/%(branches)s/BRANCH1/proj/b.txt', 'A'),
3408 conv
.logs
[6].check('Adding b.txt:1.1.4.1', (
3409 ('/%(branches)s/BRANCH2/proj/b.txt', 'A'),
3411 conv
.logs
[7].check('Adding b.txt:1.2', (
3412 ('/%(trunk)s/proj/b.txt', 'A'),
3415 conv
.logs
[8].check('Adding c.txt:1.1.2.1', (
3416 ('/%(branches)s/BRANCH1/proj/c.txt', 'A'),
3418 conv
.logs
[9].check('Adding c.txt:1.1.4.1', (
3419 ('/%(branches)s/BRANCH2/proj/c.txt', 'A'),
3423 @Cvs2SvnTestFunction
3424 def add_on_branch():
3425 "add a file on a branch using newer CVS"
3427 conv
= ensure_conversion('add-on-branch')
3428 conv
.logs
[6].check('Adding b.txt:1.1', (
3429 ('/%(trunk)s/proj/b.txt', 'A'),
3431 conv
.logs
[7].check('Adding b.txt:1.1.2.2', (
3432 ('/%(branches)s/BRANCH1/proj/b.txt', 'A'),
3434 conv
.logs
[8].check('Adding c.txt:1.1', (
3435 ('/%(trunk)s/proj/c.txt', 'A'),
3437 conv
.logs
[9].check('Removing c.txt:1.2', (
3438 ('/%(trunk)s/proj/c.txt', 'D'),
3440 conv
.logs
[10].check('Adding c.txt:1.2.2.2', (
3441 ('/%(branches)s/BRANCH2/proj/c.txt', 'A'),
3443 conv
.logs
[11].check('Adding d.txt:1.1', (
3444 ('/%(trunk)s/proj/d.txt', 'A'),
3446 conv
.logs
[12].check('Adding d.txt:1.1.2.2', (
3447 ('/%(branches)s/BRANCH3/proj/d.txt', 'A'),
3451 @Cvs2SvnTestFunction
3453 "test output in git-fast-import format"
3455 # Note: To test importing into git, do
3457 # ./run-tests <this-test-number>
3458 # rm -rf cvs2svn-tmp/main.git
3459 # git init --bare cvs2svn-tmp/main.git
3460 # cd cvs2svn-tmp/main.git
3461 # cat ../git-{blob,dump}.dat | git fast-import
3463 # Or, to load the dumpfiles separately:
3465 # cat ../git-blob.dat | git fast-import --export-marks=../git-marks.dat
3466 # cat ../git-dump.dat | git fast-import --import-marks=../git-marks.dat
3468 # Then use "gitk --all", "git log", etc. to test the contents of the
3469 # repository or "git clone" to make a non-bare clone.
3471 # We don't have the infrastructure to check that the resulting git
3472 # repository is correct, so we just check that the conversion runs
3474 conv
= GitConversion('main', None, [
3475 '--blobfile=cvs2svn-tmp/git-blob.dat',
3476 '--dumpfile=cvs2svn-tmp/git-dump.dat',
3477 '--username=cvs2git',
3478 'test-data/main-cvsrepos',
3482 @Cvs2SvnTestFunction
3484 "test cvs2git --use-external-blob-generator option"
3486 # See comment in main_git() for more information.
3488 conv
= GitConversion('main', None, [
3489 '--use-external-blob-generator',
3490 '--blobfile=cvs2svn-tmp/blobfile.out',
3491 '--dumpfile=cvs2svn-tmp/dumpfile.out',
3492 '--username=cvs2git',
3493 'test-data/main-cvsrepos',
3497 @Cvs2SvnTestFunction
3499 "test cvs2git using options file"
3501 conv
= GitConversion('main', None, [], options_file
='cvs2git.options')
3504 @Cvs2SvnTestFunction
3506 "output in git-fast-import format with inline data"
3508 # The output should be suitable for import by Mercurial.
3510 # We don't have the infrastructure to check that the resulting
3511 # Mercurial repository is correct, so we just check that the
3512 # conversion runs to completion:
3513 conv
= GitConversion('main', None, [], options_file
='cvs2hg.options')
3516 @Cvs2SvnTestFunction
3517 def invalid_symbol():
3518 "a symbol with the incorrect format"
3520 conv
= ensure_conversion('invalid-symbol')
3521 if not conv
.output_found(
3522 r
".*branch 'SYMBOL' references invalid revision 1$"
3527 @Cvs2SvnTestFunction
3528 def invalid_symbol_ignore():
3529 "ignore a symbol using a SymbolMapper"
3531 conv
= ensure_conversion(
3532 'invalid-symbol', options_file
='cvs2svn-ignore.options'
3536 @Cvs2SvnTestFunction
3537 def invalid_symbol_ignore2():
3538 "ignore a symbol using an IgnoreSymbolTransform"
3540 conv
= ensure_conversion(
3541 'invalid-symbol', options_file
='cvs2svn-ignore2.options'
3545 class EOLVariants(Cvs2SvnTestCase
):
3546 "handle various --eol-style options"
3548 eol_style_strings
= {
3555 def __init__(self
, eol_style
):
3556 self
.eol_style
= eol_style
3557 self
.dumpfile
= 'eol-variants-%s.dump' % (self
.eol_style
,)
3558 Cvs2SvnTestCase
.__init
__(
3559 self
, 'eol-variants', variant
=self
.eol_style
,
3560 dumpfile
=self
.dumpfile
,
3562 '--default-eol=%s' % (self
.eol_style
,),
3566 def run(self
, sbox
):
3567 conv
= self
.ensure_conversion()
3568 dump_contents
= open(conv
.dumpfile
, 'rb').read()
3569 expected_text
= self
.eol_style_strings
[self
.eol_style
].join(
3570 ['line 1', 'line 2', '\n\n']
3572 if not dump_contents
.endswith(expected_text
):
3576 @Cvs2SvnTestFunction
3578 "handle a file with no revisions (issue #80)"
3580 conv
= ensure_conversion('no-revs-file')
3583 @Cvs2SvnTestFunction
3584 def mirror_keyerror_test():
3585 "a case that gave KeyError in SVNRepositoryMirror"
3587 conv
= ensure_conversion('mirror-keyerror')
3590 @Cvs2SvnTestFunction
3591 def exclude_ntdb_test():
3592 "exclude a non-trunk default branch"
3594 symbol_info_file
= os
.path
.join(tmp_dir
, 'exclude-ntdb-symbol-info.txt')
3595 conv
= ensure_conversion(
3598 '--write-symbol-info=%s' % (symbol_info_file
,),
3599 '--exclude=branch3',
3601 '--exclude=vendortag3',
3602 '--exclude=vendorbranch',
3607 @Cvs2SvnTestFunction
3608 def mirror_keyerror2_test():
3609 "a case that gave KeyError in RepositoryMirror"
3611 conv
= ensure_conversion('mirror-keyerror2')
3614 @Cvs2SvnTestFunction
3615 def mirror_keyerror3_test():
3616 "a case that gave KeyError in RepositoryMirror"
3618 conv
= ensure_conversion('mirror-keyerror3')
3622 @Cvs2SvnTestFunction
3623 def add_cvsignore_to_branch_test():
3624 "check adding .cvsignore to an existing branch"
3626 # This a test for issue #122.
3628 conv
= ensure_conversion('add-cvsignore-to-branch')
3629 wc_tree
= conv
.get_wc_tree()
3630 trunk_props
= props_for_path(wc_tree
, 'trunk/dir')
3631 if trunk_props
['svn:ignore'] != '*.o\n\n':
3634 branch_props
= props_for_path(wc_tree
, 'branches/BRANCH/dir')
3635 if branch_props
['svn:ignore'] != '*.o\n\n':
3639 @Cvs2SvnTestFunction
3640 def missing_deltatext():
3641 "a revision's deltatext is missing"
3643 # This is a type of RCS file corruption that has been observed.
3644 conv
= ensure_conversion(
3645 'missing-deltatext',
3647 r
"ERROR\: .* has no deltatext section for revision 1\.1\.4\.4"
3652 @Cvs2SvnTestFunction
3653 def transform_unlabeled_branch_name():
3654 "transform name of unlabeled branch"
3656 conv
= ensure_conversion(
3659 '--symbol-transform=unlabeled-1.1.4:BRANCH2',
3662 if conv
.path_exists('branches', 'unlabeled-1.1.4'):
3663 raise Failure('Branch unlabeled-1.1.4 not excluded')
3664 if not conv
.path_exists('branches', 'BRANCH2'):
3665 raise Failure('Branch BRANCH2 not found')
3668 @Cvs2SvnTestFunction
3669 def ignore_unlabeled_branch():
3670 "ignoring an unlabeled branch is not allowed"
3672 conv
= ensure_conversion(
3674 options_file
='cvs2svn-ignore.options',
3676 r
"ERROR\: The unlabeled branch \'unlabeled\-1\.1\.4\' "
3677 r
"in \'.*\' contains commits"
3682 @Cvs2SvnTestFunction
3683 def exclude_unlabeled_branch():
3684 "exclude unlabeled branch"
3686 conv
= ensure_conversion(
3688 args
=['--exclude=unlabeled-.*'],
3690 if conv
.path_exists('branches', 'unlabeled-1.1.4'):
3691 raise Failure('Branch unlabeled-1.1.4 not excluded')
3694 @Cvs2SvnTestFunction
3695 def unlabeled_branch_name_collision():
3696 "transform unlabeled branch to same name as branch"
3698 conv
= ensure_conversion(
3701 '--symbol-transform=unlabeled-1.1.4:BRANCH',
3704 r
"ERROR\: Symbol name \'BRANCH\' is already used"
3709 @Cvs2SvnTestFunction
3710 def collision_with_unlabeled_branch_name():
3711 "transform branch to same name as unlabeled branch"
3713 conv
= ensure_conversion(
3716 '--symbol-transform=BRANCH:unlabeled-1.1.4',
3719 r
"ERROR\: Symbol name \'unlabeled\-1\.1\.4\' is already used"
3724 @Cvs2SvnTestFunction
3726 "a repo with many removable dead revisions"
3728 conv
= ensure_conversion('many-deletes')
3729 conv
.logs
[5].check('Add files on BRANCH', (
3730 ('/%(branches)s/BRANCH/proj/b.txt', 'A'),
3732 conv
.logs
[6].check('Add files on BRANCH2', (
3733 ('/%(branches)s/BRANCH2/proj/b.txt', 'A'),
3734 ('/%(branches)s/BRANCH2/proj/c.txt', 'A'),
3735 ('/%(branches)s/BRANCH2/proj/d.txt', 'A'),
3739 cvs_description
= Cvs2SvnPropertiesTestCase(
3741 doc
='test handling of CVS file descriptions',
3742 props_to_test
=['cvs:description'],
3744 ('trunk/proj/default', ['This is an example file description.']),
3745 ('trunk/proj/sub1/default', [None]),
3749 @Cvs2SvnTestFunction
3750 def include_empty_directories():
3751 "test --include-empty-directories option"
3753 conv
= ensure_conversion(
3754 'empty-directories', args
=['--include-empty-directories'],
3756 conv
.logs
[1].check('Standard project directories', (
3757 ('/%(trunk)s', 'A'),
3758 ('/%(branches)s', 'A'),
3760 ('/%(trunk)s/root-empty-directory', 'A'),
3761 ('/%(trunk)s/root-empty-directory/empty-subdirectory', 'A'),
3763 conv
.logs
[3].check('Add b.txt.', (
3764 ('/%(trunk)s/direct', 'A'),
3765 ('/%(trunk)s/direct/b.txt', 'A'),
3766 ('/%(trunk)s/direct/empty-directory', 'A'),
3767 ('/%(trunk)s/direct/empty-directory/empty-subdirectory', 'A'),
3769 conv
.logs
[4].check('Add c.txt.', (
3770 ('/%(trunk)s/indirect', 'A'),
3771 ('/%(trunk)s/indirect/subdirectory', 'A'),
3772 ('/%(trunk)s/indirect/subdirectory/c.txt', 'A'),
3773 ('/%(trunk)s/indirect/empty-directory', 'A'),
3774 ('/%(trunk)s/indirect/empty-directory/empty-subdirectory', 'A'),
3776 conv
.logs
[5].check('Remove b.txt', (
3777 ('/%(trunk)s/direct', 'D'),
3779 conv
.logs
[6].check('Remove c.txt', (
3780 ('/%(trunk)s/indirect', 'D'),
3782 conv
.logs
[7].check('Re-add b.txt.', (
3783 ('/%(trunk)s/direct', 'A'),
3784 ('/%(trunk)s/direct/b.txt', 'A'),
3785 ('/%(trunk)s/direct/empty-directory', 'A'),
3786 ('/%(trunk)s/direct/empty-directory/empty-subdirectory', 'A'),
3788 conv
.logs
[8].check('Re-add c.txt.', (
3789 ('/%(trunk)s/indirect', 'A'),
3790 ('/%(trunk)s/indirect/subdirectory', 'A'),
3791 ('/%(trunk)s/indirect/subdirectory/c.txt', 'A'),
3792 ('/%(trunk)s/indirect/empty-directory', 'A'),
3793 ('/%(trunk)s/indirect/empty-directory/empty-subdirectory', 'A'),
3795 conv
.logs
[9].check('This commit was manufactured', (
3796 ('/%(tags)s/TAG (from /%(trunk)s:8)', 'A'),
3798 conv
.logs
[10].check('This commit was manufactured', (
3799 ('/%(branches)s/BRANCH (from /%(trunk)s:8)', 'A'),
3801 conv
.logs
[11].check('Import d.txt.', (
3802 ('/%(branches)s/VENDORBRANCH', 'A'),
3803 ('/%(branches)s/VENDORBRANCH/import', 'A'),
3804 ('/%(branches)s/VENDORBRANCH/import/d.txt', 'A'),
3805 ('/%(branches)s/VENDORBRANCH/root-empty-directory', 'A'),
3806 ('/%(branches)s/VENDORBRANCH/root-empty-directory/empty-subdirectory',
3808 ('/%(branches)s/VENDORBRANCH/import/empty-directory', 'A'),
3809 ('/%(branches)s/VENDORBRANCH/import/empty-directory/empty-subdirectory',
3812 conv
.logs
[12].check('This commit was generated', (
3813 ('/%(trunk)s/import', 'A'),
3814 ('/%(trunk)s/import/d.txt '
3815 '(from /%(branches)s/VENDORBRANCH/import/d.txt:11)', 'A'),
3816 ('/%(trunk)s/import/empty-directory', 'A'),
3817 ('/%(trunk)s/import/empty-directory/empty-subdirectory', 'A'),
3821 @Cvs2SvnTestFunction
3822 def include_empty_directories_no_prune():
3823 "test --include-empty-directories with --no-prune"
3825 conv
= ensure_conversion(
3826 'empty-directories', args
=['--include-empty-directories', '--no-prune'],
3828 conv
.logs
[1].check('Standard project directories', (
3829 ('/%(trunk)s', 'A'),
3830 ('/%(branches)s', 'A'),
3832 ('/%(trunk)s/root-empty-directory', 'A'),
3833 ('/%(trunk)s/root-empty-directory/empty-subdirectory', 'A'),
3835 conv
.logs
[3].check('Add b.txt.', (
3836 ('/%(trunk)s/direct', 'A'),
3837 ('/%(trunk)s/direct/b.txt', 'A'),
3838 ('/%(trunk)s/direct/empty-directory', 'A'),
3839 ('/%(trunk)s/direct/empty-directory/empty-subdirectory', 'A'),
3841 conv
.logs
[4].check('Add c.txt.', (
3842 ('/%(trunk)s/indirect', 'A'),
3843 ('/%(trunk)s/indirect/subdirectory', 'A'),
3844 ('/%(trunk)s/indirect/subdirectory/c.txt', 'A'),
3845 ('/%(trunk)s/indirect/empty-directory', 'A'),
3846 ('/%(trunk)s/indirect/empty-directory/empty-subdirectory', 'A'),
3848 conv
.logs
[5].check('Remove b.txt', (
3849 ('/%(trunk)s/direct/b.txt', 'D'),
3851 conv
.logs
[6].check('Remove c.txt', (
3852 ('/%(trunk)s/indirect/subdirectory/c.txt', 'D'),
3854 conv
.logs
[7].check('Re-add b.txt.', (
3855 ('/%(trunk)s/direct/b.txt', 'A'),
3857 conv
.logs
[8].check('Re-add c.txt.', (
3858 ('/%(trunk)s/indirect/subdirectory/c.txt', 'A'),
3860 conv
.logs
[9].check('This commit was manufactured', (
3861 ('/%(tags)s/TAG (from /%(trunk)s:8)', 'A'),
3863 conv
.logs
[10].check('This commit was manufactured', (
3864 ('/%(branches)s/BRANCH (from /%(trunk)s:8)', 'A'),
3868 @Cvs2SvnTestFunction
3869 def exclude_symbol_default():
3870 "test 'exclude' symbol default"
3872 conv
= ensure_conversion(
3873 'symbol-mess', args
=['--symbol-default=exclude'])
3874 if conv
.path_exists('tags', 'MOSTLY_BRANCH') \
3875 or conv
.path_exists('branches', 'MOSTLY_BRANCH'):
3877 if conv
.path_exists('tags', 'MOSTLY_TAG') \
3878 or conv
.path_exists('branches', 'MOSTLY_TAG'):
3882 @Cvs2SvnTestFunction
3883 def add_on_branch2():
3884 "another add-on-branch test case"
3886 conv
= ensure_conversion('add-on-branch2')
3887 if len(conv
.logs
) != 2:
3889 conv
.logs
[2].check('add file on branch', (
3890 ('/%(branches)s/BRANCH', 'A'),
3891 ('/%(branches)s/BRANCH/file1', 'A'),
3895 @Cvs2SvnTestFunction
3896 def branch_from_vendor_branch():
3897 "branch from vendor branch"
3900 'branch-from-vendor-branch',
3901 symbol_hints_file
='branch-from-vendor-branch-symbol-hints.txt',
3905 @Cvs2SvnTestFunction
3906 def strange_default_branch():
3907 "default branch too deep in the hierarchy"
3910 'strange-default-branch',
3912 r
'ERROR\: The default branch 1\.2\.4\.3\.2\.1\.2 '
3913 r
'in file .* is not a top-level branch'
3918 @Cvs2SvnTestFunction
3920 "graft onto preferred parent that was itself moved"
3922 conv
= ensure_conversion(
3925 conv
.logs
[2].check('first', (
3926 ('/%(trunk)s/file1', 'A'),
3927 ('/%(trunk)s/file2', 'A'),
3929 conv
.logs
[3].check('This commit was manufactured', (
3930 ('/%(branches)s/b2 (from /%(trunk)s:2)', 'A'),
3932 conv
.logs
[4].check('second', (
3933 ('/%(branches)s/b2/file1', 'M'),
3935 conv
.logs
[5].check('This commit was manufactured', (
3936 ('/%(branches)s/b1 (from /%(branches)s/b2:4)', 'A'),
3939 # b2 and b1 are equally good parents for b3, so accept either one.
3940 # (Currently, cvs2svn chooses b1 as the preferred parent because it
3941 # comes earlier than b2 in alphabetical order.)
3943 conv
.logs
[6].check('This commit was manufactured', (
3944 ('/%(branches)s/b3 (from /%(branches)s/b1:5)', 'A'),
3947 conv
.logs
[6].check('This commit was manufactured', (
3948 ('/%(branches)s/b3 (from /%(branches)s/b2:4)', 'A'),
3952 @Cvs2SvnTestFunction
3953 def log_message_eols():
3954 "nonstandard EOLs in log messages"
3956 conv
= ensure_conversion(
3959 conv
.logs
[2].check('The CRLF at the end of this line\nshould', (
3960 ('/%(trunk)s/lottalogs', 'A'),
3962 conv
.logs
[3].check('The CR at the end of this line\nshould', (
3963 ('/%(trunk)s/lottalogs', 'M'),
3967 @Cvs2SvnTestFunction
3968 def missing_vendor_branch():
3969 "default branch not present in RCS file"
3971 conv
= ensure_conversion(
3972 'missing-vendor-branch',
3974 if not conv
.output_found(
3975 r
'.*vendor branch \'1\
.1\
.1\' is not present
in file and will be ignored
'
3980 @Cvs2SvnTestFunction
3982 "newphrases in RCS files"
3989 ########################################################################
3992 # list all tests here, starting with None:
4004 PruneWithCare(variant=1, trunk='a
', branches='b
', tags='c
'),
4006 PruneWithCare(variant=2, trunk='a
/1', branches='b
/1', tags='c
/1'),
4007 PruneWithCare(variant=3, trunk='a
/1', branches='a
/2', tags='a
/3'),
4008 interleaved_commits,
4011 SimpleTags(variant=1, trunk='a
', branches='b
', tags='c
'),
4012 SimpleTags(variant=2, trunk='a
/1', branches='b
/1', tags='c
/1'),
4013 SimpleTags(variant=3, trunk='a
/1', branches='a
/2', tags='a
/3'),
4014 simple_branch_commits,
4017 mixed_time_branch_with_added_file,
4023 PhoenixBranch(variant=1, trunk='a
/1', branches='b
/1', tags='c
/1'),
4028 NoTrunkPrune(variant=1, trunk='a
', branches='b
', tags='c
'),
4029 NoTrunkPrune(variant=2, trunk='a
/1', branches='b
/1', tags='c
/1'),
4030 NoTrunkPrune(variant=3, trunk='a
/1', branches='a
/2', tags='a
/3'),
4034 TaggedBranchAndTrunk(),
4035 TaggedBranchAndTrunk(variant=1, trunk='a
/1', branches='a
/2', tags='a
/3'),
4039 BranchDeleteFirst(),
4040 BranchDeleteFirst(variant=1, trunk='a
/1', branches='a
/2', tags='a
/3'),
4043 warning_expected=1),
4046 variant='encoding
', args=['--encoding
=utf_8
']),
4049 variant='fallback
-encoding
', args=['--fallback
-encoding
=utf_8
']),
4051 warning_expected=1),
4054 variant='encoding
', args=['--encoding
=utf_8
']),
4057 variant='fallback
-encoding
', args=['--fallback
-encoding
=utf_8
']),
4058 vendor_branch_sameness,
4060 vendor_branch_trunk_only,
4062 default_branches_trunk_only,
4063 default_branch_and_1_2,
4064 compose_tag_three_sources,
4067 PeerPathPruning(variant=1, trunk='a
/1', branches='a
/2', tags='a
/3'),
4069 EmptyTrunk(variant=1, trunk='a
', branches='b
', tags='c
'),
4071 EmptyTrunk(variant=2, trunk='a
/1', branches='a
/2', tags='a
/3'),
4072 no_spurious_svn_commits,
4073 invalid_closings_on_trunk,
4076 branch_from_default_branch,
4078 retain_file_in_attic_too,
4079 symbolic_name_filling_guide,
4090 questionable_branch_names,
4091 questionable_tag_names,
4093 revision_reorder_bug,
4095 vendor_branch_delete_add,
4096 resync_pass2_pull_forward,
4100 resync_pass2_push_backward,
4104 nested_ttb_directories,
4105 auto_props_ignore_case,
4106 ctrl_char_in_filename,
4107 commit_dependencies,
4110 multiply_defined_symbols,
4111 multiply_defined_symbols_renamed,
4112 multiply_defined_symbols_ignored,
4113 repeatedly_defined_symbols,
4115 double_branch_delete,
4117 overlook_symbol_mismatches,
4121 unblock_blocked_excludes,
4122 regexp_force_symbols,
4123 heuristic_symbol_default,
4124 branch_symbol_default,
4131 parent_hints_invalid,
4132 parent_hints_wildcards,
4141 tag_with_no_revision,
4145 tagging_after_delete,
4148 file_directory_conflict,
4149 attic_directory_conflict,
4151 internal_co_exclude,
4152 internal_co_trunk_only,
4153 internal_co_keywords,
4155 requires_internal_co,
4160 preferred_parent_cycle,
4161 branch_from_empty_dir,
4163 branch_from_deleted_1_1,
4171 invalid_symbol_ignore,
4172 invalid_symbol_ignore2,
4175 EOLVariants('CRLF
'),
4176 EOLVariants('native
'),
4178 mirror_keyerror_test,
4181 mirror_keyerror2_test,
4182 mirror_keyerror3_test,
4183 add_cvsignore_to_branch_test,
4185 transform_unlabeled_branch_name,
4186 ignore_unlabeled_branch,
4187 exclude_unlabeled_branch,
4188 unlabeled_branch_name_collision,
4189 collision_with_unlabeled_branch_name,
4193 include_empty_directories,
4194 include_empty_directories_no_prune,
4195 exclude_symbol_default,
4197 branch_from_vendor_branch,
4198 strange_default_branch,
4201 missing_vendor_branch,
4206 if __name__ == '__main__
':
4208 # Configure the environment for reproducable output from svn, etc.
4209 os.environ["LC_ALL"] = "C"
4211 # Unfortunately, there is no way under Windows to make Subversion
4212 # think that the local time zone is UTC, so we just work in the
4215 # The Subversion test suite code assumes it's being invoked
from
4216 # within a working copy of the Subversion sources, and tries to use
4217 # the binaries in that tree. Since the cvs2svn tree never contains
4218 # a Subversion build, we just use the system's installed binaries.
4219 svntest
.main
.svn_binary
= svn_binary
4220 svntest
.main
.svnlook_binary
= svnlook_binary
4221 svntest
.main
.svnadmin_binary
= svnadmin_binary
4222 svntest
.main
.svnversion_binary
= svnversion_binary
4224 svntest
.main
.run_tests(test_list
)