aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools/llvm_bisection_unittest.py
diff options
context:
space:
mode:
Diffstat (limited to 'llvm_tools/llvm_bisection_unittest.py')
-rwxr-xr-xllvm_tools/llvm_bisection_unittest.py624
1 files changed, 266 insertions, 358 deletions
diff --git a/llvm_tools/llvm_bisection_unittest.py b/llvm_tools/llvm_bisection_unittest.py
index e730293b..a40770a5 100755
--- a/llvm_tools/llvm_bisection_unittest.py
+++ b/llvm_tools/llvm_bisection_unittest.py
@@ -11,50 +11,57 @@
from __future__ import print_function
import json
+import os
+import subprocess
import unittest
import unittest.mock as mock
import chroot
import get_llvm_hash
+import git_llvm_rev
import llvm_bisection
import modify_a_tryjob
import test_helpers
-import update_tryjob_status
class LLVMBisectionTest(unittest.TestCase):
"""Unittests for LLVM bisection."""
- def testStartAndEndDoNotMatchJsonStartAndEnd(self):
+ def testGetRemainingRangePassed(self):
start = 100
end = 150
- json_start = 110
- json_end = 150
-
- # Verify the exception is raised when the start and end revision for LLVM
- # bisection do not match the .JSON's 'start' and 'end' values.
- with self.assertRaises(ValueError) as err:
- llvm_bisection._ValidateStartAndEndAgainstJSONStartAndEnd(
- start, end, json_start, json_end)
-
- expected_error_message = ('The start %d or the end %d version provided is '
- 'different than "start" %d or "end" %d in the '
- '.JSON file' % (start, end, json_start, json_end))
-
- self.assertEqual(str(err.exception), expected_error_message)
-
- def testStartAndEndMatchJsonStartAndEnd(self):
- start = 100
- end = 150
+ test_tryjobs = [{
+ 'rev': 110,
+ 'status': 'good',
+ 'link': 'https://some_tryjob_1_url.com'
+ }, {
+ 'rev': 120,
+ 'status': 'good',
+ 'link': 'https://some_tryjob_2_url.com'
+ }, {
+ 'rev': 130,
+ 'status': 'pending',
+ 'link': 'https://some_tryjob_3_url.com'
+ }, {
+ 'rev': 135,
+ 'status': 'skip',
+ 'link': 'https://some_tryjob_4_url.com'
+ }, {
+ 'rev': 140,
+ 'status': 'bad',
+ 'link': 'https://some_tryjob_5_url.com'
+ }]
- json_start = 100
- json_end = 150
+ # Tuple consists of the new good revision, the new bad revision, a set of
+ # 'pending' revisions, and a set of 'skip' revisions.
+ expected_revisions_tuple = 120, 140, {130}, {135}
- llvm_bisection._ValidateStartAndEndAgainstJSONStartAndEnd(
- start, end, json_start, json_end)
+ self.assertEqual(
+ llvm_bisection.GetRemainingRange(start, end, test_tryjobs),
+ expected_revisions_tuple)
- def testTryjobStatusIsMissing(self):
+ def testGetRemainingRangeFailedWithMissingStatus(self):
start = 100
end = 150
@@ -72,18 +79,14 @@ class LLVMBisectionTest(unittest.TestCase):
'link': 'https://some_tryjob_3_url.com'
}]
- # Verify the exception is raised when a tryjob does not have a value for
- # the 'status' key or the 'status' key is missing.
with self.assertRaises(ValueError) as err:
- llvm_bisection.GetStartAndEndRevision(start, end, test_tryjobs)
+ llvm_bisection.GetRemainingRange(start, end, test_tryjobs)
- expected_error_message = (
- '"status" is missing or has no value, please '
- 'go to %s and update it' % test_tryjobs[1]['link'])
+ error_message = ('"status" is missing or has no value, please '
+ 'go to %s and update it' % test_tryjobs[1]['link'])
+ self.assertEqual(str(err.exception), error_message)
- self.assertEqual(str(err.exception), expected_error_message)
-
- def testGoodRevisionGreaterThanBadRevision(self):
+ def testGetRemainingRangeFailedWithInvalidRange(self):
start = 100
end = 150
@@ -101,206 +104,68 @@ class LLVMBisectionTest(unittest.TestCase):
'link': 'https://some_tryjob_3_url.com'
}]
- # Verify the exception is raised when the new 'start' revision is greater
- # than the new 'bad' revision for bisection (i.e. bisection is broken).
with self.assertRaises(AssertionError) as err:
- llvm_bisection.GetStartAndEndRevision(start, end, test_tryjobs)
+ llvm_bisection.GetRemainingRange(start, end, test_tryjobs)
- expected_error_message = (
- 'Bisection is broken because %d (good) is >= '
- '%d (bad)' % (test_tryjobs[2]['rev'], test_tryjobs[0]['rev']))
+ expected_error_message = ('Bisection is broken because %d (good) is >= '
+ '%d (bad)' %
+ (test_tryjobs[2]['rev'], test_tryjobs[0]['rev']))
self.assertEqual(str(err.exception), expected_error_message)
- def testSuccessfullyGetNewStartAndNewEndRevision(self):
- start = 100
- end = 150
-
- test_tryjobs = [{
- 'rev': 110,
- 'status': 'good',
- 'link': 'https://some_tryjob_1_url.com'
- }, {
- 'rev': 120,
- 'status': 'good',
- 'link': 'https://some_tryjob_2_url.com'
- }, {
- 'rev': 130,
- 'status': 'pending',
- 'link': 'https://some_tryjob_3_url.com'
- }, {
- 'rev': 135,
- 'status': 'skip',
- 'link': 'https://some_tryjob_4_url.com'
- }, {
- 'rev': 140,
- 'status': 'bad',
- 'link': 'https://some_tryjob_5_url.com'
- }]
-
- # Tuple consists of the new good revision, the new bad revision, a set of
- # 'pending' revisions, and a set of 'skip' revisions.
- expected_revisions_tuple = 120, 140, {130}, {135}
-
- self.assertTupleEqual(
- llvm_bisection.GetStartAndEndRevision(start, end, test_tryjobs),
- expected_revisions_tuple)
-
@mock.patch.object(get_llvm_hash, 'GetGitHashFrom')
- def testNoRevisionsBetweenStartAndEnd(self, mock_get_git_hash):
- start = 100
- end = 110
-
- test_pending_revisions = {107}
- test_skip_revisions = {101, 102, 103, 104, 108, 109}
-
- # Simulate behavior of `GetGitHashFrom()` when the revision does not
- # exist in the LLVM source tree.
- def MockGetGitHashForRevisionRaiseException(_src_path, _revision):
- raise ValueError('Revision does not exist')
-
- mock_get_git_hash.side_effect = MockGetGitHashForRevisionRaiseException
-
- parallel = 3
-
- abs_path_to_src = '/abs/path/to/src'
-
- self.assertListEqual(
- llvm_bisection.GetRevisionsBetweenBisection(
- start, end, parallel, abs_path_to_src, test_pending_revisions,
- test_skip_revisions), [])
-
- # Assume llvm_bisection module has imported GetGitHashFrom
- @mock.patch.object(get_llvm_hash, 'GetGitHashFrom')
- def testSuccessfullyRetrievedRevisionsBetweenStartAndEnd(
- self, mock_get_git_hash):
-
- start = 100
- end = 110
-
- test_pending_revisions = set()
- test_skip_revisions = {101, 102, 103, 104, 106, 108, 109}
-
+ def testGetCommitsBetweenPassed(self, mock_get_git_hash):
+ start = git_llvm_rev.base_llvm_revision
+ end = start + 10
+ test_pending_revisions = {start + 7}
+ test_skip_revisions = {
+ start + 1, start + 2, start + 4, start + 8, start + 9
+ }
parallel = 3
-
abs_path_to_src = '/abs/path/to/src'
- # Valid revision that exist in the LLVM source tree between 'start' and
- # 'end' and were not in the 'pending' set or 'skip' set.
- expected_revisions_between_start_and_end = [105, 107]
-
- self.assertListEqual(
- llvm_bisection.GetRevisionsBetweenBisection(
- start, end, parallel, abs_path_to_src, test_pending_revisions,
- test_skip_revisions), expected_revisions_between_start_and_end)
-
- self.assertEqual(mock_get_git_hash.call_count, 2)
+ revs = ['a123testhash3', 'a123testhash5']
+ mock_get_git_hash.side_effect = revs
- # Simulate behavior of `GetGitHashFrom()` when successfully retrieved
- # a list git hashes for each revision in the revisions list.
- # Assume llvm_bisection module has imported GetGitHashFrom
- @mock.patch.object(get_llvm_hash, 'GetGitHashFrom')
- # Simulate behavior of `GetRevisionsBetweenBisection()` when successfully
- # retrieved a list of valid revisions between 'start' and 'end'.
- @mock.patch.object(llvm_bisection, 'GetRevisionsBetweenBisection')
- # Simulate behavior of `CreatTempLLVMRepo()` when successfully created a
- # worktree when a source path was not provided.
- @mock.patch.object(get_llvm_hash, 'CreateTempLLVMRepo')
- def testSuccessfullyGetRevisionsListAndHashList(
- self, mock_create_temp_llvm_repo, mock_get_revisions_between_bisection,
- mock_get_git_hash):
-
- expected_revisions_and_hash_tuple = ([102, 105, 108], [
- 'a123testhash1', 'a123testhash2', 'a123testhash3'
- ])
-
- @test_helpers.CallCountsToMockFunctions
- def MockGetGitHashForRevision(call_count, _src_path, _rev):
- # Simulate retrieving the git hash for the revision.
- if call_count < 3:
- return expected_revisions_and_hash_tuple[1][call_count]
-
- assert False, 'Called `GetGitHashFrom()` more than expected.'
-
- temp_worktree = '/abs/path/to/tmpDir'
-
- mock_create_temp_llvm_repo.return_value.__enter__.return_value.name = \
- temp_worktree
-
- # Simulate the valid revisions list.
- mock_get_revisions_between_bisection.return_value = \
- expected_revisions_and_hash_tuple[0]
-
- # Simulate behavior of `GetGitHashFrom()` by using the testing
- # function.
- mock_get_git_hash.side_effect = MockGetGitHashForRevision
+ git_hashes = [
+ git_llvm_rev.base_llvm_revision + 3, git_llvm_rev.base_llvm_revision + 5
+ ]
- start = 100
- end = 110
- parallel = 3
- src_path = None
- pending_revisions = {103, 104}
- skip_revisions = {101, 106, 107, 109}
-
- self.assertTupleEqual(
- llvm_bisection.GetRevisionsListAndHashList(
- start, end, parallel, src_path, pending_revisions, skip_revisions),
- expected_revisions_and_hash_tuple)
-
- mock_get_revisions_between_bisection.assert_called_once()
-
- self.assertEqual(mock_get_git_hash.call_count, 3)
+ self.assertEqual(
+ llvm_bisection.GetCommitsBetween(start, end, parallel, abs_path_to_src,
+ test_pending_revisions,
+ test_skip_revisions),
+ (git_hashes, revs))
- def testSuccessfullyDieWithNoRevisionsError(self):
+ def testLoadStatusFilePassedWithExistingFile(self):
start = 100
- end = 110
-
- pending_revisions = {105, 108}
- skip_revisions = {101, 102, 103, 104, 106, 107, 109}
-
- expected_no_revisions_message = ('No revisions between start %d and end '
- '%d to create tryjobs' % (start, end))
-
- expected_no_revisions_message += '\nThe following tryjobs are pending:\n' \
- + '\n'.join(str(rev) for rev in pending_revisions)
-
- expected_no_revisions_message += '\nThe following tryjobs were skipped:\n' \
- + '\n'.join(str(rev) for rev in skip_revisions)
-
- # Verify that an exception is raised when there are no revisions to launch
- # tryjobs for between 'start' and 'end' and some tryjobs are 'pending'.
- with self.assertRaises(ValueError) as err:
- llvm_bisection.DieWithNoRevisionsError(start, end, skip_revisions,
- pending_revisions)
+ end = 150
- self.assertEqual(str(err.exception), expected_no_revisions_message)
+ test_bisect_state = {'start': start, 'end': end, 'jobs': []}
- # Simulate behavior of `FindTryjobIndex()` when the index of the tryjob was
- # found.
- @mock.patch.object(update_tryjob_status, 'FindTryjobIndex', return_value=0)
- def testTryjobExistsInRevisionsToLaunch(self, mock_find_tryjob_index):
- test_existing_jobs = [{'rev': 102, 'status': 'good'}]
+ # Simulate that the status file exists.
+ with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
+ with open(temp_json_file, 'w') as f:
+ test_helpers.WritePrettyJsonFile(test_bisect_state, f)
- revision_to_launch = [102]
+ self.assertEqual(
+ llvm_bisection.LoadStatusFile(temp_json_file, start, end),
+ test_bisect_state)
- expected_revision_that_exists = 102
+ def testLoadStatusFilePassedWithoutExistingFile(self):
+ start = 200
+ end = 250
- with self.assertRaises(ValueError) as err:
- llvm_bisection.CheckForExistingTryjobsInRevisionsToLaunch(
- revision_to_launch, test_existing_jobs)
+ expected_bisect_state = {'start': start, 'end': end, 'jobs': []}
- expected_found_tryjob_index_error_message = (
- 'Revision %d exists already '
- 'in "jobs"' % expected_revision_that_exists)
+ last_tested = '/abs/path/to/file_that_does_not_exist.json'
self.assertEqual(
- str(err.exception), expected_found_tryjob_index_error_message)
-
- mock_find_tryjob_index.assert_called_once()
+ llvm_bisection.LoadStatusFile(last_tested, start, end),
+ expected_bisect_state)
@mock.patch.object(modify_a_tryjob, 'AddTryjob')
- def testSuccessfullyUpdatedStatusFileWhenExceptionIsRaised(
- self, mock_add_tryjob):
+ def testBisectPassed(self, mock_add_tryjob):
git_hash_list = ['a123testhash1', 'a123testhash2', 'a123testhash3']
revisions_list = [102, 104, 106]
@@ -343,11 +208,11 @@ class LLVMBisectionTest(unittest.TestCase):
# Verify that the status file is updated when an exception happened when
# attempting to launch a revision (i.e. progress is not lost).
with self.assertRaises(ValueError) as err:
- llvm_bisection.UpdateBisection(
- revisions_list, git_hash_list, bisection_contents, temp_json_file,
- packages, args_output.chroot_path, patch_file,
- args_output.extra_change_lists, args_output.options,
- args_output.builders, args_output.verbose)
+ llvm_bisection.Bisect(revisions_list, git_hash_list, bisection_contents,
+ temp_json_file, packages, args_output.chroot_path,
+ patch_file, args_output.extra_change_lists,
+ args_output.options, args_output.builders,
+ args_output.verbose)
expected_bisection_contents = {
'start':
@@ -368,121 +233,143 @@ class LLVMBisectionTest(unittest.TestCase):
with open(temp_json_file) as f:
json_contents = json.load(f)
- self.assertDictEqual(json_contents, expected_bisection_contents)
+ self.assertEqual(json_contents, expected_bisection_contents)
self.assertEqual(str(err.exception), 'Unable to launch tryjob')
self.assertEqual(mock_add_tryjob.call_count, 3)
- # Simulate behavior of `GetGitHashFrom()` when successfully retrieved
- # the git hash of the bad revision. Assume llvm_bisection has imported
- # GetGitHashFrom
+ @mock.patch.object(subprocess, 'check_output', return_value=None)
@mock.patch.object(
- get_llvm_hash, 'GetGitHashFrom', return_value='a123testhash4')
- def testCompletedBisectionWhenProvidedSrcPath(self, mock_get_git_hash):
- last_tested = '/some/last/tested_file.json'
+ get_llvm_hash.LLVMHash, 'GetLLVMHash', return_value='a123testhash4')
+ @mock.patch.object(llvm_bisection, 'GetCommitsBetween')
+ @mock.patch.object(llvm_bisection, 'GetRemainingRange')
+ @mock.patch.object(llvm_bisection, 'LoadStatusFile')
+ @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
+ def testMainPassed(self, mock_outside_chroot, mock_load_status_file,
+ mock_get_range, mock_get_revision_and_hash_list,
+ _mock_get_bad_llvm_hash, mock_abandon_cl):
- src_path = '/abs/path/to/src/path'
+ start = 500
+ end = 502
+ cl = 1
- # The bad revision.
- end = 150
+ bisect_state = {
+ 'start': start,
+ 'end': end,
+ 'jobs': [{
+ 'rev': 501,
+ 'status': 'bad',
+ 'cl': cl
+ }]
+ }
- llvm_bisection._NoteCompletedBisection(last_tested, src_path, end)
+ skip_revisions = {501}
+ pending_revisions = {}
- mock_get_git_hash.assert_called_once()
+ mock_load_status_file.return_value = bisect_state
- # Simulate behavior of `GetLLVMHash()` when successfully retrieved
- # the git hash of the bad revision.
- @mock.patch.object(
- get_llvm_hash.LLVMHash, 'GetLLVMHash', return_value='a123testhash5')
- def testCompletedBisectionWhenNotProvidedSrcPath(self, mock_get_git_hash):
- last_tested = '/some/last/tested_file.json'
+ mock_get_range.return_value = (start, end, pending_revisions,
+ skip_revisions)
- src_path = None
+ mock_get_revision_and_hash_list.return_value = [], []
- # The bad revision.
- end = 200
+ args_output = test_helpers.ArgsOutputTest()
+ args_output.start_rev = start
+ args_output.end_rev = end
+ args_output.parallel = 3
+ args_output.src_path = None
+ args_output.chroot_path = 'somepath'
+ args_output.cleanup = True
- llvm_bisection._NoteCompletedBisection(last_tested, src_path, end)
+ self.assertEqual(
+ llvm_bisection.main(args_output),
+ llvm_bisection.BisectionExitStatus.BISECTION_COMPLETE.value)
- mock_get_git_hash.assert_called_once()
+ mock_outside_chroot.assert_called_once()
- def testSuccessfullyLoadedStatusFile(self):
- start = 100
- end = 150
+ mock_load_status_file.assert_called_once()
- test_bisect_contents = {'start': start, 'end': end, 'jobs': []}
+ mock_get_range.assert_called_once()
- # Simulate that the status file exists.
- with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
- with open(temp_json_file, 'w') as f:
- test_helpers.WritePrettyJsonFile(test_bisect_contents, f)
+ mock_get_revision_and_hash_list.assert_called_once()
- self.assertDictEqual(
- llvm_bisection.LoadStatusFile(temp_json_file, start, end),
- test_bisect_contents)
+ mock_abandon_cl.assert_called_once()
+ self.assertEqual(
+ mock_abandon_cl.call_args,
+ mock.call(
+ [
+ os.path.join(args_output.chroot_path, 'chromite/bin/gerrit'),
+ 'abandon',
+ cl,
+ ],
+ stderr=subprocess.STDOUT,
+ encoding='utf-8',
+ ))
- def testLoadedStatusFileThatDoesNotExist(self):
- start = 200
- end = 250
+ @mock.patch.object(llvm_bisection, 'LoadStatusFile')
+ @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
+ def testMainFailedWithInvalidRange(self, mock_outside_chroot,
+ mock_load_status_file):
- expected_bisect_contents = {'start': start, 'end': end, 'jobs': []}
+ start = 500
+ end = 502
- last_tested = '/abs/path/to/file_that_does_not_exist.json'
+ bisect_state = {
+ 'start': start - 1,
+ 'end': end,
+ }
- self.assertDictEqual(
- llvm_bisection.LoadStatusFile(last_tested, start, end),
- expected_bisect_contents)
-
- # Simulate behavior of `_NoteCompletedBisection()` when there are no more
- # tryjobs to launch between start and end, so bisection is complete.
- @mock.patch.object(llvm_bisection, '_NoteCompletedBisection')
- @mock.patch.object(llvm_bisection, 'GetRevisionsListAndHashList')
- @mock.patch.object(llvm_bisection, 'GetStartAndEndRevision')
- # Simulate behavior of `_ValidateStartAndEndAgainstJSONStartAndEnd()` when
- # both start and end revisions match.
- @mock.patch.object(llvm_bisection,
- '_ValidateStartAndEndAgainstJSONStartAndEnd')
+ mock_load_status_file.return_value = bisect_state
+
+ args_output = test_helpers.ArgsOutputTest()
+ args_output.start_rev = start
+ args_output.end_rev = end
+ args_output.parallel = 3
+ args_output.src_path = None
+
+ with self.assertRaises(ValueError) as err:
+ llvm_bisection.main(args_output)
+
+ error_message = (f'The start {start} or the end {end} version provided is '
+ f'different than "start" {bisect_state["start"]} or "end" '
+ f'{bisect_state["end"]} in the .JSON file')
+
+ self.assertEqual(str(err.exception), error_message)
+
+ mock_outside_chroot.assert_called_once()
+
+ mock_load_status_file.assert_called_once()
+
+ @mock.patch.object(llvm_bisection, 'GetCommitsBetween')
+ @mock.patch.object(llvm_bisection, 'GetRemainingRange')
@mock.patch.object(llvm_bisection, 'LoadStatusFile')
- # Simulate behavior of `VerifyOutsideChroot()` when successfully invoked the
- # script outside of the chroot.
@mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
- def testSuccessfullyBisectedLLVM(
- self, mock_outside_chroot, mock_load_status_file,
- mock_validate_start_and_end, mock_get_start_and_end_revision,
- mock_get_revision_and_hash_list, mock_note_completed_bisection):
+ def testMainFailedWithPendingBuilds(self, mock_outside_chroot,
+ mock_load_status_file, mock_get_range,
+ mock_get_revision_and_hash_list):
start = 500
end = 502
+ rev = 501
- bisect_contents = {
+ bisect_state = {
'start': start,
'end': end,
'jobs': [{
- 'rev': 501,
- 'status': 'skip'
+ 'rev': rev,
+ 'status': 'pending'
}]
}
- skip_revisions = {501}
- pending_revisions = {}
+ skip_revisions = {}
+ pending_revisions = {rev}
+
+ mock_load_status_file.return_value = bisect_state
+
+ mock_get_range.return_value = (start, end, pending_revisions,
+ skip_revisions)
- # Simulate behavior of `LoadStatusFile()` when successfully loaded the
- # status file.
- mock_load_status_file.return_value = bisect_contents
-
- # Simulate behavior of `GetStartAndEndRevision()` when successfully found
- # the new start and end revision of the bisection.
- #
- # Returns new start revision, new end revision, a set of pending revisions,
- # and a set of skip revisions.
- mock_get_start_and_end_revision.return_value = (start, end,
- pending_revisions,
- skip_revisions)
-
- # Simulate behavior of `GetRevisionsListAndHashList()` when successfully
- # retrieved valid revisions (along with their git hashes) between start and
- # end (in this case, none).
mock_get_revision_and_hash_list.return_value = [], []
args_output = test_helpers.ArgsOutputTest()
@@ -491,110 +378,131 @@ class LLVMBisectionTest(unittest.TestCase):
args_output.parallel = 3
args_output.src_path = None
- self.assertEqual(
- llvm_bisection.main(args_output),
- llvm_bisection.BisectionExitStatus.BISECTION_COMPLETE.value)
+ with self.assertRaises(ValueError) as err:
+ llvm_bisection.main(args_output)
+
+ error_message = (f'No revisions between start {start} and end {end} to '
+ 'create tryjobs\nThe following tryjobs are pending:\n'
+ f'{rev}\n')
+
+ self.assertEqual(str(err.exception), error_message)
mock_outside_chroot.assert_called_once()
mock_load_status_file.assert_called_once()
- mock_validate_start_and_end.assert_called_once()
-
- mock_get_start_and_end_revision.assert_called_once()
+ mock_get_range.assert_called_once()
mock_get_revision_and_hash_list.assert_called_once()
- mock_note_completed_bisection.assert_called_once()
-
- @mock.patch.object(llvm_bisection, 'DieWithNoRevisionsError')
- # Simulate behavior of `_NoteCompletedBisection()` when there are no more
- # tryjobs to launch between start and end, so bisection is complete.
- @mock.patch.object(llvm_bisection, 'GetRevisionsListAndHashList')
- @mock.patch.object(llvm_bisection, 'GetStartAndEndRevision')
- # Simulate behavior of `_ValidateStartAndEndAgainstJSONStartAndEnd()` when
- # both start and end revisions match.
- @mock.patch.object(llvm_bisection,
- '_ValidateStartAndEndAgainstJSONStartAndEnd')
+ @mock.patch.object(llvm_bisection, 'GetCommitsBetween')
+ @mock.patch.object(llvm_bisection, 'GetRemainingRange')
@mock.patch.object(llvm_bisection, 'LoadStatusFile')
- # Simulate behavior of `VerifyOutsideChroot()` when successfully invoked the
- # script outside of the chroot.
@mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
- def testNoMoreTryjobsToLaunch(
- self, mock_outside_chroot, mock_load_status_file,
- mock_validate_start_and_end, mock_get_start_and_end_revision,
- mock_get_revision_and_hash_list, mock_die_with_no_revisions_error):
+ def testMainFailedWithDuplicateBuilds(self, mock_outside_chroot,
+ mock_load_status_file, mock_get_range,
+ mock_get_revision_and_hash_list):
start = 500
end = 502
+ rev = 501
+ git_hash = 'a123testhash1'
- bisect_contents = {
+ bisect_state = {
'start': start,
'end': end,
'jobs': [{
- 'rev': 501,
+ 'rev': rev,
'status': 'pending'
}]
}
skip_revisions = {}
- pending_revisions = {501}
-
- no_revisions_error_message = ('No more tryjobs to launch between %d and '
- '%d' % (start, end))
-
- def MockNoRevisionsErrorException(_start, _end, _skip, _pending):
- raise ValueError(no_revisions_error_message)
-
- # Simulate behavior of `LoadStatusFile()` when successfully loaded the
- # status file.
- mock_load_status_file.return_value = bisect_contents
-
- # Simulate behavior of `GetStartAndEndRevision()` when successfully found
- # the new start and end revision of the bisection.
- #
- # Returns new start revision, new end revision, a set of pending revisions,
- # and a set of skip revisions.
- mock_get_start_and_end_revision.return_value = (start, end,
- pending_revisions,
- skip_revisions)
-
- # Simulate behavior of `GetRevisionsListAndHashList()` when successfully
- # retrieved valid revisions (along with their git hashes) between start and
- # end (in this case, none).
- mock_get_revision_and_hash_list.return_value = [], []
+ pending_revisions = {rev}
+
+ mock_load_status_file.return_value = bisect_state
- # Use the test function to simulate `DieWithNoRevisionsWithError()`
- # behavior.
- mock_die_with_no_revisions_error.side_effect = MockNoRevisionsErrorException
+ mock_get_range.return_value = (start, end, pending_revisions,
+ skip_revisions)
+
+ mock_get_revision_and_hash_list.return_value = [rev], [git_hash]
- # Simulate behavior of arguments passed into the command line.
args_output = test_helpers.ArgsOutputTest()
args_output.start_rev = start
args_output.end_rev = end
args_output.parallel = 3
args_output.src_path = None
- # Verify the exception is raised when there are no more tryjobs to launch
- # between start and end when there are tryjobs that are 'pending', so
- # the actual bad revision can change when those tryjobs's 'status' are
- # updated.
with self.assertRaises(ValueError) as err:
llvm_bisection.main(args_output)
- self.assertEqual(str(err.exception), no_revisions_error_message)
+ error_message = ('Revision %d exists already in "jobs"' % rev)
+ self.assertEqual(str(err.exception), error_message)
mock_outside_chroot.assert_called_once()
mock_load_status_file.assert_called_once()
- mock_validate_start_and_end.assert_called_once()
-
- mock_get_start_and_end_revision.assert_called_once()
+ mock_get_range.assert_called_once()
mock_get_revision_and_hash_list.assert_called_once()
- mock_die_with_no_revisions_error.assert_called_once()
+ @mock.patch.object(subprocess, 'check_output', return_value=None)
+ @mock.patch.object(
+ get_llvm_hash.LLVMHash, 'GetLLVMHash', return_value='a123testhash4')
+ @mock.patch.object(llvm_bisection, 'GetCommitsBetween')
+ @mock.patch.object(llvm_bisection, 'GetRemainingRange')
+ @mock.patch.object(llvm_bisection, 'LoadStatusFile')
+ @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
+ def testMainFailedToAbandonCL(self, mock_outside_chroot,
+ mock_load_status_file, mock_get_range,
+ mock_get_revision_and_hash_list,
+ _mock_get_bad_llvm_hash, mock_abandon_cl):
+
+ start = 500
+ end = 502
+
+ bisect_state = {
+ 'start': start,
+ 'end': end,
+ 'jobs': [{
+ 'rev': 501,
+ 'status': 'bad',
+ 'cl': 0
+ }]
+ }
+
+ skip_revisions = {501}
+ pending_revisions = {}
+
+ mock_load_status_file.return_value = bisect_state
+
+ mock_get_range.return_value = (start, end, pending_revisions,
+ skip_revisions)
+
+ mock_get_revision_and_hash_list.return_value = ([], [])
+
+ error_message = 'Error message.'
+ mock_abandon_cl.side_effect = subprocess.CalledProcessError(
+ returncode=1, cmd=[], output=error_message)
+
+ args_output = test_helpers.ArgsOutputTest()
+ args_output.start_rev = start
+ args_output.end_rev = end
+ args_output.parallel = 3
+ args_output.src_path = None
+ args_output.cleanup = True
+
+ with self.assertRaises(subprocess.CalledProcessError) as err:
+ llvm_bisection.main(args_output)
+
+ self.assertEqual(err.exception.output, error_message)
+
+ mock_outside_chroot.assert_called_once()
+
+ mock_load_status_file.assert_called_once()
+
+ mock_get_range.assert_called_once()
if __name__ == '__main__':