1 # Copyright (C) 2006-2007 Jelmer Vernooij <jelmer@samba.org>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 """Tests for the bzr-svn plugin."""
23 from cStringIO import StringIO
25 from bzrlib import osutils, urlutils
26 from bzrlib.bzrdir import BzrDir
27 from bzrlib.tests import TestCaseInTempDir, TestSkipped
28 from bzrlib.trace import mutter
29 from bzrlib.urlutils import local_path_to_url
30 from bzrlib.workingtree import WorkingTree
32 import svn.core, svn.repos
33 from bzrlib.plugins.svn.ra import RemoteAccess
35 class TestCaseWithSubversionRepository(TestCaseInTempDir):
36 """A test case that provides the ability to build Subversion
40 super(TestCaseWithSubversionRepository, self).setUp()
41 self.client_ctx = svn.client.create_context()
42 self.client_ctx.log_msg_func2 = svn.client.svn_swig_py_get_commit_log_func
43 self.client_ctx.log_msg_baton2 = self.log_message_func
45 def log_message_func(self, items, pool):
46 return self.next_message
48 def make_repository(self, relpath, allow_revprop_changes=True):
49 """Create a repository.
51 :return: Handle to the repository.
53 abspath = os.path.join(self.test_dir, relpath)
55 svn.repos.create(abspath, '', '', None, None)
57 if allow_revprop_changes:
58 if sys.platform == 'win32':
59 revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change.bat")
60 open(revprop_hook, 'w').write("exit 0\n")
62 revprop_hook = os.path.join(abspath, "hooks", "pre-revprop-change")
63 open(revprop_hook, 'w').write("#!/bin/sh\n")
64 os.chmod(revprop_hook, os.stat(revprop_hook).st_mode | 0111)
66 return local_path_to_url(abspath)
68 def make_remote_bzrdir(self, relpath):
69 """Create a repository."""
71 repos_url = self.make_repository(relpath)
73 return BzrDir.open("svn+%s" % repos_url)
75 def open_local_bzrdir(self, repos_url, relpath):
76 """Open a local BzrDir."""
78 self.make_checkout(repos_url, relpath)
80 return BzrDir.open(relpath)
82 def make_local_bzrdir(self, repos_path, relpath):
83 """Create a repository and checkout."""
85 repos_url = self.make_repository(repos_path)
87 return self.open_local_bzrdir(repos_url, relpath)
89 def make_checkout(self, repos_url, relpath):
90 rev = svn.core.svn_opt_revision_t()
91 rev.kind = svn.core.svn_opt_revision_head
93 svn.client.checkout2(repos_url, relpath,
94 rev, rev, True, False, self.client_ctx)
97 def create_checkout(branch, path, revision_id=None, lightweight=False):
98 return branch.create_checkout(path, revision_id=revision_id,
99 lightweight=lightweight)
102 def open_checkout(url):
103 return WorkingTree.open(url)
106 def open_checkout_bzrdir(url):
107 return BzrDir.open(url)
110 def create_branch_convenience(url):
111 return BzrDir.create_branch_convenience(url)
113 def client_set_prop(self, path, name, value):
116 svn.client.propset2(name, value, path, False, True, self.client_ctx)
118 def client_get_prop(self, path, name, revnum=None, recursive=False):
119 rev = svn.core.svn_opt_revision_t()
122 rev.kind = svn.core.svn_opt_revision_working
124 rev.kind = svn.core.svn_opt_revision_number
125 rev.value.number = revnum
126 ret = svn.client.propget2(name, path, rev, rev, recursive,
131 return ret.values()[0]
133 def client_get_revprop(self, url, revnum, name):
134 rev = svn.core.svn_opt_revision_t()
135 rev.kind = svn.core.svn_opt_revision_number
136 rev.value.number = revnum
137 return svn.client.revprop_get(name, url, rev, self.client_ctx)[0]
139 def client_set_revprop(self, url, revnum, name, value):
140 rev = svn.core.svn_opt_revision_t()
141 rev.kind = svn.core.svn_opt_revision_number
142 rev.value.number = revnum
143 svn.client.revprop_set(name, value, url, rev, True, self.client_ctx)
145 def client_commit(self, dir, message=None, recursive=True):
146 """Commit current changes in specified working copy.
148 :param relpath: List of paths to commit.
150 olddir = os.path.abspath('.')
151 self.next_message = message
153 info = svn.client.commit2(["."], recursive, False, self.client_ctx)
155 assert info is not None
156 return (info.revision, info.date, info.author)
158 def client_add(self, relpath, recursive=True):
159 """Add specified files to working copy.
161 :param relpath: Path to the files to add.
163 svn.client.add3(relpath, recursive, False, False, self.client_ctx)
165 def revnum_to_opt_rev(self, revnum):
166 rev = svn.core.svn_opt_revision_t()
168 rev.kind = svn.core.svn_opt_revision_head
170 assert isinstance(revnum, int)
171 rev.kind = svn.core.svn_opt_revision_number
172 rev.value.number = revnum
175 def client_log(self, path, start_revnum=None, stop_revnum=None):
176 assert isinstance(path, str)
178 def rcvr(orig_paths, rev, author, date, message, pool):
179 ret[rev] = (orig_paths, author, date, message)
180 svn.client.log([path], self.revnum_to_opt_rev(start_revnum),
181 self.revnum_to_opt_rev(stop_revnum),
188 def client_delete(self, relpath):
189 """Remove specified files from working copy.
191 :param relpath: Path to the files to remove.
193 svn.client.delete2([relpath], True, self.client_ctx)
195 def client_copy(self, oldpath, newpath, revnum=None):
196 """Copy file in working copy.
198 :param oldpath: Relative path to original file.
199 :param newpath: Relative path to new file.
201 rev = svn.core.svn_opt_revision_t()
203 rev.kind = svn.core.svn_opt_revision_head
205 rev.kind = svn.core.svn_opt_revision_number
206 rev.value.number = revnum
207 svn.client.copy2(oldpath, rev, newpath, self.client_ctx)
209 def client_update(self, path):
210 rev = svn.core.svn_opt_revision_t()
211 rev.kind = svn.core.svn_opt_revision_head
212 svn.client.update(path, rev, True, self.client_ctx)
214 def build_tree(self, files):
215 """Create a directory tree.
217 :param files: Dictionary with filenames as keys, contents as
218 values. None as value indicates a directory.
228 os.makedirs(os.path.dirname(f))
231 open(f, 'w').write(files[f])
233 def make_client_and_bzrdir(self, repospath, clientpath):
234 repos_url = self.make_client(repospath, clientpath)
236 return BzrDir.open("svn+%s" % repos_url)
238 def make_client(self, repospath, clientpath, allow_revprop_changes=True):
239 """Create a repository and a checkout. Return the checkout.
241 :param relpath: Optional relpath to check out if not the full
243 :param clientpath: Path to checkout
244 :return: Repository URL.
246 repos_url = self.make_repository(repospath,
247 allow_revprop_changes=allow_revprop_changes)
248 self.make_checkout(repos_url, clientpath)
251 def dumpfile(self, repos):
252 """Create a dumpfile for the specified repository.
254 :return: File name of the dumpfile.
256 raise NotImplementedError(self.dumpfile)
258 def open_fs(self, relpath):
263 repos = svn.repos.open(relpath)
265 return svn.repos.fs(repos)
267 def commit_editor(self, url, message="Test commit"):
268 ra = RemoteAccess(url.encode('utf8'))
270 def __init__(self, ra, editor, base_revnum, base_url):
273 self.base_revnum = base_revnum
279 self.base_url = base_url
281 def _parts(self, path):
282 return path.strip("/").split("/")
284 def add_dir(self, path, copyfrom_path=None, copyfrom_rev=-1):
285 self.create.add(path)
286 if copyfrom_path is not None:
287 if copyfrom_rev == -1:
288 copyfrom_rev = self.base_revnum
289 copyfrom_path = os.path.join(self.base_url, copyfrom_path)
290 self.copyfrom[path] = (copyfrom_path, copyfrom_rev)
293 def open_dir(self, path):
295 for p in self._parts(path):
301 def add_file(self, path, contents=None):
302 self.create.add(path)
303 self.change_file(path, contents)
305 def change_file(self, path, contents=None):
306 parts = self._parts(path)
307 x = self.open_dir("/".join(parts[:-1]))
309 contents = osutils.rand_chars(100)
310 x[parts[-1]] = contents
312 def delete(self, path):
313 parts = self._parts(path)
314 x = self.open_dir("/".join(parts[:-1]))
317 def change_dir_prop(self, path, propname, propval):
319 if not path in self.props:
320 self.props[path] = {}
321 self.props[path][propname] = propval
323 def change_file_prop(self, path, propname, propval):
324 parts = self._parts(path)
325 x = self.open_dir("/".join(parts[:-1]))
327 if not path in self.props:
328 self.props[path] = {}
329 self.props[path][propname] = propval
331 def _process_dir(self, dir_baton, dir_dict, path):
332 for name, contents in dir_dict.items():
333 subpath = urlutils.join(path, name).strip("/")
335 dir_baton.delete_entry(subpath, -1)
336 elif isinstance(contents, dict):
337 if subpath in self.create:
338 child_baton = dir_baton.add_directory(subpath, self.copyfrom[subpath][0], self.copyfrom[subpath][1])
340 child_baton = dir_baton.open_directory(subpath, -1)
341 if subpath in self.props:
342 for k, v in self.props[subpath].items():
343 child_baton.change_prop(k, v)
345 self._process_dir(child_baton, dir_dict[name], subpath)
349 if subpath in self.create:
350 child_baton = dir_baton.add_file(subpath, None, -1)
352 child_baton = dir_baton.open_file(subpath)
353 if isinstance(contents, str):
354 (txdelta, txbaton) = child_baton.apply_textdelta()
355 svn.delta.svn_txdelta_send_stream(StringIO(contents), txdelta, txbaton)
356 if subpath in self.props:
357 for k, v in self.props[subpath].items():
358 child_baton.change_prop(k, v)
362 assert self._used == False
364 root_baton = self.editor.open_root(self.base_revnum)
365 self._process_dir(root_baton, self.data, "")
369 my_revnum = ra.get_latest_revnum()
370 assert my_revnum > self.base_revnum
374 base_revnum = ra.get_latest_revnum()
375 editor = ra.get_commit_editor({"svn:log": message})
376 return CommitEditor(ra, editor, base_revnum, url)
380 from unittest import TestSuite
382 from bzrlib.tests import TestUtil
384 loader = TestUtil.TestLoader()
413 suite.addTest(loader.loadTestsFromModuleNames(["%s.%s" % (__name__, i) for i in testmod_names]))