/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 breezy/tests/blackbox/test_status.py

  • Committer: Martin
  • Date: 2017-06-10 01:57:00 UTC
  • mto: This revision was merged to the branch mainline in revision 6679.
  • Revision ID: gzlist@googlemail.com-20170610015700-o3xeuyaqry2obiay
Go back to native str for urls and many other py3 changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2012, 2016 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
 
 
17
"""Tests of status command.
 
18
 
 
19
Most of these depend on the particular formatting used.
 
20
As such they really are blackbox tests even though some of the
 
21
tests are not using self.capture. If we add tests for the programmatic
 
22
interface later, they will be non blackbox tests.
 
23
"""
 
24
 
 
25
import codecs
 
26
from os import mkdir, chdir, rmdir, unlink
 
27
import sys
 
28
 
 
29
from ... import (
 
30
    bzrdir,
 
31
    conflicts,
 
32
    errors,
 
33
    osutils,
 
34
    status,
 
35
    )
 
36
import breezy.branch
 
37
from ...osutils import pathjoin
 
38
from ...revisionspec import RevisionSpec
 
39
from ...sixish import (
 
40
    BytesIO,
 
41
    )
 
42
from ...status import show_tree_status
 
43
from .. import TestCaseWithTransport, TestSkipped
 
44
from ...workingtree import WorkingTree
 
45
 
 
46
 
 
47
class BranchStatus(TestCaseWithTransport):
 
48
 
 
49
    def setUp(self):
 
50
        super(BranchStatus, self).setUp()
 
51
        # As TestCase.setUp clears all hooks, we install this default
 
52
        # post_status hook handler for the test.
 
53
        status.hooks.install_named_hook('post_status',
 
54
            status._show_shelve_summary,
 
55
            'brz status')
 
56
 
 
57
    def assertStatus(self, expected_lines, working_tree, specific_files=None,
 
58
        revision=None, short=False, pending=True, verbose=False):
 
59
        """Run status in working_tree and look for output.
 
60
 
 
61
        :param expected_lines: The lines to look for.
 
62
        :param working_tree: The tree to run status in.
 
63
        """
 
64
        output_string = self.status_string(working_tree, specific_files, revision, short,
 
65
                pending, verbose)
 
66
        self.assertEqual(expected_lines, output_string.splitlines(True))
 
67
 
 
68
    def status_string(self, wt, specific_files=None, revision=None,
 
69
        short=False, pending=True, verbose=False):
 
70
        uio = self.make_utf8_encoded_stringio()
 
71
        show_tree_status(wt, specific_files=specific_files, to_file=uio,
 
72
                revision=revision, short=short, show_pending=pending,
 
73
                verbose=verbose)
 
74
        return uio.getvalue().decode('utf-8')
 
75
 
 
76
    def test_branch_status(self):
 
77
        """Test basic branch status"""
 
78
        wt = self.make_branch_and_tree('.')
 
79
 
 
80
        # status with no commits or files - it must
 
81
        # work and show no output. We do this with no
 
82
        # commits to be sure that it's not going to fail
 
83
        # as a corner case.
 
84
        self.assertStatus([], wt)
 
85
 
 
86
        self.build_tree(['hello.c', 'bye.c'])
 
87
        self.assertStatus([
 
88
                'unknown:\n',
 
89
                '  bye.c\n',
 
90
                '  hello.c\n',
 
91
            ],
 
92
            wt)
 
93
        self.assertStatus([
 
94
                '?   bye.c\n',
 
95
                '?   hello.c\n',
 
96
            ],
 
97
            wt, short=True)
 
98
 
 
99
        # add a commit to allow showing pending merges.
 
100
        wt.commit('create a parent to allow testing merge output')
 
101
 
 
102
        wt.add_parent_tree_id('pending@pending-0-0')
 
103
        self.assertStatus([
 
104
                'unknown:\n',
 
105
                '  bye.c\n',
 
106
                '  hello.c\n',
 
107
                'pending merge tips: (use -v to see all merge revisions)\n',
 
108
                '  (ghost) pending@pending-0-0\n',
 
109
            ],
 
110
            wt)
 
111
        self.assertStatus([
 
112
                'unknown:\n',
 
113
                '  bye.c\n',
 
114
                '  hello.c\n',
 
115
                'pending merges:\n',
 
116
                '  (ghost) pending@pending-0-0\n',
 
117
            ],
 
118
            wt, verbose=True)
 
