~bzr-pqm/bzr/bzr.dev

2225.1.1 by Aaron Bentley
Added revert change display, with tests
1
# Copyright (C) 2007 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
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
17
import os
18
from StringIO import StringIO
19
2225.1.1 by Aaron Bentley
Added revert change display, with tests
20
from bzrlib import (
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
21
    delta as _mod_delta,
2225.1.1 by Aaron Bentley
Added revert change display, with tests
22
    tests,
23
    )
24
25
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
26
class InstrumentedReporter(object):
27
    def __init__(self):
28
        self.calls = []
29
30
    def report(self, file_id, path, versioned, renamed, modified, exe_change,
31
               kind):
32
        self.calls.append((file_id, path, versioned, renamed, modified,
33
                           exe_change, kind))
34
2225.1.4 by Aaron Bentley
PEP8 cleanup
35
2225.1.1 by Aaron Bentley
Added revert change display, with tests
36
class TestReportChanges(tests.TestCase):
2225.1.4 by Aaron Bentley
PEP8 cleanup
37
    """Test the new change reporting infrastructure"""
2225.1.1 by Aaron Bentley
Added revert change display, with tests
38
2225.1.3 by Aaron Bentley
change method names to assertFoo
39
    def assertReport(self, expected, file_id='fid', path='path',
40
                     versioned_change='unchanged', renamed=False,
41
                     modified='unchanged', exe_change=False,
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
42
                     kind=('file', 'file'), old_path=None,
43
                     unversioned_filter=None):
2225.1.1 by Aaron Bentley
Added revert change display, with tests
44
        result = []
45
        def result_line(format, *args):
46
            result.append(format % args)
1551.10.25 by Aaron Bentley
Make ChangeReporter private
47
        reporter = _mod_delta._ChangeReporter(result_line,
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
48
            unversioned_filter=unversioned_filter)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
49
        reporter.report(file_id, (old_path, path), versioned_change, renamed,
50
            modified, exe_change, kind)
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
51
        if expected is not None:
52
            self.assertEqualDiff(expected, result[0])
53
        else:
54
            self.assertEqual([], result)
