/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart_add.py

  • Committer: Robert Collins
  • Date: 2007-04-18 08:39:02 UTC
  • mto: (2425.1.2 integration)
  • mto: This revision was merged to the branch mainline in revision 2427.
  • Revision ID: robertc@robertcollins.net-20070418083902-4o66h9fk7zeisvwa
Command objects can now declare related help topics by having _see_also
set to a list of related topic. Updated the HACKING guide entry on
documentation to be more clear about how the help for commands is
generated and to reference this new feature. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 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
 
 
17
from cStringIO import StringIO
 
18
import os
 
19
import unittest
 
20
 
 
21
from bzrlib import errors, ignores, osutils
 
22
from bzrlib.add import (
 
23
    AddAction,
 
24
    AddFromBaseAction,
 
25
    smart_add,
 
26
    smart_add_tree,
 
27
    )
 
28
from bzrlib.tests import TestCase, TestCaseWithTransport, TestSkipped
 
29
from bzrlib.errors import NoSuchFile
 
30
from bzrlib.inventory import InventoryFile, Inventory
 
31
from bzrlib.workingtree import WorkingTree
 
32
 
 
33
 
 
34
class TestSmartAdd(TestCaseWithTransport):
 
35
 
 
36
    def test_add_dot_from_root(self):
 
37
        """Test adding . from the root of the tree.""" 
 
38
        paths = ("original/", "original/file1", "original/file2")
 
39
        self.build_tree(paths)
 
40
        wt = self.make_branch_and_tree('.')
 
41
        smart_add_tree(wt, (u".",))
 
42
        for path in paths:
 
43
            self.assertNotEqual(wt.path2id(path), None)
 
44
 
 
45
    def test_add_dot_from_subdir(self):
 
46
        """Test adding . from a subdir of the tree.""" 
 
47
        from bzrlib.add import smart_add
 
48
        paths = ("original/", "original/file1", "original/file2")
 
49
        self.build_tree(paths)
 
50
        wt = self.make_branch_and_tree('.')
 
51
        os.chdir("original")
 
52
        smart_add_tree(wt, (u".",))
 
53
        for path in paths:
 
54
            self.assertNotEqual(wt.path2id(path), None)
 
55
 
 
56
    def test_add_tree_from_above_tree(self):
 
57
        """Test adding a tree from above the tree.""" 
 
58
        from bzrlib.add import smart_add
 
59
        paths = ("original/", "original/file1", "original/file2")
 
60
        branch_paths = ("branch/", "branch/original/", "branch/original/file1",
 
61
                        "branch/original/file2")
 
62
        self.build_tree(branch_paths)
 
63
        wt = self.make_branch_and_tree('branch')
 
64
        smart_add_tree(wt, ("branch",))
 
65
        for path in paths:
 
66
            self.assertNotEqual(wt.path2id(path), None)
 
67
 
 
68
    def test_add_above_tree_preserves_tree(self):
 
69
        """Test nested trees are not affect by an add above them."""
 
70
        from bzrlib.add import smart_add
 
71
        paths = ("original/", "original/file1", "original/file2")
 
72
        child_paths = ("path",)
 
73
        full_child_paths = ("original/child", "original/child/path")
 
74
        build_paths = ("original/", "original/file1", "original/file2", 
 
75
                       "original/child/", "original/child/path")
 
76
        
 
77
        self.build_tree(build_paths)
 
78
        wt = self.make_branch_and_tree('.')
 
79
        child_tree = self.make_branch_and_tree('original/child')
 
80
        smart_add_tree(wt, (".",))
 
81
        for path in paths:
 
82
            self.assertNotEqual((path, wt.path2id(path)),
 
83
                                (path, None))
 
84
        for path in full_child_paths:
 
85
            self.assertEqual((path, wt.path2id(path)),
 
86
                             (path, None))
 
87
        for path in child_paths:
 
88
            self.assertEqual(child_tree.path2id(path), None)
 
89
 
 
90
    def test_add_paths(self):
 
91
        """Test smart-adding a list of paths."""
 
92
        from bzrlib.add import smart_add
 
93
        paths = ("file1", "file2")
 
94
        self.build_tree(paths)
 
95
        wt = self.make_branch_and_tree('.')
 
96
        smart_add_tree(wt, paths)
 
97
        for path in paths:
 
