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