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.py998
1 files changed, 537 insertions, 461 deletions
diff --git a/llvm_tools/llvm_bisection_unittest.py b/llvm_tools/llvm_bisection_unittest.py
index cc22dfa4..1e86a678 100755
--- a/llvm_tools/llvm_bisection_unittest.py
+++ b/llvm_tools/llvm_bisection_unittest.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -8,7 +8,6 @@
"""Tests for LLVM bisection."""
-from __future__ import print_function
import json
import os
@@ -25,485 +24,562 @@ import test_helpers
class LLVMBisectionTest(unittest.TestCase):
- """Unittests for LLVM bisection."""
-
- def testGetRemainingRangePassed(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.assertEqual(
- llvm_bisection.GetRemainingRange(start, end, test_tryjobs),
- expected_revisions_tuple)
-
- def testGetRemainingRangeFailedWithMissingStatus(self):
- start = 100
- end = 150
-
- test_tryjobs = [{
- 'rev': 105,
- 'status': 'good',
- 'link': 'https://some_tryjob_1_url.com'
- }, {
- 'rev': 120,
- 'status': None,
- 'link': 'https://some_tryjob_2_url.com'
- }, {
- 'rev': 140,
- 'status': 'bad',
- 'link': 'https://some_tryjob_3_url.com'
- }]
-
- with self.assertRaises(ValueError) as err:
- llvm_bisection.GetRemainingRange(start, end, test_tryjobs)
-
- 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)
-
- def testGetRemainingRangeFailedWithInvalidRange(self):
- start = 100
- end = 150
-
- test_tryjobs = [{
- 'rev': 110,
- 'status': 'bad',
- 'link': 'https://some_tryjob_1_url.com'
- }, {
- 'rev': 125,
- 'status': 'skip',
- 'link': 'https://some_tryjob_2_url.com'
- }, {
- 'rev': 140,
- 'status': 'good',
- 'link': 'https://some_tryjob_3_url.com'
- }]
-
- with self.assertRaises(AssertionError) as err:
- 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']))
-
- self.assertEqual(str(err.exception), expected_error_message)
-
- @mock.patch.object(get_llvm_hash, 'GetGitHashFrom')
- 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'
-
- revs = ['a123testhash3', 'a123testhash5']
- mock_get_git_hash.side_effect = revs
-
- git_hashes = [
- git_llvm_rev.base_llvm_revision + 3, git_llvm_rev.base_llvm_revision + 5
- ]
-
- self.assertEqual(
- llvm_bisection.GetCommitsBetween(start, end, parallel, abs_path_to_src,
- test_pending_revisions,
- test_skip_revisions),
- (git_hashes, revs))
-
- def testLoadStatusFilePassedWithExistingFile(self):
- start = 100
- end = 150
-
- test_bisect_state = {'start': start, 'end': end, 'jobs': []}
-
- # 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)
-
- self.assertEqual(
- llvm_bisection.LoadStatusFile(temp_json_file, start, end),
- test_bisect_state)
-
- def testLoadStatusFilePassedWithoutExistingFile(self):
- start = 200
- end = 250
-
- expected_bisect_state = {'start': start, 'end': end, 'jobs': []}
-
- last_tested = '/abs/path/to/file_that_does_not_exist.json'
-
- self.assertEqual(
- llvm_bisection.LoadStatusFile(last_tested, start, end),
- expected_bisect_state)
-
- @mock.patch.object(modify_a_tryjob, 'AddTryjob')
- def testBisectPassed(self, mock_add_tryjob):
-
- git_hash_list = ['a123testhash1', 'a123testhash2', 'a123testhash3']
- revisions_list = [102, 104, 106]
-
- # Simulate behavior of `AddTryjob()` when successfully launched a tryjob for
- # the updated packages.
- @test_helpers.CallCountsToMockFunctions
- def MockAddTryjob(call_count, _packages, _git_hash, _revision, _chroot_path,
- _patch_file, _extra_cls, _options, _builder, _verbose,
- _svn_revision):
-
- if call_count < 2:
- return {'rev': revisions_list[call_count], 'status': 'pending'}
-
- # Simulate an exception happened along the way when updating the
- # packages' `LLVM_NEXT_HASH`.
- if call_count == 2:
- raise ValueError('Unable to launch tryjob')
-
- assert False, 'Called `AddTryjob()` more than expected.'
-
- # Use the test function to simulate `AddTryjob()`.
- mock_add_tryjob.side_effect = MockAddTryjob
-
- start = 100
- end = 110
-
- bisection_contents = {'start': start, 'end': end, 'jobs': []}
-
- args_output = test_helpers.ArgsOutputTest()
-
- packages = ['sys-devel/llvm']
- patch_file = '/abs/path/to/PATCHES.json'
-
- # Create a temporary .JSON file to simulate a status file for bisection.
- with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
- with open(temp_json_file, 'w') as f:
- test_helpers.WritePrettyJsonFile(bisection_contents, f)
-
- # 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.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':
- start,
- 'end':
- end,
- 'jobs': [{
- 'rev': revisions_list[0],
- 'status': 'pending'
- }, {
- 'rev': revisions_list[1],
- 'status': 'pending'
- }]
- }
-
- # Verify that the launched tryjobs were added to the status file when
- # an exception happened.
- with open(temp_json_file) as f:
- json_contents = json.load(f)
-
- self.assertEqual(json_contents, expected_bisection_contents)
-
- self.assertEqual(str(err.exception), 'Unable to launch tryjob')
-
- self.assertEqual(mock_add_tryjob.call_count, 3)
-
- @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 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):
-
- start = 500
- end = 502
- cl = 1
-
- bisect_state = {
- 'start': start,
- 'end': end,
- 'jobs': [{
- 'rev': 501,
- 'status': 'bad',
- 'cl': cl
- }]
- }
-
- 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 = [], []
-
- 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
-
- self.assertEqual(
- llvm_bisection.main(args_output),
- llvm_bisection.BisectionExitStatus.BISECTION_COMPLETE.value)
-
- mock_outside_chroot.assert_called_once()
-
- mock_load_status_file.assert_called_once()
-
- mock_get_range.assert_called_once()
-
- mock_get_revision_and_hash_list.assert_called_once()
-
- 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',
- str(cl),
- ],
- stderr=subprocess.STDOUT,
- encoding='utf-8',
- ))
-
- @mock.patch.object(llvm_bisection, 'LoadStatusFile')
- @mock.patch.object(chroot, 'VerifyOutsideChroot', return_value=True)
- def testMainFailedWithInvalidRange(self, mock_outside_chroot,
- mock_load_status_file):
-
- start = 500
- end = 502
-
- bisect_state = {
- 'start': start - 1,
- 'end': end,
- }
-
- 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
+ """Unittests for LLVM bisection."""
+
+ def testGetRemainingRangePassed(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.assertEqual(
+ llvm_bisection.GetRemainingRange(start, end, test_tryjobs),
+ expected_revisions_tuple,
+ )
+
+ def testGetRemainingRangeFailedWithMissingStatus(self):
+ start = 100
+ end = 150
+
+ test_tryjobs = [
+ {
+ "rev": 105,
+ "status": "good",
+ "link": "https://some_tryjob_1_url.com",
+ },
+ {
+ "rev": 120,
+ "status": None,
+ "link": "https://some_tryjob_2_url.com",
+ },
+ {
+ "rev": 140,
+ "status": "bad",
+ "link": "https://some_tryjob_3_url.com",
+ },
+ ]
+
+ with self.assertRaises(ValueError) as err:
+ llvm_bisection.GetRemainingRange(start, end, test_tryjobs)
+
+ 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)
+
+ def testGetRemainingRangeFailedWithInvalidRange(self):
+ start = 100
+ end = 150
+
+ test_tryjobs = [
+ {
+ "rev": 110,
+ "status": "bad",
+ "link": "https://some_tryjob_1_url.com",
+ },
+ {
+ "rev": 125,
+ "status": "skip",
+ "link": "https://some_tryjob_2_url.com",
+ },
+ {
+ "rev": 140,
+ "status": "good",
+ "link": "https://some_tryjob_3_url.com",
+ },
+ ]
+
+ with self.assertRaises(AssertionError) as err:
+ 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"])
+ )
+
+ self.assertEqual(str(err.exception), expected_error_message)
+
+ @mock.patch.object(get_llvm_hash, "GetGitHashFrom")
+ 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"
+
+ revs = ["a123testhash3", "a123testhash5"]
+ mock_get_git_hash.side_effect = revs
+
+ git_hashes = [
+ git_llvm_rev.base_llvm_revision + 3,
+ git_llvm_rev.base_llvm_revision + 5,
+ ]
+
+ self.assertEqual(
+ llvm_bisection.GetCommitsBetween(
+ start,
+ end,
+ parallel,
+ abs_path_to_src,
+ test_pending_revisions,
+ test_skip_revisions,
+ ),
+ (git_hashes, revs),
+ )
+
+ def testLoadStatusFilePassedWithExistingFile(self):
+ start = 100
+ end = 150
+
+ test_bisect_state = {"start": start, "end": end, "jobs": []}
+
+ # 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)
+
+ self.assertEqual(
+ llvm_bisection.LoadStatusFile(temp_json_file, start, end),
+ test_bisect_state,
+ )
+
+ def testLoadStatusFilePassedWithoutExistingFile(self):
+ start = 200
+ end = 250
+
+ expected_bisect_state = {"start": start, "end": end, "jobs": []}
+
+ last_tested = "/abs/path/to/file_that_does_not_exist.json"
+
+ self.assertEqual(
+ llvm_bisection.LoadStatusFile(last_tested, start, end),
+ expected_bisect_state,
+ )
+
+ @mock.patch.object(modify_a_tryjob, "AddTryjob")
+ def testBisectPassed(self, mock_add_tryjob):
+
+ git_hash_list = ["a123testhash1", "a123testhash2", "a123testhash3"]
+ revisions_list = [102, 104, 106]
+
+ # Simulate behavior of `AddTryjob()` when successfully launched a tryjob for
+ # the updated packages.
+ @test_helpers.CallCountsToMockFunctions
+ def MockAddTryjob(
+ call_count,
+ _packages,
+ _git_hash,
+ _revision,
+ _chroot_path,
+ _patch_file,
+ _extra_cls,
+ _options,
+ _builder,
+ _verbose,
+ _svn_revision,
+ ):
+
+ if call_count < 2:
+ return {"rev": revisions_list[call_count], "status": "pending"}
+
+ # Simulate an exception happened along the way when updating the
+ # packages' `LLVM_NEXT_HASH`.
+ if call_count == 2:
+ raise ValueError("Unable to launch tryjob")
+
+ assert False, "Called `AddTryjob()` more than expected."
+
+ # Use the test function to simulate `AddTryjob()`.
+ mock_add_tryjob.side_effect = MockAddTryjob
+
+ start = 100
+ end = 110
+
+ bisection_contents = {"start": start, "end": end, "jobs": []}
+
+ args_output = test_helpers.ArgsOutputTest()
+
+ packages = ["sys-devel/llvm"]
+ patch_file = "/abs/path/to/PATCHES.json"
+
+ # Create a temporary .JSON file to simulate a status file for bisection.
+ with test_helpers.CreateTemporaryJsonFile() as temp_json_file:
+ with open(temp_json_file, "w") as f:
+ test_helpers.WritePrettyJsonFile(bisection_contents, f)
+
+ # 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.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": start,
+ "end": end,
+ "jobs": [
+ {"rev": revisions_list[0], "status": "pending"},
+ {"rev": revisions_list[1], "status": "pending"},
+ ],
+ }
+
+ # Verify that the launched tryjobs were added to the status file when
+ # an exception happened.
+ with open(temp_json_file) as f:
+ json_contents = json.load(f)
+
+ self.assertEqual(json_contents, expected_bisection_contents)
+
+ self.assertEqual(str(err.exception), "Unable to launch tryjob")
+
+ self.assertEqual(mock_add_tryjob.call_count, 3)
+
+ @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 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,
+ ):
+
+ start = 500
+ end = 502
+ cl = 1
+
+ bisect_state = {
+ "start": start,
+ "end": end,
+ "jobs": [{"rev": 501, "status": "bad", "cl": cl}],
+ }
+
+ 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 = [], []
+
+ 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
+
+ self.assertEqual(
+ llvm_bisection.main(args_output),
+ llvm_bisection.BisectionExitStatus.BISECTION_COMPLETE.value,
+ )
+
+ mock_outside_chroot.assert_called_once()
+
+ mock_load_status_file.assert_called_once()
+
+ mock_get_range.assert_called_once()
+
+ mock_get_revision_and_hash_list.assert_called_once()
+
+ 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",
+ str(cl),
+ ],
+ stderr=subprocess.STDOUT,
+ encoding="utf-8",
+ ),
+ )
+
+ @mock.patch.object(llvm_bisection, "LoadStatusFile")
+ @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
+ def testMainFailedWithInvalidRange(
+ self, mock_outside_chroot, mock_load_status_file
+ ):
+
+ start = 500
+ end = 502
+
+ bisect_state = {
+ "start": start - 1,
+ "end": end,
+ }
+
+ 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")
+ @mock.patch.object(chroot, "VerifyOutsideChroot", return_value=True)
+ 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_state = {
+ "start": start,
+ "end": end,
+ "jobs": [{"rev": rev, "status": "pending"}],
+ }
+
+ skip_revisions = {}
+ pending_revisions = {rev}
+
+ 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 = [], []
+
+ 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"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_get_range.assert_called_once()
+
+ mock_get_revision_and_hash_list.assert_called_once()
+
+ @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 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_state = {
+ "start": start,
+ "end": end,
+ "jobs": [{"rev": rev, "status": "pending"}],
+ }
- with self.assertRaises(ValueError) as err:
- llvm_bisection.main(args_output)
+ skip_revisions = {}
+ pending_revisions = {rev}
- 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')
+ mock_load_status_file.return_value = bisect_state
+
+ mock_get_range.return_value = (
+ start,
+ end,
+ pending_revisions,
+ skip_revisions,
+ )
- self.assertEqual(str(err.exception), error_message)
+ mock_get_revision_and_hash_list.return_value = [rev], [git_hash]
+
+ 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)
- mock_outside_chroot.assert_called_once()
+ error_message = 'Revision %d exists already in "jobs"' % rev
+ self.assertEqual(str(err.exception), error_message)
- mock_load_status_file.assert_called_once()
+ mock_outside_chroot.assert_called_once()
- @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 testMainFailedWithPendingBuilds(self, mock_outside_chroot,
- mock_load_status_file, mock_get_range,
- mock_get_revision_and_hash_list):
+ mock_load_status_file.assert_called_once()
- start = 500
- end = 502
- rev = 501
+ mock_get_range.assert_called_once()
- bisect_state = {
- 'start': start,
- 'end': end,
- 'jobs': [{
- 'rev': rev,
- 'status': 'pending'
- }]
- }
+ mock_get_revision_and_hash_list.assert_called_once()
- skip_revisions = {}
- pending_revisions = {rev}
+ @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,
+ ):
- mock_load_status_file.return_value = bisect_state
+ start = 500
+ end = 502
- mock_get_range.return_value = (start, end, pending_revisions,
- skip_revisions)
+ bisect_state = {
+ "start": start,
+ "end": end,
+ "jobs": [{"rev": 501, "status": "bad", "cl": 0}],
+ }
- mock_get_revision_and_hash_list.return_value = [], []
+ skip_revisions = {501}
+ pending_revisions = {}
- args_output = test_helpers.ArgsOutputTest()
- args_output.start_rev = start
- args_output.end_rev = end
- args_output.parallel = 3
- args_output.src_path = None
+ mock_load_status_file.return_value = bisect_state
- with self.assertRaises(ValueError) as err:
- llvm_bisection.main(args_output)
+ mock_get_range.return_value = (
+ start,
+ end,
+ pending_revisions,
+ skip_revisions,
+ )
- error_message = (f'No revisions between start {start} and end {end} to '
- 'create tryjobs\nThe following tryjobs are pending:\n'
- f'{rev}\n')
+ mock_get_revision_and_hash_list.return_value = ([], [])
- self.assertEqual(str(err.exception), error_message)
+ 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
- mock_outside_chroot.assert_called_once()
+ with self.assertRaises(subprocess.CalledProcessError) as err:
+ llvm_bisection.main(args_output)
- mock_load_status_file.assert_called_once()
+ self.assertEqual(err.exception.output, error_message)
- mock_get_range.assert_called_once()
+ mock_outside_chroot.assert_called_once()
- mock_get_revision_and_hash_list.assert_called_once()
-
- @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 testMainFailedWithDuplicateBuilds(self, mock_outside_chroot,
- mock_load_status_file, mock_get_range,
- mock_get_revision_and_hash_list):
+ mock_load_status_file.assert_called_once()
- start = 500
- end = 502
- rev = 501
- git_hash = 'a123testhash1'
+ mock_get_range.assert_called_once()
- bisect_state = {
- 'start': start,
- 'end': end,
- 'jobs': [{
- 'rev': rev,
- 'status': 'pending'
- }]
- }
- skip_revisions = {}
- pending_revisions = {rev}
-
- 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 = [rev], [git_hash]
-
- 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 = ('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_get_range.assert_called_once()
-
- mock_get_revision_and_hash_list.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__':
- unittest.main()
+if __name__ == "__main__":
+ unittest.main()