22
22
interface later, they will be non blackbox tests.
25
from cStringIO import StringIO
26
27
from os import mkdir, chdir, rmdir, unlink
29
from tempfile import TemporaryFile
35
from breezy.bzr import (
39
from ...osutils import pathjoin
40
from ...revisionspec import RevisionSpec
41
from ...sixish import (
46
from ...status import show_tree_status
47
from .. import TestCaseWithTransport, TestSkipped
48
from ...workingtree import WorkingTree
38
from bzrlib.osutils import pathjoin
39
from bzrlib.revisionspec import RevisionSpec
40
from bzrlib.status import show_tree_status
41
from bzrlib.tests import TestCaseWithTransport, TestSkipped
42
from bzrlib.workingtree import WorkingTree
51
45
class BranchStatus(TestCaseWithTransport):
54
super(BranchStatus, self).setUp()
55
# As TestCase.setUp clears all hooks, we install this default
56
# post_status hook handler for the test.
57
status.hooks.install_named_hook('post_status',
58
status._show_shelve_summary,
61
def assertStatus(self, expected_lines, working_tree, specific_files=None,
62
revision=None, short=False, pending=True, verbose=False):
47
def assertStatus(self, expected_lines, working_tree,
48
revision=None, short=False, pending=True, verbose=False):
63
49
"""Run status in working_tree and look for output.
65
51
:param expected_lines: The lines to look for.
66
52
:param working_tree: The tree to run status in.
68
output_string = self.status_string(working_tree, specific_files, revision, short,
54
output_string = self.status_string(working_tree, revision, short,
70
56
self.assertEqual(expected_lines, output_string.splitlines(True))
72
def status_string(self, wt, specific_files=None, revision=None,
73
short=False, pending=True, verbose=False):
74
uio = self.make_utf8_encoded_stringio()
75
show_tree_status(wt, specific_files=specific_files, to_file=uio,
76
revision=revision, short=short, show_pending=pending,
78
return uio.getvalue().decode('utf-8')
58
def status_string(self, wt, revision=None, short=False, pending=True,
60
# use a real file rather than StringIO because it doesn't handle
62
tof = codecs.getwriter('utf-8')(TemporaryFile())
63
show_tree_status(wt, to_file=tof, revision=revision, short=short,
64
show_pending=pending, verbose=verbose)
66
return tof.read().decode('utf-8')
80
68
def test_branch_status(self):
81
69
"""Test basic branch status"""
90
78
self.build_tree(['hello.c', 'bye.c'])
91
79
self.assertStatus([
97
85
self.assertStatus([
103
91
# add a commit to allow showing pending merges.
104
92
wt.commit('create a parent to allow testing merge output')
106
wt.add_parent_tree_id(b'pending@pending-0-0')
94
wt.add_parent_tree_id('pending@pending-0-0')
107
95
self.assertStatus([
111
'pending merge tips: (use -v to see all merge revisions)\n',
112
' (ghost) pending@pending-0-0\n',
99
'pending merge tips: (use -v to see all merge revisions)\n',
100
' (ghost) pending@pending-0-0\n',
115
103
self.assertStatus([
120
' (ghost) pending@pending-0-0\n',
108
' (ghost) pending@pending-0-0\n',
122
110
wt, verbose=True)
123
111
self.assertStatus([
126
'P (ghost) pending@pending-0-0\n',
114
'P (ghost) pending@pending-0-0\n',
129
117
self.assertStatus([
134
122
wt, pending=False)
135
123
self.assertStatus([
139
127
wt, short=True, pending=False)
185
173
self.assertEndsWith(message, "Empty commit 2\n")
186
174
wt2.commit("merged")
187
175
# must be long to make sure we see elipsis at the end
188
wt.commit("Empty commit 3 "
189
+ "blah blah blah blah " * 100)
176
wt.commit("Empty commit 3 " +
177
"blah blah blah blah " * 100)
190
178
wt2.merge_from_branch(wt.branch)
191
179
message = self.status_string(wt2, verbose=True)
192
180
self.assertStartsWith(message, "pending merges:\n")
193
self.assertTrue("Empty commit 3" in message)
181
self.assert_("Empty commit 3" in message)
194
182
self.assertEndsWith(message, "...\n")
196
184
def test_tree_status_ignores(self):
200
188
wt.commit('commit .bzrignore')
201
189
self.build_tree(['foo.c', 'foo.c~'])
202
190
self.assertStatus([
207
195
self.assertStatus([
212
200
def test_tree_status_specific_files(self):
213
201
"""Tests branch status with given specific files"""
214
202
wt = self.make_branch_and_tree('.')
217
self.build_tree(['directory/', 'directory/hello.c',
218
'bye.c', 'test.c', 'dir2/',
205
self.build_tree(['directory/','directory/hello.c', 'bye.c','test.c','dir2/'])
220
206
wt.add('directory')
222
208
wt.commit('testing')
232
' directory/hello.c\n'
239
'? directory/hello.c\n',
214
' directory/hello.c\n'
221
'? directory/hello.c\n'
245
226
self.assertRaises(errors.PathsDoNotExist,
246
227
show_tree_status,
247
wt, specific_files=['bye.c', 'test.c', 'absent.c'],
228
wt, specific_files=['bye.c','test.c','absent.c'],
251
232
show_tree_status(wt, specific_files=['directory'], to_file=tof)
253
self.assertEqual(tof.readlines(),
255
' directory/hello.c\n'
234
self.assertEquals(tof.readlines(),
236
' directory/hello.c\n'
258
239
show_tree_status(wt, specific_files=['directory'], to_file=tof,
261
self.assertEqual(tof.readlines(), ['? directory/hello.c\n'])
242
self.assertEquals(tof.readlines(), ['? directory/hello.c\n'])
264
245
show_tree_status(wt, specific_files=['dir2'], to_file=tof)
266
self.assertEqual(tof.readlines(),
247
self.assertEquals(tof.readlines(),
271
252
show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
273
self.assertEqual(tof.readlines(), ['? dir2/\n'])
254
self.assertEquals(tof.readlines(), ['? dir2/\n'])
276
257
revs = [RevisionSpec.from_string('0'), RevisionSpec.from_string('1')]
277
258
show_tree_status(wt, specific_files=['test.c'], to_file=tof,
278
259
short=True, revision=revs)
280
self.assertEqual(tof.readlines(), ['+N test.c\n'])
283
show_tree_status(wt, specific_files=['missing.c'], to_file=tof)
285
self.assertEqual(tof.readlines(),
290
show_tree_status(wt, specific_files=['missing.c'], to_file=tof,
293
self.assertEqual(tof.readlines(),
261
self.assertEquals(tof.readlines(), ['+N test.c\n'])
296
263
def test_specific_files_conflicts(self):
297
264
tree = self.make_branch_and_tree('.')
329
296
wt.commit('Create five empty files.')
330
with open('FILE_B', 'w') as f:
331
f.write('Modification to file FILE_B.')
332
with open('FILE_C', 'w') as f:
333
f.write('Modification to file FILE_C.')
297
open('FILE_B', 'w').write('Modification to file FILE_B.')
298
open('FILE_C', 'w').write('Modification to file FILE_C.')
334
299
unlink('FILE_E') # FILE_E will be versioned but missing
335
with open('FILE_Q', 'w') as f:
336
f.write('FILE_Q is added but not committed.')
300
open('FILE_Q', 'w').write('FILE_Q is added but not committed.')
337
301
wt.add('FILE_Q') # FILE_Q will be added but not committed
338
302
open('UNVERSIONED_BUT_EXISTING', 'w')
366
330
# Okay, everything's looking good with the existent files.
367
331
# Let's see what happens when we throw in non-existent files.
369
# brz st [--short] NONEXISTENT '
333
# bzr st [--short] NONEXISTENT '
374
338
out, err = self.run_bzr('status NONEXISTENT', retcode=3)
375
339
self.assertEqual(expected, out.splitlines(True))
376
340
self.assertContainsRe(err,
377
341
r'.*ERROR: Path\(s\) do not exist: '
382
346
out, err = self.run_bzr('status --short NONEXISTENT', retcode=3)
383
347
self.assertContainsRe(err,
384
348
r'.*ERROR: Path\(s\) do not exist: '
387
351
def test_status_nonexistent_file_with_others(self):
388
# brz st [--short] NONEXISTENT ...others..
352
# bzr st [--short] NONEXISTENT ...others..
389
353
wt = self._prepare_nonexistent()
399
363
out, err = self.run_bzr('status NONEXISTENT '
400
364
'FILE_A FILE_B FILE_C FILE_D FILE_E',
420
384
def test_status_multiple_nonexistent_files(self):
421
# brz st [--short] NONEXISTENT ... ANOTHER_NONEXISTENT ...
385
# bzr st [--short] NONEXISTENT ... ANOTHER_NONEXISTENT ...
422
386
wt = self._prepare_nonexistent()
430
' ANOTHER_NONEXISTENT\n',
394
' ANOTHER_NONEXISTENT\n',
433
397
out, err = self.run_bzr('status NONEXISTENT '
434
398
'FILE_A FILE_B ANOTHER_NONEXISTENT '
435
399
'FILE_C FILE_D FILE_E', retcode=3)
453
417
'ANOTHER_NONEXISTENT NONEXISTENT.*')
455
419
def test_status_nonexistent_file_with_unversioned(self):
456
# brz st [--short] NONEXISTENT A B UNVERSIONED_BUT_EXISTING C D E Q
420
# bzr st [--short] NONEXISTENT A B UNVERSIONED_BUT_EXISTING C D E Q
457
421
wt = self._prepare_nonexistent()
467
' UNVERSIONED_BUT_EXISTING\n',
431
' UNVERSIONED_BUT_EXISTING\n',
471
435
out, err = self.run_bzr('status NONEXISTENT '
472
436
'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
473
437
'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
475
439
self.assertContainsRe(err,
476
440
r'.*ERROR: Path\(s\) do not exist: '
480
'? UNVERSIONED_BUT_EXISTING\n',
444
'? UNVERSIONED_BUT_EXISTING\n',
486
450
out, err = self.run_bzr('status --short NONEXISTENT '
487
451
'FILE_A FILE_B UNVERSIONED_BUT_EXISTING '
488
452
'FILE_C FILE_D FILE_E FILE_Q', retcode=3)
489
actual = out.splitlines(True)
491
self.assertEqual(expected, actual)
453
self.assertEqual(expected, out.splitlines(True))
492
454
self.assertContainsRe(err,
493
455
r'.*ERROR: Path\(s\) do not exist: '
496
458
def test_status_out_of_date(self):
497
459
"""Simulate status of out-of-date tree after remote push"""
498
460
tree = self.make_branch_and_tree('.')
499
self.build_tree_contents([('a', b'foo\n')])
500
with tree.lock_write():
461
self.build_tree_contents([('a', 'foo\n')])
502
465
tree.commit('add test file')
503
466
# simulate what happens after a remote push
504
tree.set_last_revision(b"0")
467
tree.set_last_revision("0")
469
# before run another commands we should unlock tree
505
471
out, err = self.run_bzr('status')
506
self.assertEqual("working tree is out of date, run 'brz update'\n",
472
self.assertEqual("working tree is out of date, run 'bzr update'\n",
509
475
def test_status_on_ignored(self):
517
483
result = self.run_bzr('status')[0]
518
484
self.assertContainsRe(result, "unknown:\n test1.c\n")
519
485
short_result = self.run_bzr('status --short')[0]
520
self.assertContainsRe(short_result, "\\? test1.c\n")
486
self.assertContainsRe(short_result, "\? test1.c\n")
522
488
result = self.run_bzr('status test1.c')[0]
523
489
self.assertContainsRe(result, "unknown:\n test1.c\n")
524
490
short_result = self.run_bzr('status --short test1.c')[0]
525
self.assertContainsRe(short_result, "\\? test1.c\n")
491
self.assertContainsRe(short_result, "\? test1.c\n")
527
493
result = self.run_bzr('status test1.c~')[0]
528
494
self.assertContainsRe(result, "ignored:\n test1.c~\n")
535
501
self.assertContainsRe(short_result, "I test1.c~\nI test2.c~\n")
537
503
result = self.run_bzr('status test1.c test1.c~ test2.c~')[0]
538
self.assertContainsRe(
539
result, "unknown:\n test1.c\nignored:\n test1.c~\n test2.c~\n")
540
short_result = self.run_bzr(
541
'status --short test1.c test1.c~ test2.c~')[0]
542
self.assertContainsRe(
543
short_result, "\\? test1.c\nI test1.c~\nI test2.c~\n")
504
self.assertContainsRe(result, "unknown:\n test1.c\nignored:\n test1.c~\n test2.c~\n")
505
short_result = self.run_bzr('status --short test1.c test1.c~ test2.c~')[0]
506
self.assertContainsRe(short_result, "\? test1.c\nI test1.c~\nI test2.c~\n")
545
508
def test_status_write_lock(self):
546
509
"""Test that status works without fetching history and
552
515
wt = self.make_branch_and_tree('branch1')
554
517
wt.commit('Empty commit 1')
555
wt2 = b.controldir.sprout('branch2').open_workingtree()
518
wt2 = b.bzrdir.sprout('branch2').open_workingtree()
556
519
wt2.commit('Empty commit 2')
557
520
out, err = self.run_bzr('status branch1 -rbranch:branch2')
558
521
self.assertEqual('', out)
560
def test_status_with_shelves(self):
561
"""Ensure that _show_shelve_summary handler works.
563
wt = self.make_branch_and_tree('.')
564
self.build_tree(['hello.c'])
566
self.run_bzr(['shelve', '--all', '-m', 'foo'])
567
self.build_tree(['bye.c'])
572
'1 shelf exists. See "brz shelve --list" for details.\n',
575
self.run_bzr(['shelve', '--all', '-m', 'bar'])
576
self.build_tree(['eggs.c', 'spam.c'])
583
'2 shelves exist. See "brz shelve --list" for details.\n',
591
specific_files=['spam.c'])
594
524
class CheckoutStatus(BranchStatus):
628
559
self.build_tree(['world.txt'])
629
560
result = self.run_bzr("status -r 0")[0]
630
self.assertContainsRe(result, "added:\n hello.txt\n"
561
self.assertContainsRe(result, "added:\n hello.txt\n" \
631
562
"unknown:\n world.txt\n")
632
563
result2 = self.run_bzr("status -r 0..")[0]
633
self.assertEqual(result2, result)
564
self.assertEquals(result2, result)
635
566
def test_status_short(self):
636
567
tree = self.make_branch_and_tree('.')
648
579
self.assertContainsRe(result, "[+]N hello.txt\n")
650
581
self.build_tree(['world.txt'])
651
result = self.run_bzr("status -S -r 0")[0]
652
self.assertContainsRe(result, "[+]N hello.txt\n"
582
result = self.run_bzr("status --short -r 0")[0]
583
self.assertContainsRe(result, "[+]N hello.txt\n" \
653
584
"[?] world.txt\n")
654
result2 = self.run_bzr("status -S -r 0..")[0]
655
self.assertEqual(result2, result)
585
result2 = self.run_bzr("status --short -r 0..")[0]
586
self.assertEquals(result2, result)
657
588
def test_status_versioned(self):
658
589
tree = self.make_branch_and_tree('.')
713
644
tree.commit('added file')
715
646
self.build_tree(['file/'])
716
self.assertStatusContains(
717
'kind changed:\n file \\(file => directory\\)')
647
self.assertStatusContains('kind changed:\n file \(file => directory\)')
718
648
tree.rename_one('file', 'directory')
719
self.assertStatusContains('renamed:\n file => directory/\n'
649
self.assertStatusContains('renamed:\n file/ => directory/\n' \
720
650
'modified:\n directory/\n')
721
651
rmdir('directory')
722
652
self.assertStatusContains('removed:\n file\n')
746
676
self.build_tree(['a/a'])
748
678
a_tree.commit('a')
749
b_tree = a_tree.controldir.sprout('b').open_workingtree()
679
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
750
680
self.build_tree(['b/b'])
752
682
b_tree.commit('b')
754
684
self.run_bzr('merge ../b', working_dir='a')
755
685
out, err = self.run_bzr('status --no-pending', working_dir='a')
756
self.assertEqual(out, "added:\n b\n")
686
self.assertEquals(out, "added:\n b\n")
758
688
def test_pending_specific_files(self):
759
689
"""With a specific file list, pending merges are not shown."""
760
690
tree = self.make_branch_and_tree('tree')
761
self.build_tree_contents([('tree/a', b'content of a\n')])
691
self.build_tree_contents([('tree/a', 'content of a\n')])
763
693
r1_id = tree.commit('one')
764
alt = tree.controldir.sprout('alt').open_workingtree()
765
self.build_tree_contents([('alt/a', b'content of a\nfrom alt\n')])
694
alt = tree.bzrdir.sprout('alt').open_workingtree()
695
self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
766
696
alt_id = alt.commit('alt')
767
697
tree.merge_from_branch(alt.branch)
768
698
output = self.make_utf8_encoded_stringio()
769
699
show_tree_status(tree, to_file=output)
770
self.assertContainsRe(output.getvalue(), b'pending merge')
700
self.assertContainsRe(output.getvalue(), 'pending merge')
771
701
out, err = self.run_bzr('status tree/a')
772
702
self.assertNotContainsRe(out, 'pending merge')
775
705
class TestStatusEncodings(TestCaseWithTransport):
708
TestCaseWithTransport.setUp(self)
709
self.user_encoding = osutils._cached_user_encoding
710
self.stdout = sys.stdout
713
osutils._cached_user_encoding = self.user_encoding
714
sys.stdout = self.stdout
715
TestCaseWithTransport.tearDown(self)
777
717
def make_uncommitted_tree(self):
778
718
"""Build a branch with uncommitted unicode named changes in the cwd."""
779
719
working_tree = self.make_branch_and_tree(u'.')
780
720
filename = u'hell\u00d8'
782
self.build_tree_contents([(filename, b'contents of hello')])
722
self.build_tree_contents([(filename, 'contents of hello')])
783
723
except UnicodeEncodeError:
784
724
raise TestSkipped("can't build unicode working tree in "
785
"filesystem encoding %s" % sys.getfilesystemencoding())
725
"filesystem encoding %s" % sys.getfilesystemencoding())
786
726
working_tree.add(filename)
787
727
return working_tree
789
729
def test_stdout_ascii(self):
790
self.overrideAttr(osutils, '_cached_user_encoding', 'ascii')
730
sys.stdout = StringIO()
731
osutils._cached_user_encoding = 'ascii'
791
732
working_tree = self.make_uncommitted_tree()
792
733
stdout, stderr = self.run_bzr("status")
794
self.assertEqual(stdout, """\
735
self.assertEquals(stdout, """\
799
740
def test_stdout_latin1(self):
800
self.overrideAttr(osutils, '_cached_user_encoding', 'latin-1')
741
sys.stdout = StringIO()
742
osutils._cached_user_encoding = 'latin-1'
801
743
working_tree = self.make_uncommitted_tree()
802
744
stdout, stderr = self.run_bzr('status')
746
self.assertEquals(stdout, u"""\
809
expected = expected.encode('latin-1')
810
self.assertEqual(stdout, expected)
749
""".encode('latin-1'))