2225.1.1 by Aaron Bentley
Added revert change display, with tests
55
56
    def test_rename(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
57
        self.assertReport('R   old => path', renamed=True, old_path='old')
58
        self.assertReport('    path')
1551.10.12 by Aaron Bentley
Handle simultaneous creation+rename
59
        self.assertReport('RN  old => path', renamed=True, old_path='old',
60
                          modified='created', kind=(None, 'file'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
61
62
    def test_kind(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
63
        self.assertReport(' K  path => path/', modified='kind changed',
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
64
                          kind=('file', 'directory'), old_path='path')
2225.1.3 by Aaron Bentley
change method names to assertFoo
65
        self.assertReport(' K  path/ => path', modified='kind changed',
66
                          kind=('directory', 'file'), old_path='old')
67
        self.assertReport('RK  old => path/', renamed=True,
2225.1.5 by Aaron Bentley
Clean up whitespace changes
68
                          modified='kind changed',
2225.1.3 by Aaron Bentley
change method names to assertFoo
69
                          kind=('file', 'directory'), old_path='old')
2225.1.1 by Aaron Bentley
Added revert change display, with tests
70
    def test_new(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
71
        self.assertReport(' N  path/', modified='created',
72
                          kind=(None, 'directory'))
73
        self.assertReport('+   path/', versioned_change='added',
74
                          modified='unchanged', kind=(None, 'directory'))
1551.10.11 by Aaron Bentley
Handle case where file-id only is added
75
        self.assertReport('+   path', versioned_change='added',
76
                          modified='unchanged', kind=(None, None))
2225.1.3 by Aaron Bentley
change method names to assertFoo
77
        self.assertReport('+N  path/', versioned_change='added',
78
                          modified='created', kind=(None, 'directory'))
79
        self.assertReport('+M  path/', versioned_change='added',
80
                          modified='modified', kind=(None, 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
81
82
    def test_removal(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
83
        self.assertReport(' D  path/', modified='deleted',
84
                          kind=('directory', None), old_path='old')
85
        self.assertReport('-   path/', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
86
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
87
                          kind=(None, 'directory'))
88
        self.assertReport('-D  path', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
89
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
90
                          modified='deleted', kind=('file', 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
91
92
    def test_modification(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
93
        self.assertReport(' M  path', modified='modified')
94
        self.assertReport(' M* path', modified='modified', exe_change=True)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
95
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
96
    def test_unversioned(self):
97
        # by default any unversioned file is output
98
        self.assertReport('?   subdir/foo~', file_id=None, path='subdir/foo~',
99
            old_path=None, versioned_change='unversioned',
100
            renamed=False, modified='created', exe_change=False,
101
            kind=(None, 'file'))
102
        # but we can choose to filter these. Probably that should be done 
103
        # close to the tree, but this is a reasonable starting point.
104
        self.assertReport(None, file_id=None, path='subdir/foo~',
105
            old_path=None, versioned_change='unversioned',
106
            renamed=False, modified='created', exe_change=False,
107
            kind=(None, 'file'), unversioned_filter=lambda x:True)
108
2225.1.3 by Aaron Bentley
change method names to assertFoo
109
    def assertChangesEqual(self,
110
                           file_id='fid',
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
111
                           paths=('path', 'path'),
2225.1.3 by Aaron Bentley
change method names to assertFoo
112
                           content_change=False,
113
                           versioned=(True, True),
114
                           parent_id=('pid', 'pid'),
115
                           name=('name', 'name'),
116
                           kind=('file', 'file'),
117
                           executable=(False, False),
118
                           versioned_change='unchanged',
119
                           renamed=False,
120
                           modified='unchanged',
121
                           exe_change=False):
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
122
        reporter = InstrumentedReporter()
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
123
        _mod_delta.report_changes([(file_id, paths, content_change, versioned,
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
124
            parent_id, name, kind, executable)], reporter)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
125
        output = reporter.calls[0]
126
        self.assertEqual(file_id, output[0])
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
127
        self.assertEqual(paths, output[1])
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
128
        self.assertEqual(versioned_change, output[2])
129
        self.assertEqual(renamed, output[3])
130
        self.assertEqual(modified, output[4])
131
        self.assertEqual(exe_change, output[5])
132
        self.assertEqual(kind, output[6])
133
134
    def test_report_changes(self):
135
        """Test change detection of report_changes"""
136
        #Ensure no changes are detected by default
2225.1.3 by Aaron Bentley
change method names to assertFoo
137
        self.assertChangesEqual(modified='unchanged', renamed=False,
138
                                versioned_change='unchanged',
139
                                exe_change=False)
140
        self.assertChangesEqual(modified='kind changed',
141
                                kind=('file', 'directory'))
142
        self.assertChangesEqual(modified='created', kind=(None, 'directory'))
143
        self.assertChangesEqual(modified='deleted', kind=('directory', None))
144
        self.assertChangesEqual(content_change=True, modified='modified')
145
        self.assertChangesEqual(renamed=True, name=('old', 'new'))
146
        self.assertChangesEqual(renamed=True,
147
                                parent_id=('old-parent', 'new-parent'))
148
        self.assertChangesEqual(versioned_change='added',
149
                                versioned=(False, True))
150
        self.assertChangesEqual(versioned_change='removed',
151
                                versioned=(True, False))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
152
        # execute bit is only detected as "changed" if the file is and was
153
        # a regular file.
2225.1.3 by Aaron Bentley
change method names to assertFoo
154
        self.assertChangesEqual(exe_change=True, executable=(True, False))
155
        self.assertChangesEqual(exe_change=False, executable=(True, False),
156
                                kind=('directory', 'directory'))
157
        self.assertChangesEqual(exe_change=False, modified='kind changed',
158
                                executable=(False, True),
159
                                kind=('directory', 'file'))
1551.11.3 by Aaron Bentley
Use tree transform to emit upcoming change list
160
        self.assertChangesEqual(parent_id=('pid', None))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
161
162
        # Now make sure they all work together
2225.1.3 by Aaron Bentley
change method names to assertFoo
163
        self.assertChangesEqual(versioned_change='removed',
164
                                modified='deleted', versioned=(True, False),
165
                                kind=('directory', None))
166
        self.assertChangesEqual(versioned_change='removed',
167
                                modified='created', versioned=(True, False),
168
                                kind=(None, 'file'))
169
        self.assertChangesEqual(versioned_change='removed',
170
                                modified='modified', renamed=True,
171
                                exe_change=True, versioned=(True, False),
172
                                content_change=True, name=('old', 'new'),
173
                                executable=(False, True))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
174
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
175
    def test_report_unversioned(self):
176
        """Unversioned entries are reported well."""
177
        self.assertChangesEqual(file_id=None, paths=(None, 'full/path'),
178
                           content_change=True,
179
                           versioned=(False, False),
180
                           parent_id=(None, None),
181
                           name=(None, 'path'),
182
                           kind=(None, 'file'),
183
                           executable=(None, False),
184
                           versioned_change='unversioned',
185
                           renamed=False,
186
                           modified='created',
187
                           exe_change=False)
188
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
189
190
class TestChangesFrom (tests.TestCaseWithTransport):
191
192
    def show_string(self, delta, *args,  **kwargs):
193
        to_file = StringIO()
194
        delta.show(to_file, *args, **kwargs)
195
        return to_file.getvalue()
196
197
    def test_kind_change(self):
198
        """Doing a status when a file has changed kind should work"""
199
        tree = self.make_branch_and_tree('.')
200
        self.build_tree(['filename'])
201
        tree.add('filename', 'file-id')
202
        tree.commit('added filename')
203
        os.unlink('filename')
204
        self.build_tree(['filename/'])
205
        delta = tree.changes_from(tree.basis_tree())
206
        self.assertEqual([('filename', 'file-id', 'file', 'directory')],
207
                         delta.kind_changed)
208
        self.assertEqual([], delta.added)
209
        self.assertEqual([], delta.removed)
210
        self.assertEqual([], delta.renamed)
211
        self.assertEqual([], delta.modified)
212
        self.assertEqual([], delta.unchanged)
213
        self.assertTrue(delta.has_changed())
214
        self.assertTrue(delta.touches_file_id('file-id'))
215
        self.assertEqual('kind changed:\n  filename (file => directory)\n',
216
                         self.show_string(delta))
217
        other_delta = _mod_delta.TreeDelta()
218
        self.assertNotEqual(other_delta, delta)
219
        other_delta.kind_changed = [('filename', 'file-id', 'file',
220
                                     'symlink')]
221
        self.assertNotEqual(other_delta, delta)
222
        other_delta.kind_changed = [('filename', 'file-id', 'file',
223
                                     'directory')]
224
        self.assertEqual(other_delta, delta)
225
        self.assertEqualDiff("TreeDelta(added=[], removed=[], renamed=[],"
226
            " kind_changed=[(u'filename', 'file-id', 'file', 'directory')],"
2255.7.90 by Robert Collins
Add unversioned path reporting to TreeDelta.
227
            " modified=[], unchanged=[], unversioned=[])", repr(delta))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
228
        self.assertEqual('K  filename (file => directory) file-id\n',
229
                         self.show_string(delta, show_ids=True,
230
                         short_status=True))
231
232
        tree.rename_one('filename', 'dirname')
233
        delta = tree.changes_from(tree.basis_tree())
234
        self.assertEqual([], delta.kind_changed)
235
        # This loses the fact that kind changed, remembering it as a
236
        # modification
237
        self.assertEqual([('filename', 'dirname', 'file-id', 'directory',
238
                           True, False)], delta.renamed)
239
        self.assertTrue(delta.has_changed())
240
        self.assertTrue(delta.touches_file_id('file-id'))