119
        self.assertStatus([
 
120
                '?   bye.c\n',
 
121
                '?   hello.c\n',
 
122
                'P   (ghost) pending@pending-0-0\n',
 
123
            ],
 
124
            wt, short=True)
 
125
        self.assertStatus([
 
126
                'unknown:\n',
 
127
                '  bye.c\n',
 
128
                '  hello.c\n',
 
129
            ],
 
130
            wt, pending=False)
 
131
        self.assertStatus([
 
132
                '?   bye.c\n',
 
133
                '?   hello.c\n',
 
134
            ],
 
135
            wt, short=True, pending=False)
 
136
 
 
137
    def test_branch_status_revisions(self):
 
138
        """Tests branch status with revisions"""
 
139
        wt = self.make_branch_and_tree('.')
 
140
 
 
141
        self.build_tree(['hello.c', 'bye.c'])
 
142
        wt.add('hello.c')
 
143
        wt.add('bye.c')
 
144
        wt.commit('Test message')
 
145
 
 
146
        revs = [RevisionSpec.from_string('0')]
 
147
        self.assertStatus([
 
148
                'added:\n',
 
149
                '  bye.c\n',
 
150
                '  hello.c\n'
 
151
            ],
 
152
            wt,
 
153
            revision=revs)
 
154
 
 
155
        self.build_tree(['more.c'])
 
156
        wt.add('more.c')
 
157
        wt.commit('Another test message')
 
158
 
 
159
        revs.append(RevisionSpec.from_string('1'))
 
160
        self.assertStatus([
 
161
                'added:\n',
 
162
                '  bye.c\n',
 
163
                '  hello.c\n',
 
164
            ],
 
165
            wt,
 
166
            revision=revs)
 
167
 
 
168
    def test_pending(self):
 
169
        """Pending merges display works, including Unicode"""
 
170
        mkdir("./branch")
 
171
        wt = self.make_branch_and_tree('branch')
 
172
        b = wt.branch
 
173
        wt.commit("Empty commit 1")
 
174
        b_2_dir = b.bzrdir.sprout('./copy')
 
175
        b_2 = b_2_dir.open_branch()
 
176
        wt2 = b_2_dir.open_workingtree()
 
177
        wt.commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
 
178
        wt2.merge_from_branch(wt.branch)
 
179
        message = self.status_string(wt2, verbose=True)
 
180
        self.assertStartsWith(message, "pending merges:\n")
 
181
        self.assertEndsWith(message, "Empty commit 2\n")
 
182
        wt2.commit("merged")
 
183
        # must be long to make sure we see elipsis at the end
 
184
        wt.commit("Empty commit 3 " +
 
185
                   "blah blah blah blah " * 100)
 
186
        wt2.merge_from_branch(wt.branch)
 
187
        message = self.status_string(wt2, verbose=True)
 
188
        self.assertStartsWith(message, "pending merges:\n")
 
189
        self.assertTrue("Empty commit 3" in message)
 
190
        self.assertEndsWith(message, "...\n")
 
191
 
 
192
    def test_tree_status_ignores(self):
 
193
        """Tests branch status with ignores"""
 
194
        wt = self.make_branch_and_tree('.')
 
195
        self.run_bzr('ignore *~')
 
196
        wt.commit('commit .bzrignore')
 
197
        self.build_tree(['foo.c', 'foo.c~'])
 
198
        self.assertStatus([
 
199
                'unknown:\n',
 
200
                '  foo.c\n',
 
201
                ],
 
202
                wt)
 
203
        self.assertStatus([
 
204
                '?   foo.c\n',
 
205
                ],
 
206
                wt, short=True)
 
207
 
 
208
    def test_tree_status_specific_files(self):
 
209
        """Tests branch status with given specific files"""
 
210
        wt = self.make_branch_and_tree('.')
 
211
        b = wt.branch
 
212
 
 
213
        self.build_tree(['directory/','directory/hello.c',
 
214
                         'bye.c','test.c','dir2/',
 
215
                         'missing.c'])
 
216
        wt.add('directory')
 
217
        wt.add('test.c')
 
218
        wt.commit('testing')
 
219
        wt.add('missing.c')
 
220
        unlink('missing.c')
 
221
 
 
222
        self.assertStatus([
 
223
                'missing:\n',
 
224
                '  missing.c\n',
 
225
                'unknown:\n',
 
226
                '  bye.c\n',
 
227
                '  dir2/\n',
 
228
                '  directory/hello.c\n'
 
229
                ],
 
230
                wt)
 
