~bzr-pqm/bzr/bzr.dev

963 by Martin Pool
- add the start of a test for inventory file-id matching
1
# Copyright (C) 2005 by Canonical Ltd
2
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
17
from cStringIO import StringIO
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
18
import os
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
19
20
from bzrlib.branch import Branch
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
21
import bzrlib.errors as errors
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
22
from bzrlib.diff import internal_diff
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
23
from bzrlib.inventory import Inventory, ROOT_ID
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
24
import bzrlib.inventory as inventory
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
25
from bzrlib.osutils import has_symlinks, rename, pathjoin
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
26
from bzrlib.tests import TestCase, TestCaseWithTransport
963 by Martin Pool
- add the start of a test for inventory file-id matching
27
969 by Martin Pool
- Add less-sucky is_within_any
28
1102 by Martin Pool
- merge test refactoring from robertc
29
class TestInventory(TestCase):
30
31
    def test_is_within(self):
968 by Martin Pool
- add some passing tests for is_inside_any
32
        from bzrlib.osutils import is_inside_any
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
33
1185.31.32 by John Arbash Meinel
Updated the bzr sourcecode to use bzrlib.osutils.pathjoin rather than os.path.join to enforce internal use of / instead of \
34
        SRC_FOO_C = pathjoin('src', 'foo.c')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
35
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
36
                         (['src'], SRC_FOO_C),
968 by Martin Pool
- add some passing tests for is_inside_any
37
                         (['src'], 'src'),
38
                         ]:
39
            self.assert_(is_inside_any(dirs, fn))
40
            
969 by Martin Pool
- Add less-sucky is_within_any
41
        for dirs, fn in [(['src'], 'srccontrol'),
42
                         (['src'], 'srccontrol/foo')]:
43
            self.assertFalse(is_inside_any(dirs, fn))
44
            
1102 by Martin Pool
- merge test refactoring from robertc
45
    def test_ids(self):
963 by Martin Pool
- add the start of a test for inventory file-id matching
46
        """Test detection of files within selected directories."""
47
        inv = Inventory()
48
        
49
        for args in [('src', 'directory', 'src-id'), 
50
                     ('doc', 'directory', 'doc-id'), 
51
                     ('src/hello.c', 'file'),
52
                     ('src/bye.c', 'file', 'bye-id'),
53
                     ('Makefile', 'file')]:
54
            inv.add_path(*args)
55
            
56
        self.assertEqual(inv.path2id('src'), 'src-id')
57
        self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
58
        
59
        self.assert_('src-id' in inv)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
60
61
62
    def test_version(self):
63
        """Inventory remembers the text's version."""
64
        inv = Inventory()
65
        ie = inv.add_path('foo.txt', 'file')
66
        ## XXX
67
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
68
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
69
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
70
71
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
72
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
73
        self.assertEqual(file.kind_character(), '')
74
75
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
76
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
77
        self.assertEqual(dir.kind_character(), '/')
78
79
    def test_link_kind_character(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
80
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
81
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
82
83
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
84
        left = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
85
        left.text_sha1 = 123
86
        left.executable = True
87
        left.symlink_target='foo'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
88
        right = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
95
        left = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
96
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
97
        right = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
109
        left = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
110
        left.text_sha1 = 123
111
        left.executable = True
112
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
113
        right = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
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))
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
121
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
122
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
123
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
124
        self.failUnless(file.has_text())
125
126
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
127
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
128
        self.failIf(dir.has_text())
129
130
    def test_link_has_text(self):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
131
        link = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
132
        self.failIf(link.has_text())
133
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
134
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
135
class TestEntryDiffing(TestCaseWithTransport):
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
136
137
    def setUp(self):
