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):
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)
54
self.assertEqual(expected_lines, output_string.splitlines(True))
56
def status_string(self, wt, revision=None, short=False):
57
# use a real file rather than StringIO because it doesn't handle
59
tof = codecs.getwriter('utf-8')(TemporaryFile())
60
show_tree_status(wt, to_file=tof, revision=revision, short=short)
62
return tof.read().decode('utf-8')
64
def test_branch_status(self):
65
"""Test basic branch status"""
66
wt = self.make_branch_and_tree('.')
68
# status with no commits or files - it must
69
# work and show no output. We do this with no
70
# commits to be sure that it's not going to fail
72
self.assertStatus([], wt)
74
self.build_tree(['hello.c', 'bye.c'])
87
# add a commit to allow showing pending merges.
88
wt.commit('create a parent to allow testing merge output')
90
wt.add_parent_tree_id('pending@pending-0-0')
96
' pending@pending-0-0\n',
102
'P pending@pending-0-0\n',
106
def test_branch_status_revisions(self):
107
"""Tests branch status with revisions"""
108
wt = self.make_branch_and_tree('.')
110
self.build_tree(['hello.c', 'bye.c'])
113
wt.commit('Test message')
115
revs = [RevisionSpec.from_string('0')]
124
self.build_tree(['more.c'])
126
wt.commit('Another test message')
128
revs.append(RevisionSpec.from_string('1'))
137
def test_pending(self):
138
"""Pending merges display works, including Unicode"""
140
wt = self.make_branch_and_tree('branch')
142
wt.commit("Empty commit 1")
143
b_2_dir = b.bzrdir.sprout('./copy')
144
b_2 = b_2_dir.open_branch()
145
wt2 = b_2_dir.open_workingtree()
146
wt.commit(u"\N{TIBETAN DIGIT TWO} Empty commit 2")
147
wt2.merge_from_branch(wt.branch)
148
message = self.status_string(wt2)
149
self.assertStartsWith(message, "pending merges:\n")
150
self.assertEndsWith(message, "Empty commit 2\n")
152
# must be long to make sure we see elipsis at the end
153
wt.commit("Empty commit 3 " +
154
"blah blah blah blah " * 100)
155
wt2.merge_from_branch(wt.branch)
156
message = self.status_string(wt2)
157
self.assertStartsWith(message, "pending merges:\n")
158
self.assert_("Empty commit 3" in message)
159
self.assertEndsWith(message, "...\n")
161
def test_tree_status_ignores(self):
162
"""Tests branch status with ignores"""
163
wt = self.make_branch_and_tree('.')
164
self.run_bzr('ignore *~')
165
wt.commit('commit .bzrignore')
166
self.build_tree(['foo.c', 'foo.c~'])
177
def test_tree_status_specific_files(self):
178
"""Tests branch status with given specific files"""
179
wt = self.make_branch_and_tree('.')
182
self.build_tree(['directory/','directory/hello.c', 'bye.c','test.c','dir2/'])
191
' directory/hello.c\n'
198
'? directory/hello.c\n'
203
self.assertRaises(errors.PathsDoNotExist,
205
wt, specific_files=['bye.c','test.c','absent.c'],
209
show_tree_status(wt, specific_files=['directory'], to_file=tof)
211
self.assertEquals(tof.readlines(),
213
' directory/hello.c\n'
216
show_tree_status(wt, specific_files=['directory'], to_file=tof,
219
self.assertEquals(tof.readlines(), ['? directory/hello.c\n'])
222
show_tree_status(wt, specific_files=['dir2'], to_file=tof)
224
self.assertEquals(tof.readlines(),
229
show_tree_status(wt, specific_files=['dir2'], to_file=tof, short=True)
231
self.assertEquals(tof.readlines(), ['? dir2/\n'])
234
revs = [RevisionSpec.from_string('0'), RevisionSpec.from_string('1')]
235
show_tree_status(wt, specific_files=['test.c'], to_file=tof,
236
short=True, revision=revs)
238
self.assertEquals(tof.readlines(), ['+N test.c\n'])
240
def test_specific_files_conflicts(self):
241
tree = self.make_branch_and_tree('.')
242
self.build_tree(['dir2/'])
244
tree.commit('added dir2')
245
tree.set_conflicts(conflicts.ConflictList(
246
[conflicts.ContentsConflict('foo')]))
248
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
249
self.assertEqualDiff('', tof.getvalue())
250
tree.set_conflicts(conflicts.ConflictList(
251
[conflicts.ContentsConflict('dir2')]))
253
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
254
self.assertEqualDiff('conflicts:\n Contents conflict in dir2\n',
257
tree.set_conflicts(conflicts.ConflictList(
258
[conflicts.ContentsConflict('dir2/file1')]))
260
show_tree_status(tree, specific_files=['dir2'], to_file=tof)
261
self.assertEqualDiff('conflicts:\n Contents conflict in dir2/file1\n',
264
def test_status_nonexistent_file(self):
265
# files that don't exist in either the basis tree or working tree
266
# should give an error
267
wt = self.make_branch_and_tree('.')
268
out, err = self.run_bzr('status does-not-exist', retcode=3)
269
self.assertContainsRe(err, r'do not exist.*does-not-exist')
271
def test_status_out_of_date(self):
272
"""Simulate status of out-of-date tree after remote push"""
273
tree = self.make_branch_and_tree('.')
274
self.build_tree_contents([('a', 'foo\n')])
278
tree.commit('add test file')
279
# simulate what happens after a remote push
280
tree.set_last_revision("0")
282
# before run another commands we should unlock tree
284
out, err = self.run_bzr('status')
285
self.assertEqual("working tree is out of date, run 'bzr update'\n",
289
class CheckoutStatus(BranchStatus):
292
super(CheckoutStatus, self).setUp()
296
def make_branch_and_tree(self, relpath):
297
source = self.make_branch(pathjoin('..', relpath))
298
checkout = bzrdir.BzrDirMetaFormat1().initialize(relpath)
299
bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
300
return checkout.create_workingtree()
303
class TestStatus(TestCaseWithTransport):
305
def test_status_plain(self):
306
tree = self.make_branch_and_tree('.')
308
self.build_tree(['hello.txt'])
309
result = self.run_bzr("status")[0]
310
self.assertContainsRe(result, "unknown:\n hello.txt\n")
312
tree.add("hello.txt")
313
result = self.run_bzr("status")[0]
314
self.assertContainsRe(result, "added:\n hello.txt\n")
316
tree.commit(message="added")
317
result = self.run_bzr("status -r 0..1")[0]
318
self.assertContainsRe(result, "added:\n hello.txt\n")
320
self.build_tree(['world.txt'])
321
result = self.run_bzr("status -r 0")[0]
322
self.assertContainsRe(result, "added:\n hello.txt\n" \
323
"unknown:\n world.txt\n")
324
result2 = self.run_bzr("status -r 0..")[0]
325
self.assertEquals(result2, result)
327
def test_status_short(self):
328
tree = self.make_branch_and_tree('.')
330
self.build_tree(['hello.txt'])
331
result = self.run_bzr("status --short")[0]
332
self.assertContainsRe(result, "[?] hello.txt\n")
334
tree.add("hello.txt")
335
result = self.run_bzr("status --short")[0]
336
self.assertContainsRe(result, "[+]N hello.txt\n")
338
tree.commit(message="added")
339
result = self.run_bzr("status --short -r 0..1")[0]
340
self.assertContainsRe(result, "[+]N hello.txt\n")
342
self.build_tree(['world.txt'])
343
result = self.run_bzr("status --short -r 0")[0]
344
self.assertContainsRe(result, "[+]N hello.txt\n" \
346
result2 = self.run_bzr("status --short -r 0..")[0]
347
self.assertEquals(result2, result)
349
def test_status_versioned(self):
350
tree = self.make_branch_and_tree('.')
352
self.build_tree(['hello.txt'])
353
result = self.run_bzr("status --versioned")[0]
354
self.assertNotContainsRe(result, "unknown:\n hello.txt\n")
356
tree.add("hello.txt")
357
result = self.run_bzr("status --versioned")[0]
358
self.assertContainsRe(result, "added:\n hello.txt\n")
361
result = self.run_bzr("status --versioned -r 0..1")[0]
362
self.assertContainsRe(result, "added:\n hello.txt\n")
364
self.build_tree(['world.txt'])
365
result = self.run_bzr("status --versioned -r 0")[0]
366
self.assertContainsRe(result, "added:\n hello.txt\n")
367
self.assertNotContainsRe(result, "unknown:\n world.txt\n")
368
result2 = self.run_bzr("status --versioned -r 0..")[0]
369
self.assertEquals(result2, result)
371
def assertStatusContains(self, pattern):
372
"""Run status, and assert it contains the given pattern"""
373
result = self.run_bzr("status --short")[0]
374
self.assertContainsRe(result, pattern)
376
def test_kind_change_short(self):
377
tree = self.make_branch_and_tree('.')
378
self.build_tree(['file'])
380
tree.commit('added file')
382
self.build_tree(['file/'])
383
self.assertStatusContains('K file => file/')
384
tree.rename_one('file', 'directory')
385
self.assertStatusContains('RK file => directory/')
387
self.assertStatusContains('RD file => directory')
390
class TestStatusEncodings(TestCaseWithTransport):
393
TestCaseWithTransport.setUp(self)
394
self.user_encoding = bzrlib.user_encoding
395
self.stdout = sys.stdout
398
bzrlib.user_encoding = self.user_encoding
399
sys.stdout = self.stdout
400
TestCaseWithTransport.tearDown(self)
402
def make_uncommitted_tree(self):
403
"""Build a branch with uncommitted unicode named changes in the cwd."""
404
working_tree = self.make_branch_and_tree(u'.')
405
filename = u'hell\u00d8'
407
self.build_tree_contents([(filename, 'contents of hello')])
408
except UnicodeEncodeError:
409
raise TestSkipped("can't build unicode working tree in "
410
"filesystem encoding %s" % sys.getfilesystemencoding())
411
working_tree.add(filename)
414
def test_stdout_ascii(self):
415
sys.stdout = StringIO()
416
bzrlib.user_encoding = 'ascii'
417
working_tree = self.make_uncommitted_tree()
418
stdout, stderr = self.run_bzr("status")
420
self.assertEquals(stdout, """\
425
def test_stdout_latin1(self):
426
sys.stdout = StringIO()
427
bzrlib.user_encoding = 'latin-1'
428
working_tree = self.make_uncommitted_tree()
429
stdout, stderr = self.run_bzr('status')
431
self.assertEquals(stdout, u"""\
434
""".encode('latin-1'))