~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
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
27
from bzrlib.uncommit import uncommit
963 by Martin Pool
- add the start of a test for inventory file-id matching
28
969 by Martin Pool
- Add less-sucky is_within_any
29
1102 by Martin Pool
- merge test refactoring from robertc
30
class TestInventory(TestCase):
31
32
    def test_is_within(self):
968 by Martin Pool
- add some passing tests for is_inside_any
33
        from bzrlib.osutils import is_inside_any
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
34
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 \
35
        SRC_FOO_C = pathjoin('src', 'foo.c')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
36
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
37
                         (['src'], SRC_FOO_C),
968 by Martin Pool
- add some passing tests for is_inside_any
38
                         (['src'], 'src'),
39
                         ]:
40
            self.assert_(is_inside_any(dirs, fn))
41
            
969 by Martin Pool
- Add less-sucky is_within_any
42
        for dirs, fn in [(['src'], 'srccontrol'),
43
                         (['src'], 'srccontrol/foo')]:
44
            self.assertFalse(is_inside_any(dirs, fn))
45
            
1102 by Martin Pool
- merge test refactoring from robertc
46
    def test_ids(self):
963 by Martin Pool
- add the start of a test for inventory file-id matching
47
        """Test detection of files within selected directories."""
48
        inv = Inventory()
49
        
50
        for args in [('src', 'directory', 'src-id'), 
51
                     ('doc', 'directory', 'doc-id'), 
52
                     ('src/hello.c', 'file'),
53
                     ('src/bye.c', 'file', 'bye-id'),
54
                     ('Makefile', 'file')]:
55
            inv.add_path(*args)
56
            
57
        self.assertEqual(inv.path2id('src'), 'src-id')
58
        self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
59
        
60
        self.assert_('src-id' in inv)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
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
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
69
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
70
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
71
72
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
73
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
74
        self.assertEqual(file.kind_character(), '')
75
76
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
77
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
78
        self.assertEqual(dir.kind_character(), '/')
79
80
    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
81
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
82
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
83
84
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
85
        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
86
        left.text_sha1 = 123
87
        left.executable = True
88
        left.symlink_target='foo'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
89
        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
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):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
96
        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
97
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
98
        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
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):
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
110
        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
111
        left.text_sha1 = 123
112
        left.executable = True
113
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
114
        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
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))
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
122
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
123
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
124
        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
125
        self.failUnless(file.has_text())
126
127
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
128
        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
129
        self.failIf(dir.has_text())
130
131
    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
132
        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
133
        self.failIf(link.has_text())
134
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
135
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
136
class TestEntryDiffing(TestCaseWithTransport):
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
137
138
    def setUp(self):
