1
# Copyright (C) 2007-2010 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
"Test suite for the bzr bisect plugin."
19
from cStringIO import StringIO
26
from bzrlib.bzrdir import BzrDir
27
from bzrlib.plugins import bisect
28
from bzrlib.plugins.bisect import cmds
29
from bzrlib.tests import (
31
TestCaseWithTransport,
35
class BisectTestCase(TestCaseWithTransport):
36
"""Test harness specific to the bisect plugin."""
38
def assertRevno(self, rev):
39
"""Make sure we're at the right revision."""
41
rev_contents = {1: "one", 1.1: "one dot one", 1.2: "one dot two",
42
1.3: "one dot three", 2: "two", 3: "three",
45
test_file = open("test_file")
46
content = test_file.read().strip()
47
if content != rev_contents[rev]:
48
rev_ids = dict((rev_contents[k], k) for k in rev_contents.keys())
49
found_rev = rev_ids[content]
50
raise AssertionError("expected rev %0.1f, found rev %0.1f"
56
# These tests assume a branch with five revisions, and
57
# a branch from version 1 containing three revisions
58
# merged at version 2.
60
TestCaseWithTransport.setUp(self)
62
self.tree = self.make_branch_and_tree(".")
64
test_file = open("test_file", "w")
65
test_file.write("one")
67
self.tree.add(self.tree.relpath(os.path.join(os.getcwd(),
69
test_file_append = open("test_file_append", "a")
70
test_file_append.write("one\n")
71
test_file_append.close()
72
self.tree.add(self.tree.relpath(os.path.join(os.getcwd(),
74
self.tree.commit(message = "add test files")
76
BzrDir.open(".").sprout("../temp-clone")
77
clone_bzrdir = BzrDir.open("../temp-clone")
78
clone_tree = clone_bzrdir.open_workingtree()
79
for content in ["one dot one", "one dot two", "one dot three"]:
80
test_file = open("../temp-clone/test_file", "w")
81
test_file.write(content)
83
test_file_append = open("../temp-clone/test_file_append", "a")
84
test_file_append.write(content + "\n")
85
test_file_append.close()
86
clone_tree.commit(message = "make branch test change")
87
saved_subtree_revid = clone_tree.branch.last_revision()
89
self.tree.merge_from_branch(clone_tree.branch)
90
test_file = open("test_file", "w")
91
test_file.write("two")
93
test_file_append = open("test_file_append", "a")
94
test_file_append.write("two\n")
95
test_file_append.close()
96
self.tree.commit(message = "merge external branch")
97
shutil.rmtree("../temp-clone")
99
self.subtree_rev = saved_subtree_revid
101
file_contents = ["three", "four", "five"]
102
for content in file_contents:
103
test_file = open("test_file", "w")
104
test_file.write(content)
106
test_file_append = open("test_file_append", "a")
107
test_file_append.write(content + "\n")
108
test_file_append.close()
109
self.tree.commit(message = "make test change")
112
class BisectHarnessTests(BisectTestCase):
113
"""Tests for the harness itself."""
115
def testLastRev(self):
116
"""Test that the last revision is correct."""
117
repo = self.tree.branch.repository
118
top_revtree = repo.revision_tree(self.tree.last_revision())
119
top_revtree.lock_read()
120
top_file = top_revtree.get_file(top_revtree.path2id("test_file"))
121
test_content = top_file.read().strip()
124
assert test_content == "five"
126
def testSubtreeRev(self):
127
"""Test that the last revision in a subtree is correct."""
128
repo = self.tree.branch.repository
129
sub_revtree = repo.revision_tree(self.subtree_rev)
130
sub_revtree.lock_read()
131
sub_file = sub_revtree.get_file(sub_revtree.path2id("test_file"))
132
test_content = sub_file.read().strip()
135
assert test_content == "one dot three"
138
class BisectMetaTests(BisectTestCase):
139
"""Test the metadata provided by the package."""
141
def testVersionPresent(self):
142
"""Check if version_info is available for the plugin."""
143
assert bisect.version_info
145
def testBzrVersioning(self):
146
"""Check if API is of right version."""
147
assert bisect.bzr_minimum_api <= bzrlib.api_minimum_version
148
assert bisect.bzr_minimum_api <= bzrlib.version_info[:3]
151
class BisectCurrentUnitTests(BisectTestCase):
152
"""Test the BisectCurrent class."""
154
def testShowLog(self):
155
"""Test that the log can be shown."""
156
# Not a very good test; just makes sure the code doesn't fail,
157
# not that the output makes any sense.
159
cmds.BisectCurrent().show_rev_log(out=sio)
161
def testShowLogSubtree(self):
162
"""Test that a subtree's log can be shown."""
163
current = cmds.BisectCurrent()
164
current.switch(self.subtree_rev)
166
current.show_rev_log(out=sio)
168
def testSwitchVersions(self):
169
"""Test switching versions."""
170
current = cmds.BisectCurrent()
176
"""Test resetting the working tree to a non-bisected state."""
177
current = cmds.BisectCurrent()
181
assert not os.path.exists(cmds.bisect_rev_path)
183
def testIsMergePoint(self):
184
"""Test merge point detection."""
185
current = cmds.BisectCurrent()
187
assert not current.is_merge_point()
189
assert current.is_merge_point()
192
class BisectLogUnitTests(BisectTestCase):
193
"""Test the BisectLog class."""
195
def testCreateBlank(self):
196
"""Test creation of new log."""
197
bisect_log = cmds.BisectLog()
199
assert os.path.exists(cmds.bisect_info_path)
202
"""Test loading a log."""
203
preloaded_log = open(cmds.bisect_info_path, "w")
204
preloaded_log.write("rev1 yes\nrev2 no\nrev3 yes\n")
205
preloaded_log.close()
207
bisect_log = cmds.BisectLog()
208
assert len(bisect_log._items) == 3
209
assert bisect_log._items[0] == ("rev1", "yes")
210
assert bisect_log._items[1] == ("rev2", "no")
211
assert bisect_log._items[2] == ("rev3", "yes")
214
"""Test saving the log."""
215
bisect_log = cmds.BisectLog()
216
bisect_log._items = [("rev1", "yes"), ("rev2", "no"), ("rev3", "yes")]
219
logfile = open(cmds.bisect_info_path)
220
assert logfile.read() == "rev1 yes\nrev2 no\nrev3 yes\n"
223
class BisectFuncTests(BisectTestCase):
224
"""Functional tests for the bisect plugin."""
226
def testWorkflow(self):
227
"""Run through a basic usage scenario."""
229
# Start up the bisection. When the two ends are set, we should
230
# end up in the middle.
232
self.run_bzr(['bisect', 'start'])
233
self.run_bzr(['bisect', 'yes'])
234
self.run_bzr(['bisect', 'no', '-r', '1'])
237
# Mark feature as present in the middle. Should move us
238
# halfway back between the current middle and the start.
240
self.run_bzr(['bisect', 'yes'])
243
# Mark feature as not present. Since this is only one
244
# rev back from the lowest marked revision with the feature,
245
# the process should end, with the current rev set to the
248
self.run_bzr(['bisect', 'no'])
251
# Run again. Since we're done, this should do nothing.
253
self.run_bzr(['bisect', 'no'])
256
def testWorkflowSubtree(self):
257
"""Run through a usage scenario where the offending change
260
# Similar to testWorkflow, but make sure the plugin traverses
261
# subtrees when the "final" revision is a merge point.
263
# This part is similar to testWorkflow.
265
self.run_bzr(['bisect', 'start'])
266
self.run_bzr(['bisect', 'yes'])
267
self.run_bzr(['bisect', 'no', '-r', '1'])
268
self.run_bzr(['bisect', 'yes'])
270
# Check to make sure we're where we expect to be.
274
# Now, mark the merge point revno, meaning the feature
275
# appeared at a merge point.
277
self.run_bzr(['bisect', 'yes'])
278
self.assertRevno(1.2)
280
# Continue bisecting along the subtree to the real conclusion.
282
self.run_bzr(['bisect', 'yes'])
283
self.assertRevno(1.1)
284
self.run_bzr(['bisect', 'yes'])
285
self.assertRevno(1.1)
287
# Run again. Since we're done, this should do nothing.
289
self.run_bzr(['bisect', 'yes'])
290
self.assertRevno(1.1)
293
"""Test manually moving to a different revision during the bisection."""
295
# Set up a bisection in progress.
297
self.run_bzr(['bisect', 'start'])
298
self.run_bzr(['bisect', 'yes'])
299
self.run_bzr(['bisect', 'no', '-r', '1'])
303
self.run_bzr(['bisect', 'move', '-r', '2'])
307
"""Test resetting the tree."""
309
# Set up a bisection in progress.
311
self.run_bzr(['bisect', 'start'])
312
self.run_bzr(['bisect', 'yes'])
313
self.run_bzr(['bisect', 'no', '-r', '1'])
314
self.run_bzr(['bisect', 'yes'])
318
self.run_bzr(['bisect', 'reset'])
321
# Check that reset doesn't do anything unless there's a
322
# bisection in progress.
324
test_file = open("test_file", "w")
325
test_file.write("keep me")
328
out, err = self.run_bzr(['bisect', 'reset'], retcode=3)
329
self.assert_("No bisection in progress." in err)
331
test_file = open("test_file")
332
content = test_file.read().strip()
334
self.failUnless(content == "keep me")
337
"""Test saving the current bisection state, and re-loading it."""
339
# Set up a bisection in progress.
341
self.run_bzr(['bisect', 'start'])
342
self.run_bzr(['bisect', 'yes'])
343
self.run_bzr(['bisect', 'no', '-r', '1'])
344
self.run_bzr(['bisect', 'yes'])
348
self.run_bzr(['bisect', 'log', '-o', 'bisect_log'])
352
self.run_bzr(['bisect', 'reset'])
356
self.run_bzr(['bisect', 'replay', 'bisect_log'])
359
# Mark another state, and see if the bisect moves in the
362
self.run_bzr(['bisect', 'no'])
365
def testRunScript(self):
366
"""Make a test script and run it."""
367
test_script = open("test_script", "w")
368
test_script.write("#!/bin/sh\n"
369
"grep -q '^four' test_file_append\n")
371
os.chmod("test_script", stat.S_IRWXU)
372
self.run_bzr(['bisect', 'start'])
373
self.run_bzr(['bisect', 'yes'])
374
self.run_bzr(['bisect', 'no', '-r', '1'])
375
self.run_bzr(['bisect', 'run', './test_script'])
378
def testRunScriptMergePoint(self):
379
"""Make a test script and run it."""
380
if sys.platform == "win32":
381
raise TestSkipped("Unable to run shell script on windows")
382
test_script = open("test_script", "w")
383
test_script.write("#!/bin/sh\n"
384
"grep -q '^two' test_file_append\n")
386
os.chmod("test_script", stat.S_IRWXU)
387
self.run_bzr(['bisect', 'start'])
388
self.run_bzr(['bisect', 'yes'])
389
self.run_bzr(['bisect', 'no', '-r', '1'])
390
self.run_bzr(['bisect', 'run', './test_script'])
393
except AssertionError:
395
("bisect does not drill down into merge commits: "
396
"https://bugs.launchpad.net/bzr-bisect/+bug/539937")
398
def testRunScriptSubtree(self):
399
"""Make a test script and run it."""
400
if sys.platform == "win32":
401
raise TestSkipped("Unable to run shell script on windows")
402
test_script = open("test_script", "w")
403
test_script.write("#!/bin/sh\n"
404
"grep -q '^one dot two' test_file_append\n")
406
os.chmod("test_script", stat.S_IRWXU)
407
self.run_bzr(['bisect', 'start'])
408
self.run_bzr(['bisect', 'yes'])
409
self.run_bzr(['bisect', 'no', '-r', '1'])
410
self.run_bzr(['bisect', 'run', './test_script'])
412
self.assertRevno(1.2)
413
except AssertionError:
415
("bisect does not drill down into merge commits: "
416
"https://bugs.launchpad.net/bzr-bisect/+bug/539937")