~bzr-pqm/bzr/bzr.dev

4597.9.5 by Vincent Ladeuil
Merge bzr.dev into cleanup
1
# Copyright (C) 2007-2010 Canonical Ltd
2225.1.1 by Aaron Bentley
Added revert change display, with tests
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:
5422.3.8 by Martin Pool
Try for a better failure message in test_delta
68
            self.assertEqualDiff('\n'.join(expected_lines), '\n'.join(result))
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
69
        else:
70
            self.assertEqual([], result)
2225.1.1 by Aaron Bentley
Added revert change display, with tests
71
72
    def test_rename(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
73
        self.assertReport('R   old => path', renamed=True, old_path='old')
74
        self.assertReport('    path')
1551.10.12 by Aaron Bentley
Handle simultaneous creation+rename
75
        self.assertReport('RN  old => path', renamed=True, old_path='old',
76
                          modified='created', kind=(None, 'file'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
77
78
    def test_kind(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
79
        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.
80
                          kind=('file', 'directory'), old_path='path')
2225.1.3 by Aaron Bentley
change method names to assertFoo
81
        self.assertReport(' K  path/ => path', modified='kind changed',
82
                          kind=('directory', 'file'), old_path='old')
83
        self.assertReport('RK  old => path/', renamed=True,
2225.1.5 by Aaron Bentley
Clean up whitespace changes
84
                          modified='kind changed',
2225.1.3 by Aaron Bentley
change method names to assertFoo
85
                          kind=('file', 'directory'), old_path='old')
2225.1.1 by Aaron Bentley
Added revert change display, with tests
86
    def test_new(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
87
        self.assertReport(' N  path/', modified='created',
88
                          kind=(None, 'directory'))
89
        self.assertReport('+   path/', versioned_change='added',
90
                          modified='unchanged', kind=(None, 'directory'))
1551.10.11 by Aaron Bentley
Handle case where file-id only is added
91
        self.assertReport('+   path', versioned_change='added',
92
                          modified='unchanged', kind=(None, None))
2225.1.3 by Aaron Bentley
change method names to assertFoo
93
        self.assertReport('+N  path/', versioned_change='added',
94
                          modified='created', kind=(None, 'directory'))
95
        self.assertReport('+M  path/', versioned_change='added',
96
                          modified='modified', kind=(None, 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
97
98
    def test_removal(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
99
        self.assertReport(' D  path/', modified='deleted',
100
                          kind=('directory', None), old_path='old')
101
        self.assertReport('-   path/', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
102
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
103
                          kind=(None, 'directory'))
104
        self.assertReport('-D  path', versioned_change='removed',
2255.2.232 by Robert Collins
Make WorkingTree4 report support for references based on the repositories capabilities.
105
                          old_path='path',
2225.1.3 by Aaron Bentley
change method names to assertFoo
106
                          modified='deleted', kind=('file', 'directory'))
2225.1.1 by Aaron Bentley
Added revert change display, with tests
107
108
    def test_modification(self):
2225.1.3 by Aaron Bentley
change method names to assertFoo
109
        self.assertReport(' M  path', modified='modified')
110
        self.assertReport(' M* path', modified='modified', exe_change=True)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
111
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
112
    def test_unversioned(self):
113
        # by default any unversioned file is output
114
        self.assertReport('?   subdir/foo~', file_id=None, path='subdir/foo~',
115
            old_path=None, versioned_change='unversioned',
116
            renamed=False, modified='created', exe_change=False,
117
            kind=(None, 'file'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
118
        # 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.
119
        # close to the tree, but this is a reasonable starting point.
120
        self.assertReport(None, file_id=None, path='subdir/foo~',
121
            old_path=None, versioned_change='unversioned',
122
            renamed=False, modified='created', exe_change=False,
123
            kind=(None, 'file'), unversioned_filter=lambda x:True)
124
5504.5.1 by Rory Yorke
Show missing files in bzr status (bug 134168).
125
    def test_missing(self):
126
        self.assertReport('+!  missing.c', file_id=None, path='missing.c',
127
             old_path=None, versioned_change='added',
128
             renamed=False, modified='missing', exe_change=False,
129
             kind=(None, None))
130
3586.1.30 by Ian Clatworthy
add view support to change reporting
131
    def test_view_filtering(self):
132
        # If a file in within the view, it should appear in the output
133
        expected_lines = [
134
            "Operating on whole tree but only reporting on 'my' view.",
135
            " M  path"]
136
        self.assertReportLines(expected_lines, modified='modified',
137
            view_info=('my',['path']))
138
        # If a file in outside the view, it should not appear in the output
139
        expected_lines = [
140
            "Operating on whole tree but only reporting on 'my' view."]
141
        self.assertReportLines(expected_lines, modified='modified',
142
            path="foo", view_info=('my',['path']))
143
2225.1.3 by Aaron Bentley
change method names to assertFoo
144
    def assertChangesEqual(self,
145
                           file_id='fid',
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
146
                           paths=('path', 'path'),
2225.1.3 by Aaron Bentley
change method names to assertFoo
147
                           content_change=False,
148
                           versioned=(True, True),
149
                           parent_id=('pid', 'pid'),
150
                           name=('name', 'name'),
151
                           kind=('file', 'file'),
152
                           executable=(False, False),
153
                           versioned_change='unchanged',
154
                           renamed=False,
155
                           modified='unchanged',
156
                           exe_change=False):
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
157
        reporter = InstrumentedReporter()
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
158
        _mod_delta.report_changes([(file_id, paths, content_change, versioned,
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
159
            parent_id, name, kind, executable)], reporter)
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
160
        output = reporter.calls[0]
161
        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.
162
        self.assertEqual(paths, output[1])
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
163
        self.assertEqual(versioned_change, output[2])
164
        self.assertEqual(renamed, output[3])
165
        self.assertEqual(modified, output[4])
166
        self.assertEqual(exe_change, output[5])
167
        self.assertEqual(kind, output[6])
168
169
    def test_report_changes(self):
170
        """Test change detection of report_changes"""
171
        #Ensure no changes are detected by default
2225.1.3 by Aaron Bentley
change method names to assertFoo
172
        self.assertChangesEqual(modified='unchanged', renamed=False,
173
                                versioned_change='unchanged',
174
                                exe_change=False)
175
        self.assertChangesEqual(modified='kind changed',
176
                                kind=('file', 'directory'))
177
        self.assertChangesEqual(modified='created', kind=(None, 'directory'))
178
        self.assertChangesEqual(modified='deleted', kind=('directory', None))
179
        self.assertChangesEqual(content_change=True, modified='modified')
180
        self.assertChangesEqual(renamed=True, name=('old', 'new'))
181
        self.assertChangesEqual(renamed=True,
182
                                parent_id=('old-parent', 'new-parent'))
183
        self.assertChangesEqual(versioned_change='added',
184
                                versioned=(False, True))
185
        self.assertChangesEqual(versioned_change='removed',
186
                                versioned=(True, False))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
187
        # execute bit is only detected as "changed" if the file is and was
188
        # a regular file.
2225.1.3 by Aaron Bentley
change method names to assertFoo
189
        self.assertChangesEqual(exe_change=True, executable=(True, False))
190
        self.assertChangesEqual(exe_change=False, executable=(True, False),
191
                                kind=('directory', 'directory'))
192
        self.assertChangesEqual(exe_change=False, modified='kind changed',
193
                                executable=(False, True),
194
                                kind=('directory', 'file'))
1551.11.3 by Aaron Bentley
Use tree transform to emit upcoming change list
195
        self.assertChangesEqual(parent_id=('pid', None))
2225.1.2 by Aaron Bentley
Ensure that changes are detected correctly
196
197
        # Now make sure they all work together
2225.1.3 by Aaron Bentley
change method names to assertFoo
198
        self.assertChangesEqual(versioned_change='removed',
199
                                modified='deleted', versioned=(True, False),
200
                                kind=('directory', None))
201
        self.assertChangesEqual(versioned_change='removed',
202
                                modified='created', versioned=(True, False),
203
                                kind=(None, 'file'))
204
        self.assertChangesEqual(versioned_change='removed',
205
                                modified='modified', renamed=True,
206
                                exe_change=True, versioned=(True, False),
207
                                content_change=True, name=('old', 'new'),
208
                                executable=(False, True))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
209
2255.7.97 by Robert Collins
Teach delta.report_changes about unversioned files, removing all inventory access during status --short.
210
    def test_report_unversioned(self):
211
        """Unversioned entries are reported well."""
212
        self.assertChangesEqual(file_id=None, paths=(None, 'full/path'),
213
                           content_change=True,
214
                           versioned=(False, False),
215
                           parent_id=(None, None),
216
                           name=(None, 'path'),
217
                           kind=(None, 'file'),
218
                           executable=(None, False),
219
                           versioned_change='unversioned',
220
                           renamed=False,
221
                           modified='created',
222
                           exe_change=False)
223
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
224
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
225
class TestChangesFrom(tests.TestCaseWithTransport):
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
226
227
    def show_string(self, delta, *args,  **kwargs):
228
        to_file = StringIO()
5076.4.8 by Arnaud Jeansen
Correct last delta.show() call in test_delta
229
        _mod_delta.report_delta(to_file, delta, *args, **kwargs)
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
230
        return to_file.getvalue()
231
232
    def test_kind_change(self):
233
        """Doing a status when a file has changed kind should work"""
234
        tree = self.make_branch_and_tree('.')
235
        self.build_tree(['filename'])
236
        tree.add('filename', 'file-id')
237
        tree.commit('added filename')
238
        os.unlink('filename')
239
        self.build_tree(['filename/'])
240
        delta = tree.changes_from(tree.basis_tree())
241
        self.assertEqual([('filename', 'file-id', 'file', 'directory')],
242
                         delta.kind_changed)
243
        self.assertEqual([], delta.added)
244
        self.assertEqual([], delta.removed)
245
        self.assertEqual([], delta.renamed)
246
        self.assertEqual([], delta.modified)
247
        self.assertEqual([], delta.unchanged)
248
        self.assertTrue(delta.has_changed())
249
        self.assertTrue(delta.touches_file_id('file-id'))
250
        self.assertEqual('kind changed:\n  filename (file => directory)\n',
251
                         self.show_string(delta))
252
        other_delta = _mod_delta.TreeDelta()
253
        self.assertNotEqual(other_delta, delta)
254
        other_delta.kind_changed = [('filename', 'file-id', 'file',
255
                                     'symlink')]
256
        self.assertNotEqual(other_delta, delta)
257
        other_delta.kind_changed = [('filename', 'file-id', 'file',
258
                                     'directory')]
259
        self.assertEqual(other_delta, delta)
260
        self.assertEqualDiff("TreeDelta(added=[], removed=[], renamed=[],"
261
            " kind_changed=[(u'filename', 'file-id', 'file', 'directory')],"
2255.7.90 by Robert Collins
Add unversioned path reporting to TreeDelta.
262
            " modified=[], unchanged=[], unversioned=[])", repr(delta))
1551.10.6 by Aaron Bentley
Support kind changes in tree deltas
263
        self.assertEqual('K  filename (file => directory) file-id\n',
264
                         self.show_string(delta, show_ids=True,
265
                         short_status=True))
266
267
        tree.rename_one('filename', 'dirname')
268
        delta = tree.changes_from(tree.basis_tree())
269
        self.assertEqual([], delta.kind_changed)
270
        # This loses the fact that kind changed, remembering it as a
271
        # modification
272
        self.assertEqual([('filename', 'dirname', 'file-id', 'directory',
273
                           True, False)], delta.renamed)
274
        self.assertTrue(delta.has_changed())
275
        self.assertTrue(delta.touches_file_id('file-id'))
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
276
277
278
class TestDeltaShow(tests.TestCaseWithTransport):
279
280
    def _get_delta(self):
281
        # We build the delta from a real tree to avoid depending on internal
282
        # implementation details.
283
        wt = self.make_branch_and_tree('branch')
284
        self.build_tree_contents([('branch/f1', '1\n'),
285
                                  ('branch/f2', '2\n'),
286
                                  ('branch/f3', '3\n'),
287
                                  ('branch/f4', '4\n'),
5504.5.1 by Rory Yorke
Show missing files in bzr status (bug 134168).
288
                                  ('branch/f5', '5\n'),
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
289
                                  ('branch/dir/',),
290
                                 ])
291
        wt.add(['f1', 'f2', 'f3', 'f4', 'dir'],
292
               ['f1-id', 'f2-id', 'f3-id', 'f4-id', 'dir-id'])
293
        wt.commit('commit one', rev_id='1')
294
5504.5.1 by Rory Yorke
Show missing files in bzr status (bug 134168).
295
        # TODO add rename,removed,etc. here?
296
        wt.add('f5')
297
        os.unlink('branch/f5')
298
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
299
        long_status = """added:
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
300
  dir/
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
301
  f1
302
  f2
303
  f3
304
  f4
5504.5.1 by Rory Yorke
Show missing files in bzr status (bug 134168).
305
missing:
306
  f5
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
307
"""
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
308
        short_status = """A  dir/
309
A  f1
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
310
A  f2
311
A  f3
312
A  f4
5504.5.1 by Rory Yorke
Show missing files in bzr status (bug 134168).
313
!  f5
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
314
"""
315
316
        repo = wt.branch.repository
317
        d = wt.changes_from(repo.revision_tree(_mod_revision.NULL_REVISION))
318
        return d, long_status, short_status
319
320
    def test_delta_show_short_status_no_filter(self):
321
        d, long_status, short_status = self._get_delta()
322
        out = StringIO()
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
323
        _mod_delta.report_delta(out, d, short_status=True)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
324
        self.assertEquals(short_status, out.getvalue())
325
326
    def test_delta_show_long_status_no_filter(self):
327
        d, long_status, short_status = self._get_delta()
328
        out = StringIO()
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
329
        _mod_delta.report_delta(out, d, short_status=False)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
330
        self.assertEquals(long_status, out.getvalue())
331
332
    def test_delta_show_no_filter(self):
333
        d, long_status, short_status = self._get_delta()
334
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
335
        def not_a_filter(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
336
            return True
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
337
        _mod_delta.report_delta(out, d, short_status=True, filter=not_a_filter)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
338
        self.assertEquals(short_status, out.getvalue())
339
340
    def test_delta_show_short_status_single_file_filter(self):
341
        d, long_status, short_status = self._get_delta()
342
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
343
        def only_f2(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
344
            return path == 'f2'
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
345
        _mod_delta.report_delta(out, d, short_status=True, filter=only_f2)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
346
        self.assertEquals("A  f2\n", out.getvalue())
347
348
    def test_delta_show_long_status_single_file_filter(self):
349
        d, long_status, short_status = self._get_delta()
350
        out = StringIO()
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
351
        def only_f2(path, file_id):
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
352
            return path == 'f2'
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
353
        _mod_delta.report_delta(out, d, short_status=False, filter=only_f2)
3874.3.5 by Vincent Ladeuil
Add a 'path_filter' parameter to delta.show().
354
        self.assertEquals("added:\n  f2\n", out.getvalue())
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
355
356
    def test_delta_show_short_status_single_file_id_filter(self):
357
        d, long_status, short_status = self._get_delta()
358
        out = StringIO()
359
        def only_f2_id(path, file_id):
360
            return file_id == 'f2-id'
5076.4.5 by Arnaud Jeansen
Ported test_delta to the report_delta method
361
        _mod_delta.report_delta(out, d, short_status=True, filter=only_f2_id)
3874.3.6 by Vincent Ladeuil
Make the filter work for paths and file ids.
362
        self.assertEquals("A  f2\n", out.getvalue())
363