~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: Olaf Conradi
  • Date: 2006-03-23 23:00:08 UTC
  • mto: (1661.1.1 bzr.mbp.remember)
  • mto: This revision was merged to the branch mainline in revision 1663.
  • Revision ID: olaf@conradi.org-20060323230008-8553da6ea86bf149
Added testcase for bzr merge --remember.
Changed branch.set_parent() to accepts None to remove parent.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
from bzrlib.selftest import TestCase
 
17
from cStringIO import StringIO
 
18
import os
18
19
 
19
 
from bzrlib.inventory import Inventory, InventoryEntry
 
20
from bzrlib.branch import Branch
 
21
import bzrlib.errors as errors
 
22
from bzrlib.diff import internal_diff
 
23
from bzrlib.inventory import Inventory, ROOT_ID
 
24
import bzrlib.inventory as inventory
 
25
from bzrlib.osutils import has_symlinks, rename, pathjoin
 
26
from bzrlib.tests import TestCase, TestCaseWithTransport
20
27
 
21
28
 
22
29
class TestInventory(TestCase):
23
30
 
24
31
    def test_is_within(self):
25
32
        from bzrlib.osutils import is_inside_any
26
 
        
27
 
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
28
 
                         (['src'], 'src/foo.c'),
 
33
 
 
34
        SRC_FOO_C = pathjoin('src', 'foo.c')
 
35
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
 
36
                         (['src'], SRC_FOO_C),
29
37
                         (['src'], 'src'),
30
38
                         ]:
31
39
            self.assert_(is_inside_any(dirs, fn))
57
65
        ie = inv.add_path('foo.txt', 'file')
58
66
        ## XXX
59
67
 
 
68
 
 
69
class TestInventoryEntry(TestCase):
 
70
 
 
71
    def test_file_kind_character(self):
 
72
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
73
        self.assertEqual(file.kind_character(), '')
 
74
 
 
75
    def test_dir_kind_character(self):
 
76
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
77
        self.assertEqual(dir.kind_character(), '/')
 
78
 
 
79
    def test_link_kind_character(self):
 
80
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
81
        self.assertEqual(dir.kind_character(), '')
 
82
 
 
83
    def test_dir_detect_changes(self):
 
84
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
85
        left.text_sha1 = 123
 
86
        left.executable = True
 
87
        left.symlink_target='foo'
 
88
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
89
        right.text_sha1 = 321
 
90
        right.symlink_target='bar'
 
91
        self.assertEqual((False, False), left.detect_changes(right))
 
92
        self.assertEqual((False, False), right.detect_changes(left))
 
93
 
 
94
    def test_file_detect_changes(self):
 
95
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
96
        left.text_sha1 = 123
 
97
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
98
        right.text_sha1 = 123
 
99
        self.assertEqual((False, False), left.detect_changes(right))
 
100
        self.assertEqual((False, False), right.detect_changes(left))
 
101
        left.executable = True
 
102
        self.assertEqual((False, True), left.detect_changes(right))
 
103
        self.assertEqual((False, True), right.detect_changes(left))
 
104
        right.text_sha1 = 321
 
105
        self.assertEqual((True, True), left.detect_changes(right))
 
106
        self.assertEqual((True, True), right.detect_changes(left))
 
107
 
 
108
    def test_symlink_detect_changes(self):
 
109
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
110
        left.text_sha1 = 123
 
111
        left.executable = True
 
112
        left.symlink_target='foo'
 
113
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
114
        right.text_sha1 = 321
 
115
        right.symlink_target='foo'
 
116
        self.assertEqual((False, False), left.detect_changes(right))
 
117
        self.assertEqual((False, False), right.detect_changes(left))
 
118
        left.symlink_target = 'different'
 
119
        self.assertEqual((True, False), left.detect_changes(right))
 
120
        self.assertEqual((True, False), right.detect_changes(left))
 
121
 
 
122
    def test_file_has_text(self):
 
123
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
 
124
        self.failUnless(file.has_text())
 
125
 
 
126
    def test_directory_has_text(self):
 
127
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
 
128
        self.failIf(dir.has_text())
 
