blob: 2944e5d66b94bfa1e237ba16715ae789ad02dd74 [file] [log] [blame]
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test git_bisector module."""
from __future__ import print_function
import mock
import os
from chromite.cros_bisect import common
from chromite.cros_bisect import builder as builder_module
from chromite.cros_bisect import evaluator as evaluator_module
from chromite.cros_bisect import git_bisector
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import cros_test_lib
from chromite.lib import partial_mock
class GitMock(partial_mock.PartialCmdMock):
"""Mocks git.RunGit.
Usage:
mock = GitMock('/path/to/git_repository')
mock.AddRunGitResult(git_command, output=...)
# call git.RunGit(...)
"""
TARGET = 'chromite.lib.git'
ATTRS = ('RunGit', )
DEFAULT_ATTR = 'RunGit'
def __init__(self, cwd):
"""Constructor.
Args:
cwd: default git repository.
"""
super(GitMock, self).__init__()
self.cwd = cwd
def RunGit(self, *args, **kwargs):
"""Mocked RunGit."""
return self._results['RunGit'].LookupResult(args, kwargs=kwargs)
def AddRunGitResult(self, cmd, cwd=None, returncode=0, output='', error='',
kwargs=None, strict=False, side_effect=None):
"""Adds git command and results."""
cwd = self.cwd if cwd is None else cwd
result = self.CmdResult(returncode, output, error)
self._results['RunGit'].AddResultForParams(
(cwd, cmd,), result, kwargs=kwargs, side_effect=side_effect,
strict=strict)
class TestGitBisector(cros_test_lib.MockTempDirTestCase):
"""Tests GitBisector class."""
BOARD = 'samus'
TEST_NAME = 'graphics_WebGLAquarium'
METRIC = 'avg_fps_1000_fishes/summary/value'
REPORT_FILE = 'reports.json'
DUT_ADDR = '192.168.1.1'
DUT = commandline.DeviceParser(commandline.DEVICE_SCHEME_SSH)(DUT_ADDR)
GOOD_COMMIT_SHA1 = '44af5c9a5505'
GOOD_COMMIT_TIMESTAMP = 1486526594
GOOD_COMMIT_SCORE = common.Score([100])
GOOD_COMMIT_INFO = common.CommitInfo(
sha1=GOOD_COMMIT_SHA1, timestamp=GOOD_COMMIT_TIMESTAMP, title='good',
label='last-known-good ', score=GOOD_COMMIT_SCORE)
BAD_COMMIT_SHA1 = '6a163bb66c3e'
BAD_COMMIT_TIMESTAMP = 1486530021
BAD_COMMIT_SCORE = common.Score([80])
BAD_COMMIT_INFO = common.CommitInfo(
sha1=BAD_COMMIT_SHA1, timestamp=BAD_COMMIT_TIMESTAMP, title='bad',
label='last-known-bad ', score=BAD_COMMIT_SCORE)
CULPRIT_COMMIT_SHA1 = '12345abcde'
CULPRIT_COMMIT_TIMESTAMP = 1486530000
CULPRIT_COMMIT_SCORE = common.Score([81])
CULPRIT_COMMIT_INFO = common.CommitInfo(
sha1=CULPRIT_COMMIT_SHA1, timestamp=CULPRIT_COMMIT_TIMESTAMP, title='bad',
score=CULPRIT_COMMIT_SCORE)
THRESHOLD_SPLITTER = 95 # Score between good and bad, closer to good side.
THRESHOLD = 5 # Distance between good score and splitter.
REPEAT = 3
def setUp(self):
"""Sets up test case."""
self.options = cros_test_lib.EasyAttr(
base_dir=self.tempdir, board=self.BOARD, reuse_repo=True,
good=self.GOOD_COMMIT_SHA1, bad=self.BAD_COMMIT_SHA1, remote=self.DUT,
eval_repeat=self.REPEAT)
self.evaluator = evaluator_module.Evaluator(self.options)
self.builder = builder_module.Builder(self.options)
self.bisector = git_bisector.GitBisector(self.options, self.builder,
self.evaluator)
self.repo_dir = os.path.join(self.tempdir,
builder_module.Builder.DEFAULT_REPO_DIR)
def testInit(self):
"""Tests GitBisector() to expect default data members."""
self.assertEqual(self.GOOD_COMMIT_SHA1, self.bisector.good_commit)
self.assertEqual(self.BAD_COMMIT_SHA1, self.bisector.bad_commit)
self.assertEqual(self.DUT_ADDR, self.bisector.remote.raw)
self.assertEqual(self.REPEAT, self.bisector.eval_repeat)
self.assertEqual(self.builder, self.bisector.builder)
self.assertEqual(self.repo_dir, self.bisector.repo_dir)
self.assertIsNone(self.bisector.good_score)
self.assertIsNone(self.bisector.bad_score)
self.assertIsNone(self.bisector.threshold)
self.assertTrue(not self.bisector.current_commit)
def testInitMissingRequiredArgs(self):
"""Tests that GitBisector raises error when missing required arguments."""
options = cros_test_lib.EasyAttr()
with self.assertRaises(common.MissingRequiredOptionsException) as cm:
git_bisector.GitBisector(options, self.builder, self.evaluator)
exception_message = cm.exception.message
self.assertTrue('Missing command line' in exception_message)
self.assertTrue('GitBisector' in exception_message)
for arg in git_bisector.GitBisector.REQUIRED_ARGS:
self.assertTrue(arg in exception_message)
def testSetUp(self):
"""Tests that SetUp() calls builder.SetUp()."""
with mock.patch.object(builder_module.Builder, 'SetUp') as builder_mock:
self.bisector.SetUp()
builder_mock.assert_called_with()
def testGit(self):
"""Tests that Git() invokes git.RunGit() as expected."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
git_mock.AddRunGitResult(['ls'])
self.bisector.Git(['ls'])
def testUpdateCurrentCommit(self):
"""Tests that UpdateCurrentCommit() updates self.current_commit."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
git_mock.AddRunGitResult(
partial_mock.In('show'),
output='4fcbdaf6 1493010050 Add "cros bisect" command.\n')
self.bisector.UpdateCurrentCommit()
current_commit = self.bisector.current_commit
self.assertEqual('4fcbdaf6', current_commit.sha1)
self.assertEqual(1493010050, current_commit.timestamp)
self.assertEqual('Add "cros bisect" command.', current_commit.title)
def testUpdateCurrentCommitFail(self):
"""Tests UpdateCurrentCommit() when 'git show' returns nonzero."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
git_mock.AddRunGitResult(partial_mock.In('show'), returncode=1)
self.bisector.UpdateCurrentCommit()
self.assertTrue(not self.bisector.current_commit)
def testUpdateCurrentCommitFailUnexpectedOutput(self):
"""Tests UpdateCurrentCommit() when 'git show' gives unexpected output."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
git_mock.AddRunGitResult(partial_mock.In('show'), output='Not expected')
self.bisector.UpdateCurrentCommit()
self.assertTrue(not self.bisector.current_commit)
def MockOutBuildDeployEvaluateForSanityCheck(self):
"""Mocks out BuildDeployEvaluate behavior for SaintyCheck test.
It mocks UpdateCurrentCommit() to emit good and bad commits. And mocks
CheckLastEvaluate() to return good and bad commit score.
"""
commit_info_list = [
common.CommitInfo(sha1=self.GOOD_COMMIT_SHA1, title='good',
timestamp=self.GOOD_COMMIT_TIMESTAMP),
common.CommitInfo(sha1=self.BAD_COMMIT_SHA1, title='bad',
timestamp=self.BAD_COMMIT_TIMESTAMP)]
# This mock is problematic. The side effect should modify "self" when
# the member method is called.
def UpdateCurrentCommitSideEffect():
self.bisector.current_commit = commit_info_list.pop(0)
self.PatchObject(
git_bisector.GitBisector, 'UpdateCurrentCommit',
side_effect=UpdateCurrentCommitSideEffect)
self.PatchObject(
evaluator_module.Evaluator, 'CheckLastEvaluate',
side_effect=[self.GOOD_COMMIT_SCORE, self.BAD_COMMIT_SCORE])
def testGetCommitTimestamp(self):
"""Tests GetCommitTimeStamp by mocking git.RunGit."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
# Mock git result for GetCommitTimestamp.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.GOOD_COMMIT_SHA1]),
output=str(self.GOOD_COMMIT_TIMESTAMP))
self.assertEqual(self.GOOD_COMMIT_TIMESTAMP,
self.bisector.GetCommitTimestamp(self.GOOD_COMMIT_SHA1))
def testGetCommitTimestampNotFound(self):
"""Tests GetCommitTimeStamp when the commit is not found."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
nonexist_sha1 = '12345'
# Mock git result for GetCommitTimestamp.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', nonexist_sha1]),
output='commit does not exist')
self.assertIsNone(self.bisector.GetCommitTimestamp(nonexist_sha1))
def testSanityCheck(self):
"""Tests SanityCheck().
It tests by mocking out git commands called by SanityCheck().
"""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
# Mock git result for DoesCommitExistInRepo.
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.GOOD_COMMIT_SHA1]))
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.BAD_COMMIT_SHA1]))
# Mock git result for GetCommitTimestamp.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.GOOD_COMMIT_SHA1]),
output=str(self.GOOD_COMMIT_TIMESTAMP))
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.BAD_COMMIT_SHA1]),
output=str(self.BAD_COMMIT_TIMESTAMP))
git_mock.AddRunGitResult(['checkout', self.GOOD_COMMIT_SHA1])
git_mock.AddRunGitResult(['checkout', self.BAD_COMMIT_SHA1])
self.MockOutBuildDeployEvaluateForSanityCheck()
good_commit_info, bad_commit_info = self.bisector.SanityCheck()
self.assertTrue(good_commit_info == self.GOOD_COMMIT_INFO)
self.assertTrue(bad_commit_info == self.BAD_COMMIT_INFO)
def testSanityCheckGoodCommitNotExist(self):
"""Tests SanityCheck() that good commit does not exist."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
sync_to_head_mock = self.PatchObject(builder_module.Builder, 'SyncToHead')
# Mock git result for DoesCommitExistInRepo to return False.
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.GOOD_COMMIT_SHA1]))
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.BAD_COMMIT_SHA1]))
# Mock invalid result for GetCommitTimestamp to return None.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.GOOD_COMMIT_SHA1]),
output='invalid commit')
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.BAD_COMMIT_SHA1]),
output='invalid commit')
good_commit_info, bad_commit_info = self.bisector.SanityCheck()
self.assertIsNone(good_commit_info)
self.assertIsNone(bad_commit_info)
sync_to_head_mock.assert_called()
def testSanityCheckSyncToHeadWorks(self):
"""Tests SanityCheck() that good and bad commit do not exist.
As good and bad commit do not exist, it calls SyncToHead().
"""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
sync_to_head_mock = self.PatchObject(builder_module.Builder, 'SyncToHead')
# Mock git result for DoesCommitExistInRepo to return False.
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.GOOD_COMMIT_SHA1]),
returncode=128)
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.BAD_COMMIT_SHA1]),
returncode=128)
# Mock git result for GetCommitTimestamp.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.GOOD_COMMIT_SHA1]),
output=str(self.GOOD_COMMIT_TIMESTAMP))
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.BAD_COMMIT_SHA1]),
output=str(self.BAD_COMMIT_TIMESTAMP))
git_mock.AddRunGitResult(['checkout', self.GOOD_COMMIT_SHA1])
git_mock.AddRunGitResult(['checkout', self.BAD_COMMIT_SHA1])
self.MockOutBuildDeployEvaluateForSanityCheck()
good_commit_info, bad_commit_info = self.bisector.SanityCheck()
self.assertTrue(good_commit_info == self.GOOD_COMMIT_INFO)
self.assertTrue(bad_commit_info == self.BAD_COMMIT_INFO)
# After SyncToHead, found both bad and good commit.
sync_to_head_mock.assert_called()
def testSanityCheckWrongTimeOrder(self):
"""Tests SanityCheck() that good and bad commit have wrong time order."""
git_mock = self.StartPatcher(GitMock(self.repo_dir))
# Mock git result for DoesCommitExistInRepo.
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.GOOD_COMMIT_SHA1]))
git_mock.AddRunGitResult(
partial_mock.InOrder(['rev-list', self.BAD_COMMIT_SHA1]))
# Mock git result for GetCommitTimestamp, but swap timestamp.
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.GOOD_COMMIT_SHA1]),
output=str(self.BAD_COMMIT_TIMESTAMP))
git_mock.AddRunGitResult(
partial_mock.InOrder(['show', self.BAD_COMMIT_SHA1]),
output=str(self.GOOD_COMMIT_TIMESTAMP))
good_commit_info, bad_commit_info = self.bisector.SanityCheck()
self.assertIsNone(good_commit_info)
self.assertIsNone(bad_commit_info)
def testGetThresholdFromUser(self):
"""Tests GetThresholdFromUser()."""
self.bisector.good_score = self.GOOD_COMMIT_SCORE
self.bisector.bad_score = self.BAD_COMMIT_SCORE
input_mock = self.PatchObject(cros_build_lib, 'GetInput')
input_mock.return_value = self.THRESHOLD_SPLITTER
self.assertTrue(self.bisector.GetThresholdFromUser())
self.assertEqual(self.THRESHOLD, self.bisector.threshold)
def testGetThresholdFromUserOutOfBoundFail(self):
"""Tests GetThresholdFromUser() with out-of-bound input."""
self.bisector.good_score = self.GOOD_COMMIT_SCORE
self.bisector.bad_score = self.BAD_COMMIT_SCORE
input_mock = self.PatchObject(cros_build_lib, 'GetInput')
input_mock.side_effect = ['0', '1000', '-10']
self.assertFalse(self.bisector.GetThresholdFromUser())
self.assertIsNone(self.bisector.threshold)
self.assertEqual(3, input_mock.call_count)
def testGetThresholdFromUserRetrySuccess(self):
"""Tests GetThresholdFromUser() with retry."""
self.bisector.good_score = self.GOOD_COMMIT_SCORE
self.bisector.bad_score = self.BAD_COMMIT_SCORE
input_mock = self.PatchObject(cros_build_lib, 'GetInput')
input_mock.side_effect = ['not_a_number', '1000', self.THRESHOLD_SPLITTER]
self.assertTrue(self.bisector.GetThresholdFromUser())
self.assertEqual(self.THRESHOLD, self.bisector.threshold)
self.assertEqual(3, input_mock.call_count)
def PatchObjectForBuildDeployEval(self):
"""Returns list of patch objects.
The patch objects are to mock:
git_bisector.UpdateCurrentCommit()
evaluator.CheckLastEvaluate()
builder.Build()
builder.Deploy()
evaluator.Evaluate()
"""
return [
self.PatchObject(git_bisector.GitBisector, 'UpdateCurrentCommit'),
self.PatchObject(evaluator_module.Evaluator, 'CheckLastEvaluate'),
self.PatchObject(builder_module.Builder, 'Build'),
self.PatchObject(builder_module.Builder, 'Deploy'),
self.PatchObject(evaluator_module.Evaluator, 'Evaluate')]
def testBuildDeployEvalShortcutCheckLastEvaluate(self):
"""Tests BuildDeployEval() with CheckLastEvaluate() found last score."""
mocks = self.PatchObjectForBuildDeployEval()
mocks[1].return_value = self.GOOD_COMMIT_SCORE
# Inject this as UpdateCurrentCommit's side effect.
self.bisector.current_commit = common.CommitInfo(
sha1=self.GOOD_COMMIT_SHA1, timestamp=self.GOOD_COMMIT_TIMESTAMP,
title='good')
score = self.bisector.BuildDeployEval()
self.assertEqual(self.GOOD_COMMIT_SCORE, score)
self.assertEqual(self.GOOD_COMMIT_SCORE, self.bisector.current_commit.score)
for m in mocks[:2]:
getattr(m, 'assert_called')()
mocks[1].assert_called_with(self.GOOD_COMMIT_SHA1, self.REPEAT)
for m in mocks[2:]:
getattr(m, 'assert_not_called')()
def testBuildDeployEvalNoCheckLastEvaluate(self):
"""Tests BuildDeployEval() without last score."""
mocks = self.PatchObjectForBuildDeployEval()
mocks[1].return_value = common.Score()
mocks[4].return_value = self.GOOD_COMMIT_SCORE
# Inject this as UpdateCurrentCommit's side effect.
self.bisector.current_commit = common.CommitInfo(
sha1=self.GOOD_COMMIT_SHA1, timestamp=self.GOOD_COMMIT_TIMESTAMP,
title='good')
score = self.bisector.BuildDeployEval()
self.assertEqual(self.GOOD_COMMIT_SCORE, score)
self.assertEqual(self.GOOD_COMMIT_SCORE, self.bisector.current_commit.score)
for m in mocks:
getattr(m, 'assert_called')()
mocks[1].assert_called_with(self.GOOD_COMMIT_SHA1, self.REPEAT)
mocks[2].assert_called_with(self.GOOD_COMMIT_SHA1)
mocks[3].assert_called_with(self.DUT, mock.ANY, self.GOOD_COMMIT_SHA1)
mocks[4].assert_called_with(self.DUT, self.GOOD_COMMIT_SHA1, self.REPEAT)
def testBuildDeployEvalRaiseNoScore(self):
"""Tests BuildDeployEval() when Evaluate() failed to obtain score."""
mocks = self.PatchObjectForBuildDeployEval()
mocks[1].return_value = common.Score()
# Evaluate() failed to obtain score.
mocks[4].return_value = common.Score()
# Inject this as UpdateCurrentCommit's side effect.
self.bisector.current_commit = common.CommitInfo(
sha1=self.GOOD_COMMIT_SHA1, timestamp=self.GOOD_COMMIT_TIMESTAMP,
title='good')
self.assertRaises(Exception, self.bisector.BuildDeployEval)
self.assertFalse(self.bisector.current_commit.score)
for m in mocks:
getattr(m, 'assert_called')()
mocks[1].assert_called_with(self.GOOD_COMMIT_SHA1, self.REPEAT)
mocks[2].assert_called_with(self.GOOD_COMMIT_SHA1)
mocks[3].assert_called_with(self.DUT, mock.ANY, self.GOOD_COMMIT_SHA1)
mocks[4].assert_called_with(self.DUT, self.GOOD_COMMIT_SHA1, self.REPEAT)
def testBuildDeployEvalSuppressRaiseNoScore(self):
"""Tests BuildDeployEval() with raise_on_error unset."""
mocks = self.PatchObjectForBuildDeployEval()
mocks[1].return_value = common.Score()
# Evaluate() failed to obtain score.
mocks[4].return_value = common.Score()
# Inject this as UpdateCurrentCommit's side effect.
self.bisector.current_commit = common.CommitInfo(
sha1=self.GOOD_COMMIT_SHA1, timestamp=self.GOOD_COMMIT_TIMESTAMP,
title='good')
score = self.bisector.BuildDeployEval(raise_on_error=False)
self.assertFalse(score)
self.assertFalse(self.bisector.current_commit.score)
for m in mocks:
getattr(m, 'assert_called')()
mocks[1].assert_called_with(self.GOOD_COMMIT_SHA1, self.REPEAT)
mocks[2].assert_called_with(self.GOOD_COMMIT_SHA1)
mocks[3].assert_called_with(self.DUT, mock.ANY, self.GOOD_COMMIT_SHA1)
mocks[4].assert_called_with(self.DUT, self.GOOD_COMMIT_SHA1, self.REPEAT)
def testLabelBuild(self):
"""Tests LabelBuild()."""
# Inject good(100), bad(80) score and threshold.
self.bisector.good_score = self.GOOD_COMMIT_SCORE
self.bisector.bad_score = self.BAD_COMMIT_SCORE
self.bisector.threshold = self.THRESHOLD
good = 'good'
bad = 'bad'
# Worse than given bad score.
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([70])))
# Better than bad score, but not good enough.
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([85])))
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([90])))
# On the margin.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([95])))
# Better than the margin.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([98])))
# Better than given good score.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([110])))
# No score, default bad.
self.assertEqual(bad, self.bisector.LabelBuild(None))
self.assertEqual(bad, self.bisector.LabelBuild(common.Score()))
def testLabelBuildLowerIsBetter(self):
"""Tests LabelBuild() in lower-is-better condition."""
# Reverse good(80) and bad(100) score (lower is better), same threshold.
self.bisector.good_score = self.BAD_COMMIT_SCORE
self.bisector.bad_score = self.GOOD_COMMIT_SCORE
self.bisector.threshold = self.THRESHOLD
good = 'good'
bad = 'bad'
# Better than given good score.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([70])))
# Worse than good score, but still better than margin.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([80])))
self.assertEqual(good, self.bisector.LabelBuild(common.Score([82])))
# On the margin.
self.assertEqual(good, self.bisector.LabelBuild(common.Score([85])))
# Worse than the margin.
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([88])))
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([90])))
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([95])))
# Worse than given bad score.
self.assertEqual(bad, self.bisector.LabelBuild(common.Score([110])))
def testGitBisect(self):
"""Tests GitBisect()."""
git_mock = self.PatchObject(git_bisector.GitBisector, 'Git')
git_mock.return_value = cros_build_lib.CommandResult(
cmd=['git', 'bisect', 'reset'], output='We are not bisecting.',
returncode=0)
result, done = self.bisector.GitBisect(['reset'])
git_mock.assert_called_with(['bisect', 'reset'])
self.assertFalse(done)
self.assertEqual('We are not bisecting.', result.output)
self.assertListEqual(['git', 'bisect', 'reset'], result.cmd)
def testGitBisectDone(self):
"""Tests GitBisect() when culprit is found."""
git_mock = self.PatchObject(git_bisector.GitBisector, 'Git')
git_mock.return_value = cros_build_lib.CommandResult(
cmd=['git', 'bisect', 'bad'],
output='abcedf is the first bad commit\ncommit abcdef',
returncode=0)
result, done = self.bisector.GitBisect(['bad'])
git_mock.assert_called_with(['bisect', 'bad'])
self.assertTrue(done)
self.assertListEqual(['git', 'bisect', 'bad'], result.cmd)
def testRun(self):
"""Tests Run()."""
bisector_mock = self.StartPatcher(GitBisectorMock())
bisector_mock.sanity_check_result = (self.GOOD_COMMIT_INFO,
self.BAD_COMMIT_INFO)
bisector_mock.threshold = self.THRESHOLD
bisector_mock.git_bisect_args_result = [
(['reset'], (None, False)),
(['start'], (None, False)),
(['bad', self.BAD_COMMIT_SHA1], (None, False)),
(['good', self.GOOD_COMMIT_SHA1], (None, False)),
(['bad'],
(cros_build_lib.CommandResult(
cmd=['git', 'bisect', 'bad'],
output='%s is the first bad commit\ncommit %s' % (
self.CULPRIT_COMMIT_SHA1, self.CULPRIT_COMMIT_SHA1),
returncode=0),
True)), # bisect bad (assume it found the first bad commit).
(['log'], (None, False)),
(['reset'], (None, False))]
bisector_mock.build_deploy_eval_current_commit = [self.CULPRIT_COMMIT_INFO]
bisector_mock.build_deploy_eval_result = [self.CULPRIT_COMMIT_SCORE]
bisector_mock.label_build_result = ['bad']
run_result = self.bisector.Run()
self.assertTrue(bisector_mock.patched['SanityCheck'].called)
self.assertTrue(bisector_mock.patched['GetThresholdFromUser'].called)
self.assertEqual(7, bisector_mock.patched['GitBisect'].call_count)
self.assertTrue(bisector_mock.patched['BuildDeployEval'].called)
self.assertTrue(bisector_mock.patched['LabelBuild'].called)
self.assertEqual(self.CULPRIT_COMMIT_SHA1, run_result)
class GitBisectorMock(partial_mock.PartialMock):
"""Partial mock GitBisector to test GitBisector.Run()."""
TARGET = 'chromite.cros_bisect.git_bisector.GitBisector'
ATTRS = ('SanityCheck', 'GetThresholdFromUser', 'GitBisect',
'BuildDeployEval', 'LabelBuild')
def __init__(self):
super(GitBisectorMock, self).__init__()
self.sanity_check_result = (None, None)
self.threshold = None
self.git_bisect_args_result = []
self.build_deploy_eval_current_commit = []
self.build_deploy_eval_result = []
self.label_build_result = None
def SanityCheck(self, _):
return self.sanity_check_result
def GetThresholdFromUser(self, this):
this.threshold = self.threshold
return True
def GitBisect(self, _, bisect_op):
(expected_op, result) = self.git_bisect_args_result.pop(0)
assert cmp(expected_op, bisect_op) == 0
return result
def BuildDeployEval(self, this):
this.current_commit = self.build_deploy_eval_current_commit.pop(0)
return self.build_deploy_eval_result.pop(0)
def LabelBuild(self, _, unused_score):
return self.label_build_result.pop(0)