~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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2225.1.1 by Aaron Bentley
Added revert change display, with tests
16
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
17
import os
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
18
from cStringIO import StringIO
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
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,
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
22
    revision as _mod_revision,
2225.1.1 by Aaron Bentley
Added revert change display, with tests
23
    tests,
24
    )
25
26
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
27
class InstrumentedReporter(object):
28
    def __init__(self):
29
        self.calls = []
30
31
    def report(self, file_id, path, versioned, renamed, modified, exe_change,
32
               kind):
33
        self.calls.append((file_id, path, versioned, renamed, modified,
34
                           exe_change, kind))
35
2225.1.4 by Aaron Bentley
PEP8 cleanup
36
2225.1.1 by Aaron Bentley
Added revert change display, with tests
37
class TestReportChanges(tests.TestCase):
2225.1.4 by Aaron Bentley
PEP8 cleanup
38
    """Test the new change reporting infrastructure"""
2225.1.1 by Aaron Bentley
Added revert change display, with tests
39
2225.1.3 by Aaron Bentley
change method names to assertFoo
40
    def assertReport(self, expected, file_id='fid', path='path',
41
                     versioned_change='unchanged', renamed=False,
42
                     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.
43
                     kind=('file', 'file'), old_path=None,
3586.1.30 by Ian Clatworthy
add view support to change reporting
44
                     unversioned_filter=None, view_info=None):
45
        if expected is None:
46
            expected_lines = None
47
        else:
48
            expected_lines = [expected]
49
        self.assertReportLines(expected_lines, file_id, path,
50
                     versioned_change, renamed,
51
                     modified, exe_change,
52
                     kind, old_path,
53
                     unversioned_filter, view_info)
54
55
    def assertReportLines(self, expected_lines, file_id='fid', path='path',
56
                     versioned_change='unchanged', renamed=False,
57
                     modified='unchanged', exe_change=False,
58
                     kind=('file', 'file'), old_path=None,
59
                     unversioned_filter=None, view_info=None):
2225.1.1 by Aaron Bentley
Added revert change display, with tests
60
        result = []
61
        def result_line(format, *args):
62
            result.append(format % args)
1551.10.25 by Aaron Bentley
Make ChangeReporter private
63
        reporter = _mod_delta._ChangeReporter(result_line,
3586.1.30 by Ian Clatworthy
add view support to change reporting
64
            unversioned_filter=unversioned_filter, view_info=view_info)
2255.7.96 by Robert Collins
Change _iter_changes interface to yield both old and new paths.
65
        reporter.report(file_id, (old_path, path), versioned_change, renamed,
66
            modified, exe_change, kind)
3586.1.30 by Ian Clatworthy
add view support to change reporting
67
        if expected_lines is not None:
68
            for i in range(len(expected_lines)):
69
                self.assertEqualDiff(expected_lines[i], result[i])
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
70
        else:
71
            self.assertEqual([], result)