129
 
 
130
    def test_link_has_text(self):
 
131
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
 
132
        self.failIf(link.has_text())
 
133
 
 
134
 
 
135
class TestEntryDiffing(TestCaseWithTransport):
 
136
 
 
137
    def setUp(self):
 
138
        super(TestEntryDiffing, self).setUp()
 
139
        self.wt = self.make_branch_and_tree('.')
 
140
        self.branch = self.wt.branch
 
141
        print >> open('file', 'wb'), 'foo'
 
142
        self.wt.add(['file'], ['fileid'])
 
143
        if has_symlinks():
 
144
            os.symlink('target1', 'symlink')
 
145
            self.wt.add(['symlink'], ['linkid'])
 
146
        self.wt.commit('message_1', rev_id = '1')
 
147
        print >> open('file', 'wb'), 'bar'
 
148
        if has_symlinks():
 
149
            os.unlink('symlink')
 
150
            os.symlink('target2', 'symlink')
 
151
        self.tree_1 = self.branch.repository.revision_tree('1')
 
152
        self.inv_1 = self.branch.repository.get_inventory('1')
 
153
        self.file_1 = self.inv_1['fileid']
 
154
        self.tree_2 = self.wt
 
155
        self.inv_2 = self.tree_2.read_working_inventory()
 
156
        self.file_2 = self.inv_2['fileid']
 
157
        if has_symlinks():
 
158
            self.link_1 = self.inv_1['linkid']
 
159
            self.link_2 = self.inv_2['linkid']
 
160
 
 
161
    def test_file_diff_deleted(self):
 
162
        output = StringIO()
 
163
        self.file_1.diff(internal_diff, 
 
164
                          "old_label", self.tree_1,
 
165
                          "/dev/null", None, None,
 
166
                          output)
 
167
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
 
168
                                            "+++ /dev/null\t\n"
 
169
                                            "@@ -1,1 +0,0 @@\n"
 
170
                                            "-foo\n"
 
171
                                            "\n")
 
172
 
 
173
    def test_file_diff_added(self):
 
174
        output = StringIO()
 
175
        self.file_1.diff(internal_diff, 
 
176
                          "new_label", self.tree_1,
 
177
                          "/dev/null", None, None,
 
178
                          output, reverse=True)
 
179
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
 
180
                                            "+++ new_label\t\n"
 
181
                                            "@@ -0,0 +1,1 @@\n"
 
182
                                            "+foo\n"
 
183
                                            "\n")
 
184
 
 
185
    def test_file_diff_changed(self):
 
186
        output = StringIO()
 
187
        self.file_1.diff(internal_diff, 
 
188
                          "/dev/null", self.tree_1, 
 
189
                          "new_label", self.file_2, self.tree_2,
 
190
                          output)
 
191
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
 
192
                                            "+++ new_label\t\n"
 
193
                                            "@@ -1,1 +1,1 @@\n"
 
194
                                            "-foo\n"
 
195
                                            "+bar\n"
 
196
                                            "\n")
 
197
        
 
198
    def test_link_diff_deleted(self):
 
199
        if not has_symlinks():
 
200
            return
 
201
        output = StringIO()
 
202
        self.link_1.diff(internal_diff, 
 
203
                          "old_label", self.tree_1,
 
204
                          "/dev/null", None, None,
 
205
                          output)
 
206
        self.assertEqual(output.getvalue(),
 
207
                         "=== target was 'target1'\n")
 
208
 
 
209
    def test_link_diff_added(self):
 
210
        if not has_symlinks():
 
211
            return
 
212
        output = StringIO()
 
213
        self.link_1.diff(internal_diff, 
 
214
                          "new_label", self.tree_1,
 
215
                          "/dev/null", None, None,
 
216
                          output, reverse=True)
 
217
        self.assertEqual(output.getvalue(),
 
218
                         "=== target is 'target1'\n")
 
219
 
 
220
    def test_link_diff_changed(self):
 
221
        if not has_symlinks():
 
222
            return
 
223
        output = StringIO()
 
224
        self.link_1.diff(internal_diff, 
 
225
                          "/dev/null", self.tree_1, 
 
226
                          "new_label", self.link_2, self.tree_2,
 
227
                          output)
 