138
        super(TestEntryDiffing, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
139
        self.wt = self.make_branch_and_tree('.')
140
        self.branch = self.wt.branch
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
141
        print >> open('file', 'wb'), 'foo'
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
142
        self.wt.add(['file'], ['fileid'])
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
143
        if has_symlinks():
144
            os.symlink('target1', 'symlink')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
145
            self.wt.add(['symlink'], ['linkid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
146
        self.wt.commit('message_1', rev_id = '1')
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
147
        print >> open('file', 'wb'), 'bar'
148
        if has_symlinks():
149
            os.unlink('symlink')
150
            os.symlink('target2', 'symlink')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
151
        self.tree_1 = self.branch.repository.revision_tree('1')
152
        self.inv_1 = self.branch.repository.get_inventory('1')
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
153
        self.file_1 = self.inv_1['fileid']
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
154
        self.tree_2 = self.wt
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
155
        self.inv_2 = self.tree_2.read_working_inventory()
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
167
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
168
                                            "+++ /dev/null\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
179
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
180
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
191
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
192
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
193
                                            "@@ -1,1 +1,1 @@\n"
194
                                            "-foo\n"
195
                                            "+bar\n"
196
                                            "\n")
197
        
198
    def test_link_diff_deleted(self):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
199
        if not has_symlinks():
200
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
210
        if not has_symlinks():
211
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
221
        if not has_symlinks():
222
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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")
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
230
231
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
232
class TestSnapshot(TestCaseWithTransport):
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
242
        self.wt = self.make_branch_and_tree('.')
243
        self.branch = self.wt.branch
1185.38.7 by John Arbash Meinel
Updated build_tree to use fixed line-endings for tests which read the file contents and compare
244
        self.build_tree(['subdir/', 'subdir/file'], line_endings='binary')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
245
        self.wt.add(['subdir', 'subdir/file'],
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
246
                                       ['dirid', 'fileid'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
247
        if has_symlinks():
248
            pass
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
249
        self.wt.commit('message_1', rev_id = '1')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
250
        self.tree_1 = self.branch.repository.revision_tree('1')
251
        self.inv_1 = self.branch.repository.get_inventory('1')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
252
        self.file_1 = self.inv_1['fileid']
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
253
        self.file_active = self.wt.inventory['fileid']
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
258
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
259
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
260
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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..
1563.2.30 by Robert Collins
Remove all but fetch references to revision_store, making the repository references that are weave specific use the RevisionTextStore.text_store attribute.
266
        lines = self.branch.repository.weave_store.get_weave(
267
            'fileid', 
268
            self.branch.get_transaction()).get_lines('2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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},
1534.4.28 by Robert Collins
first cut at merge from integration.
275
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
276
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
277
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
278
        self.assertEqual(self.file_1.revision, '1')
279
        self.assertEqual(self.file_active.revision, '1')
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
280
        vf = self.branch.repository.weave_store.get_weave(
281
            'fileid', 
282
            self.branch.repository.get_transaction())
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
283
        self.assertRaises(errors.RevisionNotPresent,
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
284
                          vf.get_lines,
285
                          '2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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.
1408 by Robert Collins
we do not need revision_trees in commit, parent inventories are sufficient
290
        # 1->other, commit a merge of other against 1, results in 2.
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
299
        versionfile = self.branch.repository.weave_store.get_weave(
300
            'fileid', self.branch.repository.get_transaction())
301
        versionfile.clone_text('other', '1', ['1'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
302
        self.file_active.snapshot('2', 'subdir/file', 
303
                                  {'1':self.file_1, 'other':other_ie},
1534.4.28 by Robert Collins
first cut at merge from integration.
304
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
305
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
306
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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}, 
1534.4.28 by Robert Collins
first cut at merge from integration.
315
                                  self.wt,
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
316
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
317
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
318
        # expected outcome - file_1 has a revision id of '2'
319
        self.assertEqual(self.file_active.revision, '2')
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
320
321
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
322
class TestPreviousHeads(TestCaseWithTransport):
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
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()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
334
        self.wt = self.make_branch_and_tree('.')
335
        self.branch = self.wt.branch
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
336
        self.build_tree(['file'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
337
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
338
        self.inv_A = self.branch.repository.get_inventory('A')
1185.65.13 by Robert Collins
Merge from integration
339
        self.wt.add(['file'], ['fileid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
340
        self.wt.commit('add file', rev_id='B')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
341
        self.inv_B = self.branch.repository.get_inventory('B')
1185.67.5 by Aaron Bentley
Added write locks as appropriate
342
        self.branch.lock_write()
343
        try:
344
            self.branch.control_files.put_utf8('revision-history', 'A\n')
345
        finally:
346
            self.branch.unlock()
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
347
        self.assertEqual(self.branch.revision_history(), ['A'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
348
        self.wt.commit('another add of file', rev_id='C')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
349
        self.inv_C = self.branch.repository.get_inventory('C')
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
350
        self.wt.add_pending_merge('B')
351
        self.wt.commit('merge in B', rev_id='D')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
352
        self.inv_D = self.branch.repository.get_inventory('D')
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
353
        self.file_active = self.wt.inventory['fileid']
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
354
        self.weave = self.branch.repository.weave_store.get_weave('fileid',
1596.2.20 by Robert Collins
optimise commit to only access weaves for merged, or altered files during commit.
355
            self.branch.repository.get_transaction())
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
356
        
357
    def get_previous_heads(self, inventories):
1596.2.20 by Robert Collins
optimise commit to only access weaves for merged, or altered files during commit.
358
        return self.file_active.find_previous_heads(
359
            inventories, 
360
            self.branch.repository.weave_store,
361
            self.branch.repository.get_transaction())
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
362
        
363
    def test_fileid_in_no_inventory(self):
364
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
365
366
    def test_fileid_in_one_inventory(self):
367
        self.assertEqual({'B':self.inv_B['fileid']},
368
                         self.get_previous_heads([self.inv_B]))
369
        self.assertEqual({'B':self.inv_B['fileid']},
370
                         self.get_previous_heads([self.inv_A, self.inv_B]))
371
        self.assertEqual({'B':self.inv_B['fileid']},
372
                         self.get_previous_heads([self.inv_B, self.inv_A]))
373
374
    def test_fileid_in_two_inventories_gives_both_entries(self):
375
        self.assertEqual({'B':self.inv_B['fileid'],
376
                          'C':self.inv_C['fileid']},
377
                          self.get_previous_heads([self.inv_B, self.inv_C]))
378
        self.assertEqual({'B':self.inv_B['fileid'],
379
                          'C':self.inv_C['fileid']},
380
                          self.get_previous_heads([self.inv_C, self.inv_B]))
381
382
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
383
        self.assertEqual({'D':self.inv_D['fileid']},
384
                         self.get_previous_heads([self.inv_B, self.inv_D]))
385
        self.assertEqual({'D':self.inv_D['fileid']},
386
                         self.get_previous_heads([self.inv_D, self.inv_B]))
387
388
    # TODO: test two inventories with the same file revision 
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
389
390
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
391
class TestExecutable(TestCaseWithTransport):
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
392
393
    def test_stays_executable(self):
394
        basic_inv = """<inventory format="5">
395
<file file_id="a-20051208024829-849e76f7968d7a86" name="a" executable="yes" />
396
<file file_id="b-20051208024829-849e76f7968d7a86" name="b" />
397
</inventory>
398
"""
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
399
        wt = self.make_branch_and_tree('b1')
400
        b = wt.branch
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
401
        open('b1/a', 'wb').write('a test\n')
402
        open('b1/b', 'wb').write('b test\n')
403
        os.chmod('b1/a', 0755)
404
        os.chmod('b1/b', 0644)
405
        # Manually writing the inventory, to ensure that
406
        # the executable="yes" entry is set for 'a' and not for 'b'
407
        open('b1/.bzr/inventory', 'wb').write(basic_inv)
408
409
        a_id = "a-20051208024829-849e76f7968d7a86"
410
        b_id = "b-20051208024829-849e76f7968d7a86"
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
411
        wt = wt.bzrdir.open_workingtree()
412
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
413
414
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
415
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
416
417
        wt.commit('adding a,b', rev_id='r1')
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
418
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
419
        rev_tree = b.repository.revision_tree('r1')
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
420
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
421
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
422
423
        self.failUnless(rev_tree.inventory[a_id].executable)
424
        self.failIf(rev_tree.inventory[b_id].executable)
425
426
        # Make sure the entries are gone
427
        os.remove('b1/a')
428
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
429
        self.failIf(wt.has_id(a_id))
430
        self.failIf(wt.has_filename('a'))
431
        self.failIf(wt.has_id(b_id))
432
        self.failIf(wt.has_filename('b'))
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
433
434
        # Make sure that revert is able to bring them back,
435
        # and sets 'a' back to being executable
436
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
437
        wt.revert(['a', 'b'], rev_tree, backups=False)
438
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
439
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
440
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
441
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
442
443
        # Now remove them again, and make sure that after a
444
        # commit, they are still marked correctly
445
        os.remove('b1/a')
446
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
447
        wt.commit('removed', rev_id='r2')
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
448
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
449
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
450
        self.failIf(wt.has_id(a_id))
451
        self.failIf(wt.has_filename('a'))
452
        self.failIf(wt.has_id(b_id))
453
        self.failIf(wt.has_filename('b'))
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
454
455
        # Now revert back to the previous commit
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
456
        wt.revert([], rev_tree, backups=False)
457
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
458
459
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
460
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
461
462
        # Now make sure that 'bzr branch' also preserves the
463
        # executable bit
464
        # TODO: Maybe this should be a blackbox test
1534.4.50 by Robert Collins
Got the bzrdir api straightened out, plenty of refactoring to use it pending, but the api is up and running.
465
        d2 = b.bzrdir.clone('b2', revision_id='r1')
466
        t2 = d2.open_workingtree()
467
        b2 = t2.branch
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
468
        self.assertEquals('r1', b2.last_revision())
469
470
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
471
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
472
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
473
474
        # Make sure pull will delete the files
475
        t2.pull(b)
476
        self.assertEquals('r2', b2.last_revision())
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
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
482
        wt.commit('resurrected', rev_id='r3')
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
483
484
        t2.pull(b)
485
        self.assertEquals('r3', b2.last_revision())
486
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
487
488
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
489
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
1534.7.178 by Aaron Bentley
Fixed dangling inventory ids in revert
490
491
class TestRevert(TestCaseWithTransport):
492
    def test_dangling_id(self):
493
        wt = self.make_branch_and_tree('b1')
494
        self.assertEqual(len(wt.inventory), 1)
495
        open('b1/a', 'wb').write('a test\n')
496
        wt.add('a')
497
        self.assertEqual(len(wt.inventory), 2)
498
        os.unlink('b1/a')
499
        wt.revert([])
500
        self.assertEqual(len(wt.inventory), 1)
1534.7.193 by Aaron Bentley
Stopped revert from preserving file contents produced by merges
501
502