~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_status.py

NEWS section template into a separate file

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests of status command.
18
18
 
19
19
Most of these depend on the particular formatting used.
20
 
As such they really are blackbox tests even though some of the 
 
20
As such they really are blackbox tests even though some of the
21
21
tests are not using self.capture. If we add tests for the programmatic
22
22
interface later, they will be non blackbox tests.
23
23
"""
32
32
    bzrdir,
33
33
    conflicts,
34
34
    errors,
 
35
    osutils,
35
36
    )
36
37
import bzrlib.branch
37
38
from bzrlib.osutils import pathjoin
42
43
 
43
44
 
44
45
class BranchStatus(TestCaseWithTransport):
45
 
    
 
46
 
46
47
    def assertStatus(self, expected_lines, working_tree,
47
 
        revision=None, short=False, pending=True):
 
48
        revision=None, short=False, pending=True, verbose=False):
48
49
        """Run status in working_tree and look for output.
49
 
        
 
50
 
50
51
        :param expected_lines: The lines to look for.
51
52
        :param working_tree: The tree to run status in.
52
53
        """
53
54
        output_string = self.status_string(working_tree, revision, short,
54
 
                pending)
 
55
                pending, verbose)
55
56
        self.assertEqual(expected_lines, output_string.splitlines(True))
56
 
    
57
 
    def status_string(self, wt, revision=None, short=False, pending=True):
 
57
 
 
58
    def status_string(self, wt, revision=None, short=False, pending=True,
 
59
        verbose=False):
58
60
        # use a real file rather than StringIO because it doesn't handle
59
61
        # Unicode very well.
60
62
        tof = codecs.getwriter('utf-8')(TemporaryFile())
61
63
        show_tree_status(wt, to_file=tof, revision=revision, short=short,
62
 
                show_pending=pending)
 
64
                show_pending=pending, verbose=verbose)
63
65
        tof.seek(0)
64
66
        return tof.read().decode('utf-8')
65
67
 
94
96
                'unknown:\n',
95
97
                '  bye.c\n',
96
98
                '  hello.c\n',
 
99
                'pending merge tips: (use -v to see all merge revisions)\n',
 
100
                '  (ghost) pending@pending-0-0\n',
 
101
            ],
 
102
            wt)
 
103
        self.assertStatus([
 
104
                'unknown:\n',
 
105
                '  bye.c\n',
 
106
                '  hello.c\n',
97
107
                'pending merges:\n',
98
108
                '  (ghost) pending@pending-0-0\n',
99
109
            ],
100
 
            wt)
 
110
            wt, verbose=True)
101
111
        self.assertStatus([
102
112
                '?   bye.c\n',
103
113
                '?   hello.c\n',
137
147
        self.build_tree(['more.c'])
138
148
        wt.add('more.c')
139
149
        wt.commit('Another test message')
140
 
        
 
150
 
141
151
        revs.append(RevisionSpec.from_string('1'))
142
152
        self.assertStatus([
143
153
                'added:\n',
158
168
        wt2 = b_2_dir.open_workingtree()
159
169
        wt.commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
160
170
        wt2.merge_from_branch(wt.branch)
161
 
        message = self.status_string(wt2)
 
171
        message = self.status_string(wt2, verbose=True)
162
172
        self.assertStartsWith(message, "pending merges:\n")
163
173
        self.assertEndsWith(message, "Empty commit 2\n")
164
174
        wt2.commit("merged")
166
176
        wt.commit("Empty commit 3 " +
167
177
                   "blah blah blah blah " * 100)
168
178
        wt2.merge_from_branch(wt.branch)
169
 
        message = self.status_string(wt2)
 
179
        message = self.status_string(wt2, verbose=True)
170
180
        self.assertStartsWith(message, "pending merges:\n")
171
181
        self.assert_("Empty commit 3" in message)
172
182
        self.assertEndsWith(message, "...\n")
196
206
        wt.add('directory')
197
207
        wt.add('test.c')
198
208
        wt.commit('testing')
199
 
        
 
209
 
200
210
        self.assertStatus([
201
211
                'unknown:\n',
202
212
                '  bye.c\n',
215
225
        tof = StringIO()
216
226
        self.assertRaises(errors.PathsDoNotExist,
217
227
                          show_tree_status,
218
 
                          wt, specific_files=['bye.c','test.c','absent.c'], 
 
228
                          wt, specific_files=['bye.c','test.c','absent.c'],
219
229
                          to_file=tof)
220
 
        
 
230
 
221
231
        tof = StringIO()
222
232
        show_tree_status(wt, specific_files=['directory'], to_file=tof)
223
233
        tof.seek(0)
274
284
        self.assertEqualDiff('conflicts:\n  Contents conflict in dir2/file1\n',
275
285
                             tof.getvalue())
276
286
 
 
287
    def _prepare_nonexistent(self):
 
288
        wt = self.make_branch_and_tree('.')
 
289
        self.assertStatus([], wt)
 
290
        self.build_tree(['FILE_A', 'FILE_B', 'FILE_C', 'FILE_D', 'FILE_E', ])
 
291
        wt.add('FILE_A')
 
292
        wt.add('FILE_B')
 
293
        wt.add('FILE_C')
 
294
        wt.add('FILE_D')
 
295
        wt.add('FILE_E')
 
296
        wt.commit('Create five empty files.')
 
297
        open('FILE_B', 'w').write('Modification to file FILE_B.')
 
298
        open('FILE_C', 'w').write('Modification to file FILE_C.')
 
299
        unlink('FILE_E')  # FILE_E will be versioned but missing
 
300
        open('FILE_Q', 'w').write('FILE_Q is added but not committed.')
 
301
        wt.add('FILE_Q')  # FILE_Q will be added but not committed
 
302
        open('UNVERSIONED_BUT_EXISTING', 'w')
 
303
        return wt
 
304
 
277
305
    def test_status_nonexistent_file(self):
278
306
        # files that don't exist in either the basis tree or working tree
279
307
        # should give an error
280
 
        wt = self.make_branch_and_tree('.')
281
 
        out, err = self.run_bzr('status does-not-exist', retcode=3)
282
 
        self.assertContainsRe(err, r'do not exist.*does-not-exist')
 
308
        wt = self._prepare_nonexistent()
 
309
        self.assertStatus([
 
310
            'removed:\n',
 
311
            '  FILE_E\n',
 
312
            'added:\n',
 
313
            '  FILE_Q\n',
 
314
            'modified:\n',
 
315
            '  FILE_B\n',
 
316
            '  FILE_C\n',
 
317
            'unknown:\n',
 
318
            '  UNVERSIONED_BUT_EXISTING\n',
 
319
            ],
 
320
            wt)
 
321
        self.assertStatus([
 
322
            ' M  FILE_B\n',
 
323
            ' M  FILE_C\n',
 
324
            ' D  FILE_E\n',
 
325
            '+N  FILE_Q\n',
 
326
            '?   UNVERSIONED_BUT_EXISTING\n',
 
327
            ],
 
328
            wt, short=True)
 
329
 
 
330
        # Okay, everything's looking good with the existent files.
 
331
        # Let's see what happens when we throw in non-existent files.
 
332
 
 
333
        # bzr st [--short] NONEXISTENT '
 
334
        expected = [
 
335
          'nonexistent:\n',
 
336
          '  NONEXISTENT\n',
 
337
          ]
 
338
        out, err = self.run_bzr('status NONEXISTENT', retcode=3)
 
339
        self.assertEqual(expected, out.splitlines(True))
 
340
        self.assertContainsRe(err,
 
341
                              r'.*ERROR: Path\(s\) do not exist: '
 
342
                              'NONEXISTENT.*')
 
343
        expected = [
 
344
          'X:   NONEXISTENT\n',
 
345
          ]
 
346
        out, err = self.run_bzr('status --short NONEXISTENT', retcode=3)
 
347
        self.assertContainsRe(err,
 
348
                              r'.*ERROR: Path\(s\) do not exist: '
 
349
                              'NONEXISTENT.*')
 
350
 
 
351
    def test_status_nonexistent_file_with_others(self):
 
352
        # bzr st [--short] NONEXISTENT ...others..
 
353
        wt = self._prepare_nonexistent()
 
354
        expected = [
 
355
          'removed:\n',
 
356
          '  FILE_E\n',
 
357
          'modified:\n',
 
358
          '  FILE_B\n',
 
359
          '  FILE_C\n',
 
360
          'nonexistent:\n',
 
361
          '  NONEXISTENT\n',
 
362
          ]
 
363
        out, err = self.run_bzr('status NONEXISTENT '
 
364
                                'FILE_A FILE_B FILE_C FILE_D FILE_E',
 
365
                                retcode=3)
 
366
        self.assertEqual(expected, out.splitlines(True))
 
367
        self.assertContainsRe(err,
 
368
                              r'.*ERROR: Path\(s\) do not exist: '
 
369
                              'NONEXISTENT.*')
 
370
        expected = [
 
371
          ' D  FILE_E\n',
 
372
          ' M  FILE_C\n',
 
373
          ' M  FILE_B\n',
 
374
          'X   NONEXISTENT\n',
 
375
          ]
 
376
        out, err = self.run_bzr('status --short NONEXISTENT '
 
377
                                'FILE_A FILE_B FILE_C FILE_D FILE_E',
 
378
                                retcode=3)
 
379
        self.assertEqual(expected, out.splitlines(True))
 
380
        self.assertContainsRe(err,
 
381
                              r'.*ERROR: Path\(s\) do not exist: '
 
382
                              'NONEXISTENT.*')
 
383
 
 
384
    def test_status_multiple_nonexistent_files(self):
 
385
        # bzr st [--short] NONEXISTENT ... ANOTHER_NONEXISTENT ...
 
386
        wt = self._prepare_nonexistent()
 
387
        expected = [
 
388
          'removed:\n',
 
389
          '  FILE_E\n',
 
390
          'modified:\n',
 
391
          '  FILE_B\n',
 
392
          '  FILE_C\n',
 
393
          'nonexistent:\n',
 
394
          '  ANOTHER_NONEXISTENT\n',
 
395
          '  NONEXISTENT\n',
 
396
          ]
 
397
        out, err = self.run_bzr('status NONEXISTENT '
 
398
                                'FILE_A FILE_B ANOTHER_NONEXISTENT '
 
399
                                'FILE_C FILE_D FILE_E', retcode=3)
 
400
        self.assertEqual(expected, out.splitlines(True))
 
401
        self.assertContainsRe(err,
 
402
                              r'.*ERROR: Path\(s\) do not exist: '
 
403
                              'ANOTHER_NONEXISTENT NONEXISTENT.*')
 
404
        expected = [
 
405
          ' D  FILE_E\n',
 
406
          ' M  FILE_C\n',
 
407
          ' M  FILE_B\n',
 
408
          'X   ANOTHER_NONEXISTENT\n',
 
409
          'X   NONEXISTENT\n',
 
410
          ]
 
411
        out, err = self.run_bzr('status --short NONEXISTENT '
 
412
                                'FILE_A FILE_B ANOTHER_NONEXISTENT '
 
413
                                'FILE_C FILE_D FILE_E', retcode=3)
 
414
        self.assertEqual(expected, out.splitlines(True))
 
415
        self.assertContainsRe(err,
 
416
                              r'.*ERROR: Path\(s\) do not exist: '
 
417
                              'ANOTHER_NONEXISTENT NONEXISTENT.*')
 
418
 
 
419
    def test_status_nonexistent_file_with_unversioned(self):
 
420
        # bzr st [--short] NONEXISTENT A B UNVERSIONED_BUT_EXISTING C D E Q
 
421
        wt = self._prepare_nonexistent()
 
422
        expected = [
 
423
          'removed:\n',
 
424
          '  FILE_E\n',
 
425
          'added:\n',
 
426
          '  FILE_Q\n',
 
427
          'modified:\n',
 
428
          '  FILE_B\n',
 
429
          '  FILE_C\n',
 
430
          'unknown:\n',
 
431
          '  UNVERSIONED_BUT_EXISTING\n',
 
432
          'nonexistent:\n',
 
433
          '  NONEXISTENT\n',
 
434
          ]
 
435
        out, err = self.run_bzr('status NONEXISTENT '
 
436
                                'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
 
437
                                'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
 
438
        self.assertEqual(expected, out.splitlines(True))
 
439
        self.assertContainsRe(err,
 
440
                              r'.*ERROR: Path\(s\) do not exist: '
 
441
                              'NONEXISTENT.*')
 
442
        expected = [
 
443
          '+N  FILE_Q\n',
 
444
          '?   UNVERSIONED_BUT_EXISTING\n',
 
445
          ' D  FILE_E\n',
 
446
          ' M  FILE_C\n',
 
447
          ' M  FILE_B\n',
 
448
          'X   NONEXISTENT\n',
 
449
          ]
 
450
        out, err = self.run_bzr('status --short NONEXISTENT '
 
451
                                'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
 
452
                                'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
 
453
        self.assertEqual(expected, out.splitlines(True))
 
454
        self.assertContainsRe(err,
 
455
                              r'.*ERROR: Path\(s\) do not exist: '
 
456
                              'NONEXISTENT.*')
283
457
 
284
458
    def test_status_out_of_date(self):
285
459
        """Simulate status of out-of-date tree after remote push"""
298
472
        self.assertEqual("working tree is out of date, run 'bzr update'\n",
299
473
                         err)
300
474
 
 
475
    def test_status_write_lock(self):
 
476
        """Test that status works without fetching history and
 
