15
15
# along with this program; if not, write to the Free Software
16
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
from cStringIO import StringIO
19
22
from bzrlib.branch import Branch
20
from bzrlib.selftest import TestCaseInTempDir
23
import bzrlib.bzrdir as bzrdir
24
from bzrlib.bzrdir import BzrDir
25
from bzrlib.conflicts import *
26
import bzrlib.errors as errors
27
from bzrlib.errors import NotBranchError, NotVersionedError
28
from bzrlib.lockdir import LockDir
29
from bzrlib.osutils import pathjoin, getcwd, has_symlinks
30
from bzrlib.tests import TestCaseWithTransport, TestSkipped
21
31
from bzrlib.trace import mutter
32
from bzrlib.transport import get_transport
33
import bzrlib.workingtree as workingtree
22
34
from bzrlib.workingtree import (TreeEntry, TreeDirectory, TreeFile, TreeLink,
25
class TestTreeDirectory(TestCaseInTempDir):
37
class TestTreeDirectory(TestCaseWithTransport):
27
39
def test_kind_character(self):
28
40
self.assertEqual(TreeDirectory().kind_character(), '/')
31
class TestTreeEntry(TestCaseInTempDir):
43
class TestTreeEntry(TestCaseWithTransport):
33
45
def test_kind_character(self):
34
46
self.assertEqual(TreeEntry().kind_character(), '???')
37
class TestTreeFile(TestCaseInTempDir):
49
class TestTreeFile(TestCaseWithTransport):
39
51
def test_kind_character(self):
40
52
self.assertEqual(TreeFile().kind_character(), '')
43
class TestTreeLink(TestCaseInTempDir):
55
class TestTreeLink(TestCaseWithTransport):
45
57
def test_kind_character(self):
46
58
self.assertEqual(TreeLink().kind_character(), '')
49
class TestWorkingTree(TestCaseInTempDir):
51
def test_listfiles(self):
52
branch = Branch.initialize('.')
54
print >> open('file', 'w'), "content"
55
os.symlink('target', 'symlink')
56
tree = branch.working_tree()
57
files = list(tree.list_files())
58
self.assertEqual(files[0], ('dir', '?', 'directory', None, TreeDirectory()))
59
self.assertEqual(files[1], ('file', '?', 'file', None, TreeFile()))
60
self.assertEqual(files[2], ('symlink', '?', 'symlink', None, TreeLink()))
62
def test_construct_with_branch(self):
63
branch = Branch.initialize('.')
64
tree = WorkingTree(branch.base, branch)
65
self.assertEqual(branch, tree.branch)
66
self.assertEqual(branch.inventory, tree._inventory)
67
self.assertEqual(branch.base, tree.basedir)
61
class TestDefaultFormat(TestCaseWithTransport):
63
def test_get_set_default_format(self):
64
old_format = workingtree.WorkingTreeFormat.get_default_format()
66
self.assertTrue(isinstance(old_format, workingtree.WorkingTreeFormat3))
67
workingtree.WorkingTreeFormat.set_default_format(SampleTreeFormat())
69
# the default branch format is used by the meta dir format
70
# which is not the default bzrdir format at this point
71
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
72
dir.create_repository()
74
result = dir.create_workingtree()
75
self.assertEqual(result, 'A tree')
77
workingtree.WorkingTreeFormat.set_default_format(old_format)
78
self.assertEqual(old_format, workingtree.WorkingTreeFormat.get_default_format())
81
class SampleTreeFormat(workingtree.WorkingTreeFormat):
84
this format is initializable, unsupported to aid in testing the
85
open and open_downlevel routines.
88
def get_format_string(self):
89
"""See WorkingTreeFormat.get_format_string()."""
90
return "Sample tree format."
92
def initialize(self, a_bzrdir, revision_id=None):
93
"""Sample branches cannot be created."""
94
t = a_bzrdir.get_workingtree_transport(self)
95
t.put('format', StringIO(self.get_format_string()))
98
def is_supported(self):
101
def open(self, transport, _found=False):
102
return "opened tree."
105
class TestWorkingTreeFormat(TestCaseWithTransport):
106
"""Tests for the WorkingTreeFormat facility."""
108
def test_find_format(self):
109
# is the right format object found for a working tree?
110
# create a branch with a few known format objects.
111
self.build_tree(["foo/", "bar/"])
112
def check_format(format, url):
113
dir = format._matchingbzrdir.initialize(url)
114
dir.create_repository()
116
format.initialize(dir)
117
t = get_transport(url)
118
found_format = workingtree.WorkingTreeFormat.find_format(dir)
119
self.failUnless(isinstance(found_format, format.__class__))
120
check_format(workingtree.WorkingTreeFormat3(), "bar")
122
def test_find_format_no_tree(self):
123
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
124
self.assertRaises(errors.NoWorkingTree,
125
workingtree.WorkingTreeFormat.find_format,
128
def test_find_format_unknown_format(self):
129
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
130
dir.create_repository()
132
SampleTreeFormat().initialize(dir)
133
self.assertRaises(errors.UnknownFormatError,
134
workingtree.WorkingTreeFormat.find_format,
137
def test_register_unregister_format(self):
138
format = SampleTreeFormat()
140
dir = bzrdir.BzrDirMetaFormat1().initialize('.')
141
dir.create_repository()
144
format.initialize(dir)
145
# register a format for it.
146
workingtree.WorkingTreeFormat.register_format(format)
147
# which branch.Open will refuse (not supported)
148
self.assertRaises(errors.UnsupportedFormatError, workingtree.WorkingTree.open, '.')
149
# but open_downlevel will work
150
self.assertEqual(format.open(dir), workingtree.WorkingTree.open_downlevel('.'))
151
# unregister the format
152
workingtree.WorkingTreeFormat.unregister_format(format)
155
class TestWorkingTreeFormat3(TestCaseWithTransport):
156
"""Tests specific to WorkingTreeFormat3."""
158
def test_disk_layout(self):
159
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
160
control.create_repository()
161
control.create_branch()
162
tree = workingtree.WorkingTreeFormat3().initialize(control)
164
# format 'Bazaar-NG Working Tree format 3'
165
# inventory = blank inventory
166
# pending-merges = ''
168
# no inventory.basis yet
169
t = control.get_workingtree_transport(None)
170
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
171
t.get('format').read())
172
self.assertEqualDiff('<inventory format="5">\n'
174
t.get('inventory').read())
175
self.assertEqualDiff('### bzr hashcache v5\n',
176
t.get('stat-cache').read())
177
self.assertFalse(t.has('inventory.basis'))
178
# no last-revision file means 'None' or 'NULLREVISION'
179
self.assertFalse(t.has('last-revision'))
180
# TODO RBC 20060210 do a commit, check the inventory.basis is created
181
# correctly and last-revision file becomes present.
183
def test_uses_lockdir(self):
184
"""WorkingTreeFormat3 uses its own LockDir:
186
- lock is a directory
187
- when the WorkingTree is locked, LockDir can see that
189
t = self.get_transport()
191
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
192
repo = dir.create_repository()
193
branch = dir.create_branch()
195
tree = workingtree.WorkingTreeFormat3().initialize(dir)
196
except errors.NotLocalUrl:
197
raise TestSkipped('Not a local URL')
198
self.assertIsDirectory('.bzr', t)
199
self.assertIsDirectory('.bzr/checkout', t)
200
self.assertIsDirectory('.bzr/checkout/lock', t)
201
our_lock = LockDir(t, '.bzr/checkout/lock')
202
self.assertEquals(our_lock.peek(), None)
204
self.assertTrue(our_lock.peek())
206
self.assertEquals(our_lock.peek(), None)
209
class TestFormat2WorkingTree(TestCaseWithTransport):
210
"""Tests that are specific to format 2 trees."""
212
def create_format2_tree(self, url):
213
return self.make_branch_and_tree(
214
url, format=bzrlib.bzrdir.BzrDirFormat6())
216
def test_conflicts(self):
217
# test backwards compatability
218
tree = self.create_format2_tree('.')
219
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
221
file('lala.BASE', 'wb').write('labase')
222
expected = ContentsConflict('lala')
223
self.assertEqual(list(tree.conflicts()), [expected])
224
file('lala', 'wb').write('la')
225
tree.add('lala', 'lala-id')
226
expected = ContentsConflict('lala', file_id='lala-id')
227
self.assertEqual(list(tree.conflicts()), [expected])
228
file('lala.THIS', 'wb').write('lathis')
229
file('lala.OTHER', 'wb').write('laother')
230
# When "text conflict"s happen, stem, THIS and OTHER are text
231
expected = TextConflict('lala', file_id='lala-id')
232
self.assertEqual(list(tree.conflicts()), [expected])
233
os.unlink('lala.OTHER')
234
os.mkdir('lala.OTHER')
235
expected = ContentsConflict('lala', file_id='lala-id')
236
self.assertEqual(list(tree.conflicts()), [expected])
239
class TestNonFormatSpecificCode(TestCaseWithTransport):
240
"""This class contains tests of workingtree that are not format specific."""
69
def test_construct_without_branch(self):
70
branch = Branch.initialize('.')
71
tree = WorkingTree(branch.base)
72
self.assertEqual(branch.base, tree.branch.base)
73
self.assertEqual(branch.inventory, tree._inventory)
74
self.assertEqual(branch.base, tree.basedir)
76
def test_basic_relpath(self):
77
# for comprehensive relpath tests, see whitebox.py.
78
branch = Branch.initialize('.')
79
tree = WorkingTree(branch.base)
80
self.assertEqual('child',
81
tree.relpath(os.path.join(os.getcwd(), 'child')))
83
def test_lock_locks_branch(self):
84
branch = Branch.initialize('.')
85
tree = WorkingTree(branch.base)
87
self.assertEqual(1, tree.branch._lock_count)
88
self.assertEqual('r', tree.branch._lock_mode)
90
self.assertEqual(None, tree.branch._lock_count)
92
self.assertEqual(1, tree.branch._lock_count)
93
self.assertEqual('w', tree.branch._lock_mode)
95
self.assertEqual(None, tree.branch._lock_count)
97
def get_pullable_branches(self):
98
self.build_tree(['from/', 'from/file', 'to/'])
99
br_a = Branch.initialize('from')
101
br_a.commit('foo', rev_id='A')
102
br_b = Branch.initialize('to')
106
br_a, br_b = self.get_pullable_branches()
107
br_b.working_tree().pull(br_a)
108
self.failUnless(br_b.has_revision('A'))
109
self.assertEqual(['A'], br_b.revision_history())
111
def test_pull_clobbers(self):
112
br_a, br_b = self.get_pullable_branches()
113
br_b.commit('foo', rev_id='B')
114
self.assertEqual(['B'], br_b.revision_history())
115
br_b.working_tree().pull(br_a, clobber=True)
116
self.failUnless(br_b.has_revision('A'))
117
self.failUnless(br_b.has_revision('B'))
118
self.assertEqual(['A'], br_b.revision_history())
243
def test_gen_file_id(self):
244
self.assertStartsWith(bzrlib.workingtree.gen_file_id('bar'), 'bar-')
245
self.assertStartsWith(bzrlib.workingtree.gen_file_id('Mwoo oof\t m'), 'Mwoooofm-')
246
self.assertStartsWith(bzrlib.workingtree.gen_file_id('..gam.py'), 'gam.py-')
247
self.assertStartsWith(bzrlib.workingtree.gen_file_id('..Mwoo oof\t m'), 'Mwoooofm-')
249
def test_next_id_suffix(self):
250
bzrlib.workingtree._gen_id_suffix = None
251
bzrlib.workingtree._next_id_suffix()
252
self.assertNotEqual(None, bzrlib.workingtree._gen_id_suffix)
253
bzrlib.workingtree._gen_id_suffix = "foo-"
254
bzrlib.workingtree._gen_id_serial = 1
255
self.assertEqual("foo-2", bzrlib.workingtree._next_id_suffix())
256
self.assertEqual("foo-3", bzrlib.workingtree._next_id_suffix())
257
self.assertEqual("foo-4", bzrlib.workingtree._next_id_suffix())
258
self.assertEqual("foo-5", bzrlib.workingtree._next_id_suffix())
259
self.assertEqual("foo-6", bzrlib.workingtree._next_id_suffix())
260
self.assertEqual("foo-7", bzrlib.workingtree._next_id_suffix())
261
self.assertEqual("foo-8", bzrlib.workingtree._next_id_suffix())
262
self.assertEqual("foo-9", bzrlib.workingtree._next_id_suffix())
263
self.assertEqual("foo-10", bzrlib.workingtree._next_id_suffix())
265
def test__translate_ignore_rule(self):
266
tree = self.make_branch_and_tree('.')
267
# translation should return the regex, the number of groups in it,
268
# and the original rule in a tuple.
269
# there are three sorts of ignore rules:
270
# root only - regex is the rule itself without the leading ./
273
tree._translate_ignore_rule("./rootdirrule"))
274
# full path - regex is the rule itself
276
"(path\\/to\\/file$)",
277
tree._translate_ignore_rule("path/to/file"))
278
# basename only rule - regex is a rule that ignores everything up
279
# to the last / in the filename
281
"((?:.*/)?(?!.*/)basenamerule$)",
282
tree._translate_ignore_rule("basenamerule"))
284
def test__combine_ignore_rules(self):
285
tree = self.make_branch_and_tree('.')
286
# the combined ignore regexs need the outer group indices
287
# placed in a dictionary with the rules that were combined.
288
# an empty set of rules
289
# this is returned as a list of combined regex,rule sets, because
290
# python has a limit of 100 combined regexes.
291
compiled_rules = tree._combine_ignore_rules([])
292
self.assertEqual([], compiled_rules)
293
# one of each type of rule.
294
compiled_rules = tree._combine_ignore_rules(
295
["rule1", "rule/two", "./three"])[0]
296
# what type *is* the compiled regex to do an isinstance of ?
297
self.assertEqual(3, compiled_rules[0].groups)
299
{0:"rule1",1:"rule/two",2:"./three"},
302
def test__combine_ignore_rules_grouping(self):
303
tree = self.make_branch_and_tree('.')
304
# when there are too many rules, the output is split into groups of 100
306
for index in range(198):
308
self.assertEqual(2, len(tree._combine_ignore_rules(rules)))
310
def test__get_ignore_rules_as_regex(self):
311
tree = self.make_branch_and_tree('.')
312
# test against the default rules.
313
reference_output = tree._combine_ignore_rules(bzrlib.DEFAULT_IGNORE)[0]
314
regex_rules = tree._get_ignore_rules_as_regex()[0]
315
self.assertEqual(len(reference_output[1]), regex_rules[0].groups)
316
self.assertEqual(reference_output[1], regex_rules[1])