blob: dcf97558fb3f86b08e6fb00cf7d0d3a5cb8a526f [file] [log] [blame]
# Copyright 2019 The Fuchsia 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 recipe_engine.recipe_api import Property
from RECIPE_MODULES.fuchsia.swarming_retry import api as swarming_retry_api
DEPS = [
'fuchsia/status_check',
'fuchsia/swarming_retry',
'recipe_engine/json',
'recipe_engine/led',
'recipe_engine/properties',
'recipe_engine/step',
'recipe_engine/swarming',
'recipe_engine/time',
]
PROPERTIES = {
'full':
Property(
kind=bool,
default=False,
help='Whether to run six tasks or just one.',
),
'task_type':
Property(
kind=str,
default='test',
help='Type of tasks to create. Options: '
'"test", "internal_failure", "raising", "led", "triggered".',
),
'max_attempts':
Property(
kind=int,
default=2,
help='Overall max attempts.',
),
'last_task_max_attempts':
Property(
kind=int,
default=None,
help='Override the overall max attempts by setting on '
'Task.max_attempts. Only set on last task.',
),
'launch_deadline_time':
Property(
kind=float,
default=None,
help='Passed through to swarming_retry.Task.__init__().'
),
} # yapf: disable
class Task(swarming_retry_api.Task):
"""Required subclass for testing swarming_retry.
Defined inside a function because base class is inside api object.
"""
def __init__(self, initial_task_id, *args, **kwargs):
"""Construct a Task object.
Args:
initial_task_id (int or str): integer decimal value (since this needs
to be incremented but is then used as a str later this method
accepts both int and str types to minimize confusion, so long as
int(initial_task_id) works)
"""
super(Task, self).__init__(*args, **kwargs)
self._next_task_id = int(initial_task_id)
def launch(self):
kwargs = {
'index': len(self.attempts),
'task_id': str(self._next_task_id),
}
self._next_task_id += 1
# This looks funny but it's needed to ensure coverage of
# Attempt.task_ui_link.
if self._next_task_id % 2 == 0:
kwargs['host'] = 'testhost'
else:
kwargs['task_ui_link'] = ('https://testhost/task?id=%s' %
kwargs['task_id'])
attempt = self._api.swarming_retry.Attempt(**kwargs)
step = self._api.step('launch %s' % self.name, None)
step.presentation.step_summary_text = attempt.task_id
self.attempts.append(attempt)
return attempt
class InternalFailureTask(Task):
def process_result(self):
self.attempts[-1].failure_reason = 'internal failure'
class RaisingTask(Task):
def process_result(self):
raise self._api.step.StepFailure('something failed')
class LedTask(swarming_retry_api.LedTask):
def __init__(self, initial_task_id, api, **kwargs):
del initial_task_id # Unused.
super(LedTask, self).__init__(
api.led('get-build', 'builder'), api=api, **kwargs)
class TriggeredTask(swarming_retry_api.TriggeredTask):
def __init__(self, api, name, initial_task_id, **kwargs):
del initial_task_id # Unused.
dimensions = {
'pool': 'pool',
'device_type': 'device_type',
}
request = api.swarming.task_request().with_name(name)
request = request.with_slice(0, request[0].with_dimensions(**dimensions))
super(TriggeredTask, self).__init__(request, api=api, name=name, **kwargs)
# pylint: disable=invalid-name
def RunSteps(api, full, task_type, max_attempts, last_task_max_attempts,
launch_deadline_time):
task_types = {
'test': Task,
'internal_failure': InternalFailureTask,
'raising': RaisingTask,
'led': LedTask,
'triggered': TriggeredTask,
}
_create_task = task_types[task_type] # pylint: disable=invalid-name
if full:
tasks = [
_create_task(api=api, name='pass', initial_task_id=100),
_create_task(api=api, name='flake', initial_task_id=200),
_create_task(api=api, name='fail', initial_task_id=300),
_create_task(api=api, name='pass_long', initial_task_id=400),
_create_task(api=api, name='flake_long', initial_task_id=500),
_create_task(api=api, name='fail_long', initial_task_id=600),
]
else:
tasks = [
_create_task(
api=api,
name='task',
initial_task_id=100,
launch_deadline_time=launch_deadline_time)
]
if last_task_max_attempts:
tasks[-1].max_attempts = last_task_max_attempts
with api.swarming_retry.retry(tasks) as retry:
retry.run_tasks(max_attempts=max_attempts)
retry.present_tasks()
# Needed for coverage of Task.result.
_ = tasks[0].result
# Deliberately not calling retry.raise_failures() here. The __exit__
# portion of the context will call it for us.
def GenTests(api): # pylint: disable=invalid-name
test_api = api.swarming_retry
yield (
api.status_check.test('full_test', status='failure') +
api.properties(full=True) +
test_api.collect_data([
test_api.passed_task('pass', 100),
test_api.failed_task('flake', 200),
test_api.failed_task('fail', 300),
], iteration=0) +
test_api.collect_data([
test_api.passed_task('flake', 201),
test_api.failed_task('fail', 301),
], iteration=1) +
test_api.collect_data([
test_api.incomplete_task('pass_long', 400),
test_api.incomplete_task('flake_long', 500),
test_api.incomplete_task('fail_long', 600),
], iteration=2) +
test_api.collect_data([], iteration=3) +
test_api.collect_data([
test_api.passed_task('pass_long', 400),
], iteration=4) +
test_api.collect_data([
test_api.failed_task('flake_long', 500),
], iteration=5) +
test_api.collect_data([
test_api.failed_task('fail_long', 600),
], iteration=6) +
test_api.collect_data([], iteration=7) +
test_api.collect_data([], iteration=8) +
test_api.collect_data([
test_api.passed_task('flake_long', 501),
], iteration=9) +
test_api.collect_data([
test_api.failed_task('fail_long', 601),
], iteration=10)
) # yapf: disable
yield (
api.status_check.test('timeout_then_pass') +
api.properties(full=False) +
test_api.collect_data([test_api.timed_out_task('task', 100)]) +
test_api.collect_data([test_api.passed_task('task', 101)], iteration=1)
) # yapf: disable
yield (
api.status_check.test('internal_failure', status='failure') +
api.properties(full=False, task_type='internal_failure') +
test_api.collect_data([test_api.passed_task('task', 100)], iteration=0) +
test_api.collect_data([test_api.passed_task('task', 101)], iteration=1)
) # yapf: disable
yield (
api.status_check.test('raising_process_results', status='failure') +
api.properties(full=False, task_type='raising') +
test_api.collect_data([test_api.passed_task('task', 100)], iteration=0) +
test_api.collect_data([test_api.passed_task('task', 101)], iteration=1)
) # yapf: disable
yield (
api.status_check.test('led_task') +
api.properties(full=False, task_type='led') +
api.swarming_retry.led_data('task', 1, iteration=0) +
test_api.collect_data([test_api.failed_task('task', 1)], iteration=0) +
api.swarming_retry.led_data('task', 2, iteration=1) +
test_api.collect_data([test_api.passed_task('task', 2)], iteration=1)
) # yapf: disable
yield (
api.status_check.test('led_task_hardcoded_attempt') +
api.properties(full=False, task_type='led') +
api.swarming_retry.led_data('task', 1, attempt=0)
) # yapf: disable
yield (
api.status_check.test('triggered_task') +
api.properties(full=False, task_type='triggered') +
api.swarming_retry.trigger_data('task', 1) +
test_api.collect_data([test_api.passed_task('task', 1)], iteration=0)
) # yapf: disable
yield (
api.status_check.test('max_attempts_three', status='failure') +
api.properties(full=False, task_type='raising', max_attempts=3) +
test_api.collect_data([test_api.passed_task('task', 100)], iteration=0) +
test_api.collect_data([test_api.passed_task('task', 101)], iteration=1) +
test_api.collect_data([test_api.passed_task('task', 102)], iteration=2)
) # yapf: disable
yield (
api.status_check.test('last_task_max_attempts_low', status='failure') +
api.properties(full=False, task_type='raising', max_attempts=3,
last_task_max_attempts=1) +
test_api.collect_data([test_api.passed_task('task', 100)], iteration=0)
) # yapf: disable
yield (
api.status_check.test('last_task_max_attempts_high', status='failure') +
api.properties(full=False, task_type='raising', max_attempts=3,
last_task_max_attempts=5) +
test_api.collect_data([test_api.passed_task('task', 100)], iteration=0) +
test_api.collect_data([test_api.passed_task('task', 101)], iteration=1) +
test_api.collect_data([test_api.passed_task('task', 102)], iteration=2) +
test_api.collect_data([test_api.passed_task('task', 103)], iteration=3) +
test_api.collect_data([test_api.passed_task('task', 104)], iteration=4)
) # yapf: disable
yield (
api.status_check.test(
'last_task_max_attempts_high_mixed', status='failure') +
api.properties(full=True, task_type='raising', max_attempts=1,
last_task_max_attempts=5) +
# Names are misleading for this test. Sorry.
test_api.collect_data([
test_api.failed_task('pass', 100),
test_api.failed_task('flake', 200),
test_api.failed_task('fail', 300),
test_api.failed_task('pass_long', 400),
test_api.failed_task('flake_long', 500),
test_api.failed_task('fail_long', 600),
], iteration=0) +
test_api.collect_data(
[test_api.failed_task('fail_long', 601)], iteration=1) +
test_api.collect_data(
[test_api.failed_task('fail_long', 602)], iteration=2) +
test_api.collect_data(
[test_api.failed_task('fail_long', 603)], iteration=3) +
test_api.collect_data(
[test_api.failed_task('fail_long', 604)], iteration=4)
) # yapf: disable
yield (api.status_check.test('launch_deadline_time') +
# Settings such that launch_deadline_time is in the range
# (time.seed() + time.step(), time.seed() + 2 * time.step()]
# should result in the task being launched exactly once.
api.properties(
full=False, task_type='triggered', launch_deadline_time=1.0) +
api.time.seed(0) + api.time.step(0.6) +
api.swarming_retry.trigger_data('task', 1) +
test_api.collect_data([test_api.passed_task('task', 1)], iteration=0)
) # yapf: disable