98
            self.assertNotEqual(wt.path2id(path), None)
 
99
    
 
100
    def test_add_ignored_nested_paths(self):
 
101
        """Test smart-adding a list of paths which includes ignored ones."""
 
102
        wt = self.make_branch_and_tree('.')
 
103
        tree_shape = ("adir/", "adir/CVS/", "adir/CVS/afile", "adir/CVS/afile2")
 
104
        add_paths = ("adir/CVS", "adir/CVS/afile", "adir")
 
105
        expected_paths = ("adir", "adir/CVS", "adir/CVS/afile", "adir/CVS/afile2")
 
106
        self.build_tree(tree_shape)
 
107
        smart_add_tree(wt, add_paths)
 
108
        for path in expected_paths:
 
109
            self.assertNotEqual(wt.path2id(path), None, "No id added for %s" % path)
 
110
 
 
111
    def test_add_dry_run(self):
 
112
        """Test a dry run add, make sure nothing is added."""
 
113
        from bzrlib.commands import run_bzr
 
114
        eq = self.assertEqual
 
115
        wt = self.make_branch_and_tree('.')
 
116
        self.build_tree(['inertiatic/', 'inertiatic/esp'])
 
117
        eq(list(wt.unknowns()), ['inertiatic'])
 
118
        self.capture('add --dry-run .')
 
119
        eq(list(wt.unknowns()), ['inertiatic'])
 
120
 
 
121
    def test_add_non_existant(self):
 
122
        """Test smart-adding a file that does not exist."""
 
123
        from bzrlib.add import smart_add
 
124
        wt = self.make_branch_and_tree('.')
 
125
        self.assertRaises(NoSuchFile, smart_add_tree, wt, 'non-existant-file')
 
126
 
 
127
    def test_returns_and_ignores(self):
 
128
        """Correctly returns added/ignored files"""
 
129
        from bzrlib.commands import run_bzr
 
130
        wt = self.make_branch_and_tree('.')
 
131
        # The default ignore list includes '*.py[co]', but not CVS
 
132
        ignores._set_user_ignores(['*.py[co]'])
 
133
        self.build_tree(['inertiatic/', 'inertiatic/esp', 'inertiatic/CVS',
 
134
                        'inertiatic/foo.pyc'])
 
135
        added, ignored = smart_add_tree(wt, u'.')
 
136
        self.assertSubset(('inertiatic', 'inertiatic/esp', 'inertiatic/CVS'),
 
137
                          added)
 
138
        self.assertSubset(('*.py[co]',), ignored)
 
139
        self.assertSubset(('inertiatic/foo.pyc',), ignored['*.py[co]'])
 
140
 
 
141
 
 
142
class AddCustomIDAction(AddAction):
 
143
 
 
144
    def __call__(self, inv, parent_ie, path, kind):
 
145
        # The first part just logs if appropriate
 
146
        # Now generate a custom id
 
147
        file_id = osutils.safe_file_id(kind + '-'
 
148
                                       + path.raw_path.replace('/', '%'),
 
149
                                       warn=False)
 
150
        if self.should_print:
 
151
            self._to_file.write('added %s with id %s\n'
 
152
                                % (path.raw_path, file_id))
 
153
        return file_id
 
154
 
 
155
 
 
156
class TestSmartAddTree(TestCaseWithTransport):
 
157
    """Test smart adds with a specified branch."""
 
158
 
 
159
    def test_add_dot_from_root(self):
 
160
        """Test adding . from the root of the tree.""" 
 
161
        paths = ("original/", "original/file1", "original/file2")
 
162
        self.build_tree(paths)
 
163
        wt = self.make_branch_and_tree('.')
 
164
        smart_add_tree(wt, (u".",))
 
165
        for path in paths:
 
166
            self.assertNotEqual(wt.path2id(path), None)
 
167
 
 
168
    def test_add_dot_from_subdir(self):
 
169
        """Test adding . from a subdir of the tree.""" 
 
170
        paths = ("original/", "original/file1", "original/file2")
 
171
        self.build_tree(paths)
 
172
        wt = self.make_branch_and_tree('.')
 
173
        os.chdir("original")
 
174
        smart_add_tree(wt, (u".",))
 
175
        for path in paths:
 
176
            self.assertNotEqual(wt.path2id(path), None)
 
