1
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2006 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
17
"""Tests of status command.
20
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
24
from bzrlib.selftest import TestCaseInTempDir
37
from bzrlib.osutils import pathjoin
25
38
from bzrlib.revisionspec import RevisionSpec
26
from bzrlib.merge import merge
27
from cStringIO import StringIO
28
from bzrlib.status import show_status
29
from bzrlib.branch import Branch
31
from bzrlib.clone import copy_branch
33
class BranchStatus(TestCaseInTempDir):
35
def test_branch_status(self):
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):
36
67
"""Test basic branch status"""
37
from cStringIO import StringIO
38
from bzrlib.status import show_status
39
from bzrlib.branch import Branch
41
b = Branch.initialize('.')
45
show_status(b, to_file=tof)
46
self.assertEquals(tof.getvalue(), "")
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)
49
76
self.build_tree(['hello.c', 'bye.c'])
50
b.add_pending_merge('pending@pending-0-0')
51
show_status(b, to_file=tof)
53
self.assertEquals(tof.readlines(),
58
' pending@pending-0-0\n'
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)
61
119
def test_branch_status_revisions(self):
62
120
"""Tests branch status with revisions"""
64
b = Branch.initialize('.')
121
wt = self.make_branch_and_tree('.')
67
123
self.build_tree(['hello.c', 'bye.c'])
70
b.commit('Test message')
126
wt.commit('Test message')
74
revs.append(RevisionSpec(0))
76
show_status(b, to_file=tof, revision=revs)
79
self.assertEquals(tof.readlines(),
128
revs = [RevisionSpec.from_string('0')]
84
137
self.build_tree(['more.c'])
86
b.commit('Another test message')
89
revs.append(RevisionSpec(1))
91
show_status(b, to_file=tof, revision=revs)
94
self.assertEquals(tof.readlines(),
99
def status_string(self, branch):
101
show_status(branch, to_file=tof)
103
return tof.getvalue()
139
wt.commit('Another test message')
141
revs.append(RevisionSpec.from_string('1'))
105
150
def test_pending(self):
106
"""Pending merges display works"""
151
"""Pending merges display works, including Unicode"""
107
152
mkdir("./branch")
108
b = Branch.initialize('./branch')
109
b.commit("Empty commit 1")
110
b_2 = copy_branch(b, './copy')
111
b.commit("Empty commit 2")
112
merge(["./branch", -1], [None, None], this_dir = './copy')
113
message = self.status_string(b_2)
114
assert (message.startswith("pending merges:\n")), message
115
assert (message.endswith("Empty commit 2\n")), message
117
b.commit("Empty commit 3 blah blah blah blah blah blah blah blah blah")
118
merge(["./branch", -1], [None, None], this_dir = './copy')
119
message = self.status_string(b_2)
120
assert (message.startswith("pending merges:\n")), message
121
assert ("Empty commit 3" in message), message
122
assert (message.endswith("...\n")), message
124
def test_branch_status_specific_files(self):
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):
125
191
"""Tests branch status with given specific files"""
126
from cStringIO import StringIO
127
from bzrlib.status import show_status
128
from bzrlib.branch import Branch
130
b = Branch.initialize('.')
192
wt = self.make_branch_and_tree('.')
132
195
self.build_tree(['directory/','directory/hello.c', 'bye.c','test.c','dir2/'])
138
show_status(b, to_file=tof)
140
self.assertEquals(tof.readlines(),
144
' directory/hello.c\n'
148
show_status(b, specific_files=['bye.c','test.c','absent.c'], to_file=tof)
150
self.assertEquals(tof.readlines(),
156
show_status(b, specific_files=['directory'], to_file=tof)
158
self.assertEquals(tof.readlines(),
160
' directory/hello.c\n'
163
show_status(b, specific_files=['dir2'], to_file=tof)
165
self.assertEquals(tof.readlines(),
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",
302
class CheckoutStatus(BranchStatus):
305
super(CheckoutStatus, self).setUp()
309
def make_branch_and_tree(self, relpath):
310
source = self.make_branch(pathjoin('..', relpath))
311
checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
312
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
313
return checkout.create_workingtree()
316
class TestStatus(TestCaseWithTransport):
318
def test_status_plain(self):
319
tree = self.make_branch_and_tree('.')
321
self.build_tree(['hello.txt'])
322
result = self.run_bzr("status")[0]
323
self.assertContainsRe(result, "unknown:\n hello.txt\n")
325
tree.add("hello.txt")
326
result = self.run_bzr("status")[0]
327
self.assertContainsRe(result, "added:\n hello.txt\n")
329
tree.commit(message="added")
330
result = self.run_bzr("status -r 0..1")[0]
331
self.assertContainsRe(result, "added:\n hello.txt\n")
333
result = self.run_bzr("status -c 1")[0]
334
self.assertContainsRe(result, "added:\n hello.txt\n")
336
self.build_tree(['world.txt'])
337
result = self.run_bzr("status -r 0")[0]
338
self.assertContainsRe(result, "added:\n hello.txt\n" \
339
"unknown:\n world.txt\n")
340
result2 = self.run_bzr("status -r 0..")[0]
341
self.assertEquals(result2, result)
343
def test_status_short(self):
344
tree = self.make_branch_and_tree('.')
346
self.build_tree(['hello.txt'])
347
result = self.run_bzr("status --short")[0]
348
self.assertContainsRe(result, "[?] hello.txt\n")
350
tree.add("hello.txt")
351
result = self.run_bzr("status --short")[0]
352
self.assertContainsRe(result, "[+]N hello.txt\n")
354
tree.commit(message="added")
355
result = self.run_bzr("status --short -r 0..1")[0]
356
self.assertContainsRe(result, "[+]N hello.txt\n")
358
self.build_tree(['world.txt'])
359
result = self.run_bzr("status --short -r 0")[0]
360
self.assertContainsRe(result, "[+]N hello.txt\n" \
362
result2 = self.run_bzr("status --short -r 0..")[0]
363
self.assertEquals(result2, result)
365
def test_status_versioned(self):
366
tree = self.make_branch_and_tree('.')
368
self.build_tree(['hello.txt'])
369
result = self.run_bzr("status --versioned")[0]
370
self.assertNotContainsRe(result, "unknown:\n hello.txt\n")
372
tree.add("hello.txt")
373
result = self.run_bzr("status --versioned")[0]
374
self.assertContainsRe(result, "added:\n hello.txt\n")
377
result = self.run_bzr("status --versioned -r 0..1")[0]
378
self.assertContainsRe(result, "added:\n hello.txt\n")
380
self.build_tree(['world.txt'])
381
result = self.run_bzr("status --versioned -r 0")[0]
382
self.assertContainsRe(result, "added:\n hello.txt\n")
383
self.assertNotContainsRe(result, "unknown:\n world.txt\n")
384
result2 = self.run_bzr("status --versioned -r 0..")[0]
385
self.assertEquals(result2, result)
387
def test_status_SV(self):
388
tree = self.make_branch_and_tree('.')
390
self.build_tree(['hello.txt'])
391
result = self.run_bzr("status -SV")[0]
392
self.assertNotContainsRe(result, "hello.txt")
394
tree.add("hello.txt")
395
result = self.run_bzr("status -SV")[0]
396
self.assertContainsRe(result, "[+]N hello.txt\n")
398
tree.commit(message="added")
399
result = self.run_bzr("status -SV -r 0..1")[0]
400
self.assertContainsRe(result, "[+]N hello.txt\n")
402
self.build_tree(['world.txt'])
403
result = self.run_bzr("status -SV -r 0")[0]
404
self.assertContainsRe(result, "[+]N hello.txt\n")
406
result2 = self.run_bzr("status -SV -r 0..")[0]
407
self.assertEquals(result2, result)
409
def assertStatusContains(self, pattern):
410
"""Run status, and assert it contains the given pattern"""
411
result = self.run_bzr("status --short")[0]
412
self.assertContainsRe(result, pattern)
414
def test_kind_change_short(self):
415
tree = self.make_branch_and_tree('.')
416
self.build_tree(['file'])
418
tree.commit('added file')
420
self.build_tree(['file/'])
421
self.assertStatusContains('K file => file/')
422
tree.rename_one('file', 'directory')
423
self.assertStatusContains('RK file => directory/')
425
self.assertStatusContains('RD file => directory')
427
def test_status_illegal_revision_specifiers(self):
428
out, err = self.run_bzr('status -r 1..23..123', retcode=3)
429
self.assertContainsRe(err, 'one or two revision specifiers')
431
def test_status_no_pending(self):
432
a_tree = self.make_branch_and_tree('a')
433
self.build_tree(['a/a'])
436
b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
437
self.build_tree(['b/b'])
441
self.run_bzr('merge ../b', working_dir='a')
442
out, err = self.run_bzr('status --no-pending', working_dir='a')
443
self.assertEquals(out, "added:\n b\n")
445
def test_pending_specific_files(self):
446
"""With a specific file list, pending merges are not shown."""
447
tree = self.make_branch_and_tree('tree')
448
self.build_tree_contents([('tree/a', 'content of a\n')])
450
r1_id = tree.commit('one')
451
alt = tree.bzrdir.sprout('alt').open_workingtree()
452
self.build_tree_contents([('alt/a', 'content of a\nfrom alt\n')])
453
alt_id = alt.commit('alt')
454
tree.merge_from_branch(alt.branch)
455
output = self.make_utf8_encoded_stringio()
456
show_tree_status(tree, to_file=output)
457
self.assertContainsRe(output.getvalue(), 'pending merges:')
458
out, err = self.run_bzr('status tree/a')
459
self.assertNotContainsRe(out, 'pending merges:')
462
class TestStatusEncodings(TestCaseWithTransport):
465
TestCaseWithTransport.setUp(self)
466
self.user_encoding = bzrlib.user_encoding
467
self.stdout = sys.stdout
470
bzrlib.user_encoding = self.user_encoding
471
sys.stdout = self.stdout
472
TestCaseWithTransport.tearDown(self)
474
def make_uncommitted_tree(self):
475
"""Build a branch with uncommitted unicode named changes in the cwd."""
476
working_tree = self.make_branch_and_tree(u'.')
477
filename = u'hell\u00d8'
479
self.build_tree_contents([(filename, 'contents of hello')])
480
except UnicodeEncodeError:
481
raise TestSkipped("can't build unicode working tree in "
482
"filesystem encoding %s" % sys.getfilesystemencoding())
483
working_tree.add(filename)
486
def test_stdout_ascii(self):
487
sys.stdout = StringIO()
488
bzrlib.user_encoding = 'ascii'
489
working_tree = self.make_uncommitted_tree()
490
stdout, stderr = self.run_bzr("status")
492
self.assertEquals(stdout, """\
497
def test_stdout_latin1(self):
498
sys.stdout = StringIO()
499
bzrlib.user_encoding = 'latin-1'
500
working_tree = self.make_uncommitted_tree()
501
stdout, stderr = self.run_bzr('status')
503
self.assertEquals(stdout, u"""\
506
""".encode('latin-1'))