diff options
-rwxr-xr-x | llvm_tools/llvm_bisection.py | 21 | ||||
-rwxr-xr-x | llvm_tools/llvm_bisection_unittest.py | 76 |
2 files changed, 96 insertions, 1 deletions
diff --git a/llvm_tools/llvm_bisection.py b/llvm_tools/llvm_bisection.py index 37320baf..c8d694cd 100755 --- a/llvm_tools/llvm_bisection.py +++ b/llvm_tools/llvm_bisection.py @@ -13,6 +13,7 @@ import enum import errno import json import os +import subprocess import sys import chroot @@ -117,6 +118,13 @@ def GetCommandLineArgs(): help='display contents of a command to the terminal ' '(default: %(default)s)') + # Add argument for whether to display command contents to `stdout`. + parser.add_argument( + '--nocleanup', + action='store_false', + dest='cleanup', + help='Abandon CLs created for bisectoin') + args_output = parser.parse_args() assert args_output.start_rev < args_output.end_rev, ( @@ -348,6 +356,19 @@ def main(args_output): '\n'.join(str(rev) for rev in skip_revisions)) print(skip_revisions_message) + if args_output.cleanup: + # Abondon all the CLs created for bisection + gerrit = os.path.join(args_output.chroot_path, 'chromite/bin/gerrit') + for build in bisect_state['jobs']: + try: + subprocess.check_output([gerrit, 'abandon', build['cl']], + stderr=subprocess.STDOUT, + encoding='utf-8') + except subprocess.CalledProcessError as err: + # the CL may have been abandoned + if 'chromite.lib.gob_util.GOBError' not in err.output: + raise + return BisectionExitStatus.BISECTION_COMPLETE.value for rev in revisions: diff --git a/llvm_tools/llvm_bisection_unittest.py b/llvm_tools/llvm_bisection_unittest.py index 8478f82e..a40770a5 100755 --- a/llvm_tools/llvm_bisection_unittest.py +++ b/llvm_tools/llvm_bisection_unittest.py @@ -11,6 +11,8 @@ from __future__ import print_function import json +import os +import subprocess import unittest import unittest.mock as mock @@ -237,6 +239,7 @@ class LLVMBisectionTest(unittest.TestCase): 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') @@ -245,7 +248,7 @@ class LLVMBisectionTest(unittest.TestCase): @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_get_bad_llvm_hash, mock_abandon_cl): start = 500 end = 502 @@ -277,6 +280,7 @@ class LLVMBisectionTest(unittest.TestCase): 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), @@ -290,6 +294,19 @@ class LLVMBisectionTest(unittest.TestCase): 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', + 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, @@ -430,6 +447,63 @@ class LLVMBisectionTest(unittest.TestCase): 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() |