1
# Copyright (C) 2005, 2006 Canonical Ltd
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Tests of status command.
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.
25
from cStringIO import StringIO
27
from os import mkdir, chdir, rmdir, unlink
29
from tempfile import TemporaryFile
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
45
class BranchStatus(TestCaseWithTransport):
47
def assertStatus(self, expected_lines, working_tree,
48
revision=None, short=False, pending=True, verbose=False):
49
"""Run status in working_tree and look for output.
51
:param expected_lines: The lines to look for.
52
:param working_tree: The tree to run status in.
54
output_string = self.status_string(working_tree, revision, short,
56
self.assertEqual(expected_lines, output_string.splitlines(True))
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')
68
def test_branch_status(self):
69
"""Test basic branch status"""
70
wt = self.make_branch_and_tree('.')
72
# status with no commits or files - it must
73
# work and show no output. We do this with no
74
# commits to be sure that it's not going to fail
76
self.assertStatus([], wt)
78
self.build_tree(['hello.c', 'bye.c'])
91
# add a commit to allow showing pending merges.
92
wt.commit('create a parent to allow testing merge output')
94
wt.add_parent_tree_id('pending@pending-0-0')
99
'pending merge tips: (use -v to see all merge revisions)\n',
100
' (ghost) pending@pending-0-0\n',
108
' (ghost) pending@pending-0-0\n',
114
'P (ghost) pending@pending-0-0\n',
127
wt, short=True, pending=False)
129
def test_branch_status_revisions(self):
130
"""Tests branch status with revisions"""
131
wt = self.make_branch_and_tree('.')
133
self.build_tree(['hello.c', 'bye.c'])
136
wt.commit('Test message')
138
revs = [RevisionSpec.from_string('0')]
147
self.build_tree(['more.c'])
149
wt.commit('Another test message')
151
revs.append(RevisionSpec.from_string('1'))
160
def test_pending(self):
161
"""Pending merges display works, including Unicode"""
163
wt = self.make_branch_and_tree('branch')
165
wt.commit("Empty commit 1")
166
b_2_dir = b.bzrdir.sprout('./copy')
167
b_2 = b_2_dir.open_branch()
168
wt2 = b_2_dir.open_workingtree()
169
wt.commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
170
wt2.merge_from_branch(wt.branch)
171
message = self.status_string(wt2, verbose=True)
172
self.assertStartsWith(message, "pending merges:\n")
173
self.assertEndsWith(message, "Empty commit 2\n")
175
# must be long to make sure we see elipsis at the end
176
wt.commit("Empty commit 3 " +
177
"blah blah blah blah " * 100)
178
wt2.merge_from_branch(wt.branch)
179
message = self.status_string(wt2, verbose=True)
180
self.assertStartsWith(message, "pending merges:\n")
181
self.assert_("Empty commit 3" in message)
182
self.assertEndsWith(message, "...\n")
184
def test_tree_status_ignores(self):
185
"""Tests branch status with ignores"""
186
wt = self.make_branch_and_tree('.')
187
self.run_bzr('ignore *~')
188
wt.commit('commit .bzrignore')
189
self.build_tree(['foo.c', 'foo.c~'])
200
def test_tree_status_specific_files(self):
201
"""Tests branch status with given specific files"""
202
wt = self.make_branch_and_tree('.')
205
self.build_tree(['directory/','directory/hello.c', 'bye.c','test.c','dir2/'])
214
' directory/hello.c\n'
221
'? directory/hello.c\n'
226
self.assertRaises(errors.PathsDoNotExist,
228
wt, specific_files=['bye.c','test.c','absent.c'],
232
show_tree_status(wt, specific_files=['directory'], to_file=tof)
234
self.assertEquals(tof.readlines(),
236
' directory/hello.c\n'
239
show_tree_status(wt, specific_files=['directory'], to_file=tof,
242
self.assertEquals(tof.readlines(), ['? directory/hello.c\n'])
245
show_tree_status(wt, specific_files=['dir2'], to_file=tof)
247
self.assertEquals(tof.readlines(),
252
show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
254
self.assertEquals(tof.readlines(), ['? dir2/\n'])
257
revs = [RevisionSpec.from_string('0'), RevisionSpec.from_string('1')]
258
show_tree_status(wt, specific_files=['test.c'], to_file=tof,
259
short=True, revision=revs)
261
self.assertEquals(tof.readlines(), ['+N test.c\n'])
263
def test_specific_files_conflicts(self):
264
tree = self.make_branch_and_tree('.')
265
self.build_tree(['dir2/'])
267
tree.commit('added dir2')
268
tree.set_conflicts(conflicts.ConflictList(
269
[conflicts.ContentsConflict('foo')]))
271
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
272
self.assertEqualDiff('', tof.getvalue())
273
tree.set_conflicts(conflicts.ConflictList(
274
[conflicts.ContentsConflict('dir2')]))
276
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
277
self.assertEqualDiff('conflicts:\n Contents conflict in dir2\n',
280
tree.set_conflicts(conflicts.ConflictList(
281
[conflicts.ContentsConflict('dir2/file1')]))
283
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
284
self.assertEqualDiff('conflicts:\n Contents conflict in dir2/file1\n',
287
def test_status_nonexistent_file(self):
288
# files that don't exist in either the basis tree or working tree
289
# should give an error
290
wt = self.make_branch_and_tree('.')
291
out, err = self.run_bzr('status does-not-exist', retcode=3)
292
self.assertContainsRe(err, r'do not exist.*does-not-exist')
294
def test_status_out_of_date(self):
295
"""Simulate status of out-of-date tree after remote push"""
296
tree = self.make_branch_and_tree('.')
297
self.build_tree_contents([('a', 'foo\n')])
301
tree.commit('add test file')
302
# simulate what happens after a remote push
303
tree.set_last_revision("0")
305
# before run another commands we should unlock tree
307
out, err = self.run_bzr('status')
308
self.assertEqual("working tree is out of date, run 'bzr update'\n",
311
def test_status_write_lock(self):
312
"""Test that status works without fetching history and
315
See https://bugs.launchpad.net/bzr/+bug/149270
318
wt = self.make_branch_and_tree('branch1')
320
wt.commit('Empty commit 1')
321
wt2 = b.bzrdir.sprout('branch2').open_workingtree()
322
wt2.commit('Empty commit 2')
323
out, err = self.run_bzr('status branch1 -rbranch:branch2')
324
self.assertEqual('', out)
327
class CheckoutStatus(BranchStatus):
330
super(CheckoutStatus, self).setUp()
334
def make_branch_and_tree(self, relpath):
335
source = self.make_branch(pathjoin('..', relpath))
336
checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
337
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
338
return checkout.create_workingtree()
341
class TestStatus(TestCaseWithTransport):
343
def test_status_plain(self):
344
tree = self.make_branch_and_tree('.')
346
self.build_tree(['hello.txt'])
347
result = self.run_bzr("status")[0]
348
self.assertContainsRe(result, "unknown:\n hello.txt\n")
350
tree.add("hello.txt")
351
result = self.run_bzr("status")[0]
352
self.assertContainsRe(result, "added:\n hello.txt\n")
354
tree.commit(message="added")
355
result = self.run_bzr("status -r 0..1")[0]
356
self.assertContainsRe(result, "added:\n hello.txt\n")
358
result = self.run_bzr("status -c 1")[0]
359
self.assertContainsRe(result, "added:\n hello.txt\n")
361
self.build_tree(['world.txt'])
362
result = self.run_bzr("status -r 0")[0]
363
self.assertContainsRe(result, "added:\n hello.txt\n" \
364
"unknown:\n world.txt\n")
365
result2 = self.run_bzr("status -r 0..")[0]
366
self.assertEquals(result2, result)
368
def test_status_short(self):
369
tree = self.make_branch_and_tree('.')
371
self.build_tree(['hello.txt'])
372
result = self.run_bzr("status --short")[0]
373
self.assertContainsRe(result, "[?] hello.txt\n")
375
tree.add("hello.txt")
376
result = self.run_bzr("status --short")[0]
377
self.assertContainsRe(result, "[+]N hello.txt\n")
379
tree.commit(message="added")
380
result = self.run_bzr("status --short -r 0..1")[0]
381
self.assertContainsRe(result, "[+]N hello.txt\n")
383
self.build_tree(['world.txt'])
384
result = self.run_bzr("status --short -r 0")[0]
385
self.assertContainsRe(result, "[+]N hello.txt\n" \
387
result2 = self.run_bzr("status --short -r 0..")[0]
388
self.assertEquals(result2, result)
390
def test_status_versioned(self):
391
tree = self.make_branch_and_tree('.')
393
self.build_tree(['hello.txt'])
394
result = self.run_bzr("status --versioned")[0]
395
self.assertNotContainsRe(result, "unknown:\n hello.txt\n")
397
tree.add("hello.txt")
398
result = self.run_bzr("status --versioned")[0]
399
self.assertContainsRe(result, "added:\n hello.txt\n")
402
result = self.run_bzr("status --versioned -r 0..1")[0]
403
self.assertContainsRe(result, "added:\n hello.txt\n")
405
self.build_tree(['world.txt'])
406
result = self.run_bzr("status --versioned -r 0")[0]
407
self.assertContainsRe(result, "added:\n hello.txt\n")
408
self.assertNotContainsRe(result, "unknown:\n world.txt\n")
409
result2 = self.run_bzr("status --versioned -r 0..")[0]
410
self.assertEquals(result2, result)
412
def test_status_SV(self):
413
tree = self.make_branch_and_tree('.')
415
self.build_tree(['hello.txt'])
416
result = self.run_bzr("status -SV")[0]
417
self.assertNotContainsRe(result, "hello.txt")
419
tree.add("hello.txt")
420
result = self.run_bzr("status -SV")[0]
421
self.assertContainsRe(result, "[+]N hello.txt\n")
423
tree.commit(message="added")
424
result = self.run_bzr("status -SV -r 0..1")[0]
425
self.assertContainsRe(result, "[+]N hello.txt\n")
427
self.build_tree(['world.txt'])
428
result = self.run_bzr("status -SV -r 0")[0]
429
self.assertContainsRe(result, "[+]N hello.txt\n")
431
result2 = self.run_bzr("status -SV -r 0..")[0]
432
self.assertEquals(result2, result)
434
def assertStatusContains(self, pattern):
435
"""Run status, and assert it contains the given pattern"""
436
result = self.run_bzr("status --short")[0]
437
self.assertContainsRe(result, pattern)
439
def test_kind_change_short(self):
440
tree = self.make_branch_and_tree('.')
441
self.build_tree(['file'])
443
tree.commit('added file')
445
self.build_tree(['file/'])
446
self.assertStatusContains('K file => file/')
447
tree.rename_one('file', 'directory')
448
self.assertStatusContains('RK file => directory/')
450
self.assertStatusContains('RD file => directory')
452
def test_status_illegal_revision_specifiers(self):
453
out, err = self.run_bzr('status -r 1..23..123', retcode=3)
454
self.assertContainsRe(err, 'one or two revision specifiers')
456
def test_status_no_pending(self):
457
a_tree = self.make_branch_and_tree('a')
458
self.build_tree(['a/a'])
461
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
462
self.build_tree(['b/b'])
466
self.run_bzr('merge ../b', working_dir='a')
467
out, err = self.run_bzr('status --no-pending', working_dir='a')
468
self.assertEquals(out, "added:\n b\n")
470
def test_pending_specific_files(self):
471
"""With a specific file list, pending merges are not shown."""
472
tree = self.make_branch_and_tree('tree')
473
self.build_tree_contents([('tree/a', 'content of a\n')])
475
r1_id = tree.commit('one')
476
alt = tree.bzrdir.sprout('alt').open_workingtree()
477
self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
478
alt_id = alt.commit('alt')
479
tree.merge_from_branch(alt.branch)
480
output = self.make_utf8_encoded_stringio()
481
show_tree_status(tree, to_file=output)
482
self.assertContainsRe(output.getvalue(), 'pending merge')
483
out, err = self.run_bzr('status tree/a')
484
self.assertNotContainsRe(out, 'pending merge')
487
class TestStatusEncodings(TestCaseWithTransport):
490
TestCaseWithTransport.setUp(self)
491
self.user_encoding = osutils._cached_user_encoding
492
self.stdout = sys.stdout
495
bzrlib.user_encoding = self.user_encoding
496
sys.stdout = self.stdout
497
TestCaseWithTransport.tearDown(self)
499
def make_uncommitted_tree(self):
500
"""Build a branch with uncommitted unicode named changes in the cwd."""
501
working_tree = self.make_branch_and_tree(u'.')
502
filename = u'hell\u00d8'
504
self.build_tree_contents([(filename, 'contents of hello')])
505
except UnicodeEncodeError:
506
raise TestSkipped("can't build unicode working tree in "
507
"filesystem encoding %s" % sys.getfilesystemencoding())
508
working_tree.add(filename)
511
def test_stdout_ascii(self):
512
sys.stdout = StringIO()
513
osutils._cached_user_encoding = 'ascii'
514
working_tree = self.make_uncommitted_tree()
515
stdout, stderr = self.run_bzr("status")
517
self.assertEquals(stdout, """\
522
def test_stdout_latin1(self):
523
sys.stdout = StringIO()
524
osutils._cached_user_encoding = 'latin-1'
525
working_tree = self.make_uncommitted_tree()
526
stdout, stderr = self.run_bzr('status')
528
self.assertEquals(stdout, u"""\
531
""".encode('latin-1'))