~bzr-pqm/bzr/bzr.dev

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