231
 
 
232
        self.assertStatus([
 
233
                '?   bye.c\n',
 
234
                '?   dir2/\n',
 
235
                '+!  missing.c\n',
 
236
                '?   directory/hello.c\n'
 
237
                ],
 
238
                wt, short=True)
 
239
 
 
240
        tof = BytesIO()
 
241
        self.assertRaises(errors.PathsDoNotExist,
 
242
                          show_tree_status,
 
243
                          wt, specific_files=['bye.c','test.c','absent.c'],
 
244
                          to_file=tof)
 
245
 
 
246
        tof = BytesIO()
 
247
        show_tree_status(wt, specific_files=['directory'], to_file=tof)
 
248
        tof.seek(0)
 
249
        self.assertEqual(tof.readlines(),
 
250
                          ['unknown:\n',
 
251
                           '  directory/hello.c\n'
 
252
                           ])
 
253
        tof = BytesIO()
 
254
        show_tree_status(wt, specific_files=['directory'], to_file=tof,
 
255
                         short=True)
 
256
        tof.seek(0)
 
257
        self.assertEqual(tof.readlines(), ['?   directory/hello.c\n'])
 
258
 
 
259
        tof = BytesIO()
 
260
        show_tree_status(wt, specific_files=['dir2'], to_file=tof)
 
261
        tof.seek(0)
 
262
        self.assertEqual(tof.readlines(),
 
263
                          ['unknown:\n',
 
264
                           '  dir2/\n'
 
265
                           ])
 
266
        tof = BytesIO()
 
267
        show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
 
268
        tof.seek(0)
 
269
        self.assertEqual(tof.readlines(), ['?   dir2/\n'])
 
270
 
 
271
        tof = BytesIO()
 
272
        revs = [RevisionSpec.from_string('0'), RevisionSpec.from_string('1')]
 
273
        show_tree_status(wt, specific_files=['test.c'], to_file=tof,
 
274
                         short=True, revision=revs)
 
275
        tof.seek(0)
 
276
        self.assertEqual(tof.readlines(), ['+N  test.c\n'])
 
277
 
 
278
        tof = BytesIO()
 
279
        show_tree_status(wt, specific_files=['missing.c'], to_file=tof)
 
280
        tof.seek(0)
 
281
        self.assertEqual(tof.readlines(),
 
282
                          ['missing:\n',
 
283
                           '  missing.c\n'])
 
284
 
 
285
        tof = BytesIO()
 
286
        show_tree_status(wt, specific_files=['missing.c'], to_file=tof,
 
287
                         short=True)
 
288
        tof.seek(0)
 
289
        self.assertEqual(tof.readlines(),
 
290
                          ['+!  missing.c\n'])
 
291
 
 
292
    def test_specific_files_conflicts(self):
 
293
        tree = self.make_branch_and_tree('.')
 
294
        self.build_tree(['dir2/'])
 
295
        tree.add('dir2')
 
296
        tree.commit('added dir2')
 
297
        tree.set_conflicts(conflicts.ConflictList(
 
298
            [conflicts.ContentsConflict('foo')]))
 
299
        tof = BytesIO()
 
300
        show_tree_status(tree, specific_files=['dir2'], to_file=tof)
 
301
        self.assertEqualDiff('', tof.getvalue())
 
302
        tree.set_conflicts(conflicts.ConflictList(
 
303
            [conflicts.ContentsConflict('dir2')]))
 
304
        tof = BytesIO()
 
305
        show_tree_status(tree, specific_files=['dir2'], to_file=tof)
 
306
        self.assertEqualDiff('conflicts:\n  Contents conflict in dir2\n',
 
307
                             tof.getvalue())
 
308
 
 
309
        tree.set_conflicts(conflicts.ConflictList(
 
310
            [conflicts.ContentsConflict('dir2/file1')]))
 
311
        tof = BytesIO()
 
312
        show_tree_status(tree, specific_files=['dir2'], to_file=tof)
 
313
        self.assertEqualDiff('conflicts:\n  Contents conflict in dir2/file1\n',
 
314
                             tof.getvalue())
 
315
 
 
316
    def _prepare_nonexistent(self):
 
317
        wt = self.make_branch_and_tree('.')
 
318
        self.assertStatus([], wt)
 
319
        self.build_tree(['FILE_A', 'FILE_B', 'FILE_C', 'FILE_D', 'FILE_E', ])
 
320
        wt.add('FILE_A')
 
321
        wt.add('FILE_B')
 
322
        wt.add('FILE_C')
 
323
        wt.add('FILE_D')
 
324
        wt.add('FILE_E')
 
