aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJian Cai <jiancai@google.com>2020-08-23 20:09:02 -0700
committerJian Cai <jiancai@google.com>2020-10-02 23:41:23 +0000
commitb9a429906b7815cd9aeca6674f26e6e2e3ba56dd (patch)
treee6f169155c15bac835b476037497fca0ffa02520
parentfb8fd5be362ec7a52dc3be33859d31b0553c1038 (diff)
downloadtoolchain-utils-b9a429906b7815cd9aeca6674f26e6e2e3ba56dd.tar.gz
llvm_tools: abandon CLs after completing LLVM bisection.
Abandon CLs created for bisection if LLVM bisection successfully found the root cause. BUG=chromium:1081457 TEST=Verified locally. Change-Id: I3702c38432fbaf87f7df62418c0b29cbe4ca722a Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/2382420 Reviewed-by: Manoj Gupta <manojgupta@chromium.org> Reviewed-by: George Burgess <gbiv@chromium.org> Tested-by: Jian Cai <jiancai@google.com>
-rwxr-xr-xllvm_tools/llvm_bisection.py21
-rwxr-xr-xllvm_tools/llvm_bisection_unittest.py76
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()