~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testinv.py

  • Committer: Aaron Bentley
  • Date: 2005-09-21 15:33:23 UTC
  • mto: (1185.1.37)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: abentley@panoramicfeedback.com-20050921153323-5db674d572d7649d
Fixed bug in distance-from-root graph operation

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 cStringIO import StringIO
18
 
import os
 
17
from bzrlib.selftest import TestCase
19
18
 
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
 
19
from bzrlib.inventory import Inventory, InventoryEntry
27
20
 
28
21
 
29
22
class TestInventory(TestCase):
30
23
 
31
24
    def test_is_within(self):
32
25
        from bzrlib.osutils import is_inside_any
33
 
 
34
 
        SRC_FOO_C = pathjoin('src', 'foo.c')
35
 
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
36
 
                         (['src'], SRC_FOO_C),
 
26
        
 
27
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
 
28
                         (['src'], 'src/foo.c'),
37
29
                         (['src'], 'src'),
38
30
                         ]:
39
31
            self.assert_(is_inside_any(dirs, fn))
65
57
        ie = inv.add_path('foo.txt', 'file')
66
58
        ## XXX
67
59
 
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_lines('fileid','2',
267
 
            self.branch.get_transaction())
268
 
        self.assertEqual(lines, ['contents of subdir/file\n'])
269
 
 
270
 
    def test_snapshot_unchanged(self):
271
 
        #This tests that a simple commit does not make a new entry for
272
 
        # an unchanged inventory entry
273
 
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
274
 
                                  self.wt, 
275
 
                                  self.branch.repository.weave_store,
276
 
                                  self.branch.get_transaction())
277
 
        self.assertEqual(self.file_1.revision, '1')
278
 
        self.assertEqual(self.file_active.revision, '1')
279
 
        self.assertRaises(errors.WeaveError,
280
 
                          self.branch.repository.weave_store.get_lines, 
281
 
                          'fileid', '2', self.branch.get_transaction())
282
 
 
283
 
    def test_snapshot_merge_identical_different_revid(self):
284
 
        # This tests that a commit with two identical parents, one of which has
285
 
        # a different revision id, results in a new revision id in the entry.
286
 
        # 1->other, commit a merge of other against 1, results in 2.
287
 
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
288
 
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
289
 
        other_ie.revision = '1'
290
 
        other_ie.text_sha1 = self.file_1.text_sha1
291
 
        other_ie.text_size = self.file_1.text_size
292
 
        self.assertEqual(self.file_1, other_ie)
293
 
        other_ie.revision = 'other'
294
 
        self.assertNotEqual(self.file_1, other_ie)
295
 
        self.branch.repository.weave_store.add_identical_text('fileid', '1', 
296
 
            'other', ['1'], self.branch.get_transaction())
297
 
        self.file_active.snapshot('2', 'subdir/file', 
298
 
                                  {'1':self.file_1, 'other':other_ie},
299
 
                                  self.wt, 
300
 
                                  self.branch.repository.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.wt,
311
 
                                  self.branch.repository.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(TestCaseWithTransport):
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.wt = self.make_branch_and_tree('.')
330
 
        self.branch = self.wt.branch
331
 
        self.build_tree(['file'])
332
 
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
333
 
        self.inv_A = self.branch.repository.get_inventory('A')
334
 
        self.wt.add(['file'], ['fileid'])
335
 
        self.wt.commit('add file', rev_id='B')
336
 
        self.inv_B = self.branch.repository.get_inventory('B')
337
 
        self.branch.lock_write()
338
 
        try:
339
 
            self.branch.control_files.put_utf8('revision-history', 'A\n')
340
 
        finally:
341
 
            self.branch.unlock()
342
 
        self.assertEqual(self.branch.revision_history(), ['A'])
343
 
        self.wt.commit('another add of file', rev_id='C')
344
 
        self.inv_C = self.branch.repository.get_inventory('C')
345
 
        self.wt.add_pending_merge('B')
346
 
        self.wt.commit('merge in B', rev_id='D')
347
 
        self.inv_D = self.branch.repository.get_inventory('D')
348
 
        self.file_active = self.wt.inventory['fileid']
349
 
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
350
 
            self.branch.get_transaction())
351
 
        
352
 
    def get_previous_heads(self, inventories):
353
 
        return self.file_active.find_previous_heads(inventories, self.weave)
354
 
        
355
 
    def test_fileid_in_no_inventory(self):
356
 
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
357
 
 
358
 
    def test_fileid_in_one_inventory(self):
359
 
        self.assertEqual({'B':self.inv_B['fileid']},
360
 
                         self.get_previous_heads([self.inv_B]))
361
 
        self.assertEqual({'B':self.inv_B['fileid']},
362
 
                         self.get_previous_heads([self.inv_A, self.inv_B]))
