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
37
from bzrlib.osutils import pathjoin
38
from bzrlib.revisionspec import RevisionSpec
39
from bzrlib.status import show_tree_status
40
from bzrlib.tests import TestCaseWithTransport, TestSkipped
41
from bzrlib.workingtree import WorkingTree
44
class BranchStatus(TestCaseWithTransport):
46
def assertStatus(self, expected_lines, working_tree,
47
revision=None, short=False, pending=True):
48
"""Run status in working_tree and look for output.
50
:param expected_lines: The lines to look for.
51
:param working_tree: The tree to run status in.
53
output_string = self.status_string(working_tree, revision, short,
55
self.assertEqual(expected_lines, output_string.splitlines(True))
57
def status_string(self, wt, revision=None, short=False, pending=True):
58
# use a real file rather than StringIO because it doesn't handle
60
tof = codecs.getwriter('utf-8')(TemporaryFile())
61
show_tree_status(wt, to_file=tof, revision=revision, short=short,
64
return tof.read().decode('utf-8')
66
def test_branch_status(self):
67
"""Test basic branch status"""
68
wt = self.make_branch_and_tree('.')
70
# status with no commits or files - it must
71
# work and show no output. We do this with no
72
# commits to be sure that it's not going to fail
74
self.assertStatus([], wt)
76
self.build_tree(['hello.c', 'bye.c'])
89
# add a commit to allow showing pending merges.
90
wt.commit('create a parent to allow testing merge output')
92
wt.add_parent_tree_id('pending@pending-0-0')
98
' (ghost) pending@pending-0-0\n',
104
'P (ghost) pending@pending-0-0\n',
117
wt, short=True, pending=False)
119
def test_branch_status_revisions(self):
120
"""Tests branch status with revisions"""
121
wt = self.make_branch_and_tree('.')
123
self.build_tree(['hello.c', 'bye.c'])
126
wt.commit('Test message')
128
revs = [RevisionSpec.from_string('0')]
137
self.build_tree(['more.c'])
139
wt.commit('Another test message')
141
revs.append(RevisionSpec.from_string('1'))
150
def test_pending(self):
151
"""Pending merges display works, including Unicode"""
153
wt = self.make_branch_and_tree('branch')
155
wt.commit("Empty commit 1")
156
b_2_dir = b.bzrdir.sprout('./copy')
157
b_2 = b_2_dir.open_branch()
158
wt2 = b_2_dir.open_workingtree()
159
wt.commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
160
wt2.merge_from_branch(wt.branch)
161
message = self.status_string(wt2)
162
self.assertStartsWith(message, "pending merges:\n")
163
self.assertEndsWith(message, "Empty commit 2\n")
165
# must be long to make sure we see elipsis at the end
166
wt.commit("Empty commit 3 " +
167
"blah blah blah blah " * 100)
168
wt2.merge_from_branch(wt.branch)
169
message = self.status_string(wt2)
170
self.assertStartsWith(message, "pending merges:\n")
171
self.assert_("Empty commit 3" in message)
172
self.assertEndsWith(message, "...\n")
174
def test_tree_status_ignores(self):
175
"""Tests branch status with ignores"""
176
wt = self.make_branch_and_tree('.')
177
self.run_bzr('ignore *~')
178
wt.commit('commit .bzrignore')
179
self.build_tree(['foo.c', 'foo.c~'])
190
def test_tree_status_specific_files(self):
191
"""Tests branch status with given specific files"""
192
wt = self.make_branch_and_tree('.')
195
self.build_tree(['directory/','directory/hello.c', 'bye.c','test.c','dir2/'])
204
' directory/hello.c\n'
211
'? directory/hello.c\n'
216
self.assertRaises(errors.PathsDoNotExist,
218
wt, specific_files=['bye.c','test.c','absent.c'],
222
show_tree_status(wt, specific_files=['directory'], to_file=tof)
224
self.assertEquals(tof.readlines(),
226
' directory/hello.c\n'
229
show_tree_status(wt, specific_files=['directory'], to_file=tof,
232
self.assertEquals(tof.readlines(), ['? directory/hello.c\n'])
235
show_tree_status(wt, specific_files=['dir2'], to_file=tof)
237
self.assertEquals(tof.readlines(),
242
show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
244
self.assertEquals(tof.readlines(), ['? dir2/\n'])
247
revs = [RevisionSpec.from_string('0'), RevisionSpec.from_string('1')]
248
show_tree_status(wt, specific_files=['test.c'], to_file=tof,
249
short=True, revision=revs)
251
self.assertEquals(tof.readlines(), ['+N test.c\n'])
253
def test_specific_files_conflicts(self):
254
tree = self.make_branch_and_tree('.')
255
self.build_tree(['dir2/'])
257
tree.commit('added dir2')
258
tree.set_conflicts(conflicts.ConflictList(
259
[conflicts.ContentsConflict('foo')]))
261
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
262
self.assertEqualDiff('', tof.getvalue())
263
tree.set_conflicts(conflicts.ConflictList(
264
[conflicts.ContentsConflict('dir2')]))
266
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
267
self.assertEqualDiff('conflicts:\n Contents conflict in dir2\n',
270
tree.set_conflicts(conflicts.ConflictList(
271
[conflicts.ContentsConflict('dir2/file1')]))
273
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
274
self.assertEqualDiff('conflicts:\n Contents conflict in dir2/file1\n',
277
def test_status_nonexistent_file(self):
278
# files that don't exist in either the basis tree or working tree
279
# 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')
284
def test_status_out_of_date(self):
285
"""Simulate status of out-of-date tree after remote push"""
286
tree = self.make_branch_and_tree('.')
287
self.build_tree_contents([('a', 'foo\n')])
291
tree.commit('add test file')
292
# simulate what happens after a remote push
293
tree.set_last_revision("0")
295
# before run another commands we should unlock tree
297
out, err = self.run_bzr('status')
298
self.assertEqual("working tree is out of date, run 'bzr update'\n",
301
def test_status_write_lock(self):
302
"""Test that status works without fetching history and
305
See https://bugs.launchpad.net/bzr/+bug/149270
308
wt = self.make_branch_and_tree('branch1')
310
wt.commit('Empty commit 1')
311
wt2 = b.bzrdir.sprout('branch2').open_workingtree()
312
wt2.commit('Empty commit 2')
313
out, err = self.run_bzr('status branch1 -rbranch:branch2')
314
self.assertEqual('', out)
317
class CheckoutStatus(BranchStatus):
320
super(CheckoutStatus, self).setUp()
324
def make_branch_and_tree(self, relpath):
325
source = self.make_branch(pathjoin('..', relpath))
326
checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
327
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
328
return checkout.create_workingtree()
331
class TestStatus(TestCaseWithTransport):
333
def test_status_plain(self):
334
tree = self.make_branch_and_tree('.')
336
self.build_tree(['hello.txt'])
337
result = self.run_bzr("status")[0]
338
self.assertContainsRe(result, "unknown:\n hello.txt\n")
340
tree.add("hello.txt")
341
result = self.run_bzr("status")[0]
342
self.assertContainsRe(result, "added:\n hello.txt\n")
344
tree.commit(message="added")
345
result = self.run_bzr("status -r 0..1")[0]
346
self.assertContainsRe(result, "added:\n hello.txt\n")
348
result = self.run_bzr("status -c 1")[0]
349
self.assertContainsRe(result, "added:\n hello.txt\n")
351
self.build_tree(['world.txt'])
352
result = self.run_bzr("status -r 0")[0]
353
self.assertContainsRe(result, "added:\n hello.txt\n" \
354
"unknown:\n world.txt\n")
355
result2 = self.run_bzr("status -r 0..")[0]
356
self.assertEquals(result2, result)
358
def test_status_short(self):
359
tree = self.make_branch_and_tree('.')
361
self.build_tree(['hello.txt'])
362
result = self.run_bzr("status --short")[0]
363
self.assertContainsRe(result, "[?] hello.txt\n")
365
tree.add("hello.txt")
366
result = self.run_bzr("status --short")[0]
367
self.assertContainsRe(result, "[+]N hello.txt\n")
369
tree.commit(message="added")
370
result = self.run_bzr("status --short -r 0..1")[0]
371
self.assertContainsRe(result, "[+]N hello.txt\n")
373
self.build_tree(['world.txt'])
374
result = self.run_bzr("status --short -r 0")[0]
375
self.assertContainsRe(result, "[+]N hello.txt\n" \
377
result2 = self.run_bzr("status --short -r 0..")[0]
378
self.assertEquals(result2, result)
380
def test_status_versioned(self):
381
tree = self.make_branch_and_tree('.')
383
self.build_tree(['hello.txt'])
384
result = self.run_bzr("status --versioned")[0]
385
self.assertNotContainsRe(result, "unknown:\n hello.txt\n")
387
tree.add("hello.txt")
388
result = self.run_bzr("status --versioned")[0]
389
self.assertContainsRe(result, "added:\n hello.txt\n")
392
result = self.run_bzr("status --versioned -r 0..1")[0]
393
self.assertContainsRe(result, "added:\n hello.txt\n")
395
self.build_tree(['world.txt'])
396
result = self.run_bzr("status --versioned -r 0")[0]
397
self.assertContainsRe(result, "added:\n hello.txt\n")
398
self.assertNotContainsRe(result, "unknown:\n world.txt\n")
399
result2 = self.run_bzr("status --versioned -r 0..")[0]
400
self.assertEquals(result2, result)
402
def test_status_SV(self):
403
tree = self.make_branch_and_tree('.')
405
self.build_tree(['hello.txt'])
406
result = self.run_bzr("status -SV")[0]
407
self.assertNotContainsRe(result, "hello.txt")
409
tree.add("hello.txt")
410
result = self.run_bzr("status -SV")[0]
411
self.assertContainsRe(result, "[+]N hello.txt\n")
413
tree.commit(message="added")
414
result = self.run_bzr("status -SV -r 0..1")[0]
415
self.assertContainsRe(result, "[+]N hello.txt\n")
417
self.build_tree(['world.txt'])
418
result = self.run_bzr("status -SV -r 0")[0]
419
self.assertContainsRe(result, "[+]N hello.txt\n")
421
result2 = self.run_bzr("status -SV -r 0..")[0]
422
self.assertEquals(result2, result)
424
def assertStatusContains(self, pattern):
425
"""Run status, and assert it contains the given pattern"""
426
result = self.run_bzr("status --short")[0]
427
self.assertContainsRe(result, pattern)
429
def test_kind_change_short(self):
430
tree = self.make_branch_and_tree('.')
431
self.build_tree(['file'])
433
tree.commit('added file')
435
self.build_tree(['file/'])
436
self.assertStatusContains('K file => file/')
437
tree.rename_one('file', 'directory')
438
self.assertStatusContains('RK file => directory/')
440
self.assertStatusContains('RD file => directory')
442
def test_status_illegal_revision_specifiers(self):
443
out, err = self.run_bzr('status -r 1..23..123', retcode=3)
444
self.assertContainsRe(err, 'one or two revision specifiers')
446
def test_status_no_pending(self):
447
a_tree = self.make_branch_and_tree('a')
448
self.build_tree(['a/a'])
451
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
452
self.build_tree(['b/b'])
456
self.run_bzr('merge ../b', working_dir='a')
457
out, err = self.run_bzr('status --no-pending', working_dir='a')
458
self.assertEquals(out, "added:\n b\n")
460
def test_pending_specific_files(self):
461
"""With a specific file list, pending merges are not shown."""
462
tree = self.make_branch_and_tree('tree')
463
self.build_tree_contents([('tree/a', 'content of a\n')])
465
r1_id = tree.commit('one')
466
alt = tree.bzrdir.sprout('alt').open_workingtree()
467
self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
468
alt_id = alt.commit('alt')
469
tree.merge_from_branch(alt.branch)
470
output = self.make_utf8_encoded_stringio()
471
show_tree_status(tree, to_file=output)
472
self.assertContainsRe(output.getvalue(), 'pending merges:')
473
out, err = self.run_bzr('status tree/a')
474
self.assertNotContainsRe(out, 'pending merges:')
477
class TestStatusEncodings(TestCaseWithTransport):
480
TestCaseWithTransport.setUp(self)
481
self.user_encoding = bzrlib.user_encoding
482
self.stdout = sys.stdout
485
bzrlib.user_encoding = self.user_encoding
486
sys.stdout = self.stdout
487
TestCaseWithTransport.tearDown(self)
489
def make_uncommitted_tree(self):
490
"""Build a branch with uncommitted unicode named changes in the cwd."""
491
working_tree = self.make_branch_and_tree(u'.')
492
filename = u'hell\u00d8'
494
self.build_tree_contents([(filename, 'contents of hello')])
495
except UnicodeEncodeError:
496
raise TestSkipped("can't build unicode working tree in "
497
"filesystem encoding %s" % sys.getfilesystemencoding())
498
working_tree.add(filename)
501
def test_stdout_ascii(self):
502
sys.stdout = StringIO()
503
bzrlib.user_encoding = 'ascii'
504
working_tree = self.make_uncommitted_tree()
505
stdout, stderr = self.run_bzr("status")
507
self.assertEquals(stdout, """\
512
def test_stdout_latin1(self):
513
sys.stdout = StringIO()
514
bzrlib.user_encoding = 'latin-1'
515
working_tree = self.make_uncommitted_tree()
516
stdout, stderr = self.run_bzr('status')
518
self.assertEquals(stdout, u"""\
521
""".encode('latin-1'))