139
        super(TestEntryDiffing, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
140
        self.wt = self.make_branch_and_tree('.')
141
        self.branch = self.wt.branch
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
142
        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.
143
        self.wt.add(['file'], ['fileid'])
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
144
        if has_symlinks():
145
            os.symlink('target1', 'symlink')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
146
            self.wt.add(['symlink'], ['linkid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
147
        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
148
        print >> open('file', 'wb'), 'bar'
149
        if has_symlinks():
150
            os.unlink('symlink')
151
            os.symlink('target2', 'symlink')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
152
        self.tree_1 = self.branch.repository.revision_tree('1')
153
        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
154
        self.file_1 = self.inv_1['fileid']
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
155
        self.tree_2 = self.wt
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
156
        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
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
168
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
169
                                            "+++ /dev/null\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
180
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
181
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
192
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
193
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
194
                                            "@@ -1,1 +1,1 @@\n"
195
                                            "-foo\n"
196
                                            "+bar\n"
197
                                            "\n")
198
        
199
    def test_link_diff_deleted(self):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
200
        if not has_symlinks():
201
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
211
        if not has_symlinks():
212
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
222
        if not has_symlinks():
223
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
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")
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
231
232
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
233
class TestSnapshot(TestCaseWithTransport):
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
243
        self.wt = self.make_branch_and_tree('.')
244
        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
245
        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.
246
        self.wt.add(['subdir', 'subdir/file'],
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
247
                                       ['dirid', 'fileid'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
248
        if has_symlinks():
249
            pass
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
250
        self.wt.commit('message_1', rev_id = '1')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
251
        self.tree_1 = self.branch.repository.revision_tree('1')
252
        self.inv_1 = self.branch.repository.get_inventory('1')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
253
        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.
254
        self.file_active = self.wt.inventory['fileid']
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
255
256
    def test_snapshot_new_revision(self):
257
        # This tests that a simple commit with no parents makes a new
258
        # 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.
259
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
260
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
261
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
262
        # expected outcome - file_1 has a revision id of '2', and we can get
263
        # its text of 'file contents' out of the weave.
264
        self.assertEqual(self.file_1.revision, '1')
265
        self.assertEqual(self.file_active.revision, '2')
266
        # 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.
267
        lines = self.branch.repository.weave_store.get_weave(
268
            'fileid', 
269
            self.branch.get_transaction()).get_lines('2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
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},
1534.4.28 by Robert Collins
first cut at merge from integration.
276
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
277
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
278
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
279
        self.assertEqual(self.file_1.revision, '1')
280
        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.
281
        vf = self.branch.repository.weave_store.get_weave(
282
            'fileid', 
283
            self.branch.repository.get_transaction())
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
284
        self.assertRaises(errors.RevisionNotPresent,
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
285
                          vf.get_lines,
286
                          '2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
287
288
    def test_snapshot_merge_identical_different_revid(self):
289
        # This tests that a commit with two identical parents, one of which has
290
        # 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
291
        # 1->other, commit a merge of other against 1, results in 2.
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
292
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
293
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
294
        other_ie.revision = '1'
295
        other_ie.text_sha1 = self.file_1.text_sha1
296
        other_ie.text_size = self.file_1.text_size
297
        self.assertEqual(self.file_1, other_ie)
298
        other_ie.revision = 'other'
299
        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.
300
        versionfile = self.branch.repository.weave_store.get_weave(
301
            'fileid', self.branch.repository.get_transaction())
302
        versionfile.clone_text('other', '1', ['1'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
303
        self.file_active.snapshot('2', 'subdir/file', 
304
                                  {'1':self.file_1, 'other':other_ie},
1534.4.28 by Robert Collins
first cut at merge from integration.
305
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
306
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
307
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
308
        self.assertEqual(self.file_active.revision, '2')
309
310
    def test_snapshot_changed(self):
311
        # This tests that a commit with one different parent results in a new
312
        # revision id in the entry.
313
        self.file_active.name='newname'
314
        rename('subdir/file', 'subdir/newname')
315
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
1534.4.28 by Robert Collins
first cut at merge from integration.
316
                                  self.wt,
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
317
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
318
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
319
        # expected outcome - file_1 has a revision id of '2'
320
        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.
321
322
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
323
class TestPreviousHeads(TestCaseWithTransport):
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
324
325
    def setUp(self):
326
        # we want several inventories, that respectively
327
        # give use the following scenarios:
328
        # A) fileid not in any inventory (A),
329
        # B) fileid present in one inventory (B) and (A,B)
330
        # C) fileid present in two inventories, and they
331
        #   are not mutual descendents (B, C)
332
        # D) fileid present in two inventories and one is
333
        #   a descendent of the other. (B, D)
334
        super(TestPreviousHeads, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
335
        self.wt = self.make_branch_and_tree('.')
336
        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.
337
        self.build_tree(['file'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
338
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
339
        self.inv_A = self.branch.repository.get_inventory('A')
1185.65.13 by Robert Collins
Merge from integration
340
        self.wt.add(['file'], ['fileid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
341
        self.wt.commit('add file', rev_id='B')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
342
        self.inv_B = self.branch.repository.get_inventory('B')
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
343
        uncommit(self.branch, tree=self.wt)
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
344
        self.assertEqual(self.branch.revision_history(), ['A'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
345
        self.wt.commit('another add of file', rev_id='C')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
346
        self.inv_C = self.branch.repository.get_inventory('C')
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
347
        self.wt.add_pending_merge('B')
348
        self.wt.commit('merge in B', rev_id='D')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
349
        self.inv_D = self.branch.repository.get_inventory('D')
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
350
        self.file_active = self.wt.inventory['fileid']
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
351
        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.
352
            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.
353
        
354
    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.
355
        return self.file_active.find_previous_heads(
356
            inventories, 
357
            self.branch.repository.weave_store,
358
            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.
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 
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
386
387
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
388
class TestExecutable(TestCaseWithTransport):
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
389
390
    def test_stays_executable(self):
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
391
        a_id = "a-20051208024829-849e76f7968d7a86"
392
        b_id = "b-20051208024829-849e76f7968d7a86"
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
393
        wt = self.make_branch_and_tree('b1')
394
        b = wt.branch
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
395
        open('b1/a', 'wb').write('a test\n')
396
        open('b1/b', 'wb').write('b test\n')
397
        os.chmod('b1/a', 0755)
398
        os.chmod('b1/b', 0644)
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
399
        wt.add(['a', 'b'], [a_id, b_id])
400
        wt.inventory[a_id].executable = True
401
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
402
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
403
        # reopen the tree and ensure it stuck.
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
404
        wt = wt.bzrdir.open_workingtree()
405
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
406
407
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
408
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
409
410
        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
411
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
412
        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
413
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
414
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
415
416
        self.failUnless(rev_tree.inventory[a_id].executable)
417
        self.failIf(rev_tree.inventory[b_id].executable)
418
419
        # Make sure the entries are gone
420
        os.remove('b1/a')
421
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
422
        self.failIf(wt.has_id(a_id))
423
        self.failIf(wt.has_filename('a'))
424
        self.failIf(wt.has_id(b_id))
425
        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
426
427
        # Make sure that revert is able to bring them back,
428
        # and sets 'a' back to being executable
429
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
430
        wt.revert(['a', 'b'], rev_tree, backups=False)
431
        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
432
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
433
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
434
        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
435
436
        # Now remove them again, and make sure that after a
437
        # commit, they are still marked correctly
438
        os.remove('b1/a')
439
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
440
        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
441
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
442
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
443
        self.failIf(wt.has_id(a_id))
444
        self.failIf(wt.has_filename('a'))
445
        self.failIf(wt.has_id(b_id))
446
        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
447
448
        # Now revert back to the previous commit
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
449
        wt.revert([], rev_tree, backups=False)
450
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
451
452
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
453
        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
454
455
        # Now make sure that 'bzr branch' also preserves the
456
        # executable bit
457
        # 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.
458
        d2 = b.bzrdir.clone('b2', revision_id='r1')
459
        t2 = d2.open_workingtree()
460
        b2 = t2.branch
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
461
        self.assertEquals('r1', b2.last_revision())
462
463
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
464
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
465
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
466
467
        # Make sure pull will delete the files
468
        t2.pull(b)
469
        self.assertEquals('r2', b2.last_revision())
470
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
471
472
        # Now commit the changes on the first branch
473
        # so that the second branch can pull the changes
474
        # 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.
475
        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
476
477
        t2.pull(b)
478
        self.assertEquals('r3', b2.last_revision())
479
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
480
481
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
482
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
1534.7.178 by Aaron Bentley
Fixed dangling inventory ids in revert
483
484
class TestRevert(TestCaseWithTransport):
485
    def test_dangling_id(self):
486
        wt = self.make_branch_and_tree('b1')
487
        self.assertEqual(len(wt.inventory), 1)
488
        open('b1/a', 'wb').write('a test\n')
489
        wt.add('a')
490
        self.assertEqual(len(wt.inventory), 2)
491
        os.unlink('b1/a')
492
        wt.revert([])
493
        self.assertEqual(len(wt.inventory), 1)
1534.7.193 by Aaron Bentley
Stopped revert from preserving file contents produced by merges
494
495