diff options
author | Qijiang Fan <fqj1994@gmail.com> | 2012-06-08 17:42:38 +0800 |
---|---|---|
committer | Qijiang Fan <fqj1994@gmail.com> | 2012-06-08 17:42:38 +0800 |
commit | 41ab46d551a1287a4dfb60d04e132af468923d95 (patch) | |
tree | e3523593e894b9ae7dffb9e57856b5909096737c | |
parent | 4d96cae645ca33d56b4a4961f2b59bef82773b58 (diff) |
Upstream version 1.4
40 files changed, 710 insertions, 425 deletions
diff --git a/.hg_archival.txt b/.hg_archival.txt index 916b999..bfa4098 100644 --- a/.hg_archival.txt +++ b/.hg_archival.txt @@ -1,4 +1,4 @@ repo: f2636cfed11500fdc47d1e3822d8e4a2bd636bf7 -node: 0cbf9fd89672e73165e1bb4db1ec8f7f65b95c94 +node: 07234759a3f750029ccaa001837d42fa12dd33ee branch: default -tag: 1.3 +tag: 1.4 @@ -5,3 +5,4 @@ 093ae2915b452539b44390ee4ea14987484e1eee 1.1.2 708234ad6c97fb52417e0b46a86c8373e25123a5 1.2 4bbc6bf947f56a92e95a04a27b94a9f72d5482d7 1.2.1 +0cbf9fd89672e73165e1bb4db1ec8f7f65b95c94 1.3 diff --git a/MANIFEST.in b/MANIFEST.in index 9f0c6c7..b79e61d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include Makefile *.rst +include Makefile hgsubversion/help/*.rst recursive-include tests *.py *.sh *.svndump *.txt @@ -17,11 +17,11 @@ check: check-demandimport check-subvertpy check-swig check-demandimport: # verify that hgsubversion loads properly without bindings, but fails # when actually used - ! LC_ALL=C HGSUBVERSION_BINDINGS=none \ + ! LC_ALL=C HGSUBVERSION_BINDINGS=none HGRCPATH=/dev/null \ hg --config extensions.hgsubversion=./hgsubversion \ version 2>&1 \ | egrep '(^abort:|failed to import extension)' - LC_ALL=C HGSUBVERSION_BINDINGS=none \ + LC_ALL=C HGSUBVERSION_BINDINGS=none HGRCPATH=/dev/null \ hg --config extensions.hgsubversion=./hgsubversion \ version --svn 2>&1 \ | egrep '(^abort:|failed to import extension)' diff --git a/hgsubversion/__init__.py b/hgsubversion/__init__.py index cc336c8..f87b52f 100644 --- a/hgsubversion/__init__.py +++ b/hgsubversion/__init__.py @@ -177,6 +177,8 @@ def extsetup(): def reposetup(ui, repo): if repo.local(): svnrepo.generate_repo_class(ui, repo) + for tunnel in ui.configlist('hgsubversion', 'tunnels'): + hg.schemes['svn+' + tunnel] = svnrepo _old_local = hg.schemes['file'] def _lookup(url): diff --git a/hgsubversion/editor.py b/hgsubversion/editor.py index 54c04ac..286a6e3 100644 --- a/hgsubversion/editor.py +++ b/hgsubversion/editor.py @@ -10,6 +10,18 @@ import svnwrap import util import svnexternals +class NeverClosingStringIO(object): + def __init__(self): + self._fp = cStringIO.StringIO() + + def __getattr__(self, name): + return getattr(self._fp, name) + + def close(self): + # svn 1.7 apply_delta driver now calls close() on passed file + # object which prevent us from calling getvalue() afterwards. + pass + class RevisionData(object): __slots__ = [ @@ -331,11 +343,11 @@ class HgEditor(svnwrap.Editor): if self.current.file in self.current.missing: return lambda x: None base = self.current.files[self.current.file] - target = cStringIO.StringIO() + target = NeverClosingStringIO() self.stream = target handler = svnwrap.apply_txdelta(base, target) - if not callable(handler): #pragma: no cover + if not callable(handler): # pragma: no cover raise hgutil.Abort('Error in Subversion bindings: ' 'cannot call handler!') def txdelt_window(window): @@ -346,12 +358,12 @@ class HgEditor(svnwrap.Editor): # window being None means commit this file if not window: self.current.files[self.current.file] = target.getvalue() - except svnwrap.SubversionException, e: #pragma: no cover + except svnwrap.SubversionException, e: # pragma: no cover if e.args[1] == svnwrap.ERR_INCOMPLETE_DATA: self.current.missing.add(self.current.file) - else: #pragma: no cover + else: # pragma: no cover raise hgutil.Abort(*e.args) - except: #pragma: no cover + except: # pragma: no cover print len(base), self.current.file self._exception_info = sys.exc_info() raise diff --git a/hgsubversion/help/subversion.rst b/hgsubversion/help/subversion.rst index a5bf85e..0209c1c 100644 --- a/hgsubversion/help/subversion.rst +++ b/hgsubversion/help/subversion.rst @@ -306,6 +306,17 @@ settings: Set the username or password for accessing Subversion repositories. + ``hgsubversion.password_stores`` + + List of methods to use for storing passwords (similar to the option of the + same name in the subversion configuration files). Default is + ``gnome_keyring,keychain,kwallet,windows``. Password stores can be disabled + completely by setting this to an empty value. + + .. NOTE:: + + Password stores are only supported with the SWIG bindings. + ``hgsubversion.stupid`` Setting this boolean option to true will force using a slower method for pulling revisions from Subversion. This method is compatible with servers diff --git a/hgsubversion/maps.py b/hgsubversion/maps.py index 93a3cbb..6bfca52 100644 --- a/hgsubversion/maps.py +++ b/hgsubversion/maps.py @@ -21,7 +21,7 @@ class AuthorMap(dict): The ui argument is used to print diagnostic messages. The path argument is the location of the backing store, - typically .hg/authormap. + typically .hg/svn/authors. ''' self.ui = ui self.path = path @@ -252,41 +252,54 @@ class RevMap(dict): class FileMap(object): - def __init__(self, repo): - self.ui = repo.ui + VERSION = 1 + + def __init__(self, ui, path): + '''Initialise a new FileMap. + + The ui argument is used to print diagnostic messages. + + The path argument is the location of the backing store, + typically .hg/svn/filemap. + ''' + self.ui = ui + self.path = path self.include = {} self.exclude = {} - filemap = repo.ui.config('hgsubversion', 'filemap') - if filemap and os.path.exists(filemap): - self.load(filemap) + if os.path.isfile(self.path): + self._load() + else: + self._write() def _rpairs(self, name): - yield '.', name e = len(name) while e != -1: yield name[:e], name[e+1:] e = name.rfind('/', 0, e) + yield '.', name def check(self, m, path): m = getattr(self, m) for pre, _suf in self._rpairs(path): - if pre not in m: - continue - return m[pre] - return None + if pre in m: + return m[pre] + return -1 def __contains__(self, path): - if len(self.include) and len(path): + if not len(path): + return True + if len(self.include): inc = self.check('include', path) + elif not len(self.exclude): + return True else: - inc = path - if len(self.exclude) and len(path): + inc = 0 + if len(self.exclude): exc = self.check('exclude', path) else: - exc = None - if inc is None or exc is not None: - return False - return True + exc = -1 + # respect rule order: newer rules override older + return inc > exc # Needed so empty filemaps are false def __len__(self): @@ -300,11 +313,20 @@ class FileMap(object): return bits = m.strip('e'), path self.ui.debug('%sing %s\n' % bits) - mapping[path] = path + # respect rule order + mapping[path] = len(self) + if fn != self.path: + f = open(self.path, 'a') + f.write(m + ' ' + path + '\n') + f.close() def load(self, fn): self.ui.note('reading file map from %s\n' % fn) f = open(fn, 'r') + self.load_fd(f, fn) + f.close() + + def load_fd(self, f, fn): for line in f: if line.strip() == '' or line.strip()[0] == '#': continue @@ -319,6 +341,20 @@ class FileMap(object): except IndexError: msg = 'ignoring bad line in filemap %s: %s\n' self.ui.warn(msg % (fn, line.rstrip())) + + def _load(self): + self.ui.note('reading in-repo file map from %s\n' % self.path) + f = open(self.path) + ver = int(f.readline()) + if ver != self.VERSION: + print 'filemap too new -- please upgrade' + raise NotImplementedError + self.load_fd(f, self.path) + f.close() + + def _write(self): + f = open(self.path, 'w') + f.write('%s\n' % self.VERSION) f.close() class BranchMap(dict): diff --git a/hgsubversion/replay.py b/hgsubversion/replay.py index 10d3b48..9a59002 100644 --- a/hgsubversion/replay.py +++ b/hgsubversion/replay.py @@ -71,7 +71,7 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun): updateexternals(ui, meta, current) - if current.exception is not None: #pragma: no cover + if current.exception is not None: # pragma: no cover traceback.print_exception(*current.exception) raise ReplayException() if current.missing: @@ -194,7 +194,7 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun): raise IOError(errno.ENOENT, 'deleting all files') # True here meant nuke all files, shouldn't happen with branch closing - if current.emptybranches[branch]: #pragma: no cover + if current.emptybranches[branch]: # pragma: no cover raise hgutil.Abort('Empty commit to an open branch attempted. ' 'Please report this issue.') diff --git a/hgsubversion/stupid.py b/hgsubversion/stupid.py index 159396e..cf39869 100644 --- a/hgsubversion/stupid.py +++ b/hgsubversion/stupid.py @@ -12,41 +12,114 @@ import svnwrap import svnexternals import util +# Here is a diff mixing content and property changes in svn >= 1.7 +# +# Index: a +# =================================================================== +# --- a (revision 12) +# +++ a (working copy) +# @@ -1,2 +1,3 @@ +# a +# a +# +a +# +# Property changes on: a +# ___________________________________________________________________ +# Added: svn:executable +# ## -0,0 +1 ## +# +* + +class ParseError(Exception): + pass -binary_file_re = re.compile(r'''Index: ([^\n]*) +index_header = r'''Index: ([^\n]*) =* -Cannot display: file marked as a binary type.''') - -property_exec_set_re = re.compile(r'''Property changes on: ([^\n]*) -_* -(?:Added|Name): svn:executable - \+''') - -property_exec_removed_re = re.compile(r'''Property changes on: ([^\n]*) -_* -(?:Deleted|Name): svn:executable - -''') - -empty_file_patch_wont_make_re = re.compile(r'''Index: ([^\n]*)\n=*\n(?=Index:)''') - -any_file_re = re.compile(r'''^Index: ([^\n]*)\n=*\n''', re.MULTILINE) - -property_special_set_re = re.compile(r'''Property changes on: ([^\n]*) -_* -(?:Added|Name): svn:special - \+''') +''' -property_special_removed_re = re.compile(r'''Property changes on: ([^\n]*) +property_header = r'''Property changes on: ([^\n]*) _* -(?:Deleted|Name): svn:special - \-''') +''' + +headers_re = re.compile('(?:' + '|'.join([ + index_header, + property_header, + ]) + ')') + +property_special_added = r'''(?:Added|Name): (svn:special) +(?: \+|## -0,0 \+1 ## +\+)''' + +property_special_deleted = r'''(?:Deleted|Name): (svn:special) +(?: \-|## -1 \+0,0 ## +\-)''' + +property_exec_added = r'''(?:Added|Name): (svn:executable) +(?: \+|## -0,0 \+1 ## +\+)''' + +property_exec_deleted = r'''(?:Deleted|Name): (svn:executable) +(?: \-|## -1 \+0,0 ## +\-)''' + +properties_re = re.compile('(?:' + '|'.join([ + property_special_added, + property_special_deleted, + property_exec_added, + property_exec_deleted, + ]) + ')') + +class filediff: + def __init__(self, name): + self.name = name + self.diff = None + self.binary = False + self.executable = None + self.symlink = None + self.hasprops = False + + def isempty(self): + return (not self.diff and not self.binary and not self.hasprops) + + def maybedir(self): + return (not self.diff and not self.binary and self.hasprops + and self.symlink is None and self.executable is None) + +def parsediff(diff): + changes = {} + headers = headers_re.split(diff)[1:] + if (len(headers) % 3) != 0: + # headers should be a sequence of (index file, property file, data) + raise ParseError('unexpected diff format') + files = [] + for i in xrange(len(headers)/3): + iname, pname, data = headers[3*i:3*i+3] + fname = iname or pname + if fname not in changes: + changes[fname] = filediff(fname) + files.append(changes[fname]) + f = changes[fname] + if iname is not None: + if data.strip(): + f.binary = data.lstrip().startswith( + 'Cannot display: file marked as a binary type.') + if not f.binary and '@@' in data: + # Non-empty diff + f.diff = data + else: + f.hasprops = True + for m in properties_re.finditer(data): + p = m.group(1, 2, 3, 4) + if p[0] or p[1]: + f.symlink = bool(p[0]) + elif p[2] or p[3]: + f.executable = bool(p[2]) + return files class BadPatchApply(Exception): pass - -def print_your_svn_is_old_message(ui): #pragma: no cover +def print_your_svn_is_old_message(ui): # pragma: no cover ui.status("In light of that, I'll fall back and do diffs, but it won't do " "as good a job. You should really upgrade your server.\n") @@ -215,17 +288,12 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx): if '\0' in d: raise BadPatchApply('binary diffs are not supported') files_data = {} - # we have to pull each binary file by hand as a fulltext, - # which sucks but we've got no choice - binary_files = set(binary_file_re.findall(d)) - touched_files = set(binary_files) - d2 = empty_file_patch_wont_make_re.sub('', d) - d2 = property_exec_set_re.sub('', d2) - d2 = property_exec_removed_re.sub('', d2) + changed = parsediff(d) # Here we ensure that all files, including the new empty ones # are marked as touched. Content is loaded on demand. - touched_files.update(any_file_re.findall(d)) - if d2.strip() and len(re.findall('\n[-+]', d2.strip())) > 0: + touched_files = set(f.name for f in changed) + d2 = '\n'.join(f.diff for f in changed if f.diff) + if changed: files_data = patchrepo(ui, meta, parentctx, cStringIO.StringIO(d2)) for x in files_data.iterkeys(): ui.note('M %s\n' % x) @@ -233,23 +301,6 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx): ui.status('Not using patch for %s, diff had no hunks.\n' % r.revnum) - exec_files = {} - for m in property_exec_removed_re.findall(d): - exec_files[m] = False - for m in property_exec_set_re.findall(d): - exec_files[m] = True - touched_files.update(exec_files) - link_files = {} - for m in property_special_set_re.findall(d): - # TODO(augie) when a symlink is removed, patching will fail. - # We're seeing that above - there's gotta be a better - # workaround than just bailing like that. - assert m in files_data - link_files[m] = True - for m in property_special_removed_re.findall(d): - assert m in files_data - link_files[m] = False - unknown_files = set() for p in r.paths: action = r.paths[p].action @@ -273,9 +324,35 @@ def diff_branchrev(ui, svn, meta, branch, branchpath, r, parentctx): touched_files.update(files_data) touched_files.update(unknown_files) + # As of svn 1.7, diff may contain a lot of property changes for + # directories. We do not what to include these in our touched + # files list so we try to filter them while minimizing the number + # of svn API calls. + property_files = set(f.name for f in changed if f.maybedir()) + property_files.discard('.') + touched_files.discard('.') + branchprefix = (branchpath and branchpath + '/') or branchpath + for f in list(property_files): + if f in parentctx: + continue + # We can be smarter here by checking if f is a subcomponent + # of a know path in parentctx or touched_files. KISS for now. + kind = svn.checkpath(branchprefix + f, r.revnum) + if kind == 'd': + touched_files.discard(f) + copies = getcopies(svn, meta, branch, branchpath, r, touched_files, parentctx) + # We note binary files because svn's diff format doesn't describe + # what changed, only that a change occurred. This means we'll have + # to pull them as fulltexts from the server outside the diff + # apply. + binary_files = set(f.name for f in changed if f.binary) + exec_files = dict((f.name, f.executable) for f in changed + if f.executable is not None) + link_files = dict((f.name, f.symlink) for f in changed + if f.symlink is not None) def filectxfn(repo, memctx, path): if path in files_data and files_data[path] is None: raise IOError(errno.ENOENT, '%s is deleted' % path) @@ -638,7 +715,13 @@ def convert_rev(ui, meta, svn, r, tbdelta, firstrun): deleted_branches[b] = parentctx.node() continue - incremental = (meta.revmap.oldest > 0) + # The nullrev check might not be necessary in theory but svn < + # 1.7 failed to diff branch creation so the diff_branchrev() + # path does not support this case with svn >= 1.7. We can fix + # it, or we can force the existing fetch_branchrev() path. Do + # the latter for now. + incremental = (meta.revmap.oldest > 0 and + parentctx.rev() != node.nullrev) if incremental: try: diff --git a/hgsubversion/svncommands.py b/hgsubversion/svncommands.py index 0ae1262..eec9e51 100644 --- a/hgsubversion/svncommands.py +++ b/hgsubversion/svncommands.py @@ -43,13 +43,16 @@ def verify(ui, repo, args=None, **opts): srev, branch, branchpath = meta.get_source_rev(ctx=ctx) branchpath = branchpath[len(svn.subdir.lstrip('/')):] + branchurl = ('%s/%s' % (url, branchpath)).strip('/') - ui.write('verifying %s against r%i\n' % (ctx, srev)) + ui.write('verifying %s against %s@%i\n' % (ctx, branchurl, srev)) svnfiles = set() result = 0 - for fn, type in svn.list_files(branchpath, srev): + svndata = svn.list_files(branchpath, srev) + for i, (fn, type) in enumerate(svndata): + util.progress(ui, 'verify', i) if type != 'f': continue svnfiles.add(fn) @@ -57,7 +60,11 @@ def verify(ui, repo, args=None, **opts): if branchpath: fp = branchpath + '/' + fn data, mode = svn.get_file(posixpath.normpath(fp), srev) - fctx = ctx[fn] + try: + fctx = ctx[fn] + except error.LookupError: + result = 1 + continue dmatch = fctx.data() == data mmatch = fctx.flags() == mode if not (dmatch and mmatch): @@ -66,8 +73,16 @@ def verify(ui, repo, args=None, **opts): hgfiles = set(ctx) - util.ignoredfiles if hgfiles != svnfiles: - missing = set(hgfiles).symmetric_difference(svnfiles) - ui.write('missing files: %s\n' % (', '.join(missing))) + unexpected = hgfiles - svnfiles + if unexpected: + ui.write('unexpected files:\n') + for f in sorted(unexpected): + ui.write(' %s\n' % f) + missing = svnfiles - hgfiles + if missing: + ui.write('missing files:\n') + for f in sorted(missing): + ui.write(' %s\n' % f) result = 1 return result diff --git a/hgsubversion/svnexternals.py b/hgsubversion/svnexternals.py index 38cc40b..0169265 100644 --- a/hgsubversion/svnexternals.py +++ b/hgsubversion/svnexternals.py @@ -95,9 +95,9 @@ def diff(ext1, ext2): class BadDefinition(Exception): pass -re_defold = re.compile(r'^\s*(.*?)\s+(?:-r\s*(\d+|\{REV\})\s+)?([a-zA-Z]+://.*)\s*$') -re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z]+://|\^/).*)\s+(\S+)\s*$') -re_scheme = re.compile(r'^[a-zA-Z]+://') +re_defold = re.compile(r'^\s*(.*?)\s+(?:-r\s*(\d+|\{REV\})\s+)?([a-zA-Z+]+://.*)\s*$') +re_defnew = re.compile(r'^\s*(?:-r\s*(\d+|\{REV\})\s+)?((?:[a-zA-Z+]+://|\^/).*)\s+(\S+)\s*$') +re_scheme = re.compile(r'^[a-zA-Z+]+://') def parsedefinition(line): """Parse an external definition line, return a tuple (path, rev, source) @@ -443,3 +443,9 @@ if subrepo: if self._state[1] == 'HEAD': rev = 'HEAD' return rev + + def basestate(self): + # basestate() was introduced by bcb973abcc0b in 2.2 + if self._state[1] == 'HEAD': + return 'HEAD' + return super(svnsubrepo, self).basestate() diff --git a/hgsubversion/svnmeta.py b/hgsubversion/svnmeta.py index 68ab4a7..adc8f58 100644 --- a/hgsubversion/svnmeta.py +++ b/hgsubversion/svnmeta.py @@ -13,21 +13,20 @@ import maps import editor -def pickle_atomic(data, file_path, dir=None): +def pickle_atomic(data, file_path): """pickle some data to a path atomically. This is present because I kept corrupting my revmap by managing to hit ^C during the pickle of that file. """ - try: - f, path = tempfile.mkstemp(prefix='pickling', dir=dir) - f = os.fdopen(f, 'w') - pickle.dump(data, f) - f.close() - except: #pragma: no cover - raise + f = hgutil.atomictempfile(file_path, 'w+b', 0644) + pickle.dump(data, f) + # Older versions of hg have .rename() instead of .close on + # atomictempfile. + if getattr(hgutil.atomictempfile, 'rename', False): + f.rename() else: - hgutil.rename(path, file_path) + f.close() class SVNMeta(object): @@ -55,6 +54,7 @@ class SVNMeta(object): 'usebranchnames', True) branchmap = self.ui.config('hgsubversion', 'branchmap') tagmap = self.ui.config('hgsubversion', 'tagmap') + filemap = self.ui.config('hgsubversion', 'filemap') self.branches = {} if os.path.exists(self.branch_info_file): @@ -76,8 +76,7 @@ class SVNMeta(object): self.repo.ui.setconfig('hgsubversion', 'layout', self._layout) else: self._layout = None - pickle_atomic(self.tag_locations, self.tag_locations_file, - self.meta_data_dir) + pickle_atomic(self.tag_locations, self.tag_locations_file) # ensure nested paths are handled properly self.tag_locations.sort() self.tag_locations.reverse() @@ -94,8 +93,11 @@ class SVNMeta(object): if tagmap: self.tagmap.load(tagmap) + self.filemap = maps.FileMap(self.ui, self.filemap_file) + if filemap: + self.filemap.load(filemap) + self.lastdate = '1970-01-01 00:00:00 -0000' - self.filemap = maps.FileMap(repo) self.addedtags = {} self.deletedtags = {} @@ -192,6 +194,10 @@ class SVNMeta(object): return os.path.join(self.meta_data_dir, 'authors') @property + def filemap_file(self): + return os.path.join(self.meta_data_dir, 'filemap') + + @property def branchmapfile(self): return os.path.join(self.meta_data_dir, 'branchmap') @@ -217,7 +223,7 @@ class SVNMeta(object): '''Save the Subversion metadata. This should really be called after every revision is created. ''' - pickle_atomic(self.branches, self.branch_info_file, self.meta_data_dir) + pickle_atomic(self.branches, self.branch_info_file) def localname(self, path): """Compute the local name for a branch located at path. diff --git a/hgsubversion/svnrepo.py b/hgsubversion/svnrepo.py index 6bb3cd9..c471fa5 100644 --- a/hgsubversion/svnrepo.py +++ b/hgsubversion/svnrepo.py @@ -21,6 +21,13 @@ from mercurial import util as hgutil from mercurial import httprepo import mercurial.repo +try: + from mercurial import phases + phases.public # defeat demand import +except ImportError: + phases = None + +import re import util import wrappers import svnwrap @@ -76,7 +83,11 @@ def generate_repo_class(ui, repo): class svnlocalrepo(superclass): def svn_commitctx(self, ctx): """Commits a ctx, but defeats manifest recycling introduced in hg 1.9.""" - return self.commitctx(ctxctx(ctx)) + hash = self.commitctx(ctxctx(ctx)) + if phases is not None and getattr(self, 'pushkey', False): + # set phase to be public + self.pushkey('phases', self[hash].hex(), str(phases.draft), str(phases.public)) + return hash # TODO use newbranch to allow branch creation in Subversion? @remotesvn @@ -107,6 +118,14 @@ class svnremoterepo(mercurial.repo.repository): raise hgutil.Abort('no Subversion URL specified') self.path = path self.capabilities = set(['lookup', 'subversion']) + pws = self.ui.config('hgsubversion', 'password_stores', None) + if pws is not None: + # Split pws at comas and strip neighbouring whitespace (whitespace + # at the beginning and end of pws has already been removed by the + # config parser). + self.password_stores = re.split(r'\s*,\s*', pws) + else: + self.password_stores = None @propertycache def svnauth(self): @@ -127,7 +146,7 @@ class svnremoterepo(mercurial.repo.repository): @propertycache def svn(self): try: - return svnwrap.SubversionRepo(*self.svnauth) + return svnwrap.SubversionRepo(*self.svnauth, password_stores=self.password_stores) except svnwrap.SubversionConnectionException, e: self.ui.traceback() raise hgutil.Abort(e) diff --git a/hgsubversion/svnwrap/subvertpy_wrapper.py b/hgsubversion/svnwrap/subvertpy_wrapper.py index 0023d5a..d85ee80 100644 --- a/hgsubversion/svnwrap/subvertpy_wrapper.py +++ b/hgsubversion/svnwrap/subvertpy_wrapper.py @@ -31,7 +31,7 @@ except ImportError: def _versionstr(v): return '.'.join(str(d) for d in v) -if subvertpy.__version__ < subvertpy_required: #pragma: no cover +if subvertpy.__version__ < subvertpy_required: # pragma: no cover raise ImportError('Subvertpy %s or later required, ' 'but %s found' % (_versionstr(subvertpy_required), @@ -165,8 +165,11 @@ class SubversionRepo(object): This wrapper uses Subvertpy, an alternate set of bindings for Subversion that's more pythonic and sucks less. See earlier in this file for version requirements. + + Note that password stores do not work, the parameter is only here + to ensure that the API is the same as for the SWIG wrapper. """ - def __init__(self, url='', username='', password='', head=None): + def __init__(self, url='', username='', password='', head=None, password_stores=None): parsed = common.parse_url(url, username, password) # --username and --password override URL credentials self.username = parsed[0] @@ -338,7 +341,7 @@ class SubversionRepo(object): commit_info.append(args) commit_info = [] revprops = { properties.PROP_REVISION_LOG: message } - #revprops.update(props) + # revprops.update(props) commiteditor = self.remote.get_commit_editor(revprops, commitcb) paths = set(paths) @@ -417,7 +420,7 @@ class SubversionRepo(object): try: self.remote.replay(revision, oldestrev, AbstractEditor(editor)) - except (SubversionException, NotImplementedError), e: #pragma: no cover + except (SubversionException, NotImplementedError), e: # pragma: no cover # can I depend on this number being constant? if (isinstance(e, NotImplementedError) or e.args[1] == subvertpy.ERR_RA_NOT_IMPLEMENTED or diff --git a/hgsubversion/svnwrap/svn_swig_wrapper.py b/hgsubversion/svnwrap/svn_swig_wrapper.py index 50ec898..d232f03 100644 --- a/hgsubversion/svnwrap/svn_swig_wrapper.py +++ b/hgsubversion/svnwrap/svn_swig_wrapper.py @@ -29,7 +29,7 @@ except ImportError: raise ImportError('Subversion %d.%d.%d or later required, ' 'but no bindings were found' % required_bindings) -if current_bindings < required_bindings: #pragma: no cover +if current_bindings < required_bindings: # pragma: no cover raise ImportError('Subversion %d.%d.%d or later required, ' 'but bindings for %d.%d.%d found' % (required_bindings + current_bindings)) @@ -60,7 +60,7 @@ def optrev(revnum): svn_config = core.svn_config_get_config(None) class RaCallbacks(ra.Callbacks): @staticmethod - def open_tmp_file(pool): #pragma: no cover + def open_tmp_file(pool): # pragma: no cover (fd, fn) = tempfile.mkstemp() os.close(fd) return fn @@ -82,13 +82,13 @@ def ieditor(fn): def fun(self, *args, **kwargs): try: return fn(self, *args, **kwargs) - except: #pragma: no cover + except: # pragma: no cover if self.current.exception is not None: self.current.exception = sys.exc_info() raise return fun -def user_pass_prompt(realm, default_username, ms, pool): #pragma: no cover +def user_pass_prompt(realm, default_username, ms, pool): # pragma: no cover # FIXME: should use getpass() and username() from mercurial.ui creds = core.svn_auth_cred_simple_t() creds.may_save = ms @@ -103,7 +103,7 @@ def user_pass_prompt(realm, default_username, ms, pool): #pragma: no cover creds.password = getpass.getpass('Password for %s: ' % creds.username) return creds -def _create_auth_baton(pool): +def _create_auth_baton(pool, password_stores): """Create a Subversion authentication baton. """ # Give the client context baton a suite of authentication # providers.h @@ -124,7 +124,9 @@ def _create_auth_baton(pool): None) if getprovider: # Available in svn >= 1.6 - for name in ('gnome_keyring', 'keychain', 'kwallet', 'windows'): + if password_stores is None: + password_stores = ('gnome_keyring', 'keychain', 'kwallet', 'windows') + for name in password_stores: for type in ('simple', 'ssl_client_cert_pw', 'ssl_server_trust'): p = getprovider(name, type, pool) if p: @@ -158,14 +160,14 @@ class SubversionRepo(object): It uses the SWIG Python bindings, see above for requirements. """ - def __init__(self, url='', username='', password='', head=None): + def __init__(self, url='', username='', password='', head=None, password_stores=None): parsed = common.parse_url(url, username, password) # --username and --password override URL credentials self.username = parsed[0] self.password = parsed[1] self.svn_url = parsed[2] self.auth_baton_pool = core.Pool() - self.auth_baton = _create_auth_baton(self.auth_baton_pool) + self.auth_baton = _create_auth_baton(self.auth_baton_pool, password_stores) # self.init_ra_and_client() assumes that a pool already exists self.pool = core.Pool() @@ -233,7 +235,7 @@ class SubversionRepo(object): holder = [] ra.get_log(self.ra, [''], self.HEAD, 1, - 1, #limit of how many log messages to load + 1, # limit of how many log messages to load True, # don't need to know changed paths True, # stop on copies lambda paths, revnum, author, date, message, pool: @@ -295,7 +297,7 @@ class SubversionRepo(object): paths, start + 1, stop, - chunk_size, #limit of how many log messages to load + chunk_size, # limit of how many log messages to load True, # don't need to know changed paths True, # stop on copies callback, @@ -403,7 +405,7 @@ class SubversionRepo(object): try: ra.replay(self.ra, revision, oldest_rev_i_have, True, e_ptr, e_baton, self.pool) - except SubversionException, e: #pragma: no cover + except SubversionException, e: # pragma: no cover # can I depend on this number being constant? if (e.apr_err == core.SVN_ERR_RA_NOT_IMPLEMENTED or e.apr_err == core.SVN_ERR_UNSUPPORTED_FEATURE): diff --git a/hgsubversion/wrappers.py b/hgsubversion/wrappers.py index 3c16b31..e255569 100644 --- a/hgsubversion/wrappers.py +++ b/hgsubversion/wrappers.py @@ -1,6 +1,11 @@ from hgext import rebase as hgrebase from mercurial import cmdutil +try: + from mercurial import discovery + discovery.nullid # force demandimport to import the module +except ImportError: + discovery = None from mercurial import patch from mercurial import hg from mercurial import util as hgutil @@ -90,7 +95,14 @@ def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None) meta = repo.svnmeta(svn.uuid, svn.subdir) parent = repo.parents()[0].node() hashes = meta.revmap.hashes() - return util.outgoing_common_and_heads(repo, hashes, parent) + common, heads = util.outgoing_common_and_heads(repo, hashes, parent) + if discovery is not None: + outobj = getattr(discovery, 'outgoing', None) + if outobj is not None: + # Mercurial 2.1 and later + return outobj(repo.changelog, common, heads) + # Mercurial 2.0 and earlier + return common, heads def findoutgoing(repo, dest=None, heads=None, force=False): @@ -98,7 +110,7 @@ def findoutgoing(repo, dest=None, heads=None, force=False): """ assert dest.capable('subversion') # split off #rev; TODO implement --revision/#rev support - #svnurl, revs, checkout = util.parseurl(dest.svnurl, heads) + # svnurl, revs, checkout = util.parseurl(dest.svnurl, heads) svn = dest.svn meta = repo.svnmeta(svn.uuid, svn.subdir) parent = repo.parents()[0].node() @@ -354,11 +366,11 @@ def pull(repo, source, heads=[], force=False): converted = True firstrun = False - except svnwrap.SubversionRepoCanNotReplay, e: #pragma: no cover + except svnwrap.SubversionRepoCanNotReplay, e: # pragma: no cover ui.status('%s\n' % e.message) stupidmod.print_your_svn_is_old_message(ui) have_replay = False - except svnwrap.SubversionException, e: #pragma: no cover + except svnwrap.SubversionException, e: # pragma: no cover if (e.args[1] == svnwrap.ERR_RA_DAV_REQUEST_FAILED and '502' in str(e) and tries < 3): @@ -476,6 +488,8 @@ def clone(orig, ui, source, dest=None, **opts): data['srcrepo'], data['dstrepo'] = orig(ui, *args, **opts) + return data['srcrepo'], data['dstrepo'] + for opt, (section, name) in optionmap.iteritems(): if opt in opts and opts[opt]: ui.setconfig(section, name, str(opts.pop(opt))) diff --git a/tests/comprehensive/test_stupid_pull.py b/tests/comprehensive/test_stupid_pull.py index 466d460..17e8122 100644 --- a/tests/comprehensive/test_stupid_pull.py +++ b/tests/comprehensive/test_stupid_pull.py @@ -19,11 +19,12 @@ from hgsubversion import wrappers def _do_case(self, name, layout): subdir = test_util.subdir.get(name, '') - self._load_fixture_and_fetch(name, subdir=subdir, stupid=False, layout=layout) + repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=False, + layout=layout) assert len(self.repo) > 0, 'Repo had no changes, maybe you need to add a subdir entry in test_util?' wc2_path = self.wc_path + '_stupid' u = ui.ui() - checkout_path = self.repo_path + checkout_path = repo_path if subdir: checkout_path += '/' + subdir u.setconfig('hgsubversion', 'stupid', '1') diff --git a/tests/fixtures/binaryfiles-broken.svndump b/tests/fixtures/binaryfiles-broken.svndump Binary files differnew file mode 100644 index 0000000..08863e0 --- /dev/null +++ b/tests/fixtures/binaryfiles-broken.svndump diff --git a/tests/test_externals.py b/tests/test_externals.py index 2927b11..50dd424 100644 --- a/tests/test_externals.py +++ b/tests/test_externals.py @@ -278,9 +278,7 @@ d3/ext3 = [hgsubversion] d3:^/trunk/common/ext ext3 class TestPushExternals(test_util.TestBase): def test_push_externals(self, stupid=False): - test_util.load_fixture_and_fetch('pushexternals.svndump', - self.repo_path, - self.wc_path) + repo = self._load_fixture_and_fetch('pushexternals.svndump') # Add a new reference on an existing and non-existing directory changes = [ ('.hgsvnexternals', '.hgsvnexternals', @@ -331,10 +329,8 @@ class TestPushExternals(test_util.TestBase): if subrepo is None: return - test_util.load_fixture_and_fetch('pushexternals.svndump', - self.repo_path, - self.wc_path, - externals='subrepos') + repo, repo_path = self.load_and_fetch('pushexternals.svndump', + externals='subrepos') # Add a new reference on an existing and non-existing directory changes = [ ('.hgsub', '.hgsub', """\ @@ -350,16 +346,16 @@ HEAD subdir2/deps/project2 ('subdir1/a', 'subdir1/a', 'a'), ('subdir2/a', 'subdir2/a', 'a'), ] - self.svnco('externals/project2', '2', 'dir/deps/project2') - self.svnco('externals/project1', '2', 'subdir1/deps/project1') - self.svnco('externals/project2', '2', 'subdir2/deps/project2') + self.svnco(repo_path, 'externals/project2', '2', 'dir/deps/project2') + self.svnco(repo_path, 'externals/project1', '2', 'subdir1/deps/project1') + self.svnco(repo_path, 'externals/project2', '2', 'subdir2/deps/project2') self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) # Check .hgsub and .hgsubstate were not pushed self.assertEqual(['dir', 'subdir1', 'subdir1/a', 'subdir2', - 'subdir2/a'], self.svnls('trunk')) + 'subdir2/a'], test_util.svnls(repo_path, 'trunk')) # Remove all references from one directory, add a new one # to the other (test multiline entries) @@ -375,8 +371,8 @@ HEAD subdir1/deps/project2 # This removal used to trigger the parent directory removal ('subdir1/a', None, None), ] - self.svnco('externals/project1', '2', 'subdir1/deps/project1') - self.svnco('externals/project2', '2', 'subdir1/deps/project2') + self.svnco(repo_path, 'externals/project1', '2', 'subdir1/deps/project1') + self.svnco(repo_path, 'externals/project2', '2', 'subdir1/deps/project2') self.commitchanges(changes) self.pushrevisions(stupid) self.assertchanges(changes, self.repo['tip']) diff --git a/tests/test_fetch_branches.py b/tests/test_fetch_branches.py index a9645f0..7a9287c 100644 --- a/tests/test_fetch_branches.py +++ b/tests/test_fetch_branches.py @@ -7,15 +7,9 @@ from mercurial import node from mercurial import util as hgutil class TestFetchBranches(test_util.TestBase): - def _load_fixture_and_fetch(self, fixture_name, stupid, noupdate=True, - subdir=''): - return test_util.load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, stupid=stupid, - noupdate=noupdate, subdir=subdir) - def _load_fixture_and_fetch_with_anchor(self, fixture_name, anchor): - test_util.load_svndump_fixture(self.repo_path, fixture_name) - source = '%s#%s' % (test_util.fileurl(self.repo_path), anchor) + repo_path = self.load_svndump(fixture_name) + source = '%s#%s' % (test_util.fileurl(repo_path), anchor) test_util.hgclone(self.ui(), source, self.wc_path) return hg.repository(self.ui(), self.wc_path) @@ -31,7 +25,8 @@ class TestFetchBranches(test_util.TestBase): return self.branches(repo)[0] def test_rename_branch_parent(self, stupid=False): - repo = self._load_fixture_and_fetch('rename_branch_parent_dir.svndump', stupid) + repo = self._load_fixture_and_fetch('rename_branch_parent_dir.svndump', + stupid=stupid) heads = [repo[n] for n in repo.heads()] heads = dict([(ctx.branch(), ctx) for ctx in heads]) # Let these tests disabled yet as the fix is not obvious @@ -41,7 +36,8 @@ class TestFetchBranches(test_util.TestBase): self.test_rename_branch_parent(stupid=True) def test_unrelatedbranch(self, stupid=False): - repo = self._load_fixture_and_fetch('unrelatedbranch.svndump', stupid) + repo = self._load_fixture_and_fetch('unrelatedbranch.svndump', + stupid=stupid) heads = [repo[n] for n in repo.heads()] heads = dict([(ctx.branch(), ctx) for ctx in heads]) # Let these tests disabled yet as the fix is not obvious @@ -52,7 +48,8 @@ class TestFetchBranches(test_util.TestBase): self.test_unrelatedbranch(True) def test_unorderedbranch(self, stupid=False): - repo = self._load_fixture_and_fetch('unorderedbranch.svndump', stupid) + repo = self._load_fixture_and_fetch('unorderedbranch.svndump', + stupid=stupid) r = repo['branch'] self.assertEqual(0, r.parents()[0].rev()) self.assertEqual(['a', 'c', 'z'], sorted(r.manifest())) @@ -62,7 +59,7 @@ class TestFetchBranches(test_util.TestBase): def test_renamed_branch_to_trunk(self, stupid=False): repo = self._load_fixture_and_fetch('branch_rename_to_trunk.svndump', - stupid) + stupid=stupid) self.assertEqual(repo['default'].parents()[0].branch(), 'dev_branch') self.assert_('iota' in repo['default']) self.assertEqual(repo['old_trunk'].parents()[0].branch(), 'default') @@ -75,14 +72,15 @@ class TestFetchBranches(test_util.TestBase): def test_replace_trunk_with_branch(self, stupid=False): repo = self._load_fixture_and_fetch('replace_trunk_with_branch.svndump', - stupid) + stupid=stupid) self.assertEqual(repo['default'].parents()[0].branch(), 'test') self.assertEqual(repo['tip'].branch(), 'default') self.assertEqual(repo['tip'].extra().get('close'), '1') self.assertEqual(self.openbranches(repo), ['default']) def test_copybeforeclose(self, stupid=False): - repo = self._load_fixture_and_fetch('copybeforeclose.svndump', stupid) + repo = self._load_fixture_and_fetch('copybeforeclose.svndump', + stupid=stupid) self.assertEqual(repo['tip'].branch(), 'test') self.assertEqual(repo['test'].extra().get('close'), '1') self.assertEqual(repo['test']['b'].data(), 'a\n') @@ -95,13 +93,13 @@ class TestFetchBranches(test_util.TestBase): def test_branch_create_with_dir_delete_works(self, stupid=False): repo = self._load_fixture_and_fetch('branch_create_with_dir_delete.svndump', - stupid) + stupid=stupid) self.assertEqual(repo['tip'].manifest().keys(), ['alpha', 'beta', 'iota', 'gamma', ]) def test_branch_tip_update_to_default(self, stupid=False): repo = self._load_fixture_and_fetch('unorderedbranch.svndump', - stupid, noupdate=False) + stupid=stupid, noupdate=False) self.assertEqual(repo[None].branch(), 'default') self.assertTrue('tip' not in repo[None].tags()) @@ -117,7 +115,8 @@ class TestFetchBranches(test_util.TestBase): self.assertTrue('c' not in repo.branchtags()) def test_branches_weird_moves(self, stupid=False): - repo = self._load_fixture_and_fetch('renamedproject.svndump', stupid, + repo = self._load_fixture_and_fetch('renamedproject.svndump', + stupid=stupid, subdir='project') heads = [repo[n] for n in repo.heads()] heads = dict((ctx.branch(), ctx) for ctx in heads) @@ -131,7 +130,7 @@ class TestFetchBranches(test_util.TestBase): def test_branch_delete_parent_dir(self, stupid=False): repo = self._load_fixture_and_fetch('branch_delete_parent_dir.svndump', - stupid) + stupid=stupid) openb, closedb = self.branches(repo) self.assertEqual(openb, []) self.assertEqual(closedb, ['dev_branch']) @@ -139,7 +138,7 @@ class TestFetchBranches(test_util.TestBase): def test_replace_branch_with_branch(self, stupid=False): repo = self._load_fixture_and_fetch('replace_branch_with_branch.svndump', - stupid) + stupid=stupid) self.assertEqual(7, len(repo)) # tip is former topological branch1 being closed ctx = repo['tip'] diff --git a/tests/test_fetch_command.py b/tests/test_fetch_command.py index f86416c..b4e3fce 100644 --- a/tests/test_fetch_command.py +++ b/tests/test_fetch_command.py @@ -152,12 +152,11 @@ class TestBasicRepoLayout(test_util.TestBase): self.test_fetch_when_trunk_has_no_files(stupid=True) def test_path_quoting(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, - 'non_ascii_path_1.svndump') + repo_path = self.load_svndump('non_ascii_path_1.svndump') subdir = '/b\xC3\xB8b' quoted_subdir = urllib.quote(subdir) - repo_url = test_util.fileurl(self.repo_path) + repo_url = test_util.fileurl(repo_path) wc_path = self.wc_path wc2_path = wc_path + '-2' @@ -189,10 +188,7 @@ class TestBasicRepoLayout(test_util.TestBase): class TestStupidPull(test_util.TestBase): def test_stupid(self): - repo = test_util.load_fixture_and_fetch('two_heads.svndump', - self.repo_path, - self.wc_path, - True) + repo = self._load_fixture_and_fetch('two_heads.svndump', stupid=True) self.assertEqual(node.hex(repo[0].node()), '434ed487136c1b47c1e8f952edb4dc5a8e6328df') self.assertEqual(node.hex(repo['tip'].node()), @@ -210,12 +206,9 @@ class TestStupidPull(test_util.TestBase): self.assertEqual(len(repo.heads()), 2) def test_oldest_not_trunk_and_tag_vendor_branch(self): - repo = test_util.load_fixture_and_fetch( + repo = self._load_fixture_and_fetch( 'tagged_vendor_and_oldest_not_trunk.svndump', - self.repo_path, - self.wc_path, - True) - repo = hg.repository(ui.ui(), self.wc_path) + stupid=True) self.assertEqual(node.hex(repo['oldest'].node()), '926671740dec045077ab20f110c1595f935334fa') self.assertEqual(repo['tip'].parents()[0].parents()[0], diff --git a/tests/test_fetch_command_regexes.py b/tests/test_fetch_command_regexes.py index c783f30..6c976f4 100644 --- a/tests/test_fetch_command_regexes.py +++ b/tests/test_fetch_command_regexes.py @@ -48,28 +48,99 @@ Name: svn:special class RegexTests(unittest.TestCase): def test_empty_file_re(self): - matches = stupid.empty_file_patch_wont_make_re.findall(two_empties) - assert sorted(matches) == ['__init__.py', 'bar/__init__.py'] + changed = stupid.parsediff(two_empties) + self.assertEqual(3, len(changed)) + self.assertEqual('__init__.py', changed[0].name) + self.assert_(changed[0].isempty()) + self.assertEqual('bar/__init__.py', changed[1].name) + self.assert_(changed[1].isempty()) + self.assertEqual('bar/test_muhaha.py', changed[2].name) + self.assert_(not changed[2].isempty()) def test_any_matches_just_one(self): pat = '''Index: trunk/django/contrib/admin/urls/__init__.py =================================================================== ''' - matches = stupid.any_file_re.findall(pat) - assert len(matches) == 1 + changed = stupid.parsediff(pat) + self.assertEqual(['trunk/django/contrib/admin/urls/__init__.py'], + [f.name for f in changed]) def test_special_re(self): - matches = stupid.property_special_set_re.findall(special_delta) - assert len(matches) == 1 + changed = stupid.parsediff(special_delta) + self.assertEqual(1, len(changed)) + self.assert_(changed[0].symlink) def test_any_file_re(self): - matches = stupid.any_file_re.findall(two_empties) - assert sorted(matches) == ['__init__.py', 'bar/__init__.py', - 'bar/test_muhaha.py'] + changed = stupid.parsediff(two_empties) + self.assertEqual(['__init__.py', 'bar/__init__.py', 'bar/test_muhaha.py'], + [f.name for f in changed]) def test_binary_file_re(self): - matches = stupid.binary_file_re.findall(binary_delta) - assert matches == ['trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc'] + changed = stupid.parsediff(binary_delta) + binaries = [f.name for f in changed if f.binary] + self.assertEqual(['trunk/functional_tests/doc_tests/test_doctest_fixtures/doctest_fixtures_fixtures.pyc'], + binaries) + + def test_diff16(self): + data = """Index: d3/d +=================================================================== +--- d3/d (revision 0) ++++ d3/d (revision 6) +@@ -0,0 +1 @@ ++d + +Property changes on: d3 +___________________________________________________________________ +Added: svn:externals + + ^/trunk/common/ext ext3 + + + +Property changes on: . +___________________________________________________________________ +Added: svn:mergeinfo + Merged /branches/branch:r4-5 +""" + changed = stupid.parsediff(data) + self.assertEqual(['d3/d', 'd3', '.'], [f.name for f in changed]) + data = """Property changes on: empty1 +___________________________________________________________________ +Deleted: svn:executable + - * + + +Property changes on: empty2 +___________________________________________________________________ +Added: svn:executable + + * + + +Property changes on: binary1 +___________________________________________________________________ +Deleted: svn:executable + - * + + +Property changes on: text1 +___________________________________________________________________ +Deleted: svn:executable + - * + + +Property changes on: binary2 +___________________________________________________________________ +Added: svn:executable + + * + + +Property changes on: text2 +___________________________________________________________________ +Added: svn:executable + + * +""" + changed = stupid.parsediff(data) + self.assertEqual(['empty1', 'empty2', 'binary1', 'text1', 'binary2', 'text2'], + [f.name for f in changed]) def suite(): return unittest.TestLoader().loadTestsFromTestCase(RegexTests) diff --git a/tests/test_fetch_exec.py b/tests/test_fetch_exec.py index 4742d27..3cd4c6e 100644 --- a/tests/test_fetch_exec.py +++ b/tests/test_fetch_exec.py @@ -5,16 +5,12 @@ import unittest from mercurial import node class TestFetchExec(test_util.TestBase): - def _load_fixture_and_fetch(self, fixture_name, stupid): - return test_util.load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, stupid=stupid) - def assertexec(self, ctx, files, isexec=True): for f in files: self.assertEqual(isexec, 'x' in ctx[f].flags()) def test_exec(self, stupid=False): - repo = self._load_fixture_and_fetch('executebit.svndump', stupid) + repo = self._load_fixture_and_fetch('executebit.svndump', stupid=stupid) self.assertexec(repo[0], ['text1', 'binary1', 'empty1'], True) self.assertexec(repo[0], ['text2', 'binary2', 'empty2'], False) self.assertexec(repo[1], ['text1', 'binary1', 'empty1'], False) @@ -24,7 +20,8 @@ class TestFetchExec(test_util.TestBase): self.test_exec(True) def test_empty_prop_val_executable(self, stupid=False): - repo = self._load_fixture_and_fetch('executable_file_empty_prop.svndump', stupid) + repo = self._load_fixture_and_fetch('executable_file_empty_prop.svndump', + stupid=stupid) self.assertEqual(node.hex(repo['tip'].node()), '08e6b380bf291b361a418203a1cb9427213cd1fd') self.assertEqual(repo['tip']['foo'].flags(), 'x') diff --git a/tests/test_fetch_mappings.py b/tests/test_fetch_mappings.py index 83149ef..02727f6 100644 --- a/tests/test_fetch_mappings.py +++ b/tests/test_fetch_mappings.py @@ -32,14 +32,14 @@ class MapTests(test_util.TestBase): return os.path.join(self.tmpdir, 'tagmap') def test_author_map(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') authormap = open(self.authors, 'w') authormap.write('Augie=Augie Fackler <durin42@gmail.com> # stuffy\n') authormap.write("Augie Fackler <durin42@gmail.com>\n") authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, authors=self.authors) self.assertEqual(self.repo[0].user(), 'Augie Fackler <durin42@gmail.com>') @@ -50,13 +50,13 @@ class MapTests(test_util.TestBase): self.test_author_map(True) def test_author_map_closing_author(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') authormap = open(self.authors, 'w') authormap.write("evil=Testy <test@test>") authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, authors=self.authors) self.assertEqual(self.repo[0].user(), 'Augie@5b65bade-98f3-4993-a01f-b7a6710da339') @@ -67,7 +67,8 @@ class MapTests(test_util.TestBase): self.test_author_map_closing_author(True) def test_author_map_no_author(self, stupid=False): - self._load_fixture_and_fetch('no-author.svndump', stupid=stupid) + repo, repo_path = self.load_and_fetch('no-author.svndump', + stupid=stupid) users = set(self.repo[r].user() for r in self.repo) expected_users = ['(no author)@%s' % self.repo.svnmeta().uuid] self.assertEqual(sorted(users), expected_users) @@ -78,7 +79,7 @@ class MapTests(test_util.TestBase): authormap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'authormap', self.authors) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, authors=self.authors) users = set(self.repo[r].user() for r in self.repo) expected_users = ['Testy <test@example.com>'] @@ -100,13 +101,13 @@ class MapTests(test_util.TestBase): self.assertEqual(fromself.symmetric_difference(all_tests), set()) def test_file_map(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') filemap = open(self.filemap, 'w') filemap.write("include alpha\n") filemap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'filemap', self.filemap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, filemap=self.filemap) self.assertEqual(node.hex(self.repo[0].node()), '88e2c7492d83e4bf30fbb2dcbf6aa24d60ac688d') self.assertEqual(node.hex(self.repo['default'].node()), 'e524296152246b3837fe9503c83b727075835155') @@ -116,13 +117,13 @@ class MapTests(test_util.TestBase): self.assertRaises(hgutil.Abort, self.test_file_map, True) def test_file_map_exclude(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, 'replace_trunk_with_branch.svndump') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') filemap = open(self.filemap, 'w') filemap.write("exclude alpha\n") filemap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'filemap', self.filemap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, filemap=self.filemap) self.assertEqual(node.hex(self.repo[0].node()), '2c48f3525926ab6c8b8424bcf5eb34b149b61841') self.assertEqual(node.hex(self.repo['default'].node()), 'b37a3c0297b71f989064d9b545b5a478bbed7cc1') @@ -131,15 +132,34 @@ class MapTests(test_util.TestBase): # TODO: re-enable test if we ever reinstate this feature self.assertRaises(hgutil.Abort, self.test_file_map_exclude, True) + def test_file_map_rule_order(self): + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') + filemap = open(self.filemap, 'w') + filemap.write("exclude alpha\n") + filemap.write("include .\n") + filemap.write("exclude gamma\n") + filemap.close() + ui = self.ui(False) + ui.setconfig('hgsubversion', 'filemap', self.filemap) + commands.clone(ui, test_util.fileurl(repo_path), + self.wc_path, filemap=self.filemap) + # The exclusion of alpha is overridden by the later rule to + # include all of '.', whereas gamma should remain excluded + # because it's excluded after the root directory. + self.assertEqual(self.repo[0].manifest().keys(), + ['alpha', 'beta']) + self.assertEqual(self.repo['default'].manifest().keys(), + ['alpha', 'beta']) + def test_branchmap(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump') + repo_path = self.load_svndump('branchmap.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("badname = good-name # stuffy\n") branchmap.write("feature = default\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) branches = set(self.repo[i].branch() for i in self.repo) self.assert_('badname' not in branches) @@ -151,13 +171,13 @@ class MapTests(test_util.TestBase): def test_branchmap_tagging(self, stupid=False): '''test tagging a renamed branch, which used to raise an exception''' - test_util.load_svndump_fixture(self.repo_path, 'commit-to-tag.svndump') + repo_path = self.load_svndump('commit-to-tag.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("magic = art\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) branches = set(self.repo[i].branch() for i in self.repo) self.assertEquals(sorted(branches), ['art', 'closeme']) @@ -167,13 +187,13 @@ class MapTests(test_util.TestBase): def test_branchmap_empty_commit(self, stupid=False): '''test mapping an empty commit on a renamed branch''' - test_util.load_svndump_fixture(self.repo_path, 'propset-branch.svndump') + repo_path = self.load_svndump('propset-branch.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("the-branch = bob\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) branches = set(self.repo[i].branch() for i in self.repo) self.assertEquals(sorted(branches), ['bob', 'default']) @@ -184,14 +204,14 @@ class MapTests(test_util.TestBase): def test_branchmap_combine(self, stupid=False): '''test combining two branches, but retaining heads''' - test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump') + repo_path = self.load_svndump('branchmap.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("badname = default\n") branchmap.write("feature = default\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) branches = set(self.repo[i].branch() for i in self.repo) self.assertEquals(sorted(branches), ['default']) @@ -209,14 +229,14 @@ class MapTests(test_util.TestBase): def test_branchmap_rebuildmeta(self, stupid=False): '''test rebuildmeta on a branchmapped clone''' - test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump') + repo_path = self.load_svndump('branchmap.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("badname = dit\n") branchmap.write("feature = dah\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) originfo = self.repo.svnmeta().branches @@ -225,7 +245,7 @@ class MapTests(test_util.TestBase): src, dest = test_util.hgclone(ui, self.wc_path, self.wc_path + '_clone', update=False) svncommands.rebuildmeta(ui, dest, - args=[test_util.fileurl(self.repo_path)]) + args=[test_util.fileurl(repo_path)]) # just check the keys; assume the contents are unaffected by the branch # map and thus properly tested by other tests @@ -238,14 +258,14 @@ class MapTests(test_util.TestBase): def test_branchmap_verify(self, stupid=False): '''test verify on a branchmapped clone''' - test_util.load_svndump_fixture(self.repo_path, 'branchmap.svndump') + repo_path = self.load_svndump('branchmap.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("badname = dit\n") branchmap.write("feature = dah\n") branchmap.close() ui = self.ui(stupid) ui.setconfig('hgsubversion', 'branchmap', self.branchmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, branchmap=self.branchmap) repo = self.repo @@ -264,7 +284,7 @@ class MapTests(test_util.TestBase): is to not convert the 'this' branches. Until we can do that, we settle with aborting. ''' - test_util.load_svndump_fixture(self.repo_path, 'propset-branch.svndump') + repo_path = self.load_svndump('propset-branch.svndump') branchmap = open(self.branchmap, 'w') branchmap.write("closeme =\n") branchmap.close() @@ -272,8 +292,7 @@ class MapTests(test_util.TestBase): maps.BranchMap, self.ui(), self.branchmap) def test_tagmap(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, - 'basic_tag_tests.svndump') + repo_path = self.load_svndump('basic_tag_tests.svndump') tagmap = open(self.tagmap, 'w') tagmap.write("tag_r3 = 3.x # stuffy\n") tagmap.write("copied_tag = \n") @@ -281,7 +300,7 @@ class MapTests(test_util.TestBase): ui = self.ui(stupid) ui.setconfig('hgsubversion', 'tagmap', self.tagmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, tagmap=self.tagmap) tags = self.repo.tags() assert 'tag_r3' not in tags @@ -292,8 +311,7 @@ class MapTests(test_util.TestBase): self.test_tagmap(True) def test_tagren_changed(self, stupid=False): - test_util.load_svndump_fixture(self.repo_path, - 'commit-to-tag.svndump') + repo_path = self.load_svndump('commit-to-tag.svndump') tagmap = open(self.tagmap, 'w') tagmap.write("edit-at-create = edit-past\n") tagmap.write("also-edit = \n") @@ -302,7 +320,7 @@ class MapTests(test_util.TestBase): ui = self.ui(stupid) ui.setconfig('hgsubversion', 'tagmap', self.tagmap) - commands.clone(ui, test_util.fileurl(self.repo_path), + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path, tagmap=self.tagmap) tags = self.repo.tags() @@ -310,8 +328,8 @@ class MapTests(test_util.TestBase): self.test_tagren_changed(True) def test_empty_log_message(self, stupid=False): - repo = self._load_fixture_and_fetch('empty-log-message.svndump', - stupid=stupid) + repo, repo_path = self.load_and_fetch('empty-log-message.svndump', + stupid=stupid) self.assertEqual(repo['tip'].description(), '') @@ -319,7 +337,7 @@ class MapTests(test_util.TestBase): ui = self.ui(stupid) ui.setconfig('hgsubversion', 'defaultmessage', 'blyf') - commands.clone(ui, test_util.fileurl(self.repo_path), self.wc_path) + commands.clone(ui, test_util.fileurl(repo_path), self.wc_path) self.assertEqual(self.repo['tip'].description(), 'blyf') diff --git a/tests/test_fetch_symlinks.py b/tests/test_fetch_symlinks.py index ecb2522..7f9252b 100644 --- a/tests/test_fetch_symlinks.py +++ b/tests/test_fetch_symlinks.py @@ -3,12 +3,8 @@ import test_util import unittest class TestFetchSymlinks(test_util.TestBase): - def _load_fixture_and_fetch(self, fixture_name, stupid): - return test_util.load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, stupid=stupid) - def test_symlinks(self, stupid=False): - repo = self._load_fixture_and_fetch('symlinks.svndump', stupid) + repo = self._load_fixture_and_fetch('symlinks.svndump', stupid=stupid) # Check symlinks throughout history links = { 0: { @@ -43,7 +39,9 @@ class TestFetchSymlinks(test_util.TestBase): for rev in repo: ctx = repo[rev] for f in ctx.manifest(): - self.assertEqual(f in links[rev], 'l' in ctx[f].flags()) + l = 'l' in ctx[f].flags() + lref = f in links[rev] + self.assertEqual(lref, l, '%r != %r for %s@%r' % (lref, l, f, rev)) if f in links[rev]: self.assertEqual(links[rev][f], ctx[f].data()) for f in links[rev]: diff --git a/tests/test_fetch_truncated.py b/tests/test_fetch_truncated.py index 9cb7300..6f3e991 100644 --- a/tests/test_fetch_truncated.py +++ b/tests/test_fetch_truncated.py @@ -8,8 +8,8 @@ from mercurial import hg class TestFetchTruncatedHistory(test_util.TestBase): def test_truncated_history(self, stupid=False): # Test repository does not follow the usual layout - test_util.load_svndump_fixture(self.repo_path, 'truncatedhistory.svndump') - svn_url = test_util.fileurl(self.repo_path + '/project2') + repo_path = self.load_svndump('truncatedhistory.svndump') + svn_url = test_util.fileurl(repo_path + '/project2') commands.clone(self.ui(stupid), svn_url, self.wc_path, noupdate=True) repo = hg.repository(self.ui(stupid), self.wc_path) diff --git a/tests/test_pull.py b/tests/test_pull.py index b0f61b2..556fbf1 100644 --- a/tests/test_pull.py +++ b/tests/test_pull.py @@ -11,38 +11,38 @@ class TestPull(test_util.TestBase): def setUp(self): super(TestPull, self).setUp() - def _load_fixture_and_fetch(self, fixture_name): - return test_util.load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, stupid=False, - noupdate=False) + def _loadupdate(self, fixture_name): + repo, repo_path = self.load_and_fetch(fixture_name, stupid=False, + noupdate=False) + return repo, repo_path def test_nochanges(self): - self._load_fixture_and_fetch('single_rev.svndump') + self._loadupdate('single_rev.svndump') state = self.repo.parents() commands.pull(self.repo.ui, self.repo) self.assertEqual(state, self.repo.parents()) def test_onerevision_noupdate(self): - repo = self._load_fixture_and_fetch('single_rev.svndump') + repo, repo_path = self._loadupdate('single_rev.svndump') state = repo.parents() - self._add_svn_rev({'trunk/alpha': 'Changed'}) + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) commands.pull(self.repo.ui, repo) self.assertEqual(state, repo.parents()) self.assertTrue('tip' not in repo[None].tags()) def test_onerevision_doupdate(self): - repo = self._load_fixture_and_fetch('single_rev.svndump') + repo, repo_path = self._loadupdate('single_rev.svndump') state = repo.parents() - self._add_svn_rev({'trunk/alpha': 'Changed'}) + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) commands.pull(self.repo.ui, repo, update=True) self.failIfEqual(state, repo.parents()) self.assertTrue('tip' in repo[None].tags()) def test_onerevision_divergent(self): - repo = self._load_fixture_and_fetch('single_rev.svndump') + repo, repo_path = self._loadupdate('single_rev.svndump') self.commitchanges((('alpha', 'alpha', 'Changed another way'),)) state = repo.parents() - self._add_svn_rev({'trunk/alpha': 'Changed one way'}) + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed one way'}) try: commands.pull(self.repo.ui, repo, update=True) except hgutil.Abort: @@ -53,7 +53,7 @@ class TestPull(test_util.TestBase): self.assertEqual(len(repo.heads()), 2) def test_tag_repull_doesnt_happen(self): - repo = self._load_fixture_and_fetch('branchtagcollision.svndump') + repo = self._loadupdate('branchtagcollision.svndump')[0] oldheads = map(node.hex, repo.heads()) commands.pull(repo.ui, repo) self.assertEqual(oldheads, map(node.hex, repo.heads())) diff --git a/tests/test_push_command.py b/tests/test_push_command.py index 0f078b4..8342100 100644 --- a/tests/test_push_command.py +++ b/tests/test_push_command.py @@ -3,6 +3,7 @@ import test_util import atexit import errno import os +import sys import random import shutil import socket @@ -22,9 +23,7 @@ import time class PushTests(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) - test_util.load_fixture_and_fetch('simple_branch.svndump', - self.repo_path, - self.wc_path) + self.repo_path = self.load_and_fetch('simple_branch.svndump')[1] def test_cant_push_empty_ctx(self): repo = self.repo @@ -77,15 +76,15 @@ class PushTests(test_util.TestBase): self.assertEqual(new_hash, tip.node()) def internal_push_over_svnserve(self, subdir='', commit=True): - test_util.load_svndump_fixture(self.repo_path, 'simple_branch.svndump') - open(os.path.join(self.repo_path, 'conf', 'svnserve.conf'), + repo_path = self.load_svndump('simple_branch.svndump') + open(os.path.join(repo_path, 'conf', 'svnserve.conf'), 'w').write('[general]\nanon-access=write\n[sasl]\n') self.port = random.randint(socket.IPPORT_USERRESERVED, 65535) self.host = 'localhost' args = ['svnserve', '--daemon', '--foreground', '--listen-port=%d' % self.port, '--listen-host=%s' % self.host, - '--root=%s' % self.repo_path] + '--root=%s' % repo_path] svnserve = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -133,8 +132,10 @@ class PushTests(test_util.TestBase): self.assertNotEqual('an_author', tip.user()) self.assertEqual('(no author)', tip.user().rsplit('@', 1)[0]) finally: - # TODO: use svnserve.kill() in Python >2.5 - test_util.kill_process(svnserve) + if sys.version_info >= (2,6): + svnserve.kill() + else: + test_util.kill_process(svnserve) def test_push_over_svnserve(self): self.internal_push_over_svnserve() diff --git a/tests/test_push_dirs.py b/tests/test_push_dirs.py index 3b43130..442d3fe 100644 --- a/tests/test_push_dirs.py +++ b/tests/test_push_dirs.py @@ -4,7 +4,7 @@ import unittest class TestPushDirectories(test_util.TestBase): def test_push_dirs(self): - self._load_fixture_and_fetch('emptyrepo.svndump') + repo_path = self.load_and_fetch('emptyrepo.svndump')[1] changes = [ # Single file in single directory @@ -18,7 +18,7 @@ class TestPushDirectories(test_util.TestBase): ] self.commitchanges(changes) self.pushrevisions() - self.assertEqual(self.svnls('trunk'), + self.assertEqual(test_util.svnls(repo_path, 'trunk'), ['d1', 'd1/a', 'd2', 'd2/a', 'd2/b', 'd31', 'd31/d32', 'd31/d32/a', 'd31/d32/d33', 'd31/d32/d33/d34', 'd31/d32/d33/d34/a']) @@ -42,17 +42,17 @@ class TestPushDirectories(test_util.TestBase): ] self.commitchanges(changes) self.pushrevisions() - self.assertEqual(self.svnls('trunk'), + self.assertEqual(test_util.svnls(repo_path, 'trunk'), ['d2', 'd2/b', 'd31', 'd31/d32', 'd31/d32/a', ]) def test_push_new_dir_project_root_not_repo_root(self): - self._load_fixture_and_fetch('fetch_missing_files_subdir.svndump', - subdir='foo') + repo_path = self.load_and_fetch('fetch_missing_files_subdir.svndump', + subdir='foo')[1] changes = [('magic_new/a', 'magic_new/a', 'ohai',), ] self.commitchanges(changes) self.pushrevisions() - self.assertEqual(self.svnls('foo/trunk'), ['bar', + self.assertEqual(test_util.svnls(repo_path, 'foo/trunk'), ['bar', 'bar/alpha', 'bar/beta', 'bar/delta', @@ -62,38 +62,38 @@ class TestPushDirectories(test_util.TestBase): 'magic_new/a']) def test_push_new_file_existing_dir_root_not_repo_root(self): - self._load_fixture_and_fetch('empty_dir_in_trunk_not_repo_root.svndump', - subdir='project') + repo_path = self.load_and_fetch('empty_dir_in_trunk_not_repo_root.svndump', + subdir='project')[1] changes = [('narf/a', 'narf/a', 'ohai',), ] self.commitchanges(changes) - self.assertEqual(self.svnls('project/trunk'), ['a', + self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a', 'narf', ]) self.pushrevisions() - self.assertEqual(self.svnls('project/trunk'), ['a', + self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a', 'narf', 'narf/a']) changes = [('narf/a', None, None,), ] self.commitchanges(changes) self.pushrevisions() - self.assertEqual(self.svnls('project/trunk'), ['a']) + self.assertEqual(test_util.svnls(repo_path, 'project/trunk'), ['a']) def test_push_single_dir_change_in_subdir(self): # Tests simple pushing from default branch to a single dir repo # Changes a file in a subdir (regression). - repo = self._load_fixture_and_fetch('branch_from_tag.svndump', - stupid=False, - layout='single', - subdir='tags') + repo, repo_path = self.load_and_fetch('branch_from_tag.svndump', + stupid=False, + layout='single', + subdir='tags') changes = [('tag_r3/alpha', 'tag_r3/alpha', 'foo'), ('tag_r3/new', 'tag_r3/new', 'foo'), ('new_dir/new', 'new_dir/new', 'foo'), ] self.commitchanges(changes) self.pushrevisions() - self.assertEqual(self.svnls('tags'), + self.assertEqual(test_util.svnls(repo_path, 'tags'), ['copied_tag', 'copied_tag/alpha', 'copied_tag/beta', diff --git a/tests/test_push_eol.py b/tests/test_push_eol.py index fe89529..24eaf16 100644 --- a/tests/test_push_eol.py +++ b/tests/test_push_eol.py @@ -5,9 +5,7 @@ import unittest class TestPushEol(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) - test_util.load_fixture_and_fetch('emptyrepo.svndump', - self.repo_path, - self.wc_path) + self._load_fixture_and_fetch('emptyrepo.svndump') def _test_push_dirs(self, stupid): changes = [ diff --git a/tests/test_push_renames.py b/tests/test_push_renames.py index ea7acff..a5fbb4c 100644 --- a/tests/test_push_renames.py +++ b/tests/test_push_renames.py @@ -6,10 +6,8 @@ import unittest class TestPushRenames(test_util.TestBase): def setUp(self): test_util.TestBase.setUp(self) - test_util.load_fixture_and_fetch('pushrenames.svndump', - self.repo_path, - self.wc_path, - True) + self.repo_path = self.load_and_fetch('pushrenames.svndump', + stupid=True)[1] def _debug_print_copies(self, ctx): w = sys.stderr.write @@ -110,9 +108,8 @@ class TestPushRenames(test_util.TestBase): ] self.commitchanges(changes) self.pushrevisions() - # print '\n'.join(sorted(self.svnls('trunk'))) assert reduce(lambda x, y: x and y, - ('geek' not in f for f in self.svnls('trunk'))), ( + ('geek' not in f for f in test_util.svnls(self.repo_path, 'trunk'))), ( 'This failure means rename of an entire tree is broken.' ' There is a print on the preceding line commented out ' 'that should help you.') diff --git a/tests/test_rebuildmeta.py b/tests/test_rebuildmeta.py index 7182dbb..bee8e37 100644 --- a/tests/test_rebuildmeta.py +++ b/tests/test_rebuildmeta.py @@ -28,7 +28,8 @@ def _do_case(self, name, stupid, single): layout = 'auto' if single: layout = 'single' - self._load_fixture_and_fetch(name, subdir=subdir, stupid=stupid, layout=layout) + repo, repo_path = self.load_and_fetch(name, subdir=subdir, stupid=stupid, + layout=layout) assert len(self.repo) > 0 wc2_path = self.wc_path + '_clone' u = ui.ui() @@ -44,7 +45,7 @@ def _do_case(self, name, stupid, single): try: svncommands.rebuildmeta(u, dest, - args=[test_util.fileurl(self.repo_path + + args=[test_util.fileurl(repo_path + subdir), ]) finally: # remove the wrapper diff --git a/tests/test_single_dir_clone.py b/tests/test_single_dir_clone.py index 2e035a5..52ebe0f 100644 --- a/tests/test_single_dir_clone.py +++ b/tests/test_single_dir_clone.py @@ -80,10 +80,10 @@ class TestSingleDir(test_util.TestBase): def test_push_single_dir(self): # Tests simple pushing from default branch to a single dir repo - repo = self._load_fixture_and_fetch('branch_from_tag.svndump', - stupid=False, - layout='single', - subdir='') + repo, repo_path = self.load_and_fetch('branch_from_tag.svndump', + stupid=False, + layout='single', + subdir='') def file_callback(repo, memctx, path): if path == 'adding_file': return context.memfilectx(path=path, @@ -109,14 +109,16 @@ class TestSingleDir(test_util.TestBase): repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions() - self.assertTrue('adding_file' in self.svnls('')) + self.assertTrue('adding_file' in test_util.svnls(repo_path, '')) self.assertEqual('application/octet-stream', - self.svnpropget('adding_binary', 'svn:mime-type')) + test_util.svnpropget(repo_path, 'adding_binary', + 'svn:mime-type')) # Now add another commit and test mime-type being reset changes = [('adding_binary', 'adding_binary', 'no longer binary')] self.commitchanges(changes) self.pushrevisions() - self.assertEqual('', self.svnpropget('adding_binary', 'svn:mime-type')) + self.assertEqual('', test_util.svnpropget(repo_path, 'adding_binary', + 'svn:mime-type')) def test_push_single_dir_at_subdir(self): repo = self._load_fixture_and_fetch('branch_from_tag.svndump', @@ -152,11 +154,11 @@ class TestSingleDir(test_util.TestBase): # Pushes two outgoing over one incoming svn rev # (used to cause an "unknown revision") # This can happen if someone committed to svn since our last pull (race). - repo = self._load_fixture_and_fetch('branch_from_tag.svndump', - stupid=False, - layout='single', - subdir='trunk') - self._add_svn_rev({'trunk/alpha': 'Changed'}) + repo, repo_path = self.load_and_fetch('branch_from_tag.svndump', + stupid=False, + layout='single', + subdir='trunk') + self.add_svn_rev(repo_path, {'trunk/alpha': 'Changed'}) def file_callback(repo, memctx, path): return context.memfilectx(path=path, data='data of %s' % path, @@ -175,18 +177,18 @@ class TestSingleDir(test_util.TestBase): repo.commitctx(ctx) hg.update(repo, repo['tip'].node()) self.pushrevisions(expected_extra_back=1) - self.assertTrue('trunk/one' in self.svnls('')) - self.assertTrue('trunk/two' in self.svnls('')) + self.assertTrue('trunk/one' in test_util.svnls(repo_path, '')) + self.assertTrue('trunk/two' in test_util.svnls(repo_path, '')) def test_push_single_dir_branch(self): # Tests local branches pushing to a single dir repo. Creates a fork at # tip. The default branch adds a file called default, while branch foo # adds a file called foo, then tries to push the foo branch and default # branch in that order. - repo = self._load_fixture_and_fetch('branch_from_tag.svndump', - stupid=False, - layout='single', - subdir='') + repo, repo_path = self.load_and_fetch('branch_from_tag.svndump', + stupid=False, + layout='single', + subdir='') def file_callback(data): def cb(repo, memctx, path): if path == data: @@ -214,7 +216,7 @@ class TestSingleDir(test_util.TestBase): hg.update(repo, repo['foo'].node()) self.pushrevisions() repo = self.repo # repo is outdated after the rebase happens, refresh - self.assertTrue('foo' in self.svnls('')) + self.assertTrue('foo' in test_util.svnls(repo_path, '')) self.assertEqual(repo.branchtags().keys(), ['default']) # Have to cross to another branch head, so hg.update doesn't work commands.update(ui.ui(), @@ -222,19 +224,18 @@ class TestSingleDir(test_util.TestBase): self.repo.branchheads('default')[1], clean=True) self.pushrevisions() - self.assertTrue('default' in self.svnls('')) + self.assertTrue('default' in test_util.svnls(repo_path, '')) self.assertEquals(len(self.repo.branchheads('default')), 1) @test_util.requiresoption('branch') def test_push_single_dir_renamed_branch(self, stupid=False): # Tests pulling and pushing with a renamed branch # Based on test_push_single_dir - test_util.load_svndump_fixture(self.repo_path, - 'branch_from_tag.svndump') + repo_path = self.load_svndump('branch_from_tag.svndump') cmd = ['clone', '--layout=single', '--branch=flaf'] if stupid: cmd.append('--stupid') - cmd += [test_util.fileurl(self.repo_path), self.wc_path] + cmd += [test_util.fileurl(repo_path), self.wc_path] test_util.dispatch(cmd) def file_callback(repo, memctx, path): @@ -256,7 +257,7 @@ class TestSingleDir(test_util.TestBase): self.repo.commitctx(ctx) hg.update(self.repo, self.repo['tip'].node()) self.pushrevisions() - self.assertTrue('adding_file' in self.svnls('')) + self.assertTrue('adding_file' in test_util.svnls(repo_path, '')) self.assertEquals(set(['flaf']), set(self.repo[i].branch() for i in self.repo)) diff --git a/tests/test_startrev.py b/tests/test_startrev.py index 026e5be..e544bff 100644 --- a/tests/test_startrev.py +++ b/tests/test_startrev.py @@ -36,6 +36,7 @@ def buildmethod(case, name, subdir, stupid): # these fixtures contain no files at HEAD and would result in empty clones nofiles = set([ 'binaryfiles.svndump', + 'binaryfiles-broken.svndump', 'emptyrepo.svndump', ]) diff --git a/tests/test_svnwrap.py b/tests/test_svnwrap.py index c5a5d2e..dae7dff 100644 --- a/tests/test_svnwrap.py +++ b/tests/test_svnwrap.py @@ -1,23 +1,12 @@ import test_util -import imp import os import subprocess -import shutil import tempfile import unittest from hgsubversion import svnwrap -import os -import stat -def force_rm(path): - os.chmod( - path, - os.stat(path).st_mode | stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH - ) - os.remove(path) - class TestBasicRepoLayout(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.mkdtemp('svnwrap_test') @@ -35,8 +24,7 @@ class TestBasicRepoLayout(unittest.TestCase): def tearDown(self): del self.repo - shutil.rmtree(self.tmpdir, onerror=lambda func, path, e: force_rm(path)) - + test_util.rmtree(self.tmpdir) def test_num_revs(self): revs = list(self.repo.revisions()) diff --git a/tests/test_tags.py b/tests/test_tags.py index 55cbb0c..630e039 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -12,10 +12,6 @@ from hgsubversion import svncommands from hgsubversion import svnrepo class TestTags(test_util.TestBase): - def _load_fixture_and_fetch(self, fixture_name, stupid=False): - return test_util.load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, stupid=stupid) - def test_tags(self, stupid=False): repo = self._load_fixture_and_fetch('basic_tag_tests.svndump', stupid=stupid) @@ -109,8 +105,8 @@ rename a tag self.test_most_recent_is_edited(True) def test_most_recent_is_edited(self, stupid=False): - repo = self._load_fixture_and_fetch('most-recent-is-edit-tag.svndump', - stupid=stupid) + repo, repo_path = self.load_and_fetch('most-recent-is-edit-tag.svndump', + stupid=stupid) self.repo.ui.status( "Note: this test failing may be because of a rebuildmeta failure.\n" "You should check that before assuming issues with this test.\n") @@ -118,7 +114,7 @@ rename a tag src, dest = test_util.hgclone(repo.ui, self.wc_path, wc2_path, update=False) svncommands.rebuildmeta(repo.ui, dest, - args=[test_util.fileurl(self.repo_path), ]) + args=[test_util.fileurl(repo_path), ]) commands.pull(self.repo.ui, self.repo, stupid=stupid) dtags, srctags = dest.tags(), self.repo.tags() dtags.pop('tip') diff --git a/tests/test_urls.py b/tests/test_urls.py index d18aeb5..249a743 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -63,10 +63,9 @@ class TestSubversionUrls(test_util.TestBase): def test_quoting(self): ui = self.ui() - test_util.load_svndump_fixture(self.repo_path, - 'non_ascii_path_1.svndump') + repo_path = self.load_svndump('non_ascii_path_1.svndump') - repo_url = test_util.fileurl(self.repo_path) + repo_url = test_util.fileurl(repo_path) subdir = '/b\xC3\xB8b' quoted_subdir = urllib.quote(subdir) diff --git a/tests/test_util.py b/tests/test_util.py index 63b02f8..0343fe2 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -2,7 +2,6 @@ import StringIO import difflib import errno import gettext -import imp import os import shutil import stat @@ -158,18 +157,6 @@ def testui(stupid=False, layout='auto', startrev=0): u.setconfig('hgsubversion', 'startrev', startrev) return u -def load_svndump_fixture(path, fixture_name): - '''Loads an svnadmin dump into a fresh repo at path, which should not - already exist. - ''' - if os.path.exists(path): rmtree(path) - subprocess.call(['svnadmin', 'create', path, ], - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - inp = open(os.path.join(FIXTURES, fixture_name)) - proc = subprocess.Popen(['svnadmin', 'load', path, ], stdin=inp, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - proc.communicate() - def dispatch(cmd): try: req = dispatchmod.request(cmd) @@ -177,31 +164,6 @@ def dispatch(cmd): except AttributeError, e: dispatchmod.dispatch(cmd) -def load_fixture_and_fetch(fixture_name, repo_path, wc_path, stupid=False, - subdir='', noupdate=True, layout='auto', - startrev=0, externals=None): - load_svndump_fixture(repo_path, fixture_name) - if subdir: - repo_path += '/' + subdir - - cmd = [ - 'clone', - '--layout=%s' % layout, - '--startrev=%s' % startrev, - fileurl(repo_path), - wc_path, - ] - if stupid: - cmd.append('--stupid') - if noupdate: - cmd.append('--noupdate') - if externals: - cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals] - - dispatch(cmd) - - return hg.repository(testui(), wc_path) - def rmtree(path): # Read-only files cannot be removed under Windows for root, dirs, files in os.walk(path): @@ -244,6 +206,32 @@ def hgclone(ui, source, dest, update=True): src, dest = hg.clone(ui, source, dest, update=update) return src, dest +def svnls(repo_path, path, rev='HEAD'): + path = repo_path + '/' + path + path = util.normalize_url(fileurl(path)) + args = ['svn', 'ls', '-r', rev, '-R', path] + p = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = p.communicate() + if p.returncode: + raise Exception('svn ls failed on %s: %r' % (path, stderr)) + entries = [e.strip('/') for e in stdout.splitlines()] + entries.sort() + return entries + +def svnpropget(repo_path, path, prop, rev='HEAD'): + path = repo_path + '/' + path + path = util.normalize_url(fileurl(path)) + args = ['svn', 'propget', '-r', str(rev), prop, path] + p = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = p.communicate() + if p.returncode: + raise Exception('svn ls failed on %s: %r' % (path, stderr)) + return stdout.strip() + class TestBase(unittest.TestCase): def setUp(self): _verify_our_modules() @@ -263,7 +251,7 @@ class TestBase(unittest.TestCase): for l in '[extensions]', 'hgsubversion=': print >> rc, l - self.repo_path = '%s/testrepo' % self.tmpdir + self.repocount = 0 self.wc_path = '%s/testrepo_wc' % self.tmpdir self.svn_wc = None @@ -275,6 +263,10 @@ class TestBase(unittest.TestCase): self.patch = (ui.ui.write_err, ui.ui.write) setattr(ui.ui, self.patch[0].func_name, self.patch[1]) + def _makerepopath(self): + self.repocount += 1 + return '%s/testrepo-%d' % (self.tmpdir, self.repocount) + def tearDown(self): for var, val in self.oldenv.iteritems(): if val is None: @@ -291,24 +283,61 @@ class TestBase(unittest.TestCase): def ui(self, stupid=False, layout='auto'): return testui(stupid, layout) - def _load_fixture_and_fetch(self, fixture_name, subdir=None, stupid=False, - layout='auto', startrev=0, externals=None): + def load_svndump(self, fixture_name): + '''Loads an svnadmin dump into a fresh repo. Return the svn repo + path. + ''' + path = self._makerepopath() + assert not os.path.exists(path) + subprocess.call(['svnadmin', 'create', path,], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + inp = open(os.path.join(FIXTURES, fixture_name)) + proc = subprocess.Popen(['svnadmin', 'load', path,], stdin=inp, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + proc.communicate() + return path + + def load_and_fetch(self, fixture_name, subdir=None, stupid=False, + layout='auto', startrev=0, externals=None, + noupdate=True): if layout == 'single': if subdir is None: subdir = 'trunk' elif subdir is None: subdir = '' - return load_fixture_and_fetch(fixture_name, self.repo_path, - self.wc_path, subdir=subdir, - stupid=stupid, layout=layout, - startrev=startrev, externals=externals) - - def _add_svn_rev(self, changes): + repo_path = self.load_svndump(fixture_name) + projectpath = repo_path + if subdir: + projectpath += '/' + subdir + + cmd = [ + 'clone', + '--layout=%s' % layout, + '--startrev=%s' % startrev, + fileurl(projectpath), + self.wc_path, + ] + if stupid: + cmd.append('--stupid') + if noupdate: + cmd.append('--noupdate') + if externals: + cmd[:0] = ['--config', 'hgsubversion.externals=%s' % externals] + + dispatch(cmd) + + return hg.repository(testui(), self.wc_path), repo_path + + def _load_fixture_and_fetch(self, *args, **kwargs): + repo, repo_path = self.load_and_fetch(*args, **kwargs) + return repo + + def add_svn_rev(self, repo_path, changes): '''changes is a dict of filename -> contents''' if self.svn_wc is None: self.svn_wc = os.path.join(self.tmpdir, 'testsvn_wc') subprocess.call([ - 'svn', 'co', '-q', fileurl(self.repo_path), + 'svn', 'co', '-q', fileurl(repo_path), self.svn_wc ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -338,26 +367,12 @@ class TestBase(unittest.TestCase): self.assertEqual(expected_extra_back, after - before) return res - def svnls(self, path, rev='HEAD'): - path = self.repo_path + '/' + path - path = util.normalize_url(fileurl(path)) - args = ['svn', 'ls', '-r', rev, '-R', path] - p = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - stdout, stderr = p.communicate() - if p.returncode: - raise Exception('svn ls failed on %s: %r' % (path, stderr)) - entries = [e.strip('/') for e in stdout.splitlines()] - entries.sort() - return entries - - def svnco(self, svnpath, rev, path): + def svnco(self, repo_path, svnpath, rev, path): path = os.path.join(self.wc_path, path) subpath = os.path.dirname(path) if not os.path.isdir(subpath): os.makedirs(subpath) - svnpath = fileurl(self.repo_path + '/' + svnpath) + svnpath = fileurl(repo_path + '/' + svnpath) args = ['svn', 'co', '-r', rev, svnpath, path] p = subprocess.Popen(args, stdout=subprocess.PIPE, @@ -366,18 +381,6 @@ class TestBase(unittest.TestCase): if p.returncode: raise Exception('svn co failed on %s: %r' % (svnpath, stderr)) - def svnpropget(self, path, prop, rev='HEAD'): - path = self.repo_path + '/' + path - path = util.normalize_url(fileurl(path)) - args = ['svn', 'propget', '-r', str(rev), prop, path] - p = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - stdout, stderr = p.communicate() - if p.returncode: - raise Exception('svn ls failed on %s: %r' % (path, stderr)) - return stdout.strip() - def commitchanges(self, changes, parent='tip', message='automated test'): """Commit changes to mercurial directory diff --git a/tests/test_utility_commands.py b/tests/test_utility_commands.py index 2de14bb..10926e2 100644 --- a/tests/test_utility_commands.py +++ b/tests/test_utility_commands.py @@ -2,6 +2,7 @@ import test_util import os import unittest +import re from hgext import rebase from mercurial import hg @@ -25,13 +26,12 @@ Last Changed Rev: %(rev)s Last Changed Date: %(date)s ''' -class UtilityTests(test_util.TestBase): - @property - def repourl(self): - return util.normalize_url(test_util.fileurl(self.repo_path)) +def repourl(repo_path): + return util.normalize_url(test_util.fileurl(repo_path)) +class UtilityTests(test_util.TestBase): def test_info_output(self): - self._load_fixture_and_fetch('two_heads.svndump') + repo, repo_path = self.load_and_fetch('two_heads.svndump') hg.update(self.repo, 'the_branch') u = self.ui() u.pushbuffer() @@ -39,7 +39,7 @@ class UtilityTests(test_util.TestBase): actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)', - 'repourl': self.repourl, + 'repourl': repourl(repo_path), 'branch': 'branches/the_branch', 'rev': 5, }) @@ -50,7 +50,7 @@ class UtilityTests(test_util.TestBase): actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', - 'repourl': self.repourl, + 'repourl': repourl(repo_path), 'branch': 'trunk', 'rev': 6, }) @@ -61,14 +61,14 @@ class UtilityTests(test_util.TestBase): actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:05 +0000 (Wed, 08 Oct 2008)', - 'repourl': self.repourl, + 'repourl': repourl(repo_path), 'branch': 'branches/the_branch', 'rev': 5, }) self.assertMultiLineEqual(actual, expected) def test_info_single(self): - self._load_fixture_and_fetch('two_heads.svndump', subdir='trunk') + repo, repo_path = self.load_and_fetch('two_heads.svndump', subdir='trunk') hg.update(self.repo, 'tip') u = self.ui() u.pushbuffer() @@ -76,7 +76,7 @@ class UtilityTests(test_util.TestBase): actual = u.popbuffer() expected = (expected_info_output % {'date': '2008-10-08 01:39:29 +0000 (Wed, 08 Oct 2008)', - 'repourl': self.repourl, + 'repourl': repourl(repo_path), 'branch': 'trunk', 'rev': 6, }) @@ -153,7 +153,7 @@ class UtilityTests(test_util.TestBase): self.assertEqual(actual, '4:1083037b18d8\n') def test_outgoing_output(self): - self._load_fixture_and_fetch('two_heads.svndump') + repo, repo_path = self.load_and_fetch('two_heads.svndump') u = self.ui() parents = (self.repo['the_branch'].node(), revlog.nullid,) def filectxfn(repo, memctx, path): @@ -173,13 +173,13 @@ class UtilityTests(test_util.TestBase): new = self.repo.commitctx(ctx) hg.update(self.repo, new) u.pushbuffer() - commands.outgoing(u, self.repo, self.repourl) + commands.outgoing(u, self.repo, repourl(repo_path)) actual = u.popbuffer() self.assertTrue(node.hex(self.repo['localbranch'].node())[:8] in actual) self.assertEqual(actual.strip(), '5:6de15430fa20') hg.update(self.repo, 'default') u.pushbuffer() - commands.outgoing(u, self.repo, self.repourl) + commands.outgoing(u, self.repo, repourl(repo_path)) actual = u.popbuffer() self.assertEqual(actual, '') @@ -211,11 +211,10 @@ class UtilityTests(test_util.TestBase): def test_genignore(self): """ Test generation of .hgignore file. """ - test_util.load_fixture_and_fetch('ignores.svndump', self.repo_path, - self.wc_path, noupdate=False) + repo = self._load_fixture_and_fetch('ignores.svndump', noupdate=False) u = self.ui() u.pushbuffer() - svncommands.genignore(u, self.repo, self.wc_path) + svncommands.genignore(u, repo, self.wc_path) self.assertMultiLineEqual(open(os.path.join(self.wc_path, '.hgignore')).read(), '.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n') @@ -229,26 +228,44 @@ class UtilityTests(test_util.TestBase): '.hgignore\nsyntax:glob\nblah\notherblah\nbaz/magic\n') def test_list_authors(self): - test_util.load_svndump_fixture(self.repo_path, - 'replace_trunk_with_branch.svndump') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') u = self.ui() u.pushbuffer() svncommands.listauthors(u, - args=[test_util.fileurl(self.repo_path)], + args=[test_util.fileurl(repo_path)], authors=None) actual = u.popbuffer() self.assertMultiLineEqual(actual, 'Augie\nevil\n') - def test_list_authors_map(self): - test_util.load_svndump_fixture(self.repo_path, - 'replace_trunk_with_branch.svndump') - author_path = os.path.join(self.repo_path, 'authors') + repo_path = self.load_svndump('replace_trunk_with_branch.svndump') + author_path = os.path.join(repo_path, 'authors') svncommands.listauthors(self.ui(), - args=[test_util.fileurl(self.repo_path)], - authors=author_path) + args=[test_util.fileurl(repo_path)], + authors=author_path) self.assertMultiLineEqual(open(author_path).read(), 'Augie=\nevil=\n') + def test_svnverify(self): + repo, repo_path = self.load_and_fetch('binaryfiles.svndump', + noupdate=False) + ret = svncommands.verify(self.ui(), repo, [], rev=1) + self.assertEqual(0, ret) + repo_path = self.load_svndump('binaryfiles-broken.svndump') + u = self.ui() + u.pushbuffer() + ret = svncommands.verify(u, repo, [test_util.fileurl(repo_path)], + rev=1) + output = u.popbuffer() + self.assertEqual(1, ret) + output = re.sub(r'file://\S+', 'file://', output) + self.assertEqual("""\ +verifying d51f46a715a1 against file:// +difference in file binary2 +unexpected files: + binary1 +missing files: + binary3 +""", output) def suite(): all_tests = [unittest.TestLoader().loadTestsFromTestCase(UtilityTests), |