blob: f9bde6e5be177e290d7c87dd00832ca0a47f1361 [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from . import bisect_exceptions
from . import config_validation
def perform_bisect(api, **flags):
# Try catch all the exceptions thrown in bisection so that recipe can
# post the failed job result to dashboard
try:
bisect_attempts = []
if api.m.chromium.c.TARGET_PLATFORM != 'android':
_perform_single_bisect(api, bisect_attempts, **flags)
else:
# pick an available device if targe platform is android
connected_devices = _get_connected_devices(api)
if not connected_devices:
raise api.m.step.StepFailure(
'No Android test devices are available')
for device in connected_devices:
api.m.bisect_tester_staging.device_to_test = device
try:
_perform_single_bisect(api, bisect_attempts, **flags)
break
except api.m.step.StepFailure: # pragma: no cover
# Redo the bisect job if target platform is android and bisect
# failed because the test device disconnected
current_connected_devices = _get_connected_devices(api)
if (api.m.bisect_tester_staging.device_to_test not in
current_connected_devices):
continue
else:
raise
except bisect_exceptions.InconclusiveBisectException as e:
message = 'Bisect cannot identify a culprit: ' + e.message
if bisect_attempts:
bisect_attempts[-1].aborted_reason = message
bisect_attempts[-1].print_result_debug_info()
bisect_attempts[-1].post_result()
# The message may include line breaks for listing untestable ranges. This is
# not accepted by buildbot for the purpose of providing a failure reason
message = message.replace('\n', ' ')
raise api.m.step.StepFailure(message)
except Exception as e: # pylint: disable=bare-except
if bisect_attempts:
bisect_attempts[-1].aborted_reason = e.message
bisect_attempts[-1].post_result()
raise
def _perform_single_bisect(api, bisect_attempts, **flags):
bisect_config = config_validation.from_properties(
dict(api.m.properties.get('bisect_config')))
if bisect_attempts: # pragma: no cover
bisect_config['good_revision'] = bisect_attempts[-1].lkgr.commit_hash
bisect_config['bad_revision'] = bisect_attempts[-1].fkbr.commit_hash
bisector = api.create_bisector(bisect_config, **flags)
try:
bisect_attempts.append(bisector)
with api.m.step.nest('Gathering reference values'):
_gather_reference_range(bisector)
if (not bisector.failed and bisector.check_improvement_direction() and
bisector.check_initial_confidence()):
if bisector.check_reach_adjacent_revision(
bisector.good_rev): # pragma: no cover
# Only show this step if bisect has reached adjacent revisions.
with api.m.step.nest(str('Check bisect finished on revision ' +
bisector.good_rev.revision_string())):
if bisector.check_bisect_finished(bisector.good_rev):
bisector.bisect_over = True
if not bisector.bisect_over:
_bisect_main_loop(bisector)
else:
bisector.bisect_over = True
bisector.print_result_debug_info()
bisector.post_result(halt_on_failure=True)
except bisect_exceptions.BisectException as e:
bisector.bisect_over = True
raise e
def _get_connected_devices(api):
api.m.chromium_android.device_status()
return api.m.chromium_android.devices
def _gather_reference_range(bisector): # pragma: no cover
bisector.good_rev.start_job()
bisector.bad_rev.start_job()
if bisector.good_rev.failed:
bisector.surface_result('REF_RANGE_FAIL')
bisector.failed = True
raise bisect_exceptions.InconclusiveBisectException(
'Testing the "good" revision failed: ' +
bisector.good_rev.failure_reason)
elif bisector.bad_rev.failed:
bisector.surface_result('REF_RANGE_FAIL')
bisector.failed = True
raise bisect_exceptions.InconclusiveBisectException(
'Testing the "bad" revision failed: ' +
bisector.bad_rev.failure_reason)
def _bisect_main_loop(bisector): # pragma: no cover
"""This is the main bisect loop.
It gets an evenly distributed number of revisions in the candidate range,
then it starts them in parallel and waits for them to finish.
"""
while not bisector.bisect_over:
with bisector.api.m.step.nest('Bisecting revision'):
step_result = bisector.api.m.step.active_result
revision_to_check = bisector.get_revision_to_eval()
if not revision_to_check:
bisector.bisect_over = True
break
step_result.presentation.step_text += (
bisector.api.m.test_utils.format_step_text(
[['Revision: %s' % revision_to_check.revision_string()]]))
bisector.post_result(halt_on_failure=False)
revision_to_check.start_job()
if bisector.check_reach_adjacent_revision(revision_to_check):
# Only show this step if bisect has reached adjacent revisions.
with bisector.api.m.step.nest(
str('Check bisect finished on revision ' +
revision_to_check.revision_string())):
if bisector.check_bisect_finished(revision_to_check):
bisector.bisect_over = True