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.
35
from bzrlib.transport import get_transport
36
from bzrlib.tests import (
39
TestCaseWithTransport,
42
from bzrlib.tests.bzrdir_implementations.test_bzrdir import TestCaseWithBzrDir
43
from bzrlib.tests.workingtree_implementations import (
44
WorkingTreeTestProviderAdapter,
46
from bzrlib.revision import NULL_REVISION
47
from bzrlib.revisiontree import RevisionTree
48
from bzrlib.transform import TransformPreview
49
from bzrlib.workingtree import (
54
from bzrlib.workingtree_4 import (
60
def return_parameter(testcase, something):
61
"""A trivial thunk to return its input."""
65
def revision_tree_from_workingtree(testcase, tree):
66
"""Create a revision tree from a working tree."""
67
revid = tree.commit('save tree', allow_pointless=True, recursive=None)
68
return tree.branch.repository.revision_tree(revid)
71
def _dirstate_tree_from_workingtree(testcase, tree):
72
revid = tree.commit('save tree', allow_pointless=True, recursive=None)
73
return tree.basis_tree()
76
def preview_tree_pre(testcase, tree):
77
tt = TransformPreview(tree)
78
testcase.addCleanup(tt.finalize)
79
preview_tree = tt.get_preview_tree()
80
preview_tree.set_parent_ids(tree.get_parent_ids())
84
def preview_tree_post(testcase, tree):
85
basis = tree.basis_tree()
86
tt = TransformPreview(basis)
87
testcase.addCleanup(tt.finalize)
88
pp = progress.ProgressPhase('', 1, progress.DummyProgress())
90
testcase.addCleanup(tree.unlock)
91
transform._prepare_revert_transform(basis, tree, tt, None, False, pp,
93
preview_tree = tt.get_preview_tree()
94
preview_tree.set_parent_ids(tree.get_parent_ids())
98
class TestTreeImplementationSupport(TestCaseWithTransport):
100
def test_revision_tree_from_workingtree(self):
101
tree = self.make_branch_and_tree('.')
102
tree = revision_tree_from_workingtree(self, tree)
103
self.assertIsInstance(tree, RevisionTree)
106
class TestCaseWithTree(TestCaseWithBzrDir):
108
def make_branch_and_tree(self, relpath):
109
made_control = self.make_bzrdir(relpath, format=
110
self.workingtree_format._matchingbzrdir)
111
made_control.create_repository()
112
made_control.create_branch()
113
return self.workingtree_format.initialize(made_control)
115
def workingtree_to_test_tree(self, tree):
116
return self._workingtree_to_test_tree(self, tree)
118
def _convert_tree(self, tree, converter=None):
119
"""helper to convert using the converter or a supplied one."""
120
# convert that to the final shape
121
if converter is None:
122
converter = self.workingtree_to_test_tree
123
return converter(tree)
125
def get_tree_no_parents_no_content(self, empty_tree, converter=None):
126
"""Make a tree with no parents and no contents from empty_tree.
128
:param empty_tree: A working tree with no content and no parents to
131
empty_tree.set_root_id('empty-root-id')
132
return self._convert_tree(empty_tree, converter)
134
def _make_abc_tree(self, tree):
135
"""setup an abc content tree."""
136
files = ['a', 'b/', 'b/c']
137
self.build_tree(files, line_endings='binary',
138
transport=tree.bzrdir.root_transport)
139
tree.set_root_id('root-id')
140
tree.add(files, ['a-id', 'b-id', 'c-id'])
142
def get_tree_no_parents_abc_content(self, tree, converter=None):
143
"""return a test tree with a, b/, b/c contents."""
144
self._make_abc_tree(tree)
145
return self._convert_tree(tree, converter)
147
def get_tree_no_parents_abc_content_2(self, tree, converter=None):
148
"""return a test tree with a, b/, b/c contents.
150
This variation changes the content of 'a' to foobar\n.
152
self._make_abc_tree(tree)
153
f = open(tree.basedir + '/a', 'wb')
158
return self._convert_tree(tree, converter)
160
def get_tree_no_parents_abc_content_3(self, tree, converter=None):
161
"""return a test tree with a, b/, b/c contents.
163
This variation changes the executable flag of b/c to True.
165
self._make_abc_tree(tree)
166
tt = transform.TreeTransform(tree)
167
trans_id = tt.trans_id_tree_path('b/c')
168
tt.set_executability(True, trans_id)
170
return self._convert_tree(tree, converter)
172
def get_tree_no_parents_abc_content_4(self, tree, converter=None):
173
"""return a test tree with d, b/, b/c contents.
175
This variation renames a to d.
177
self._make_abc_tree(tree)
178
tree.rename_one('a', 'd')
179
return self._convert_tree(tree, converter)
181
def get_tree_no_parents_abc_content_5(self, tree, converter=None):
182
"""return a test tree with d, b/, b/c contents.
184
This variation renames a to d and alters its content to 'bar\n'.
186
self._make_abc_tree(tree)
187
tree.rename_one('a', 'd')
188
f = open(tree.basedir + '/d', 'wb')
193
return self._convert_tree(tree, converter)
195
def get_tree_no_parents_abc_content_6(self, tree, converter=None):
196
"""return a test tree with a, b/, e contents.
198
This variation renames b/c to e, and makes it executable.
200
self._make_abc_tree(tree)
201
tt = transform.TreeTransform(tree)
202
trans_id = tt.trans_id_tree_path('b/c')
203
parent_trans_id = tt.trans_id_tree_path('')
204
tt.adjust_path('e', parent_trans_id, trans_id)
205
tt.set_executability(True, trans_id)
207
return self._convert_tree(tree, converter)
209
def get_tree_with_subdirs_and_all_content_types(self):
210
"""Return a test tree with subdirs and all content types.
211
See get_tree_with_subdirs_and_all_supported_content_types for details.
213
return self.get_tree_with_subdirs_and_all_supported_content_types(True)
215
def get_tree_with_subdirs_and_all_supported_content_types(self, symlinks):
216
"""Return a test tree with subdirs and all supported content types.
217
Some content types may not be created on some platforms
218
(like symlinks on native win32)
220
:param symlinks: control is symlink should be created in the tree.
221
Note: if you wish to automatically set this
222
parameters depending on underlying system,
223
please use value returned
224
by bzrlib.osutils.has_symlinks() function.
226
The returned tree has the following inventory:
227
[('', inventory.ROOT_ID),
229
('1top-dir', '1top-dir'),
230
(u'2utf\u1234file', u'0utf\u1234file'),
231
('symlink', 'symlink'), # only if symlinks arg is True
232
('1top-dir/0file-in-1topdir', '1file-in-1topdir'),
233
('1top-dir/1dir-in-1topdir', '0dir-in-1topdir')]
234
where each component has the type of its name -
235
i.e. '1file..' is afile.
237
note that the order of the paths and fileids is deliberately
238
mismatched to ensure that the result order is path based.
240
tree = self.make_branch_and_tree('.')
244
'1top-dir/0file-in-1topdir',
245
'1top-dir/1dir-in-1topdir/'
250
u'0utf\u1234file'.encode('utf8'),
255
self.build_tree(paths)
258
'This platform does not support unicode file paths.')
260
tt = transform.TreeTransform(tree)
262
root_transaction_id = tt.trans_id_tree_path('')
263
tt.new_symlink('symlink',
264
root_transaction_id, 'link-target', 'symlink')
266
return self.workingtree_to_test_tree(tree)
268
def get_tree_with_utf8(self, tree):
269
"""Generate a tree with a utf8 revision and unicode paths."""
270
self._create_tree_with_utf8(tree)
271
return self.workingtree_to_test_tree(tree)
273
def _create_tree_with_utf8(self, tree):
274
"""Generate a tree with a utf8 revision and unicode paths."""
275
# We avoid combining characters in file names here, normalization
276
# checks (as performed by some file systems (OSX) are outside the scope
277
# of these tests). We use the euro sign \N{Euro Sign} or \u20ac in
278
# unicode strings or '\xe2\x82\ac' (its utf-8 encoding) in raw strings.
281
u'ba\N{Euro Sign}r/',
282
u'ba\N{Euro Sign}r/ba\N{Euro Sign}z',
284
# bzr itself does not create unicode file ids, but we want them for
286
file_ids = ['TREE_ROOT',
287
'fo\xe2\x82\xaco-id',
288
'ba\xe2\x82\xacr-id',
289
'ba\xe2\x82\xacz-id',
292
self.build_tree(paths[1:])
294
raise tests.TestSkipped('filesystem does not support unicode.')
295
if tree.get_root_id() is None:
296
# Some trees do not have a root yet.
297
tree.add(paths, file_ids)
299
# Some trees will already have a root
300
tree.set_root_id(file_ids[0])
301
tree.add(paths[1:], file_ids[1:])
303
tree.commit(u'in\xedtial', rev_id=u'r\xe9v-1'.encode('utf8'))
304
except errors.NonAsciiRevisionId:
305
raise tests.TestSkipped('non-ascii revision ids not supported')
307
def get_tree_with_merged_utf8(self, tree):
308
"""Generate a tree with utf8 ancestors."""
309
self._create_tree_with_utf8(tree)
310
tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
311
self.build_tree([u'tree2/ba\N{Euro Sign}r/qu\N{Euro Sign}x'])
312
tree2.add([u'ba\N{Euro Sign}r/qu\N{Euro Sign}x'],
313
[u'qu\N{Euro Sign}x-id'.encode('utf-8')])
314
tree2.commit(u'to m\xe9rge', rev_id=u'r\xe9v-2'.encode('utf8'))
316
tree.merge_from_branch(tree2.branch)
317
tree.commit(u'm\xe9rge', rev_id=u'r\xe9v-3'.encode('utf8'))
318
return self.workingtree_to_test_tree(tree)
321
class TreeTestProviderAdapter(WorkingTreeTestProviderAdapter):
322
"""Generate test suites for each Tree implementation in bzrlib.
324
Currently this covers all working tree formats, and RevisionTree and
325
DirStateRevisionTree by committing a working tree to create the revision
329
def __init__(self, transport_server, transport_readonly_server, formats):
330
"""Create a TreeTestProviderAdapter.
332
:param formats: [workingtree_format]
334
super(TreeTestProviderAdapter, self).__init__(transport_server,
335
transport_readonly_server, formats)
336
# now adjust the scenarios and add the non-working-tree tree scenarios.
337
for scenario in self.scenarios:
338
# for working tree adapted tests, preserve the tree
339
scenario[1]["_workingtree_to_test_tree"] = return_parameter
340
# add RevisionTree scenario
341
self.scenarios.append(self.create_tree_scenario(RevisionTree.__name__,
342
revision_tree_from_workingtree,))
344
# also test WorkingTree4's RevisionTree implementation which is
346
self.scenarios.append(self.create_tree_scenario(
347
DirStateRevisionTree.__name__, _dirstate_tree_from_workingtree,
348
WorkingTreeFormat4()))
349
self.scenarios.append(self.create_tree_scenario('PreviewTree',
351
self.scenarios.append(self.create_tree_scenario('PreviewTreePost',
354
def create_tree_scenario(self, name, converter, workingtree_format=None):
355
"""Create a scenario for the specified converter
357
:param name: The name to append to tests using this converter
358
:param converter: A function that converts a workingtree into the
360
:param workingtree_format: The particular workingtree format to
362
:return: a (name, options) tuple, where options is a dict of values
363
to be used as members of the TestCase.
365
if workingtree_format is None:
366
workingtree_format = WorkingTreeFormat._default_format
367
scenario_options = WorkingTreeTestProviderAdapter.create_scenario(self,
368
workingtree_format)[1]
369
scenario_options["_workingtree_to_test_tree"] = converter
370
return name, scenario_options
373
def load_tests(basic_tests, module, loader):
374
result = loader.suiteClass()
375
# add the tests for this module
376
result.addTests(basic_tests)
378
test_tree_implementations = [
379
'bzrlib.tests.tree_implementations.test_annotate_iter',
380
'bzrlib.tests.tree_implementations.test_get_file_mtime',
381
'bzrlib.tests.tree_implementations.test_get_root_id',
382
'bzrlib.tests.tree_implementations.test_get_symlink_target',
383
'bzrlib.tests.tree_implementations.test_inv',
384
'bzrlib.tests.tree_implementations.test_iter_search_rules',
385
'bzrlib.tests.tree_implementations.test_list_files',
386
'bzrlib.tests.tree_implementations.test_path_content_summary',
387
'bzrlib.tests.tree_implementations.test_revision_tree',
388
'bzrlib.tests.tree_implementations.test_test_trees',
389
'bzrlib.tests.tree_implementations.test_tree',
390
'bzrlib.tests.tree_implementations.test_walkdirs',
393
adapter = TreeTestProviderAdapter(
395
# None here will cause a readonly decorator to be created
396
# by the TestCaseWithTransport.get_readonly_transport method.
398
WorkingTreeFormat._formats.values() + _legacy_formats)
400
# add the tests for the sub modules
401
adapt_modules(test_tree_implementations, adapter, loader, result)