177
 
 
178
    def test_add_tree_from_above_tree(self):
 
179
        """Test adding a tree from above the tree.""" 
 
180
        paths = ("original/", "original/file1", "original/file2")
 
181
        branch_paths = ("branch/", "branch/original/", "branch/original/file1",
 
182
                        "branch/original/file2")
 
183
        self.build_tree(branch_paths)
 
184
        tree = self.make_branch_and_tree('branch')
 
185
        smart_add_tree(tree, ("branch",))
 
186
        for path in paths:
 
187
            self.assertNotEqual(tree.path2id(path), None)
 
188
 
 
189
    def test_add_above_tree_preserves_tree(self):
 
190
        """Test nested trees are not affect by an add above them."""
 
191
        paths = ("original/", "original/file1", "original/file2")
 
192
        child_paths = ("path")
 
193
        full_child_paths = ("original/child", "original/child/path")
 
194
        build_paths = ("original/", "original/file1", "original/file2", 
 
195
                       "original/child/", "original/child/path")
 
196
        self.build_tree(build_paths)
 
197
        tree = self.make_branch_and_tree('.')
 
198
        child_tree = self.make_branch_and_tree("original/child")
 
199
        smart_add_tree(tree, (u".",))
 
200
        for path in paths:
 
201
            self.assertNotEqual((path, tree.path2id(path)),
 
202
                                (path, None))
 
203
        for path in full_child_paths:
 
204
            self.assertEqual((path, tree.path2id(path)),
 
205
                             (path, None))
 
206
        for path in child_paths:
 
207
            self.assertEqual(child_tree.path2id(path), None)
 
208
 
 
209
    def test_add_paths(self):
 
210
        """Test smart-adding a list of paths."""
 
211
        paths = ("file1", "file2")
 
212
        self.build_tree(paths)
 
213
        wt = self.make_branch_and_tree('.')
 
214
        smart_add_tree(wt, paths)
 
215
        for path in paths:
 
216
            self.assertNotEqual(wt.path2id(path), None)
 
217
 
 
218
    def test_add_multiple_dirs(self):
 
219
        """Test smart adding multiple directories at once."""
 
220
        added_paths = ['file1', 'file2',
 
221
                       'dir1/', 'dir1/file3',
 
222
                       'dir1/subdir2/', 'dir1/subdir2/file4',
 
223
                       'dir2/', 'dir2/file5',
 
224
                      ]
 
225
        not_added = ['file6', 'dir3/', 'dir3/file7', 'dir3/file8']
 
226
        self.build_tree(added_paths)
 
227
        self.build_tree(not_added)
 
228
 
 
229
        wt = self.make_branch_and_tree('.')
 
230
        smart_add_tree(wt, ['file1', 'file2', 'dir1', 'dir2'])
 
231
 
 
232
        for path in added_paths:
 
233
            self.assertNotEqual(None, wt.path2id(path.rstrip('/')),
 
234
                    'Failed to add path: %s' % (path,))
 
235
        for path in not_added:
 
236
            self.assertEqual(None, wt.path2id(path.rstrip('/')),
 
237
                    'Accidentally added path: %s' % (path,))
 
238
 
 
239
    def test_custom_ids(self):
 
240
        sio = StringIO()
 
241
        action = AddCustomIDAction(to_file=sio, should_print=True)
 
242
        self.build_tree(['file1', 'dir1/', 'dir1/file2'])
 
243
 
 
244
        wt = self.make_branch_and_tree('.')
 
245
        smart_add_tree(wt, ['.'], action=action)
 
246
        # The order of adds is not strictly fixed:
 
247
        sio.seek(0)
 
248
        lines = sorted(sio.readlines())
 
249
        self.assertEqualDiff(['added dir1 with id directory-dir1\n',
 
250
                              'added dir1/file2 with id file-dir1%file2\n',
 
251
                              'added file1 with id file-file1\n',
 
252
                             ], lines)
 
253
        wt.lock_read()
 
254
        self.addCleanup(wt.unlock)
 
255
        self.assertEqual([('', wt.path2id('')),
 
256
                          ('dir1', 'directory-dir1'),
 
257
                          ('dir1/file2', 'file-dir1%file2'),
 
258
                          ('file1', 'file-file1'),
 
259
                         ], [(path, ie.file_id) for path, ie
 
260
                                in wt.inventory.iter_entries()])
 