2225.1.1 by Aaron Bentley
Added revert change display, with tests
72
73
    def test_rename(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
74
        self.assertReport('R   old => path', renamed=True, old_path='old')
75
        self.assertReport('    path')
1551.10.12 by Aaron Bentley
Handle simultaneous creation+rename
76
        self.assertReport('RN  old => path', renamed=True, old_path='old',
77
                          modified='created', kind=(None, 'file'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
78
79
    def test_kind(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
80
        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.
81
                          kind=('file', 'directory'), old_path='path')
2225.1.3 by Aaron Bentley
change method names to assertFoo
82
        self.assertReport(' K  path/ => path', modified='kind changed',
83
                          kind=('directory', 'file'), old_path='old')
84
        self.assertReport('RK  old => path/', renamed=True,
2225.1.5 by Aaron Bentley
Clean up whitespace changes
85
                          modified='kind changed',
2225.1.3 by Aaron Bentley
change method names to assertFoo
86
                          kind=('file', 'directory'), old_path='old')
2225.1.1 by Aaron Bentley
Added revert change display, with tests
87
    def test_new(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
88
        self.assertReport(' N  path/', modified='created',
89
                          kind=(None, 'directory'))
90
        self.assertReport('+   path/', versioned_change='added',
91
                          modified='unchanged', kind=(None, 'directory'))
1551.10.11 by Aaron Bentley
Handle case where file-id only is added
92
        self.assertReport('+   path', versioned_change='added',
93
                          modified='unchanged', kind=(None, None))
2225.1.3 by Aaron Bentley
change method names to assertFoo
94
        self.assertReport('+N  path/', versioned_change='added',
95
                          modified='created', kind=(None, 'directory'))
96
        self.assertReport('+M  path/', versioned_change='added',
97
                          modified='modified', kind=(None, 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
98
99
    def test_removal(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
100
        self.assertReport(' D  path/', modified='deleted',
101
                          kind=('directory', None), old_path='old')
102
        self.assertReport('-   path/', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
103
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
104
                          kind=(None, 'directory'))
105
        self.assertReport('-D  path', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
106
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
107
                          modified='deleted', kind=('file', 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
108
109
    def test_modification(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
110
        self.assertReport(' M  path', modified='modified')
111
        self.assertReport(' M* path', modified='modified', exe_change=True)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
112
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
113
    def test_unversioned(self):
114
        # by default any unversioned file is output
115
        self.assertReport('?   subdir/foo~', file_id=None, path='subdir/foo~',
116
            old_path=None, versioned_change='unversioned',
117
            renamed=False, modified='created', exe_change=False,
118
            kind=(None, 'file'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
119
        # but we can choose to filter these. Probably that should be done
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
120
        # close to the tree, but this is a reasonable starting point.
121
        self.assertReport(None, file_id=None, path='subdir/foo~',
122
            old_path=None, versioned_change='unversioned',
123
            renamed=False, modified='created', exe_change=False,
124
            kind=(None, 'file'), unversioned_filter=lambda x:True)
125
3586.1.30 by Ian Clatworthy
add view support to change reporting
126
    def test_view_filtering(self):
127
        # If a file in within the view, it should appear in the output
128
        expected_lines = [
129
            "Operating on whole tree but only reporting on 'my' view.",
130
            " M  path"]
131
        self.assertReportLines(expected_lines, modified='modified',
132
            view_info=('my',['path']))
133
        # If a file in outside the view, it should not appear in the output
134
        expected_lines = [
135
            "Operating on whole tree but only reporting on 'my' view."]
136
        self.assertReportLines(expected_lines, modified='modified',
137
            path="foo", view_info=('my',['path']))
138
2225.1.3 by Aaron Bentley
change method names to assertFoo
139
    def assertChangesEqual(self,
140
                           file_id='fid',
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
141
                           paths=('path', 'path'),
2225.1.3 by Aaron Bentley
change method names to assertFoo
142
                           content_change=False,
143
                           versioned=(True, True),
144
                           parent_id=('pid', 'pid'),
145
                           name=('name', 'name'),
146
                           kind=('file', 'file'),
147
                           executable=(False, False),
148
                           versioned_change='unchanged',
149
                           renamed=False,
150
                           modified='unchanged',
151
                           exe_change=False):
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
152
        reporter = InstrumentedReporter()
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
153
        _mod_delta.report_changes([(file_id, paths, content_change, versioned,
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
154
            parent_id, name, kind, executable)], reporter)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
155
        output = reporter.calls[0]
156
        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.
157
        self.assertEqual(paths, output[1])
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
158
        self.assertEqual(versioned_change, output[2])
159
        self.assertEqual(renamed, output[3])
160
        self.assertEqual(modified, output[4])
161
        self.assertEqual(exe_change, output[5])
162
        self.assertEqual(kind, output[6])
163
164
    def test_report_changes(self):
165
        """Test change detection of report_changes"""
166
        #Ensure no changes are detected by default
2225.1.3 by Aaron Bentley
change method names to assertFoo
167
        self.assertChangesEqual(modified='unchanged', renamed=False,
168
                                versioned_change='unchanged',
169
                                exe_change=False)
170
        self.assertChangesEqual(modified='kind changed',
171
                                kind=('file', 'directory'))
172
        self.assertChangesEqual(modified='created', kind=(None, 'directory'))
173
        self.assertChangesEqual(modified='deleted', kind=('directory', None))
174
        self.assertChangesEqual(content_change=True, modified='modified')
175
        self.assertChangesEqual(renamed=True, name=('old', 'new'))
176
        self.assertChangesEqual(renamed=True,
177
                                parent_id=('old-parent', 'new-parent'))
178
        self.assertChangesEqual(versioned_change='added',
179
                                versioned=(False, True))
180
        self.assertChangesEqual(versioned_change='removed',
181
                                versioned=(True, False))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
182
        # execute bit is only detected as "changed" if the file is and was
183
        # a regular file.
2225.1.3 by Aaron Bentley
change method names to assertFoo
184
        self.assertChangesEqual(exe_change=True, executable=(True, False))
185
        self.assertChangesEqual(exe_change=False, executable=(True, False),
186
                                kind=('directory', 'directory'))
187
        self.assertChangesEqual(exe_change=False, modified='kind changed',
188
                                executable=(False, True),
189
                                kind=('directory', 'file'))
1551.11.3 by Aaron Bentley
Use tree transform to emit upcoming change list
190
        self.assertChangesEqual(parent_id=('pid', None))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
191
192
        # Now make sure they all work together
2225.1.3 by Aaron Bentley
change method names to assertFoo
193
        self.assertChangesEqual(versioned_change='removed',
194
                                modified='deleted', versioned=(True, False),
195
                                kind=('directory', None))
196
        self.assertChangesEqual(versioned_change='removed',
197
                                modified='created', versioned=(True, False),
198
                                kind=(None, 'file'))
199
        self.assertChangesEqual(versioned_change='removed',
200
                                modified='modified', renamed=True,
201
                                exe_change=True, versioned=(True, False),
202
                                content_change=True, name=('old', 'new'),
203
                                executable=(False, True))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
204
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
205
    def test_report_unversioned(self):
206
        """Unversioned entries are reported well."""
207
        self.assertChangesEqual(file_id=None, paths=(None, 'full/path'),
208
                           content_change=True,
209
                           versioned=(False, False),
210
                           parent_id=(None, None),
211
                           name=(None, 'path'),
212
                           kind=(None, 'file'),
213
                           executable=(None, False),
214
                           versioned_change='unversioned',
215
                           renamed=False,
216
                           modified='created',
217
                           exe_change=False)
218
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
219
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
220
class TestChangesFrom(tests.TestCaseWithTransport):
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
221
222
    def show_string(self, delta, *args,  **kwargs):
223
        to_file = StringIO()
224
        delta.show(to_file, *args, **kwargs)
225
        return to_file.getvalue()
226
227
    def test_kind_change(self):
228
        """Doing a status when a file has changed kind should work"""
229
        tree = self.make_branch_and_tree('.')
230
        self.build_tree(['filename'])
231
        tree.add('filename', 'file-id')
232
        tree.commit('added filename')
233
        os.unlink('filename')
234
        self.build_tree(['filename/'])
235
        delta = tree.changes_from(tree.basis_tree())
236
        self.assertEqual([('filename', 'file-id', 'file', 'directory')],
237
                         delta.kind_changed)
238
        self.assertEqual([], delta.added)
239
        self.assertEqual([], delta.removed)
240
        self.assertEqual([], delta.renamed)
241
        self.assertEqual([], delta.modified)
242
        self.assertEqual([], delta.unchanged)
243
        self.assertTrue(delta.has_changed())
244
        self.assertTrue(delta.touches_file_id('file-id'))
245
        self.assertEqual('kind changed:\n  filename (file => directory)\n',
246
                         self.show_string(delta))
247
        other_delta = _mod_delta.TreeDelta()
248
        self.assertNotEqual(other_delta, delta)
249
        other_delta.kind_changed = [('filename', 'file-id', 'file',
250
                                     'symlink')]
251
        self.assertNotEqual(other_delta, delta)
252
        other_delta.kind_changed = [('filename', 'file-id', 'file',
253
                                     'directory')]
254
        self.assertEqual(other_delta, delta)
255
        self.assertEqualDiff("TreeDelta(added=[], removed=[], renamed=[],"
256
            " kind_changed=[(u'filename', 'file-id', 'file', 'directory')],"
2255.7.90 by Robert Collins
Add unversioned path reporting to TreeDelta.
257
            " modified=[], unchanged=[], unversioned=[])", repr(delta))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
258
        self.assertEqual('K  filename (file => directory) file-id\n',
259
                         self.show_string(delta, show_ids=True,
260
                         short_status=True))
261
262
        tree.rename_one('filename', 'dirname')
263
        delta = tree.changes_from(tree.basis_tree())
264
        self.assertEqual([], delta.kind_changed)
265
        # This loses the fact that kind changed, remembering it as a
266
        # modification
267
        self.assertEqual([('filename', 'dirname', 'file-id', 'directory',
268
                           True, False)], delta.renamed)
269
        self.assertTrue(delta.has_changed())
270
        self.assertTrue(delta.touches_file_id('file-id'))
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
271
272
273
class TestDeltaShow(tests.TestCaseWithTransport):
274
275
    def _get_delta(self):
276
        # We build the delta from a real tree to avoid depending on internal
277
        # implementation details.
278
        wt = self.make_branch_and_tree('branch')
279
        self.build_tree_contents([('branch/f1', '1\n'),
280
                                  ('branch/f2', '2\n'),
281
                                  ('branch/f3', '3\n'),
282
                                  ('branch/f4', '4\n'),
283
                                  ('branch/dir/',),
284
                                 ])
285
        wt.add(['f1', 'f2', 'f3', 'f4', 'dir'],
286
               ['f1-id', 'f2-id', 'f3-id', 'f4-id', 'dir-id'])
287
        wt.commit('commit one', rev_id='1')
288
289
        long_status = """added:
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
290
  dir/
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
291
  f1
292
  f2
293
  f3
294
  f4
295
"""
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
296
        short_status = """A  dir/
297
A  f1
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
298
A  f2
299
A  f3
300
A  f4
301
"""
302
303
        repo = wt.branch.repository
304
        d = wt.changes_from(repo.revision_tree(_mod_revision.NULL_REVISION))
305
        return d, long_status, short_status
306
307
    def test_delta_show_short_status_no_filter(self):
308
        d, long_status, short_status = self._get_delta()
309
        out = StringIO()
310
        d.show(out, short_status=True)
311
        self.assertEquals(short_status, out.getvalue())
312
313
    def test_delta_show_long_status_no_filter(self):
314
        d, long_status, short_status = self._get_delta()
315
        out = StringIO()
316
        d.show(out, short_status=False)
317
        self.assertEquals(long_status, out.getvalue())
318
319
    def test_delta_show_no_filter(self):
320
        d, long_status, short_status = self._get_delta()
321
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
322
        def not_a_filter(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
323
            return True
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
324
        d.show(out, short_status=True, filter=not_a_filter)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
325
        self.assertEquals(short_status, out.getvalue())
326
327
    def test_delta_show_short_status_single_file_filter(self):
328
        d, long_status, short_status = self._get_delta()
329
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
330
        def only_f2(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
331
            return path == 'f2'
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
332
        d.show(out, short_status=True, filter=only_f2)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
333
        self.assertEquals("A  f2\n", out.getvalue())
334
335
    def test_delta_show_long_status_single_file_filter(self):
336
        d, long_status, short_status = self._get_delta()
337
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
338
        def only_f2(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
339
            return path == 'f2'
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
340
        d.show(out, short_status=False, filter=only_f2)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
341
        self.assertEquals("added:\n  f2\n", out.getvalue())
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
342
343
    def test_delta_show_short_status_single_file_id_filter(self):
344
        d, long_status, short_status = self._get_delta()
345
        out = StringIO()
346
        def only_f2_id(path, file_id):
347
            return file_id == 'f2-id'
348
        d.show(out, short_status=True, filter=only_f2_id)
349
        self.assertEquals("A  f2\n", out.getvalue())
350