325
        wt.commit('Create five empty files.')
 
326
        with open('FILE_B', 'w') as f: f.write('Modification to file FILE_B.')
 
327
        with open('FILE_C', 'w') as f: f.write('Modification to file FILE_C.')
 
328
        unlink('FILE_E')  # FILE_E will be versioned but missing
 
329
        with open('FILE_Q', 'w') as f: f.write('FILE_Q is added but not committed.')
 
330
        wt.add('FILE_Q')  # FILE_Q will be added but not committed
 
331
        open('UNVERSIONED_BUT_EXISTING', 'w')
 
332
        return wt
 
333
 
 
334
    def test_status_nonexistent_file(self):
 
335
        # files that don't exist in either the basis tree or working tree
 
336
        # should give an error
 
337
        wt = self._prepare_nonexistent()
 
338
        self.assertStatus([
 
339
            'removed:\n',
 
340
            '  FILE_E\n',
 
341
            'added:\n',
 
342
            '  FILE_Q\n',
 
343
            'modified:\n',
 
344
            '  FILE_B\n',
 
345
            '  FILE_C\n',
 
346
            'unknown:\n',
 
347
            '  UNVERSIONED_BUT_EXISTING\n',
 
348
            ],
 
349
            wt)
 
350
        self.assertStatus([
 
351
            ' M  FILE_B\n',
 
352
            ' M  FILE_C\n',
 
353
            ' D  FILE_E\n',
 
354
            '+N  FILE_Q\n',
 
355
            '?   UNVERSIONED_BUT_EXISTING\n',
 
356
            ],
 
357
            wt, short=True)
 
358
 
 
359
        # Okay, everything's looking good with the existent files.
 
360
        # Let's see what happens when we throw in non-existent files.
 
361
 
 
362
        # brz st [--short] NONEXISTENT '
 
363
        expected = [
 
364
          'nonexistent:\n',
 
365
          '  NONEXISTENT\n',
 
366
          ]
 
367
        out, err = self.run_bzr('status NONEXISTENT', retcode=3)
 
368
        self.assertEqual(expected, out.splitlines(True))
 
369
        self.assertContainsRe(err,
 
370
                              r'.*ERROR: Path\(s\) do not exist: '
 
371
                              'NONEXISTENT.*')
 
372
        expected = [
 
373
          'X:   NONEXISTENT\n',
 
374
          ]
 
375
        out, err = self.run_bzr('status --short NONEXISTENT', retcode=3)
 
376
        self.assertContainsRe(err,
 
377
                              r'.*ERROR: Path\(s\) do not exist: '
 
378
                              'NONEXISTENT.*')
 
379
 
 
380
    def test_status_nonexistent_file_with_others(self):
 
381
        # brz st [--short] NONEXISTENT ...others..
 
382
        wt = self._prepare_nonexistent()
 
383
        expected = [
 
384
          'removed:\n',
 
385
          '  FILE_E\n',
 
386
          'modified:\n',
 
387
          '  FILE_B\n',
 
388
          '  FILE_C\n',
 
389
          'nonexistent:\n',
 
390
          '  NONEXISTENT\n',
 
391
          ]
 
392
        out, err = self.run_bzr('status NONEXISTENT '
 
393
                                'FILE_A FILE_B FILE_C FILE_D FILE_E',
 
394
                                retcode=3)
 
395
        self.assertEqual(expected, out.splitlines(True))
 
396
        self.assertContainsRe(err,
 
397
                              r'.*ERROR: Path\(s\) do not exist: '
 
398
                              'NONEXISTENT.*')
 
399
        expected = [
 
400
          ' D  FILE_E\n',
 
401
          ' M  FILE_C\n',
 
402
          ' M  FILE_B\n',
 
403
          'X   NONEXISTENT\n',
 
404
          ]
 
405
        out, err = self.run_bzr('status --short NONEXISTENT '
 
406
                                'FILE_A FILE_B FILE_C FILE_D FILE_E',
 
407
                                retcode=3)
 
408
        self.assertEqual(expected, out.splitlines(True))
 
409
        self.assertContainsRe(err,
 
410
                              r'.*ERROR: Path\(s\) do not exist: '
 
411
                              'NONEXISTENT.*')
 
412
 
 
413
    def test_status_multiple_nonexistent_files(self):
 
414
        # brz st [--short] NONEXISTENT ... ANOTHER_NONEXISTENT ...
 
415
        wt = self._prepare_nonexistent()
 