261
 
 
262
 
 
263
class TestAddFrom(TestCaseWithTransport):
 
264
    """Tests for AddFromBaseAction"""
 
265
 
 
266
    def make_base_tree(self):
 
267
        self.base_tree = self.make_branch_and_tree('base')
 
268
        self.build_tree(['base/a', 'base/b',
 
269
                         'base/dir/', 'base/dir/a',
 
270
                         'base/dir/subdir/',
 
271
                         'base/dir/subdir/b',
 
272
                        ])
 
273
        self.base_tree.add(['a', 'b', 'dir', 'dir/a',
 
274
                            'dir/subdir', 'dir/subdir/b'])
 
275
        self.base_tree.commit('creating initial tree.')
 
276
 
 
277
    def add_helper(self, base_tree, base_path, new_tree, file_list,
 
278
                   should_print=False):
 
279
        to_file = StringIO()
 
280
        base_tree.lock_read()
 
281
        try:
 
282
            new_tree.lock_write()
 
283
            try:
 
284
                action = AddFromBaseAction(base_tree, base_path,
 
285
                                           to_file=to_file,
 
286
                                           should_print=should_print)
 
287
                smart_add_tree(new_tree, file_list, action=action)
 
288
            finally:
 
289
                new_tree.unlock()
 
290
        finally:
 
291
            base_tree.unlock()
 
292
        return to_file.getvalue()
 
293
 
 
294
    def test_copy_all(self):
 
295
        self.make_base_tree()
 
296
        new_tree = self.make_branch_and_tree('new')
 
297
        files = ['a', 'b',
 
298
                 'dir/', 'dir/a',
 
299
                 'dir/subdir/',
 
300
                 'dir/subdir/b',
 
301
                ]
 
302
        self.build_tree(['new/' + fn for fn in files])
 
303
        self.add_helper(self.base_tree, '', new_tree, ['new'])
 
304
 
 
305
        for fn in files:
 
306
            base_file_id = self.base_tree.path2id(fn)
 
307
            new_file_id = new_tree.path2id(fn)
 
308
            self.assertEqual(base_file_id, new_file_id)
 
309
 
 
310
    def test_copy_from_dir(self):
 
311
        self.make_base_tree()
 
312
        new_tree = self.make_branch_and_tree('new')
 
313
 
 
314
        self.build_tree(['new/a', 'new/b', 'new/c',
 
315
                         'new/subdir/', 'new/subdir/b', 'new/subdir/d'])
 
316
        new_tree.set_root_id(self.base_tree.get_root_id())
 
317
        self.add_helper(self.base_tree, 'dir', new_tree, ['new'])
 
318
 
 
319
        # We know 'a' and 'b' exist in the root, and they are being added
 
320
        # in a new 'root'. Since ROOT ids have been set as the same, we will
 
321
        # use those ids
 
322
        self.assertEqual(self.base_tree.path2id('a'),
 
323
                         new_tree.path2id('a'))
 
324
        self.assertEqual(self.base_tree.path2id('b'),
 
325
                         new_tree.path2id('b'))
 
326
 
 
327
        # Because we specified 'dir/' as the base path, and we have
 
328
        # nothing named 'subdir' in the base tree, we should grab the
 
329
        # ids from there
 
330
        self.assertEqual(self.base_tree.path2id('dir/subdir'),
 
331
                         new_tree.path2id('subdir'))
 
332
        self.assertEqual(self.base_tree.path2id('dir/subdir/b'),
 
333
                         new_tree.path2id('subdir/b'))
 
334
 
 
335
        # These should get newly generated ids
 
336
        c_id = new_tree.path2id('c')
 
337
        self.assertNotEqual(None, c_id)
 
338
        self.base_tree.lock_read()
 
339
        self.addCleanup(self.base_tree.unlock)
 
340
        self.failIf(c_id in self.base_tree)
 
341
 
 
342
        d_id = new_tree.path2id('subdir/d')
 
343
        self.assertNotEqual(None, d_id)
 
344
        self.failIf(d_id in self.base_tree)
 
345
 
 
346
    def test_copy_existing_dir(self):
 
347
        self.make_base_tree()
 
348
        new_tree = self.make_branch_and_tree('new')
 
349
        self.build_tree(['new/subby/', 'new/subby/a', 'new/subby/b'])
 
