1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
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 2 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
18
"""Tree implementation tests for bzr.
20
These test the conformance of all the tree variations to the expected API.
21
Specific tests for individual variations are in other places such as:
23
- tests/test_revision.py
24
- tests/test_workingtree.py
25
- tests/workingtree_implementations/*.py.
34
from bzrlib.transport import get_transport
35
from bzrlib.tests import (
38
TestCaseWithTransport,
41
from bzrlib.tests.bzrdir_implementations.test_bzrdir import TestCaseWithBzrDir
42
from bzrlib.tests.workingtree_implementations import (
43
WorkingTreeTestProviderAdapter,
45
from bzrlib.revisiontree import RevisionTree
46
from bzrlib.transform import TransformPreview
47
from bzrlib.workingtree import (
52
from bzrlib.workingtree_4 import (
58
def return_parameter(testcase, something):
59
"""A trivial thunk to return its input."""
63
def revision_tree_from_workingtree(testcase, tree):
64
"""Create a revision tree from a working tree."""
65
revid = tree.commit('save tree', allow_pointless=True, recursive=None)
66
return tree.branch.repository.revision_tree(revid)
69
def _dirstate_tree_from_workingtree(testcase, tree):
70
revid = tree.commit('save tree', allow_pointless=True, recursive=None)
71
return tree.basis_tree()
74
def preview_tree_pre(testcase, tree):
75
tt = TransformPreview(tree)
76
testcase.addCleanup(tt.finalize)
77
preview_tree = tt.get_preview_tree()
78
preview_tree.set_parent_ids(tree.get_parent_ids())
82
class TestTreeImplementationSupport(TestCaseWithTransport):
84
def test_revision_tree_from_workingtree(self):
85
tree = self.make_branch_and_tree('.')
86
tree = revision_tree_from_workingtree(self, tree)
87
self.assertIsInstance(tree, RevisionTree)
90
class TestCaseWithTree(TestCaseWithBzrDir):
92
def make_branch_and_tree(self, relpath):
93
made_control = self.make_bzrdir(relpath, format=
94
self.workingtree_format._matchingbzrdir)
95
made_control.create_repository()
96
made_control.create_branch()
97
return self.workingtree_format.initialize(made_control)
99
def workingtree_to_test_tree(self, tree):
100
return self._workingtree_to_test_tree(self, tree)
102
def _convert_tree(self, tree, converter=None):
103
"""helper to convert using the converter or a supplied one."""
104
# convert that to the final shape
105
if converter is None:
106
converter = self.workingtree_to_test_tree
107
return converter(tree)
109
def get_tree_no_parents_no_content(self, empty_tree, converter=None):
110
"""Make a tree with no parents and no contents from empty_tree.
112
:param empty_tree: A working tree with no content and no parents to
115
empty_tree.set_root_id('empty-root-id')
116
return self._convert_tree(empty_tree, converter)
118
def _make_abc_tree(self, tree):
119
"""setup an abc content tree."""
120
files = ['a', 'b/', 'b/c']
121
self.build_tree(files, line_endings='binary',
122
transport=tree.bzrdir.root_transport)
123
tree.set_root_id('root-id')
124
tree.add(files, ['a-id', 'b-id', 'c-id'])
126
def get_tree_no_parents_abc_content(self, tree, converter=None):
127
"""return a test tree with a, b/, b/c contents."""
128
self._make_abc_tree(tree)
129
return self._convert_tree(tree, converter)
131
def get_tree_no_parents_abc_content_2(self, tree, converter=None):
132
"""return a test tree with a, b/, b/c contents.
134
This variation changes the content of 'a' to foobar\n.
136
self._make_abc_tree(tree)
137
f = open(tree.basedir + '/a', 'wb')
142
return self._convert_tree(tree, converter)
144
def get_tree_no_parents_abc_content_3(self, tree, converter=None):
145
"""return a test tree with a, b/, b/c contents.
147
This variation changes the executable flag of b/c to True.
149
self._make_abc_tree(tree)
150
tt = transform.TreeTransform(tree)
151
trans_id = tt.trans_id_tree_path('b/c')
152
tt.set_executability(True, trans_id)
154
return self._convert_tree(tree, converter)
156
def get_tree_no_parents_abc_content_4(self, tree, converter=None):
157
"""return a test tree with d, b/, b/c contents.
159
This variation renames a to d.
161
self._make_abc_tree(tree)
162
tree.rename_one('a', 'd')
163
return self._convert_tree(tree, converter)
165
def get_tree_no_parents_abc_content_5(self, tree, converter=None):
166
"""return a test tree with d, b/, b/c contents.
168
This variation renames a to d and alters its content to 'bar\n'.
170
self._make_abc_tree(tree)
171
tree.rename_one('a', 'd')
172
f = open(tree.basedir + '/d', 'wb')
177
return self._convert_tree(tree, converter)
179
def get_tree_no_parents_abc_content_6(self, tree, converter=None):
180
"""return a test tree with a, b/, e contents.
182
This variation renames b/c to e, and makes it executable.
184
self._make_abc_tree(tree)
185
tt = transform.TreeTransform(tree)
186
trans_id = tt.trans_id_tree_path('b/c')
187
parent_trans_id = tt.trans_id_tree_path('')
188
tt.adjust_path('e', parent_trans_id, trans_id)
189
tt.set_executability(True, trans_id)
191
return self._convert_tree(tree, converter)
193
def get_tree_with_subdirs_and_all_content_types(self):
194
"""Return a test tree with subdirs and all content types.
195
See get_tree_with_subdirs_and_all_supported_content_types for details.
197
return self.get_tree_with_subdirs_and_all_supported_content_types(True)
199
def get_tree_with_subdirs_and_all_supported_content_types(self, symlinks):
200
"""Return a test tree with subdirs and all supported content types.
201
Some content types may not be created on some platforms
202
(like symlinks on native win32)
204
:param symlinks: control is symlink should be created in the tree.
205
Note: if you wish to automatically set this
206
parameters depending on underlying system,
207
please use value returned
208
by bzrlib.osutils.has_symlinks() function.
210
The returned tree has the following inventory:
211
[('', inventory.ROOT_ID),
213
('1top-dir', '1top-dir'),
214
(u'2utf\u1234file', u'0utf\u1234file'),
215
('symlink', 'symlink'), # only if symlinks arg is True
216
('1top-dir/0file-in-1topdir', '1file-in-1topdir'),
217
('1top-dir/1dir-in-1topdir', '0dir-in-1topdir')]
218
where each component has the type of its name -
219
i.e. '1file..' is afile.
221
note that the order of the paths and fileids is deliberately
222
mismatched to ensure that the result order is path based.
224
tree = self.make_branch_and_tree('.')
228
'1top-dir/0file-in-1topdir',
229
'1top-dir/1dir-in-1topdir/'
234
u'0utf\u1234file'.encode('utf8'),
239
self.build_tree(paths)
242
'This platform does not support unicode file paths.')
244
tt = transform.TreeTransform(tree)
246
root_transaction_id = tt.trans_id_tree_path('')
247
tt.new_symlink('symlink',
248
root_transaction_id, 'link-target', 'symlink')
250
return self.workingtree_to_test_tree(tree)
252
def get_tree_with_utf8(self, tree):
253
"""Generate a tree with a utf8 revision and unicode paths."""
254
self._create_tree_with_utf8(tree)
255
return self.workingtree_to_test_tree(tree)
257
def _create_tree_with_utf8(self, tree):
258
"""Generate a tree with a utf8 revision and unicode paths."""
264
# bzr itself does not create unicode file ids, but we want them for
266
file_ids = ['TREE_ROOT',
272
self.build_tree(paths[1:])
274
raise tests.TestSkipped('filesystem does not support unicode.')
275
if tree.get_root_id() is None:
276
# Some trees do not have a root yet.
277
tree.add(paths, file_ids)
279
# Some trees will already have a root
280
tree.set_root_id(file_ids[0])
281
tree.add(paths[1:], file_ids[1:])
283
tree.commit(u'in\xedtial', rev_id=u'r\xe9v-1'.encode('utf8'))
284
except errors.NonAsciiRevisionId:
285
raise tests.TestSkipped('non-ascii revision ids not supported')
287
def get_tree_with_merged_utf8(self, tree):
288
"""Generate a tree with utf8 ancestors."""
289
self._create_tree_with_utf8(tree)
290
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
291
self.build_tree([u'tree2/b\xe5r/z\xf7z'])
292
tree2.add([u'b\xe5r/z\xf7z'], [u'z\xf7z-id'.encode('utf-8')])
293
tree2.commit(u'to m\xe9rge', rev_id=u'r\xe9v-2'.encode('utf8'))
295
tree.merge_from_branch(tree2.branch)
296
tree.commit(u'm\xe9rge', rev_id=u'r\xe9v-3'.encode('utf8'))
297
return self.workingtree_to_test_tree(tree)
300
class TreeTestProviderAdapter(WorkingTreeTestProviderAdapter):
301
"""Generate test suites for each Tree implementation in bzrlib.
303
Currently this covers all working tree formats, and RevisionTree and
304
DirStateRevisionTree by committing a working tree to create the revision
308
def __init__(self, transport_server, transport_readonly_server, formats):
309
"""Create a TreeTestProviderAdapter.
311
:param formats: [workingtree_format]
313
super(TreeTestProviderAdapter, self).__init__(transport_server,
314
transport_readonly_server, formats)
315
# now adjust the scenarios and add the non-working-tree tree scenarios.
316
for scenario in self.scenarios:
317
# for working tree adapted tests, preserve the tree
318
scenario[1]["_workingtree_to_test_tree"] = return_parameter
319
# add RevisionTree scenario
320
self.scenarios.append(self.create_tree_scenario(RevisionTree.__name__,
321
revision_tree_from_workingtree,))
323
# also test WorkingTree4's RevisionTree implementation which is
325
self.scenarios.append(self.create_tree_scenario(
326
DirStateRevisionTree.__name__, _dirstate_tree_from_workingtree,
327
WorkingTreeFormat4()))
328
self.scenarios.append(self.create_tree_scenario('PreviewTree',
331
def create_tree_scenario(self, name, converter, workingtree_format=None):
332
"""Create a scenario for the specified converter
334
:param name: The name to append to tests using this converter
335
:param converter: A function that converts a workingtree into the
337
:param workingtree_format: The particular workingtree format to
339
:return: a (name, options) tuple, where options is a dict of values
340
to be used as members of the TestCase.
342
if workingtree_format is None:
343
workingtree_format = WorkingTreeFormat._default_format
344
scenario_options = WorkingTreeTestProviderAdapter.create_scenario(self,
345
workingtree_format)[1]
346
scenario_options["_workingtree_to_test_tree"] = converter
347
return name, scenario_options
350
def load_tests(basic_tests, module, loader):
351
result = loader.suiteClass()
352
# add the tests for this module
353
result.addTests(basic_tests)
355
test_tree_implementations = [
356
'bzrlib.tests.tree_implementations.test_annotate_iter',
357
'bzrlib.tests.tree_implementations.test_get_file_mtime',
358
'bzrlib.tests.tree_implementations.test_get_root_id',
359
'bzrlib.tests.tree_implementations.test_get_symlink_target',
360
'bzrlib.tests.tree_implementations.test_inv',
361
'bzrlib.tests.tree_implementations.test_iter_search_rules',
362
'bzrlib.tests.tree_implementations.test_list_files',
363
'bzrlib.tests.tree_implementations.test_path_content_summary',
364
'bzrlib.tests.tree_implementations.test_revision_tree',
365
'bzrlib.tests.tree_implementations.test_test_trees',
366
'bzrlib.tests.tree_implementations.test_tree',
367
'bzrlib.tests.tree_implementations.test_walkdirs',
370
adapter = TreeTestProviderAdapter(
372
# None here will cause a readonly decorator to be created
373
# by the TestCaseWithTransport.get_readonly_transport method.
375
WorkingTreeFormat._formats.values() + _legacy_formats)
377
# add the tests for the sub modules
378
adapt_modules(test_tree_implementations, adapter, loader, result)