1# Copyright (C) 2005, 2006, 2007, 2009, 2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17from io import StringIO
18
19from .. import (
20    add,
21    cache_utf8,
22    errors,
23    tests,
24    )
25from ..bzr import (
26    inventory,
27    )
28
29
30class AddCustomIDAction(add.AddAction):
31
32    def __call__(self, inv, parent_ie, path, kind):
33        # The first part just logs if appropriate
34        # Now generate a custom id
35        file_id = cache_utf8.encode(kind + '-' + path.replace('/', '%'))
36        if self.should_print:
37            self._to_file.write('added %s with id %s\n'
38                                % (path, file_id.decode('utf-8')))
39        return file_id
40
41
42class TestAddFrom(tests.TestCaseWithTransport):
43    """Tests for AddFromBaseAction"""
44
45    def make_base_tree(self):
46        self.base_tree = self.make_branch_and_tree('base')
47        self.build_tree(['base/a', 'base/b',
48                         'base/dir/', 'base/dir/a',
49                         'base/dir/subdir/',
50                         'base/dir/subdir/b',
51                         ])
52        self.base_tree.add(['a', 'b', 'dir', 'dir/a',
53                            'dir/subdir', 'dir/subdir/b'])
54        self.base_tree.commit('creating initial tree.')
55
56    def add_helper(self, base_tree, base_path, new_tree, file_list,
57                   should_print=False):
58        to_file = StringIO()
59        base_tree.lock_read()
60        try:
61            new_tree.lock_write()
62            try:
63                action = add.AddFromBaseAction(base_tree, base_path,
64                                               to_file=to_file,
65                                               should_print=should_print)
66                new_tree.smart_add(file_list, action=action)
67            finally:
68                new_tree.unlock()
69        finally:
70            base_tree.unlock()
71        return to_file.getvalue()
72
73    def test_copy_all(self):
74        self.make_base_tree()
75        new_tree = self.make_branch_and_tree('new')
76        files = ['a', 'b',
77                 'dir/', 'dir/a',
78                 'dir/subdir/',
79                 'dir/subdir/b',
80                 ]
81        self.build_tree(['new/' + fn for fn in files])
82        self.add_helper(self.base_tree, '', new_tree, ['new'])
83
84        for fn in files:
85            base_file_id = self.base_tree.path2id(fn)
86            new_file_id = new_tree.path2id(fn)
87            self.assertEqual(base_file_id, new_file_id)
88
89    def test_copy_from_dir(self):
90        self.make_base_tree()
91        new_tree = self.make_branch_and_tree('new')
92
93        self.build_tree(['new/a', 'new/b', 'new/c',
94                         'new/subdir/', 'new/subdir/b', 'new/subdir/d'])
95        new_tree.set_root_id(self.base_tree.path2id(''))
96        self.add_helper(self.base_tree, 'dir', new_tree, ['new'])
97
98        # We know 'a' and 'b' exist in the root, and they are being added
99        # in a new 'root'. Since ROOT ids have been set as the same, we will
100        # use those ids
101        self.assertEqual(self.base_tree.path2id('a'),
102                         new_tree.path2id('a'))
103        self.assertEqual(self.base_tree.path2id('b'),
104                         new_tree.path2id('b'))
105
106        # Because we specified 'dir/' as the base path, and we have
107        # nothing named 'subdir' in the base tree, we should grab the
108        # ids from there
109        self.assertEqual(self.base_tree.path2id('dir/subdir'),
110                         new_tree.path2id('subdir'))
111        self.assertEqual(self.base_tree.path2id('dir/subdir/b'),
112                         new_tree.path2id('subdir/b'))
113
114        # These should get newly generated ids
115        c_id = new_tree.path2id('c')
116        self.assertNotEqual(None, c_id)
117        self.base_tree.lock_read()
118        self.addCleanup(self.base_tree.unlock)
119        self.assertRaises(errors.NoSuchId, self.base_tree.id2path, c_id)
120
121        d_id = new_tree.path2id('subdir/d')
122        self.assertNotEqual(None, d_id)
123        self.assertRaises(errors.NoSuchId, self.base_tree.id2path, d_id)
124
125    def test_copy_existing_dir(self):
126        self.make_base_tree()
127        new_tree = self.make_branch_and_tree('new')
128        self.build_tree(['new/subby/', 'new/subby/a', 'new/subby/b'])
129
130        subdir_file_id = self.base_tree.path2id('dir/subdir')
131        new_tree.add(['subby'], [subdir_file_id])
132        self.add_helper(self.base_tree, '', new_tree, ['new'])
133        # Because 'subby' already points to subdir, we should add
134        # 'b' with the same id
135        self.assertEqual(self.base_tree.path2id('dir/subdir/b'),
136                         new_tree.path2id('subby/b'))
137
138        # 'subby/a' should be added with a new id because there is no
139        # matching path or child of 'subby'.
140        a_id = new_tree.path2id('subby/a')
141        self.assertNotEqual(None, a_id)
142        self.base_tree.lock_read()
143        self.addCleanup(self.base_tree.unlock)
144        self.assertRaises(errors.NoSuchId, self.base_tree.id2path, a_id)
145
146
147class TestAddActions(tests.TestCase):
148
149    def test_quiet(self):
150        self.run_action("")
151
152    def test__print(self):
153        self.run_action("adding path\n")
154
155    def run_action(self, output):
156        inv = inventory.Inventory()
157        stdout = StringIO()
158        action = add.AddAction(to_file=stdout, should_print=bool(output))
159
160        self.apply_redirected(None, stdout, None, action, inv, None,
161                              'path', 'file')
162        self.assertEqual(stdout.getvalue(), output)
163