350
 
 
351
        subdir_file_id = self.base_tree.path2id('dir/subdir')
 
352
        new_tree.add(['subby'], [subdir_file_id])
 
353
        self.add_helper(self.base_tree, '', new_tree, ['new'])
 
354
        # Because 'subby' already points to subdir, we should add
 
355
        # 'b' with the same id
 
356
        self.assertEqual(self.base_tree.path2id('dir/subdir/b'),
 
357
                         new_tree.path2id('subby/b'))
 
358
 
 
359
        # 'subby/a' should be added with a new id because there is no
 
360
        # matching path or child of 'subby'.
 
361
        a_id = new_tree.path2id('subby/a')
 
362
        self.assertNotEqual(None, a_id)
 
363
        self.base_tree.lock_read()
 
364
        self.addCleanup(self.base_tree.unlock)
 
365
        self.failIf(a_id in self.base_tree)
 
366
 
 
367
 
 
368
class TestAddNonNormalized(TestCaseWithTransport):
 
369
 
 
370
    def make(self):
 
371
        try:
 
372
            self.build_tree([u'a\u030a'])
 
373
        except UnicodeError:
 
374
            raise TestSkipped('Filesystem cannot create unicode filenames')
 
375
 
 
376
        self.wt = self.make_branch_and_tree('.')
 
377
 
 
378
    def test_accessible_explicit(self):
 
379
        self.make()
 
380
        orig = osutils.normalized_filename
 
381
        osutils.normalized_filename = osutils._accessible_normalized_filename
 
382
        try:
 
383
            smart_add_tree(self.wt, [u'a\u030a'])
 
384
            self.wt.lock_read()
 
385
            self.addCleanup(self.wt.unlock)
 
386
            self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
 
387
                    [(path, ie.kind) for path,ie in 
 
388
                        self.wt.inventory.iter_entries()])
 
389
        finally:
 
390
            osutils.normalized_filename = orig
 
391
 
 
392
    def test_accessible_implicit(self):
 
393
        self.make()
 
394
        orig = osutils.normalized_filename
 
395
        osutils.normalized_filename = osutils._accessible_normalized_filename
 
396
        try:
 
397
            smart_add_tree(self.wt, [])
 
398
            self.wt.lock_read()
 
399
            self.addCleanup(self.wt.unlock)
 
400
            self.assertEqual([('', 'directory'), (u'\xe5', 'file')],
 
401
                    [(path, ie.kind) for path,ie in 
 
402
                        self.wt.inventory.iter_entries()])
 
403
        finally:
 
404
            osutils.normalized_filename = orig
 
405
 
 
406
    def test_inaccessible_explicit(self):
 
407
        self.make()
 
408
        orig = osutils.normalized_filename
 
409
        osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
410
        try:
 
411
            self.assertRaises(errors.InvalidNormalization,
 
412
                    smart_add_tree, self.wt, [u'a\u030a'])
 
413
        finally:
 
414
            osutils.normalized_filename = orig
 
415
 
 
416
    def test_inaccessible_implicit(self):
 
417
        self.make()
 
418
        orig = osutils.normalized_filename
 
419
        osutils.normalized_filename = osutils._inaccessible_normalized_filename
 
420
        try:
 
421
            # TODO: jam 20060701 In the future, this should probably
 
422
            #       just ignore files that don't fit the normalization
 
423
            #       rules, rather than exploding
 
424
            self.assertRaises(errors.InvalidNormalization,
 
425
                    smart_add_tree, self.wt, [])
 
426
        finally:
 
427
            osutils.normalized_filename = orig
 
428
 
 
429
 
 
430
class TestAddActions(TestCase):
 
431
 
 
432
    def test_quiet(self):
 
433
        self.run_action("")
 
434
 
 
435
    def test__print(self):
 
436
        self.run_action("added path\n")
 
437
 
 
438
    def run_action(self, output):
 
439
        from bzrlib.add import AddAction, FastPath
 
440
        inv = Inventory()
 
441
        stdout = StringIO()
 
442
        action = AddAction(to_file=stdout, should_print=bool(output))
 
443
 
 
444
        self.apply_redirected(None, stdout, None, action, inv, None, FastPath('path'), 'file')
 
445
        self.assertEqual(stdout.getvalue(), output)