228
        self.assertEqual(output.getvalue(),
 
229
                         "=== target changed 'target1' => 'target2'\n")
 
230
 
 
231
 
 
232
class TestSnapshot(TestCaseWithTransport):
 
233
 
 
234
    def setUp(self):
 
235
        # for full testing we'll need a branch
 
236
        # with a subdir to test parent changes.
 
237
        # and a file, link and dir under that.
 
238
        # but right now I only need one attribute
 
239
        # to change, and then test merge patterns
 
240
        # with fake parent entries.
 
241
        super(TestSnapshot, self).setUp()
 
242
        self.wt = self.make_branch_and_tree('.')
 
243
        self.branch = self.wt.branch
 
244
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
 
245
        self.wt.add(['subdir', 'subdir/file'],
 
246
                                       ['dirid', 'fileid'])
 
247
        if has_symlinks():
 
248
            pass
 
249
        self.wt.commit('message_1', rev_id = '1')
 
250
        self.tree_1 = self.branch.repository.revision_tree('1')
 
251
        self.inv_1 = self.branch.repository.get_inventory('1')
 
252
        self.file_1 = self.inv_1['fileid']
 
253
        self.file_active = self.wt.inventory['fileid']
 
254
 
 
255
    def test_snapshot_new_revision(self):
 
256
        # This tests that a simple commit with no parents makes a new
 
257
        # revision value in the inventory entry
 
258
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
 
259
                                  self.branch.repository.weave_store,
 
260
                                  self.branch.get_transaction())
 
261
        # expected outcome - file_1 has a revision id of '2', and we can get
 
262
        # its text of 'file contents' out of the weave.
 
263
        self.assertEqual(self.file_1.revision, '1')
 
264
        self.assertEqual(self.file_active.revision, '2')
 
265
        # this should be a separate test probably, but lets check it once..
 
266
        lines = self.branch.repository.weave_store.get_weave(
 
267
            'fileid', 
 
268
            self.branch.get_transaction()).get_lines('2')
 
269
        self.assertEqual(lines, ['contents of subdir/file\n'])
 
270
 
 
271
    def test_snapshot_unchanged(self):
 
272
        #This tests that a simple commit does not make a new entry for
 
273
        # an unchanged inventory entry
 
274
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
 
275
                                  self.wt, 
 
276
                                  self.branch.repository.weave_store,
 
277
                                  self.branch.get_transaction())
 
278
        self.assertEqual(self.file_1.revision, '1')
 
279
        self.assertEqual(self.file_active.revision, '1')
 
280
        vf = self.branch.repository.weave_store.get_weave(
 
281
            'fileid', 
 
282
            self.branch.repository.get_transaction())
 
283
        self.assertRaises(errors.RevisionNotPresent,
 
284
                          vf.get_lines,
 
285
                          '2')
 
286
 
 
287
    def test_snapshot_merge_identical_different_revid(self):
 
288
        # This tests that a commit with two identical parents, one of which has
 
289
        # a different revision id, results in a new revision id in the entry.
 
290
        # 1->other, commit a merge of other against 1, results in 2.
 
291
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
 
292
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
 
293
        other_ie.revision = '1'
 
294
        other_ie.text_sha1 = self.file_1.text_sha1
 
295
        other_ie.text_size = self.file_1.text_size
 
296
        self.assertEqual(self.file_1, other_ie)
 
297
        other_ie.revision = 'other'
 
298
        self.assertNotEqual(self.file_1, other_ie)
 
299
        versionfile = self.branch.repository.weave_store.get_weave(
 
300
            'fileid', self.branch.repository.get_transaction())
 
301
        versionfile.clone_text('other', '1', ['1'])
 
302
        self.file_active.snapshot('2', 'subdir/file', 
 
303
                                  {'1':self.file_1, 'other':other_ie},
 
304
                                  self.wt, 
 
305
                                  self.branch.repository.weave_store,
 
306
                                  self.branch.get_transaction())
 
307
        self.assertEqual(self.file_active.revision, '2')
 
308
 
 
309
    def test_snapshot_changed(self):
 
