/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
23
from bzrlib.inventory import (Inventory, ROOT_ID, InventoryFile,
24
    InventoryDirectory, InventoryEntry)
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
25
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 \
26
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.
27
from bzrlib.tests import TestCase, TestCaseWithTransport
1551.2.54 by abentley
Fixed executability test
28
from bzrlib.transform import TreeTransform
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
29
from bzrlib.uncommit import uncommit
963 by Martin Pool
- add the start of a test for inventory file-id matching
30
969 by Martin Pool
- Add less-sucky is_within_any
31
1102 by Martin Pool
- merge test refactoring from robertc
32
class TestInventory(TestCase):
33
34
    def test_is_within(self):
968 by Martin Pool
- add some passing tests for is_inside_any
35
        from bzrlib.osutils import is_inside_any
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
36
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 \
37
        SRC_FOO_C = pathjoin('src', 'foo.c')
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
38
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
39
                         (['src'], SRC_FOO_C),
968 by Martin Pool
- add some passing tests for is_inside_any
40
                         (['src'], 'src'),
41
                         ]:
42
            self.assert_(is_inside_any(dirs, fn))
43
            
969 by Martin Pool
- Add less-sucky is_within_any
44
        for dirs, fn in [(['src'], 'srccontrol'),
45
                         (['src'], 'srccontrol/foo')]:
46
            self.assertFalse(is_inside_any(dirs, fn))
47
            
1102 by Martin Pool
- merge test refactoring from robertc
48
    def test_ids(self):
963 by Martin Pool
- add the start of a test for inventory file-id matching
49
        """Test detection of files within selected directories."""
50
        inv = Inventory()
51
        
52
        for args in [('src', 'directory', 'src-id'), 
53
                     ('doc', 'directory', 'doc-id'), 
54
                     ('src/hello.c', 'file'),
55
                     ('src/bye.c', 'file', 'bye-id'),
56
                     ('Makefile', 'file')]:
57
            inv.add_path(*args)
58
            
59
        self.assertEqual(inv.path2id('src'), 'src-id')
60
        self.assertEqual(inv.path2id('src/bye.c'), 'bye-id')
61
        
62
        self.assert_('src-id' in inv)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
63
64
    def test_version(self):
65
        """Inventory remembers the text's version."""
66
        inv = Inventory()
67
        ie = inv.add_path('foo.txt', 'file')
68
        ## XXX