416
        expected = [
 
417
          'removed:\n',
 
418
          '  FILE_E\n',
 
419
          'modified:\n',
 
420
          '  FILE_B\n',
 
421
          '  FILE_C\n',
 
422
          'nonexistent:\n',
 
423
          '  ANOTHER_NONEXISTENT\n',
 
424
          '  NONEXISTENT\n',
 
425
          ]
 
426
        out, err = self.run_bzr('status NONEXISTENT '
 
427
                                'FILE_A FILE_B ANOTHER_NONEXISTENT '
 
428
                                'FILE_C FILE_D FILE_E', retcode=3)
 
429
        self.assertEqual(expected, out.splitlines(True))
 
430
        self.assertContainsRe(err,
 
431
                              r'.*ERROR: Path\(s\) do not exist: '
 
432
                              'ANOTHER_NONEXISTENT NONEXISTENT.*')
 
433
        expected = [
 
434
          ' D  FILE_E\n',
 
435
          ' M  FILE_C\n',
 
436
          ' M  FILE_B\n',
 
437
          'X   ANOTHER_NONEXISTENT\n',
 
438
          'X   NONEXISTENT\n',
 
439
          ]
 
440
        out, err = self.run_bzr('status --short NONEXISTENT '
 
441
                                'FILE_A FILE_B ANOTHER_NONEXISTENT '
 
442
                                'FILE_C FILE_D FILE_E', retcode=3)
 
443
        self.assertEqual(expected, out.splitlines(True))
 
444
        self.assertContainsRe(err,
 
445
                              r'.*ERROR: Path\(s\) do not exist: '
 
446
                              'ANOTHER_NONEXISTENT NONEXISTENT.*')
 
447
 
 
448
    def test_status_nonexistent_file_with_unversioned(self):
 
449
        # brz st [--short] NONEXISTENT A B UNVERSIONED_BUT_EXISTING C D E Q
 
450
        wt = self._prepare_nonexistent()
 
451
        expected = [
 
452
          'removed:\n',
 
453
          '  FILE_E\n',
 
454
          'added:\n',
 
455
          '  FILE_Q\n',
 
456
          'modified:\n',
 
457
          '  FILE_B\n',
 
458
          '  FILE_C\n',
 
459
          'unknown:\n',
 
460
          '  UNVERSIONED_BUT_EXISTING\n',
 
461
          'nonexistent:\n',
 
462
          '  NONEXISTENT\n',
 
463
          ]
 