363
 
        self.assertEqual({'B':self.inv_B['fileid']},
364
 
                         self.get_previous_heads([self.inv_B, self.inv_A]))
365
 
 
366
 
    def test_fileid_in_two_inventories_gives_both_entries(self):
367
 
        self.assertEqual({'B':self.inv_B['fileid'],
368
 
                          'C':self.inv_C['fileid']},
369
 
                          self.get_previous_heads([self.inv_B, self.inv_C]))
370
 
        self.assertEqual({'B':self.inv_B['fileid'],
371
 
                          'C':self.inv_C['fileid']},
372
 
                          self.get_previous_heads([self.inv_C, self.inv_B]))
373
 
 
374
 
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
375
 
        self.assertEqual({'D':self.inv_D['fileid']},
376
 
                         self.get_previous_heads([self.inv_B, self.inv_D]))
377
 
        self.assertEqual({'D':self.inv_D['fileid']},
378
 
                         self.get_previous_heads([self.inv_D, self.inv_B]))
379
 
 
380
 
    # TODO: test two inventories with the same file revision 
381
 
 
382
 
 
383
 
class TestExecutable(TestCaseWithTransport):
384
 
 
385
 
    def test_stays_executable(self):
386
 
        basic_inv = """<inventory format="5">
387
 
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
388
 
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
389
 
</inventory>
390
 
"""
391
 
        wt = self.make_branch_and_tree('b1')
392
 
        b = wt.branch
393
 
        open('b1/a', 'wb').write('a test\n')
394
 
        open('b1/b', 'wb').write('b test\n')
395
 
        os.chmod('b1/a', 0755)
396
 
        os.chmod('b1/b', 0644)
397
 
        # Manually writing the inventory, to ensure that
398
 
        # the executable="yes" entry is set for 'a' and not for 'b'
399
 
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
400
 
 
401
 
        a_id = "a-20051208024829-849e76f7968d7a86"
402
 
        b_id = "b-20051208024829-849e76f7968d7a86"
403
 
        wt = wt.bzrdir.open_workingtree()
404
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
405
 
 
406
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
407
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
408
 
 
409
 
        wt.commit('adding a,b', rev_id='r1')
410
 
 
411
 
        rev_tree = b.repository.revision_tree('r1')
412
 
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
413
 
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
414
 
 
415
 
        self.failUnless(rev_tree.inventory[a_id].executable)
416
 
        self.failIf(rev_tree.inventory[b_id].executable)
417
 
 
418
 
        # Make sure the entries are gone
419
 
        os.remove('b1/a')
420
 
        os.remove('b1/b')
421
 
        self.failIf(wt.has_id(a_id))
422
 
        self.failIf(wt.has_filename('a'))
423
 
        self.failIf(wt.has_id(b_id))
424
 
        self.failIf(wt.has_filename('b'))
425
 
 
426
 
        # Make sure that revert is able to bring them back,
427
 
        # and sets 'a' back to being executable
428
 
 
429
 
        wt.revert(['a', 'b'], rev_tree, backups=False)
430
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
431
 
 
432
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
433
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
434
 
 
435
 
        # Now remove them again, and make sure that after a
436
 
        # commit, they are still marked correctly
437
 
        os.remove('b1/a')
438
 
        os.remove('b1/b')
439
 
        wt.commit('removed', rev_id='r2')
440
 
 
441
 
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
442
 
        self.failIf(wt.has_id(a_id))
443
 
        self.failIf(wt.has_filename('a'))
444
 
        self.failIf(wt.has_id(b_id))
445
 
        self.failIf(wt.has_filename('b'))
446
 
 
447
 
        # Now revert back to the previous commit
448
 
        wt.revert([], rev_tree, backups=False)
449
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
450
 
 
451
 
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
452
 
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
453
 
 
454
 
        # Now make sure that 'bzr branch' also preserves the
455
 
        # executable bit
456
 
        # TODO: Maybe this should be a blackbox test
457
 
        d2 = b.bzrdir.clone('b2', revision_id='r1')
458
 
        t2 = d2.open_workingtree()
459
 
        b2 = t2.branch
460
 
        self.assertEquals('r1', b2.last_revision())
461
 
 
462
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
463
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
464
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
465
 
 
466
 
        # Make sure pull will delete the files
467
 
        t2.pull(b)
468
 
        self.assertEquals('r2', b2.last_revision())
469
 
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
470
 
 
471
 
        # Now commit the changes on the first branch
472
 
        # so that the second branch can pull the changes
473
 
        # and make sure that the executable bit has been copied
474
 
        wt.commit('resurrected', rev_id='r3')
475
 
 
476
 
        t2.pull(b)
477
 
        self.assertEquals('r3', b2.last_revision())
478
 
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
479
 
 
480
 
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
481
 
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")