69
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
70
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
71
class TestInventoryEntry(TestCase):
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
72
73
    def test_file_kind_character(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
74
        file = inventory.InventoryFile('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
75
        self.assertEqual(file.kind_character(), '')
76
77
    def test_dir_kind_character(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
78
        dir = inventory.InventoryDirectory('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
79
        self.assertEqual(dir.kind_character(), '/')
80
81
    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
82
        dir = inventory.InventoryLink('123', 'hello.c', ROOT_ID)
1399.1.2 by Robert Collins
push kind character creation into InventoryEntry and TreeEntry
83
        self.assertEqual(dir.kind_character(), '')
1399.1.3 by Robert Collins
move change detection for text and metadata from delta to entry.detect_changes
84
85
    def test_dir_detect_changes(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
86
        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
87
        left.text_sha1 = 123
88
        left.executable = True
89
        left.symlink_target='foo'
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
90
        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
91
        right.text_sha1 = 321
92
        right.symlink_target='bar'
93
        self.assertEqual((False, False), left.detect_changes(right))
94
        self.assertEqual((False, False), right.detect_changes(left))
95
96
    def test_file_detect_changes(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
97
        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
98
        left.text_sha1 = 123
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
99
        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
100
        right.text_sha1 = 123
101
        self.assertEqual((False, False), left.detect_changes(right))
102
        self.assertEqual((False, False), right.detect_changes(left))
103
        left.executable = True
104
        self.assertEqual((False, True), left.detect_changes(right))
105
        self.assertEqual((False, True), right.detect_changes(left))
106
        right.text_sha1 = 321
107
        self.assertEqual((True, True), left.detect_changes(right))
108
        self.assertEqual((True, True), right.detect_changes(left))
109
110
    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
111
        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
112
        left.text_sha1 = 123
113
        left.executable = True
114
        left.symlink_target='foo'
1399.1.10 by Robert Collins
remove kind from the InventoryEntry constructor - only child classes should be created now
115
        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
116
        right.text_sha1 = 321
117
        right.symlink_target='foo'
118
        self.assertEqual((False, False), left.detect_changes(right))
119
        self.assertEqual((False, False), right.detect_changes(left))
120
        left.symlink_target = 'different'
121
        self.assertEqual((True, False), left.detect_changes(right))
122
        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
123
1399.1.5 by Robert Collins
move checking whether an entry stores text into inventory.py from fetch,py
124
    def test_file_has_text(self):
1399.1.9 by Robert Collins
factor out file related logic from InventoryEntry to InventoryFile
125
        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
126
        self.failUnless(file.has_text())
127
128
    def test_directory_has_text(self):
1399.1.8 by Robert Collins
factor out inventory directory logic into 'InventoryDirectory' class
129
        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
130
        self.failIf(dir.has_text())
131
132
    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
133
        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
134
        self.failIf(link.has_text())
135
1713.1.11 by Robert Collins
refactor smart_add to pass around the parent inventory entry and use that, resulting in another 100bzrlib/inventory.py performance improvement, and making inventory writing the dominating factory in add. (Robert Collins)
136
    def test_make_entry(self):
137
        self.assertIsInstance(inventory.make_entry("file", "name", ROOT_ID),
138
            inventory.InventoryFile)
139
        self.assertIsInstance(inventory.make_entry("symlink", "name", ROOT_ID),
140
            inventory.InventoryLink)
141
        self.assertIsInstance(inventory.make_entry("directory", "name", ROOT_ID),
142
            inventory.InventoryDirectory)
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
143
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
144
class TestEntryDiffing(TestCaseWithTransport):
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
145
146
    def setUp(self):
147
        super(TestEntryDiffing, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
148
        self.wt = self.make_branch_and_tree('.')
149
        self.branch = self.wt.branch
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
150
        print >> open('file', 'wb'), 'foo'
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
151
        print >> open('binfile', 'wb'), 'foo'
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
152
        self.wt.add(['file'], ['fileid'])
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
153
        self.wt.add(['binfile'], ['binfileid'])
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
154
        if has_symlinks():
155
            os.symlink('target1', 'symlink')
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
156
            self.wt.add(['symlink'], ['linkid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
157
        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
158
        print >> open('file', 'wb'), 'bar'
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
159
        print >> open('binfile', 'wb'), 'x' * 1023 + '\x00'
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
160
        if has_symlinks():
161
            os.unlink('symlink')
162
            os.symlink('target2', 'symlink')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
163
        self.tree_1 = self.branch.repository.revision_tree('1')
164
        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
165
        self.file_1 = self.inv_1['fileid']
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
166
        self.file_1b = self.inv_1['binfileid']
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
167
        self.tree_2 = self.wt
1497 by Robert Collins
Move Branch.read_working_inventory to WorkingTree.
168
        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
169
        self.file_2 = self.inv_2['fileid']
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
170
        self.file_2b = self.inv_2['binfileid']
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
171
        if has_symlinks():
172
            self.link_1 = self.inv_1['linkid']
173
            self.link_2 = self.inv_2['linkid']
174
175
    def test_file_diff_deleted(self):
176
        output = StringIO()
177
        self.file_1.diff(internal_diff, 
178
                          "old_label", self.tree_1,
179
                          "/dev/null", None, None,
180
                          output)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
181
        self.assertEqual(output.getvalue(), "--- old_label\t\n"
182
                                            "+++ /dev/null\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
183
                                            "@@ -1,1 +0,0 @@\n"
184
                                            "-foo\n"
185
                                            "\n")
186
187
    def test_file_diff_added(self):
188
        output = StringIO()
189
        self.file_1.diff(internal_diff, 
190
                          "new_label", self.tree_1,
191
                          "/dev/null", None, None,
192
                          output, reverse=True)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
193
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
194
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
195
                                            "@@ -0,0 +1,1 @@\n"
196
                                            "+foo\n"
197
                                            "\n")
198
199
    def test_file_diff_changed(self):
200
        output = StringIO()
201
        self.file_1.diff(internal_diff, 
202
                          "/dev/null", self.tree_1, 
203
                          "new_label", self.file_2, self.tree_2,
204
                          output)
1185.35.29 by Aaron Bentley
Support whitespace in diff filenames
205
        self.assertEqual(output.getvalue(), "--- /dev/null\t\n"
206
                                            "+++ new_label\t\n"
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
207
                                            "@@ -1,1 +1,1 @@\n"
208
                                            "-foo\n"
209
                                            "+bar\n"
210
                                            "\n")
211
        
1558.15.2 by Aaron Bentley
Implemented binary file handling for diff
212
    def test_file_diff_binary(self):
213
        output = StringIO()
214
        self.file_1.diff(internal_diff, 
215
                          "/dev/null", self.tree_1, 
216
                          "new_label", self.file_2b, self.tree_2,
217
                          output)
1558.15.11 by Aaron Bentley
Apply merge review suggestions
218
        self.assertEqual(output.getvalue(), 
219
                         "Binary files /dev/null and new_label differ\n")
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
220
    def test_link_diff_deleted(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
                          "old_label", self.tree_1,
226
                          "/dev/null", None, None,
227
                          output)
228
        self.assertEqual(output.getvalue(),
229
                         "=== target was 'target1'\n")
230
231
    def test_link_diff_added(self):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
232
        if not has_symlinks():
233
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
234
        output = StringIO()
235
        self.link_1.diff(internal_diff, 
236
                          "new_label", self.tree_1,
237
                          "/dev/null", None, None,
238
                          output, reverse=True)
239
        self.assertEqual(output.getvalue(),
240
                         "=== target is 'target1'\n")
241
242
    def test_link_diff_changed(self):
1431 by Robert Collins
BUGFIX: disable symlink support tests when no symlink support is present on the system.
243
        if not has_symlinks():
244
            return
1399.1.4 by Robert Collins
move diff and symlink conditionals into inventory.py from diff.py
245
        output = StringIO()
246
        self.link_1.diff(internal_diff, 
247
                          "/dev/null", self.tree_1, 
248
                          "new_label", self.link_2, self.tree_2,
249
                          output)
250
        self.assertEqual(output.getvalue(),
251
                         "=== target changed 'target1' => 'target2'\n")
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
252
253
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
254
class TestSnapshot(TestCaseWithTransport):
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
255
256
    def setUp(self):
257
        # for full testing we'll need a branch
258
        # with a subdir to test parent changes.
259
        # and a file, link and dir under that.
260
        # but right now I only need one attribute
261
        # to change, and then test merge patterns
262
        # with fake parent entries.
263
        super(TestSnapshot, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
264
        self.wt = self.make_branch_and_tree('.')
265
        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
266
        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.
267
        self.wt.add(['subdir', 'subdir/file'],
1508.1.5 by Robert Collins
Move add from Branch to WorkingTree.
268
                                       ['dirid', 'fileid'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
269
        if has_symlinks():
270
            pass
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
271
        self.wt.commit('message_1', rev_id = '1')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
272
        self.tree_1 = self.branch.repository.revision_tree('1')
273
        self.inv_1 = self.branch.repository.get_inventory('1')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
274
        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.
275
        self.file_active = self.wt.inventory['fileid']
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
276
277
    def test_snapshot_new_revision(self):
278
        # This tests that a simple commit with no parents makes a new
279
        # 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.
280
        self.file_active.snapshot('2', 'subdir/file', {}, self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
281
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
282
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
283
        # expected outcome - file_1 has a revision id of '2', and we can get
284
        # its text of 'file contents' out of the weave.
285
        self.assertEqual(self.file_1.revision, '1')
286
        self.assertEqual(self.file_active.revision, '2')
287
        # 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.
288
        lines = self.branch.repository.weave_store.get_weave(
289
            'fileid', 
290
            self.branch.get_transaction()).get_lines('2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
291
        self.assertEqual(lines, ['contents of subdir/file\n'])
292
293
    def test_snapshot_unchanged(self):
294
        #This tests that a simple commit does not make a new entry for
295
        # an unchanged inventory entry
296
        self.file_active.snapshot('2', 'subdir/file', {'1':self.file_1},
1534.4.28 by Robert Collins
first cut at merge from integration.
297
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
298
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
299
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
300
        self.assertEqual(self.file_1.revision, '1')
301
        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.
302
        vf = self.branch.repository.weave_store.get_weave(
303
            'fileid', 
304
            self.branch.repository.get_transaction())
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
305
        self.assertRaises(errors.RevisionNotPresent,
1563.2.35 by Robert Collins
cleanup deprecation warnings and finish conversion so the inventory is knit based too.
306
                          vf.get_lines,
307
                          '2')
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
308
309
    def test_snapshot_merge_identical_different_revid(self):
310
        # This tests that a commit with two identical parents, one of which has
311
        # 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
312
        # 1->other, commit a merge of other against 1, results in 2.
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
313
        other_ie = inventory.InventoryFile('fileid', 'newname', self.file_1.parent_id)
314
        other_ie = inventory.InventoryFile('fileid', 'file', self.file_1.parent_id)
315
        other_ie.revision = '1'
316
        other_ie.text_sha1 = self.file_1.text_sha1
317
        other_ie.text_size = self.file_1.text_size
318
        self.assertEqual(self.file_1, other_ie)
319
        other_ie.revision = 'other'
320
        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.
321
        versionfile = self.branch.repository.weave_store.get_weave(
322
            'fileid', self.branch.repository.get_transaction())
323
        versionfile.clone_text('other', '1', ['1'])
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
324
        self.file_active.snapshot('2', 'subdir/file', 
325
                                  {'1':self.file_1, 'other':other_ie},
1534.4.28 by Robert Collins
first cut at merge from integration.
326
                                  self.wt, 
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
327
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
328
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
329
        self.assertEqual(self.file_active.revision, '2')
330
331
    def test_snapshot_changed(self):
332
        # This tests that a commit with one different parent results in a new
333
        # revision id in the entry.
334
        self.file_active.name='newname'
335
        rename('subdir/file', 'subdir/newname')
336
        self.file_active.snapshot('2', 'subdir/newname', {'1':self.file_1}, 
1534.4.28 by Robert Collins
first cut at merge from integration.
337
                                  self.wt,
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
338
                                  self.branch.repository.weave_store,
1417.1.8 by Robert Collins
use transactions in the weave store interface, which enables caching for log
339
                                  self.branch.get_transaction())
1407 by Robert Collins
define some expected behaviour for inventory_entry.snapshot
340
        # expected outcome - file_1 has a revision id of '2'
341
        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.
342
343
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
344
class TestPreviousHeads(TestCaseWithTransport):
1411 by Robert Collins
use weave ancestry to determine inventory entry previous heads, prevent propogating 'I did a merge' merges.
345
346
    def setUp(self):
347
        # we want several inventories, that respectively
348
        # give use the following scenarios:
349
        # A) fileid not in any inventory (A),
350
        # B) fileid present in one inventory (B) and (A,B)
351
        # C) fileid present in two inventories, and they
352
        #   are not mutual descendents (B, C)
353
        # D) fileid present in two inventories and one is
354
        #   a descendent of the other. (B, D)
355
        super(TestPreviousHeads, self).setUp()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
356
        self.wt = self.make_branch_and_tree('.')
357
        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.
358
        self.build_tree(['file'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
359
        self.wt.commit('new branch', allow_pointless=True, rev_id='A')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
360
        self.inv_A = self.branch.repository.get_inventory('A')
1185.65.13 by Robert Collins
Merge from integration
361
        self.wt.add(['file'], ['fileid'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
362
        self.wt.commit('add file', rev_id='B')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
363
        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
364
        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.
365
        self.assertEqual(self.branch.revision_history(), ['A'])
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
366
        self.wt.commit('another add of file', rev_id='C')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
367
        self.inv_C = self.branch.repository.get_inventory('C')
1457.1.17 by Robert Collins
Branch.commit() has moved to WorkingTree.commit(). (Robert Collins)
368
        self.wt.add_pending_merge('B')
369
        self.wt.commit('merge in B', rev_id='D')
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
370
        self.inv_D = self.branch.repository.get_inventory('D')
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
371
        self.file_active = self.wt.inventory['fileid']
1185.67.2 by Aaron Bentley
Renamed Branch.storage to Branch.repository
372
        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.
373
            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.
374
        
375
    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.
376
        return self.file_active.find_previous_heads(
377
            inventories, 
378
            self.branch.repository.weave_store,
379
            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.
380
        
381
    def test_fileid_in_no_inventory(self):
382
        self.assertEqual({}, self.get_previous_heads([self.inv_A]))
383
384
    def test_fileid_in_one_inventory(self):
385
        self.assertEqual({'B':self.inv_B['fileid']},
386
                         self.get_previous_heads([self.inv_B]))
387
        self.assertEqual({'B':self.inv_B['fileid']},
388
                         self.get_previous_heads([self.inv_A, self.inv_B]))
389
        self.assertEqual({'B':self.inv_B['fileid']},
390
                         self.get_previous_heads([self.inv_B, self.inv_A]))
391
392
    def test_fileid_in_two_inventories_gives_both_entries(self):
393
        self.assertEqual({'B':self.inv_B['fileid'],
394
                          'C':self.inv_C['fileid']},
395
                          self.get_previous_heads([self.inv_B, self.inv_C]))
396
        self.assertEqual({'B':self.inv_B['fileid'],
397
                          'C':self.inv_C['fileid']},
398
                          self.get_previous_heads([self.inv_C, self.inv_B]))
399
400
    def test_fileid_in_two_inventories_already_merged_gives_head(self):
401
        self.assertEqual({'D':self.inv_D['fileid']},
402
                         self.get_previous_heads([self.inv_B, self.inv_D]))
403
        self.assertEqual({'D':self.inv_D['fileid']},
404
                         self.get_previous_heads([self.inv_D, self.inv_B]))
405
406
    # 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
407
408
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
409
class TestDescribeChanges(TestCase):
410
411
    def test_describe_change(self):
412
        # we need to test the following change combinations:
413
        # rename
414
        # reparent
415
        # modify
416
        # gone
417
        # added
418
        # renamed/reparented and modified
419
        # change kind (perhaps can't be done yet?)
420
        # also, merged in combination with all of these?
421
        old_a = InventoryFile('a-id', 'a_file', ROOT_ID)
422
        old_a.text_sha1 = '123132'
423
        old_a.text_size = 0
424
        new_a = InventoryFile('a-id', 'a_file', ROOT_ID)
425
        new_a.text_sha1 = '123132'
426
        new_a.text_size = 0
427
428
        self.assertChangeDescription('unchanged', old_a, new_a)
429
430
        new_a.text_size = 10
431
        new_a.text_sha1 = 'abcabc'
432
        self.assertChangeDescription('modified', old_a, new_a)
433
434
        self.assertChangeDescription('added', None, new_a)
435
        self.assertChangeDescription('removed', old_a, None)
436
        # perhaps a bit questionable but seems like the most reasonable thing...
437
        self.assertChangeDescription('unchanged', None, None)
438
439
        # in this case it's both renamed and modified; show a rename and 
440
        # modification:
441
        new_a.name = 'newfilename'
442
        self.assertChangeDescription('modified and renamed', old_a, new_a)
443
444
        # reparenting is 'renaming'
445
        new_a.name = old_a.name
446
        new_a.parent_id = 'somedir-id'
447
        self.assertChangeDescription('modified and renamed', old_a, new_a)
448
449
        # reset the content values so its not modified
450
        new_a.text_size = old_a.text_size
451
        new_a.text_sha1 = old_a.text_sha1
452
        new_a.name = old_a.name
453
454
        new_a.name = 'newfilename'
455
        self.assertChangeDescription('renamed', old_a, new_a)
456
457
        # reparenting is 'renaming'
458
        new_a.name = old_a.name
459
        new_a.parent_id = 'somedir-id'
460
        self.assertChangeDescription('renamed', old_a, new_a)
461
462
    def assertChangeDescription(self, expected_change, old_ie, new_ie):
463
        change = InventoryEntry.describe_change(old_ie, new_ie)
464
        self.assertEqual(expected_change, change)
465
466
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
467
class TestExecutable(TestCaseWithTransport):
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
468
469
    def test_stays_executable(self):
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
470
        a_id = "a-20051208024829-849e76f7968d7a86"
471
        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.
472
        wt = self.make_branch_and_tree('b1')
473
        b = wt.branch
1551.2.54 by abentley
Fixed executability test
474
        tt = TreeTransform(wt)
475
        tt.new_file('a', tt.root, 'a test\n', a_id, True)
476
        tt.new_file('b', tt.root, 'b test\n', b_id, False)
477
        tt.apply()
478
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
479
        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
480
1666.1.4 by Robert Collins
* 'Metadir' is now the default disk format. This improves behaviour in
481
        # reopen the tree and ensure it stuck.
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
482
        wt = wt.bzrdir.open_workingtree()
483
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
484
485
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
486
        self.failIf(wt.is_executable(b_id), "'b' gained an execute bit")
487
488
        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
489
1185.65.17 by Robert Collins
Merge from integration, mode-changes are broken.
490
        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
491
        self.failUnless(rev_tree.is_executable(a_id), "'a' lost the execute bit")
492
        self.failIf(rev_tree.is_executable(b_id), "'b' gained an execute bit")
493
494
        self.failUnless(rev_tree.inventory[a_id].executable)
495
        self.failIf(rev_tree.inventory[b_id].executable)
496
497
        # Make sure the entries are gone
498
        os.remove('b1/a')
499
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
500
        self.failIf(wt.has_id(a_id))
501
        self.failIf(wt.has_filename('a'))
502
        self.failIf(wt.has_id(b_id))
503
        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
504
505
        # Make sure that revert is able to bring them back,
506
        # and sets 'a' back to being executable
507
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
508
        wt.revert(['a', 'b'], rev_tree, backups=False)
509
        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
510
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
511
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
512
        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
513
514
        # Now remove them again, and make sure that after a
515
        # commit, they are still marked correctly
516
        os.remove('b1/a')
517
        os.remove('b1/b')
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
518
        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
519
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
520
        self.assertEqual([], [cn for cn,ie in wt.inventory.iter_entries()])
521
        self.failIf(wt.has_id(a_id))
522
        self.failIf(wt.has_filename('a'))
523
        self.failIf(wt.has_id(b_id))
524
        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
525
526
        # Now revert back to the previous commit
1508.1.19 by Robert Collins
Give format3 working trees their own last-revision marker.
527
        wt.revert([], rev_tree, backups=False)
528
        self.assertEqual(['a', 'b'], [cn for cn,ie in wt.inventory.iter_entries()])
529
530
        self.failUnless(wt.is_executable(a_id), "'a' lost the execute bit")
531
        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
532
533
        # Now make sure that 'bzr branch' also preserves the
534
        # executable bit
535
        # 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.
536
        d2 = b.bzrdir.clone('b2', revision_id='r1')
537
        t2 = d2.open_workingtree()
538
        b2 = t2.branch
1185.31.55 by John Arbash Meinel
Added (win32 failing) test to make sure is_executable is maintained over several operations
539
        self.assertEquals('r1', b2.last_revision())
540
541
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
542
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
543
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
544
545
        # Make sure pull will delete the files
546
        t2.pull(b)
547
        self.assertEquals('r2', b2.last_revision())
548
        self.assertEqual([], [cn for cn,ie in t2.inventory.iter_entries()])
549
550
        # Now commit the changes on the first branch
551
        # so that the second branch can pull the changes
552
        # 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.
553
        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
554
555
        t2.pull(b)
556
        self.assertEquals('r3', b2.last_revision())
557
        self.assertEqual(['a', 'b'], [cn for cn,ie in t2.inventory.iter_entries()])
558
559
        self.failUnless(t2.is_executable(a_id), "'a' lost the execute bit")
560
        self.failIf(t2.is_executable(b_id), "'b' gained an execute bit")
1534.7.178 by Aaron Bentley
Fixed dangling inventory ids in revert
561
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
562
1534.7.178 by Aaron Bentley
Fixed dangling inventory ids in revert
563
class TestRevert(TestCaseWithTransport):
1668.1.5 by Martin Pool
[broken] fix up display of files changed by a commit
564
1534.7.178 by Aaron Bentley
Fixed dangling inventory ids in revert
565
    def test_dangling_id(self):
566
        wt = self.make_branch_and_tree('b1')
567
        self.assertEqual(len(wt.inventory), 1)
568
        open('b1/a', 'wb').write('a test\n')
569
        wt.add('a')
570
        self.assertEqual(len(wt.inventory), 2)
571
        os.unlink('b1/a')
572
        wt.revert([])
573
        self.assertEqual(len(wt.inventory), 1)