310
        # This tests that a commit with one different parent results in a new
 
311
        # revision id in the entry.
 
312
        self.file_active.name='newname'
 
313
        rename('subdir/file', 'subdir/newname')
 
314
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
 
315
                                  self.wt,
 
316
                                  self.branch.repository.weave_store,
 
317
                                  self.branch.get_transaction())
 
318
        # expected outcome - file_1 has a revision id of '2'
 
319
        self.assertEqual(self.file_active.revision, '2')
 
320
 
 
321
 
 
322
class TestPreviousHeads(TestCaseWithTransport):
 
323
 
 
324
    def setUp(self):
 
325
        # we want several inventories, that respectively
 
326
        # give use the following scenarios:
 
327
        # A) fileid not in any inventory (A),
 
328
        # B) fileid present in one inventory (B) and (A,B)
 
329
        # C) fileid present in two inventories, and they
 
330
        #   are not mutual descendents (B, C)
 
331
        # D) fileid present in two inventories and one is
 
332
        #   a descendent of the other. (B, D)
 
333
        super(TestPreviousHeads, self).setUp()
 
334
        self.wt = self.make_branch_and_tree('.')
 
335
        self.branch = self.wt.branch
 
336
        self.build_tree(['file'])
 
337
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
 
338
        self.inv_A = self.branch.repository.get_inventory('A')
 
339
        self.wt.add(['file'], ['fileid'])
 
340
        self.wt.commit('add file', rev_id='B')
 
341
        self.inv_B = self.branch.repository.get_inventory('B')
 
342
        self.branch.lock_write()
 
343
        try:
 
344
            self.branch.control_files.put_utf8('revision-history', 'A\n')
 
345
        finally:
 
346
            self.branch.unlock()
 
347
        self.assertEqual(self.branch.revision_history(), ['A'])
 
348
        self.wt.commit('another add of file', rev_id='C')
 
349
        self.inv_C = self.branch.repository.get_inventory('C')
 
350
        self.wt.add_pending_merge('B')
 
351
        self.wt.commit('merge in B', rev_id='D')
 
352
        self.inv_D = self.branch.repository.get_inventory('D')
 
353
        self.file_active = self.wt.inventory['fileid']
 
354
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
 
355
            self.branch.get_transaction())
 
356
        
 
357
    def get_previous_heads(self, inventories):
 
358
        return self.file_active.find_previous_heads(inventories, self.weave)
 
359
        
 
360
    def test_fileid_in_no_inventory(self):
 
361
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
 
362
 
 
363
    def test_fileid_in_one_inventory(self):
 
364
        self.assertEqual({'B':self.inv_B['fileid']},
 
365
                         self.get_previous_heads([self.inv_B]))
 
366
        self.assertEqual({'B':self.inv_B['fileid']},
 
367
                         self.get_previous_heads([self.inv_A, self.inv_B]))
 
368
        self.assertEqual({'B':self.inv_B['fileid']},
 
369
                         self.get_previous_heads([self.inv_B, self.inv_A]))
 
370
 
 
371
    def test_fileid_in_two_inventories_gives_both_entries(self):
 
372
        self.assertEqual({'B':self.inv_B['fileid'],
 
373
                          'C':self.inv_C['fileid']},
 
374
                          self.get_previous_heads([self.inv_B, self.inv_C]))
 
375
        self.assertEqual({'B':self.inv_B['fileid'],
 
376
                          'C':self.inv_C['fileid']},
 
377
                          self.get_previous_heads([self.inv_C, self.inv_B]))
 
378
 
 
379
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
 
380
        self.assertEqual({'D':self.inv_D['fileid']},
 
381
                         self.get_previous_heads([self.inv_B, self.inv_D]))
 
382
        self.assertEqual({'D':self.inv_D['fileid']},
 
383
                         self.get_previous_heads([self.inv_D, self.inv_B]))
 
384
 
 
385
    # TODO: test two inventories with the same file revision 
 
386
 
 
387
 
 
388
class TestExecutable(TestCaseWithTransport):
 
389
 
 
390
    def test_stays_executable(self):
 