477
        having a write lock.
 
478
 
 
479
        See https://bugs.launchpad.net/bzr/+bug/149270
 
480
        """
 
481
        mkdir('branch1')
 
482
        wt = self.make_branch_and_tree('branch1')
 
483
        b = wt.branch
 
484
        wt.commit('Empty commit 1')
 
485
        wt2 = b.bzrdir.sprout('branch2').open_workingtree()
 
486
        wt2.commit('Empty commit 2')
 
487
        out, err = self.run_bzr('status branch1 -rbranch:branch2')
 
488
        self.assertEqual('', out)
 
489
 
301
490
 
302
491
class CheckoutStatus(BranchStatus):
303
492
 
305
494
        super(CheckoutStatus, self).setUp()
306
495
        mkdir('codir')
307
496
        chdir('codir')
308
 
        
 
497
 
309
498
    def make_branch_and_tree(self, relpath):
310
499
        source = self.make_branch(pathjoin('..', relpath))
311
500
        checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
438
627
        b_tree.add('b')
439
628
        b_tree.commit('b')
440
629
 
441
 
        chdir('a')
442
 
        self.run_bzr('merge ../b')
443
 
        out, err = self.run_bzr('status --no-pending')
 
630
        self.run_bzr('merge ../b', working_dir='a')
 
631
        out, err = self.run_bzr('status --no-pending', working_dir='a')
444
632
        self.assertEquals(out, "added:\n  b\n")
445
633
 
 
634
    def test_pending_specific_files(self):
 
635
        """With a specific file list, pending merges are not shown."""
 
636
        tree = self.make_branch_and_tree('tree')
 
637
        self.build_tree_contents([('tree/a', 'content of a\n')])
 
638
        tree.add('a')
 
639
        r1_id = tree.commit('one')
 
640
        alt = tree.bzrdir.sprout('alt').open_workingtree()
 
641
        self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
 
642
        alt_id = alt.commit('alt')
 
643
        tree.merge_from_branch(alt.branch)
 
644
        output = self.make_utf8_encoded_stringio()
 
645
        show_tree_status(tree, to_file=output)
 
646
        self.assertContainsRe(output.getvalue(), 'pending merge')
 
647
        out, err = self.run_bzr('status tree/a')
 
648
        self.assertNotContainsRe(out, 'pending merge')
446
649
 
447
650
 
448
651
class TestStatusEncodings(TestCaseWithTransport):
449
 
    
 
652
 
450
653
    def setUp(self):
451
654
        TestCaseWithTransport.setUp(self)
452
 
        self.user_encoding = bzrlib.user_encoding
 
655
        self.user_encoding = osutils._cached_user_encoding
453
656
        self.stdout = sys.stdout
454
657
 
455
658
    def tearDown(self):
456
 
        bzrlib.user_encoding = self.user_encoding
 
659
        osutils._cached_user_encoding = self.user_encoding
457
660
        sys.stdout = self.stdout
458
661
        TestCaseWithTransport.tearDown(self)
459
662
 
471
674
 
472
675
    def test_stdout_ascii(self):
473
676
        sys.stdout = StringIO()
474
 
        bzrlib.user_encoding = 'ascii'
 
677
        osutils._cached_user_encoding = 'ascii'
475
678
        working_tree = self.make_uncommitted_tree()
476
679
        stdout, stderr = self.run_bzr("status")
477
680
 
482
685
 
483
686
    def test_stdout_latin1(self):
484
687
        sys.stdout = StringIO()
485
 
        bzrlib.user_encoding = 'latin-1'
 
688
        osutils._cached_user_encoding = 'latin-1'
486
689
        working_tree = self.make_uncommitted_tree()
487
690
        stdout, stderr = self.run_bzr('status')
488
691