464
        out, err = self.run_bzr('status NONEXISTENT '
 
465
                                'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
 
466
                                'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
 
467
        self.assertEqual(expected, out.splitlines(True))
 
468
        self.assertContainsRe(err,
 
469
                              r'.*ERROR: Path\(s\) do not exist: '
 
470
                              'NONEXISTENT.*')
 
471
        expected = sorted([
 
472
          '+N  FILE_Q\n',
 
473
          '?   UNVERSIONED_BUT_EXISTING\n',
 
474
          ' D  FILE_E\n',
 
475
          ' M  FILE_C\n',
 
476
          ' M  FILE_B\n',
 
477
          'X   NONEXISTENT\n',
 
478
          ])
 
479
        out, err = self.run_bzr('status --short NONEXISTENT '
 
480
                                'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
 
481
                                'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
 
482
        actual = out.splitlines(True)
 
483
        actual.sort()
 
484
        self.assertEqual(expected, actual)
 
485
        self.assertContainsRe(err,
 
486
                              r'.*ERROR: Path\(s\) do not exist: '
 
487
                              'NONEXISTENT.*')
 
488
 
 
489
    def test_status_out_of_date(self):
 
490
        """Simulate status of out-of-date tree after remote push"""
 
491
        tree = self.make_branch_and_tree('.')
 
492
        self.build_tree_contents([('a', 'foo\n')])
 
493
        tree.lock_write()
 
494
        try:
 
495
            tree.add(['a'])
 
496
            tree.commit('add test file')
 
497
            # simulate what happens after a remote push
 
498
            tree.set_last_revision("0")
 
499
        finally:
 
500
            # before run another commands we should unlock tree
 
501
            tree.unlock()
 
502
        out, err = self.run_bzr('status')
 
503
        self.assertEqual("working tree is out of date, run 'brz update'\n",
 
504
                         err)
 
505
 
 
506
    def test_status_on_ignored(self):
 
507
        """Tests branch status on an unversioned file which is considered ignored.
 
508
 
 
509
        See https://bugs.launchpad.net/bzr/+bug/40103
 
510
        """
 
511
        tree = self.make_branch_and_tree('.')
 
512
 
 
513
        self.build_tree(['test1.c', 'test1.c~', 'test2.c~'])
 
514
        result = self.run_bzr('status')[0]
 
515
        self.assertContainsRe(result, "unknown:\n  test1.c\n")
 
516
        short_result = self.run_bzr('status --short')[0]
 
517
        self.assertContainsRe(short_result, "\?   test1.c\n")
 
518
 
 
519
        result = self.run_bzr('status test1.c')[0]
 
520
        self.assertContainsRe(result, "unknown:\n  test1.c\n")
 
521
        short_result = self.run_bzr('status --short test1.c')[0]
 
522
        self.assertContainsRe(short_result, "\?   test1.c\n")
 
523
 
 
524
        result = self.run_bzr('status test1.c~')[0]
 
525
        self.assertContainsRe(result, "ignored:\n  test1.c~\n")
 
526
        short_result = self.run_bzr('status --short test1.c~')[0]
 
527
        self.assertContainsRe(short_result, "I   test1.c~\n")
 
528
 
 
529
        result = self.run_bzr('status test1.c~ test2.c~')[0]
 
530
        self.assertContainsRe(result, "ignored:\n  test1.c~\n  test2.c~\n")
 
531
        short_result = self.run_bzr('status --short test1.c~ test2.c~')[0]
 
532
        self.assertContainsRe(short_result, "I   test1.c~\nI   test2.c~\n")
 
533
 
 
534
        result = self.run_bzr('status test1.c test1.c~ test2.c~')[0]
 
535
        self.assertContainsRe(result, "unknown:\n  test1.c\nignored:\n  test1.c~\n  test2.c~\n")
 
536
        short_result = self.run_bzr('status --short test1.c test1.c~ test2.c~')[0]
 
537
        self.assertContainsRe(short_result, "\?   test1.c\nI   test1.c~\nI   test2.c~\n")
 
538
 
 
539
    def test_status_write_lock(self):
 
540
        """Test that status works without fetching history and
 
541
        having a write lock.
 
542
 
 
543
        See https://bugs.launchpad.net/bzr/+bug/149270
 
544
        """
 
545
        mkdir('branch1')
 
546
        wt = self.make_branch_and_tree('branch1')
 
547
        b = wt.branch
 
548
        wt.commit('Empty commit 1')
 
549
        wt2 = b.bzrdir.sprout('branch2').open_workingtree()
 
550
        wt2.commit('Empty commit 2')
 
551
        out, err = self.run_bzr('status branch1 -rbranch:branch2')
 
552
        self.assertEqual('', out)
 
553
 
 
554
    def test_status_with_shelves(self):
 
555
        """Ensure that _show_shelve_summary handler works.
 
556
        """
 
557
        wt = self.make_branch_and_tree('.')
 
558
        self.build_tree(['hello.c'])
 
559
        wt.add('hello.c')
 
560
        self.run_bzr(['shelve', '--all', '-m', 'foo'])
 
561
        self.build_tree(['bye.c'])
 
562
        wt.add('bye.c')
 
563
        self.assertStatus([
 
564
                'added:\n',
 
565
                '  bye.c\n',
 
566
                '1 shelf exists. See "brz shelve --list" for details.\n',
 
567
            ],
 
568
            wt)
 
569
        self.run_bzr(['shelve', '--all', '-m', 'bar'])
 
570
        self.build_tree(['eggs.c', 'spam.c'])
 
571
        wt.add('eggs.c')
 
572
        wt.add('spam.c')
 
573
        self.assertStatus([
 
574
                'added:\n',
 
575
                '  eggs.c\n',
 
576
                '  spam.c\n',
 
577
                '2 shelves exist. See "brz shelve --list" for details.\n',
 
578
            ],
 
579
            wt)
 
580
        self.assertStatus([
 
581
                'added:\n',
 
582
                '  spam.c\n',
 
583
            ],
 
584
            wt,
 
585
            specific_files=['spam.c'])
 
586
 
 
587
 
 
588
class CheckoutStatus(BranchStatus):
 
589
 
 
590
    def setUp(self):
 
591
        super(CheckoutStatus, self).setUp()
 
592
        mkdir('codir')
 
593
        chdir('codir')
 
594
 
 
595
    def make_branch_and_tree(self, relpath):
 
596
        source = self.make_branch(pathjoin('..', relpath))
 
597
        checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
 
598
        checkout.set_branch_reference(source)
 
599
        return checkout.create_workingtree()
 
600
 
 
601
 
 
602
class TestStatus(TestCaseWithTransport):
 
603
 
 
604
    def test_status_plain(self):
 
605
        tree = self.make_branch_and_tree('.')
 
606
 
 
607
        self.build_tree(['hello.txt'])
 
608
        result = self.run_bzr("status")[0]
 
609
        self.assertContainsRe(result, "unknown:\n  hello.txt\n")
 
610
 
 
611
        tree.add("hello.txt")
 
612
        result = self.run_bzr("status")[0]
 
613
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
614
 
 
615
        tree.commit(message="added")
 
616
        result = self.run_bzr("status -r 0..1")[0]
 
617
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
618
 
 
619
        result = self.run_bzr("status -c 1")[0]
 
620
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
621
 
 
622
        self.build_tree(['world.txt'])
 
623
        result = self.run_bzr("status -r 0")[0]
 
624
        self.assertContainsRe(result, "added:\n  hello.txt\n" \
 
625
                                      "unknown:\n  world.txt\n")
 
626
        result2 = self.run_bzr("status -r 0..")[0]
 
627
        self.assertEqual(result2, result)
 
628
 
 
629
    def test_status_short(self):
 
630
        tree = self.make_branch_and_tree('.')
 
631
 
 
632
        self.build_tree(['hello.txt'])
 
633
        result = self.run_bzr("status --short")[0]
 
634
        self.assertContainsRe(result, "[?]   hello.txt\n")
 
635
 
 
636
        tree.add("hello.txt")
 
637
        result = self.run_bzr("status --short")[0]
 
638
        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
639
 
 
640
        tree.commit(message="added")
 
641
        result = self.run_bzr("status --short -r 0..1")[0]
 
642
        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
643
 
 
644
        self.build_tree(['world.txt'])
 
645
        result = self.run_bzr("status -S -r 0")[0]
 
646
        self.assertContainsRe(result, "[+]N  hello.txt\n" \
 
647
                                      "[?]   world.txt\n")
 
648
        result2 = self.run_bzr("status -S -r 0..")[0]
 
649
        self.assertEqual(result2, result)
 
650
 
 
651
    def test_status_versioned(self):
 
652
        tree = self.make_branch_and_tree('.')
 
653
 
 
654
        self.build_tree(['hello.txt'])
 
655
        result = self.run_bzr("status --versioned")[0]
 
656
        self.assertNotContainsRe(result, "unknown:\n  hello.txt\n")
 
657
 
 
658
        tree.add("hello.txt")
 
659
        result = self.run_bzr("status --versioned")[0]
 
660
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
661
 
 
662
        tree.commit("added")
 
663
        result = self.run_bzr("status --versioned -r 0..1")[0]
 
664
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
665
 
 
666
        self.build_tree(['world.txt'])
 
667
        result = self.run_bzr("status --versioned -r 0")[0]
 
668
        self.assertContainsRe(result, "added:\n  hello.txt\n")
 
669
        self.assertNotContainsRe(result, "unknown:\n  world.txt\n")
 
670
        result2 = self.run_bzr("status --versioned -r 0..")[0]
 
671
        self.assertEqual(result2, result)
 
672
 
 
673
    def test_status_SV(self):
 
674
        tree = self.make_branch_and_tree('.')
 
675
 
 
676
        self.build_tree(['hello.txt'])
 
677
        result = self.run_bzr("status -SV")[0]
 
678
        self.assertNotContainsRe(result, "hello.txt")
 
679
 
 
680
        tree.add("hello.txt")
 
681
        result = self.run_bzr("status -SV")[0]
 
682
        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
683
 
 
684
        tree.commit(message="added")
 
685
        result = self.run_bzr("status -SV -r 0..1")[0]
 
686
        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
687
 
 
688
        self.build_tree(['world.txt'])
 
689
        result = self.run_bzr("status -SV -r 0")[0]
 
690
        self.assertContainsRe(result, "[+]N  hello.txt\n")
 
691
 
 
692
        result2 = self.run_bzr("status -SV -r 0..")[0]
 
693
        self.assertEqual(result2, result)
 
694
 
 
695
    def assertStatusContains(self, pattern, short=False):
 
696
        """Run status, and assert it contains the given pattern"""
 
697
        if short:
 
698
            result = self.run_bzr("status --short")[0]
 
699
        else:
 
700
            result = self.run_bzr("status")[0]
 
701
        self.assertContainsRe(result, pattern)
 
702
 
 
703
    def test_kind_change_plain(self):
 
704
        tree = self.make_branch_and_tree('.')
 
705
        self.build_tree(['file'])
 
706
        tree.add('file')
 
707
        tree.commit('added file')
 
708
        unlink('file')
 
709
        self.build_tree(['file/'])
 
710
        self.assertStatusContains('kind changed:\n  file \(file => directory\)')
 
711
        tree.rename_one('file', 'directory')
 
712
        self.assertStatusContains('renamed:\n  file/ => directory/\n' \
 
713
                                  'modified:\n  directory/\n')
 
714
        rmdir('directory')
 
715
        self.assertStatusContains('removed:\n  file\n')
 
716
 
 
717
    def test_kind_change_short(self):
 
718
        tree = self.make_branch_and_tree('.')
 
719
        self.build_tree(['file'])
 
720
        tree.add('file')
 
721
        tree.commit('added file')
 
722
        unlink('file')
 
723
        self.build_tree(['file/'])
 
724
        self.assertStatusContains('K  file => file/',
 
725
                                   short=True)
 
726
        tree.rename_one('file', 'directory')
 
727
        self.assertStatusContains('RK  file => directory/',
 
728
                                   short=True)
 
729
        rmdir('directory')
 
730
        self.assertStatusContains('RD  file => directory',
 
731
                                   short=True)
 
732
 
 
733
    def test_status_illegal_revision_specifiers(self):
 
734
        out, err = self.run_bzr('status -r 1..23..123', retcode=3)
 
735
        self.assertContainsRe(err, 'one or two revision specifiers')
 
736
 
 
737
    def test_status_no_pending(self):
 
738
        a_tree = self.make_branch_and_tree('a')
 
739
        self.build_tree(['a/a'])
 
740
        a_tree.add('a')
 
741
        a_tree.commit('a')
 
742
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
 
743
        self.build_tree(['b/b'])
 
744
        b_tree.add('b')
 
745
        b_tree.commit('b')
 
746
 
 
747
        self.run_bzr('merge ../b', working_dir='a')
 
748
        out, err = self.run_bzr('status --no-pending', working_dir='a')
 
749
        self.assertEqual(out, "added:\n  b\n")
 
750
 
 
751
    def test_pending_specific_files(self):
 
752
        """With a specific file list, pending merges are not shown."""
 
753
        tree = self.make_branch_and_tree('tree')
 
754
        self.build_tree_contents([('tree/a', 'content of a\n')])
 
755
        tree.add('a')
 
756
        r1_id = tree.commit('one')
 
757
        alt = tree.bzrdir.sprout('alt').open_workingtree()
 
758
        self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
 
759
        alt_id = alt.commit('alt')
 
760
        tree.merge_from_branch(alt.branch)
 
761
        output = self.make_utf8_encoded_stringio()
 
762
        show_tree_status(tree, to_file=output)
 
763
        self.assertContainsRe(output.getvalue(), 'pending merge')
 
764
        out, err = self.run_bzr('status tree/a')
 
765
        self.assertNotContainsRe(out, 'pending merge')
 
766
 
 
767
 
 
768
class TestStatusEncodings(TestCaseWithTransport):
 
769
 
 
770
    def make_uncommitted_tree(self):
 
771
        """Build a branch with uncommitted unicode named changes in the cwd."""
 
772
        working_tree = self.make_branch_and_tree(u'.')
 
773
        filename = u'hell\u00d8'
 
774
        try:
 
775
            self.build_tree_contents([(filename, 'contents of hello')])
 
776
        except UnicodeEncodeError:
 
777
            raise TestSkipped("can't build unicode working tree in "
 
778
                "filesystem encoding %s" % sys.getfilesystemencoding())
 
779
        working_tree.add(filename)
 
780
        return working_tree
 
781
 
 
782
    def test_stdout_ascii(self):
 
783
        self.overrideAttr(osutils, '_cached_user_encoding', 'ascii')
 
784
        working_tree = self.make_uncommitted_tree()
 
785
        stdout, stderr = self.run_bzr("status")
 
786
 
 
787
        self.assertEqual(stdout, """\
 
788
added:
 
789
  hell?
 
790
""")
 
791
 
 
792
    def test_stdout_latin1(self):
 
793
        self.overrideAttr(osutils, '_cached_user_encoding', 'latin-1')
 
794
        working_tree = self.make_uncommitted_tree()
 
795
        stdout, stderr = self.run_bzr('status')
 
796
 
 
797
        self.assertEqual(stdout, u"""\
 
798
added:
 
799
  hell\u00d8
 
800
""".encode('latin-1'))
 
801