391
        basic_inv = """<inventory format="5">
 
392
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
 
393
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
 
394
</inventory>
 
395
"""
 
396
        wt = self.make_branch_and_tree('b1')
 
397
        b = wt.branch
 
398
        open('b1/a', 'wb').write('a test\n')
 
399
        open('b1/b', 'wb').write('b test\n')
 
400
        os.chmod('b1/a', 0755)
 
401
        os.chmod('b1/b', 0644)
 
402
        # Manually writing the inventory, to ensure that
 
403
        # the executable="yes" entry is set for 'a' and not for 'b'
 
404
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
 
405
 
 
406
        a_id = "a-20051208024829-849e76f7968d7a86"
 
407
        b_id = "b-20051208024829-849e76f7968d7a86"
 
408
        wt = wt.bzrdir.open_workingtree()
 
409
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
410
 
 
411
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
412
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
413
 
 
414
        wt.commit('adding a,b', rev_id='r1')
 
415
 
 
416
        rev_tree = b.repository.revision_tree('r1')
 
417
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
 
418
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
 
419
 
 
420
        self.failUnless(rev_tree.inventory[a_id].executable)
 
421
        self.failIf(rev_tree.inventory[b_id].executable)
 
422
 
 
423
        # Make sure the entries are gone
 
424
        os.remove('b1/a')
 
425
        os.remove('b1/b')
 
426
        self.failIf(wt.has_id(a_id))
 
427
        self.failIf(wt.has_filename('a'))
 
428
        self.failIf(wt.has_id(b_id))
 
429
        self.failIf(wt.has_filename('b'))
 
430
 
 
431
        # Make sure that revert is able to bring them back,
 
432
        # and sets 'a' back to being executable
 
433
 
 
434
        wt.revert(['a', 'b'], rev_tree, backups=False)
 
435
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
436
 
 
437
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
438
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
439
 
 
440
        # Now remove them again, and make sure that after a
 
441
        # commit, they are still marked correctly
 
442
        os.remove('b1/a')
 
443
        os.remove('b1/b')
 
444
        wt.commit('removed', rev_id='r2')
 
445
 
 
446
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
 
447
        self.failIf(wt.has_id(a_id))
 
448
        self.failIf(wt.has_filename('a'))
 
449
        self.failIf(wt.has_id(b_id))
 
450
        self.failIf(wt.has_filename('b'))
 
451
 
 
452
        # Now revert back to the previous commit
 
453
        wt.revert([], rev_tree, backups=False)
 
454
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
 
455
 
 
456
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
 
457
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
 
458
 
 
459
        # Now make sure that 'bzr branch' also preserves the
 
460
        # executable bit
 
461
        # TODO: Maybe this should be a blackbox test
 
462
        d2 = b.bzrdir.clone('b2', revision_id='r1')
 
463
        t2 = d2.open_workingtree()
 
464
        b2 = t2.branch
 
465
        self.assertEquals('r1', b2.last_revision())
 
466
 
 
467
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
 
468
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
 
469
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
470
 
 
471
        # Make sure pull will delete the files
 
472
        t2.pull(b)
 
473
        self.assertEquals('r2', b2.last_revision())
 
474
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
 
475
 
 
476
        # Now commit the changes on the first branch
 
477
        # so that the second branch can pull the changes
 
478
        # and make sure that the executable bit has been copied
 
479
        wt.commit('resurrected', rev_id='r3')
 
480
 
 
481
        t2.pull(b)
 
482
        self.assertEquals('r3', b2.last_revision())
 
483
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
 
484
 
 
485
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
 
486
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
 
487
 
 
488
class TestRevert(TestCaseWithTransport):
 
489
    def test_dangling_id(self):
 
490
        wt = self.make_branch_and_tree('b1')
 
491
        self.assertEqual(len(wt.inventory), 1)
 
492
        open('b1/a', 'wb').write('a test\n')
 
493
        wt.add('a')
 
494
        self.assertEqual(len(wt.inventory), 2)
 
495
        os.unlink('b1/a')
 
496
        wt.revert([])
 
497
        self.assertEqual(len(wt.inventory), 1)
 
498
 
 
499