~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_inv.py

  • Committer: John Arbash Meinel
  • Date: 2006-01-23 22:33:23 UTC
  • mto: This revision was merged to the branch mainline in revision 1551.
  • Revision ID: john@arbash-meinel.com-20060123223323-16eb865383bf650a
From Jari Alto: Makefile fixes (clean target error suppression)

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