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