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