169
174
t = control.get_workingtree_transport(None)
170
175
self.assertEqualDiff('Bazaar-NG Working Tree format 3',
171
176
t.get('format').read())
177
# self.assertContainsRe(t.get('inventory').read(),
178
# '<inventory file_id="[^"]*" format="5">\n'
181
# WorkingTreeFormat3 doesn't default to creating a unique root id,
182
# because it is incompatible with older bzr versions
183
self.assertContainsRe(t.get('inventory').read(),
184
'<inventory format="5">\n'
187
self.assertEqualDiff('### bzr hashcache v5\n',
188
t.get('stat-cache').read())
189
self.assertFalse(t.has('inventory.basis'))
190
# no last-revision file means 'None' or 'NULLREVISION'
191
self.assertFalse(t.has('last-revision'))
192
# TODO RBC 20060210 do a commit, check the inventory.basis is created
193
# correctly and last-revision file becomes present.
195
def test_uses_lockdir(self):
196
"""WorkingTreeFormat3 uses its own LockDir:
198
- lock is a directory
199
- when the WorkingTree is locked, LockDir can see that
201
t = self.get_transport()
203
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
204
repo = dir.create_repository()
205
branch = dir.create_branch()
207
tree = workingtree.WorkingTreeFormat3().initialize(dir)
208
except errors.NotLocalUrl:
209
raise TestSkipped('Not a local URL')
210
self.assertIsDirectory('.bzr', t)
211
self.assertIsDirectory('.bzr/checkout', t)
212
self.assertIsDirectory('.bzr/checkout/lock', t)
213
our_lock = LockDir(t, '.bzr/checkout/lock')
214
self.assertEquals(our_lock.peek(), None)
216
self.assertTrue(our_lock.peek())
218
self.assertEquals(our_lock.peek(), None)
220
def test_missing_pending_merges(self):
221
control = bzrdir.BzrDirMetaFormat1().initialize(self.get_url())
222
control.create_repository()
223
control.create_branch()
224
tree = workingtree.WorkingTreeFormat3().initialize(control)
225
tree._control_files._transport.delete("pending-merges")
226
self.assertEqual([], tree.get_parent_ids())
229
class TestWorkingTreeFormat4(TestCaseWithTransport):
230
"""Tests specific to WorkingTreeFormat4."""
232
def test_disk_layout(self):
233
control = bzrdir.BzrDir.create(self.get_url())
234
control.create_repository()
235
control.create_branch()
236
tree = workingtree.WorkingTreeFormat4().initialize(control)
238
# format 'Bazaar Working Tree format 4'
239
# inventory = blank inventory
240
# pending-merges = ''
242
# no inventory.basis yet
243
t = control.get_workingtree_transport(None)
244
self.assertEqualDiff('Bazaar Working Tree format 4',
245
t.get('format').read())
172
246
self.assertEqualDiff('<inventory format="5">\n'
173
247
'</inventory>\n',
174
248
t.get('inventory').read())
179
253
self.assertFalse(t.has('last-revision'))
180
254
# TODO RBC 20060210 do a commit, check the inventory.basis is created
181
255
# correctly and last-revision file becomes present.
256
# manually make a dirstate toc check the format is as desired.
257
state = dirstate.DirState.on_file(t.local_abspath('dirstate'))
258
self.assertEqual([], state.get_parent_ids())
183
260
def test_uses_lockdir(self):
184
"""WorkingTreeFormat3 uses its own LockDir:
261
"""WorkingTreeFormat4 uses its own LockDir:
186
263
- lock is a directory
187
264
- when the WorkingTree is locked, LockDir can see that
266
# this test could be factored into a subclass of tests common to both
267
# format 3 and 4, but for now its not much of an issue as there is only one in common.
189
268
t = self.get_transport()
269
tree = self.make_workingtree()
270
self.assertIsDirectory('.bzr', t)
271
self.assertIsDirectory('.bzr/checkout', t)
272
self.assertIsDirectory('.bzr/checkout/lock', t)
273
our_lock = LockDir(t, '.bzr/checkout/lock')
274
self.assertEquals(our_lock.peek(), None)
276
self.assertTrue(our_lock.peek())
278
self.assertEquals(our_lock.peek(), None)
280
def make_workingtree(self):
190
281
url = self.get_url()
191
282
dir = bzrdir.BzrDirMetaFormat1().initialize(url)
192
283
repo = dir.create_repository()
193
284
branch = dir.create_branch()
195
tree = workingtree.WorkingTreeFormat3().initialize(dir)
286
return workingtree.WorkingTreeFormat4().initialize(dir)
196
287
except errors.NotLocalUrl:
197
288
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)
290
# TODO: test that dirstate also stores & retrieves the parent list of
291
# workingtree-parent revisions, including when they have multiple parents.
292
# (in other words, the case when we're constructing a merge of
293
# revisions which are themselves merges.)
295
# The simplest case is that the the workingtree's primary
296
# parent tree can be retrieved. This is required for all WorkingTrees,
297
# and covered by the generic tests.
299
def test_dirstate_stores_all_parent_inventories(self):
300
tree = self.make_workingtree()
302
# We're going to build in tree a working tree
303
# with three parent trees, with some files in common.
305
# We really don't want to do commit or merge in the new dirstate-based
306
# tree, because that might not work yet. So instead we build
307
# revisions elsewhere and pull them across, doing by hand part of the
308
# work that merge would do.
310
subtree = self.make_branch_and_tree('subdir')
311
self.build_tree(['subdir/file-a',])
312
subtree.add(['file-a'], ['id-a'])
313
rev1 = subtree.commit('commit in subdir')
314
rev1_tree = subtree.basis_tree()
316
subtree2 = subtree.bzrdir.sprout('subdir2').open_workingtree()
317
self.build_tree(['subdir2/file-b'])
318
subtree2.add(['file-b'], ['id-b'])
319
rev2 = subtree2.commit('commit in subdir2')
320
rev2_tree = subtree2.basis_tree()
322
subtree.merge_from_branch(subtree2.branch)
323
rev3 = subtree.commit('merge from subdir2')
324
rev3_tree = subtree.basis_tree()
326
repo = tree.branch.repository
327
repo.fetch(subtree.branch.repository, rev3)
328
# will also pull the others...
330
# tree doesn't contain a text merge yet but we'll just
331
# set the parents as if a merge had taken place.
332
# this should cause the tree data to be folded into the
334
## import pdb;pdb.set_trace()
335
tree.set_parent_trees([
338
(rev3, rev3_tree), ])
340
# now we should be able to get them back out
341
self.assertTreesEqual(tree.revision_tree(rev1), rev1_tree)
342
self.assertTreesEqual(tree.revision_tree(rev2), rev2_tree)
343
self.assertTreesEqual(tree.revision_tree(rev3), rev3_tree)
345
def test_dirstate_doesnt_read_parents_from_repo_when_setting(self):
346
"""Setting parent trees on a dirstate working tree takes
347
the trees it's given and doesn't need to read them from the
350
tree = self.make_workingtree()
352
subtree = self.make_branch_and_tree('subdir')
353
rev1 = subtree.commit('commit in subdir')
354
rev1_tree = subtree.basis_tree()
356
tree.branch.pull(subtree.branch)
358
# break the repository's legs to make sure it only uses the trees
359
# it's given; any calls to forbidden methods will raise an
361
repo = tree.branch.repository
362
repo.get_revision = self.fail
363
repo.get_inventory = self.fail
364
repo.get_inventory_xml = self.fail
365
# try to set the parent trees.
366
tree.set_parent_trees([(rev1, rev1_tree)])
368
def test_dirstate_doesnt_read_from_repo_when_returning_cache_tree(self):
369
"""Getting parent trees from a dirstate tree does not read from the
370
repos inventory store. This is an important part of the dirstate
371
performance optimisation work.
373
tree = self.make_workingtree()
375
subtree = self.make_branch_and_tree('subdir')
376
rev1 = subtree.commit('commit in subdir')
377
rev1_tree = subtree.basis_tree()
378
rev2 = subtree.commit('second commit in subdir', allow_pointless=True)
379
rev2_tree = subtree.basis_tree()
381
tree.branch.pull(subtree.branch)
383
# break the repository's legs to make sure it only uses the trees
384
# it's given; any calls to forbidden methods will raise an
386
repo = tree.branch.repository
387
repo.get_revision = self.fail
388
repo.get_inventory = self.fail
389
repo.get_inventory_xml = self.fail
390
# set the parent trees.
391
tree.set_parent_trees([(rev1, rev1_tree), (rev2, rev2_tree)])
392
# read the first tree
393
result_rev1_tree = tree.revision_tree(rev1)
395
result_rev2_tree = tree.revision_tree(rev2)
396
# compare - there should be no differences between the handed and
398
self.assertTreesEqual(rev1_tree, result_rev1_tree)
399
self.assertTreesEqual(rev2_tree, result_rev2_tree)
401
def test_dirstate_doesnt_cache_non_parent_trees(self):
402
"""Getting parent trees from a dirstate tree does not read from the
403
repos inventory store. This is an important part of the dirstate
404
performance optimisation work.
406
tree = self.make_workingtree()
408
# make a tree that we can try for, which is able to be returned but
410
subtree = self.make_branch_and_tree('subdir')
411
rev1 = subtree.commit('commit in subdir')
412
tree.branch.pull(subtree.branch)
414
self.assertRaises(errors.NoSuchRevision, tree.revision_tree, rev1)
416
def test_no_dirstate_outside_lock(self):
417
# temporary test until the code is mature enough to test from outside.
418
"""Getting a dirstate object fails if there is no lock."""
419
def lock_and_call_current_dirstate(tree, lock_method):
420
getattr(tree, lock_method)()
421
tree.current_dirstate()
423
tree = self.make_workingtree()
424
self.assertRaises(errors.NotWriteLocked, tree.current_dirstate)
425
lock_and_call_current_dirstate(tree, 'lock_read')
426
self.assertRaises(errors.NotWriteLocked, tree.current_dirstate)
427
lock_and_call_current_dirstate(tree, 'lock_write')
428
self.assertRaises(errors.NotWriteLocked, tree.current_dirstate)
429
lock_and_call_current_dirstate(tree, 'lock_tree_write')
430
self.assertRaises(errors.NotWriteLocked, tree.current_dirstate)
432
def test_new_dirstate_on_new_lock(self):
433
# until we have detection for when a dirstate can be reused, we
434
# want to reparse dirstate on every new lock.
435
known_dirstates = set()
436
def lock_and_compare_all_current_dirstate(tree, lock_method):
437
getattr(tree, lock_method)()
438
state = tree.current_dirstate()
439
self.assertFalse(state in known_dirstates)
440
known_dirstates.add(state)
442
tree = self.make_workingtree()
443
# lock twice with each type to prevent silly per-lock-type bugs.
444
# each lock and compare looks for a unique state object.
445
lock_and_compare_all_current_dirstate(tree, 'lock_read')
446
lock_and_compare_all_current_dirstate(tree, 'lock_read')
447
lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
448
lock_and_compare_all_current_dirstate(tree, 'lock_tree_write')
449
lock_and_compare_all_current_dirstate(tree, 'lock_write')
450
lock_and_compare_all_current_dirstate(tree, 'lock_write')
453
class TestFormat2WorkingTree(TestCaseWithTransport):
454
"""Tests that are specific to format 2 trees."""
208
456
def create_format2_tree(self, url):
209
return BzrDir.create_standalone_workingtree(url)
457
return self.make_branch_and_tree(
458
url, format=bzrlib.bzrdir.BzrDirFormat6())
211
def test_conflicts_format2(self):
460
def test_conflicts(self):
212
461
# test backwards compatability
213
462
tree = self.create_format2_tree('.')
214
463
self.assertRaises(errors.UnsupportedOperation, tree.set_conflicts,
216
465
file('lala.BASE', 'wb').write('labase')
217
expected = ContentsConflict('lala')
466
expected = conflicts.ContentsConflict('lala')
218
467
self.assertEqual(list(tree.conflicts()), [expected])
219
468
file('lala', 'wb').write('la')
220
469
tree.add('lala', 'lala-id')
221
expected = ContentsConflict('lala', file_id='lala-id')
470
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
222
471
self.assertEqual(list(tree.conflicts()), [expected])
223
472
file('lala.THIS', 'wb').write('lathis')
224
473
file('lala.OTHER', 'wb').write('laother')
225
474
# When "text conflict"s happen, stem, THIS and OTHER are text
226
expected = TextConflict('lala', file_id='lala-id')
475
expected = conflicts.TextConflict('lala', file_id='lala-id')
227
476
self.assertEqual(list(tree.conflicts()), [expected])
228
477
os.unlink('lala.OTHER')
229
478
os.mkdir('lala.OTHER')
230
expected = ContentsConflict('lala', file_id='lala-id')
479
expected = conflicts.ContentsConflict('lala', file_id='lala-id')
231
480
self.assertEqual(list(tree.conflicts()), [expected])
483
class TestNonFormatSpecificCode(TestCaseWithTransport):
484
"""This class contains tests of workingtree that are not format specific."""
486
def test_gen_file_id(self):
487
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_file_id,
489
self.assertStartsWith(file_id, 'filename-')
491
def test_gen_root_id(self):
492
file_id = self.applyDeprecated(zero_thirteen, workingtree.gen_root_id)
493
self.assertStartsWith(file_id, 'tree_root-')
496
class InstrumentedTree(object):
497
"""A instrumented tree to check the needs_tree_write_lock decorator."""
502
def lock_tree_write(self):
503
self._locks.append('t')
505
@needs_tree_write_lock
506
def method_with_tree_write_lock(self, *args, **kwargs):
507
"""A lock_tree_write decorated method that returns its arguments."""
510
@needs_tree_write_lock
511
def method_that_raises(self):
512
"""This method causes an exception when called with parameters.
514
This allows the decorator code to be checked - it should still call
519
self._locks.append('u')
522
class TestInstrumentedTree(TestCase):
524
def test_needs_tree_write_lock(self):
525
"""@needs_tree_write_lock should be semantically transparent."""
526
tree = InstrumentedTree()
528
'method_with_tree_write_lock',
529
tree.method_with_tree_write_lock.__name__)
531
"A lock_tree_write decorated method that returns its arguments.",
532
tree.method_with_tree_write_lock.__doc__)
535
result = tree.method_with_tree_write_lock(1,2,3, a='b')
536
self.assertEqual((args, kwargs), result)
537
self.assertEqual(['t', 'u'], tree._locks)
538
self.assertRaises(TypeError, tree.method_that_raises, 'foo')
539
self.assertEqual(['t', 'u', 't', 'u'], tree._locks)