blob: 222ac662858aa667990421c42f071e50d2b6ceb6 [file] [log] [blame]
# Copyright 2015 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import datetime
from recipe_engine import recipe_api
from recipe_engine.post_process import StepSuccess, StepFailure, StepException
DEPS = [
'context',
'step',
]
@recipe_api.ignore_warnings('recipe_engine/STEP_NEST_PRESENTATION_DEPRECATED')
def RunSteps(api):
# Nest all steps below this.
with api.step.nest('complicated thing'):
with api.step.nest('first part'):
api.step('wait a bit', ['sleep', '1'])
# Outer nested step's status gets the worst child's status by default.
with api.step.nest('inherit status'):
with api.step.nest('inner step') as inner_step_presentation:
inner_step_presentation.step_text += 'Hey!'
inner_step_presentation.status = api.step.EXCEPTION
# But, you could also pick the last status.
with api.step.nest('last status', status='last'):
with api.step.nest('failpants') as failpants_presentation:
failpants_presentation.status = api.step.EXCEPTION
api.step('everything OK', ['echo', 'hi'])
# DEPRECATED; DO NOT USE
with api.step.nest('extra_nonsense') as fake_step_data:
fake_step_data.presentation.step_text = (
'Just use the yielded object as StepPresentation directly, do not'
'use the `.presentation` accessor.'
)
# Exceptions bubbling out take precedence.
try:
with api.step.nest('exception status'):
api.step('I am fine', ['echo', 'the chillest'])
raise Exception('Whoa! Bang!')
except Exception:
pass
try:
with api.step.nest('failure status'):
api.step('I fail', ['echo', 'fail'])
except api.step.StepFailure:
pass
try:
with api.step.nest('timeout status'):
api.step(
'I fail', ['echo', 'fail'], timeout=datetime.timedelta(seconds=1))
except api.step.StepFailure as ex:
assert ex.had_timeout
# Duplicate nesting names with unique child steps
for i in range(3):
with api.step.nest('Do Iteration'):
api.step('Iterate %d' % i, ['echo', 'lerpy'])
api.step('simple thing', ['sleep', '1'])
# Note that "|" is a reserved character:
try:
api.step('cool|step', ['echo', 'hi'])
assert False # pragma: no cover
except ValueError:
pass
# OK to have a nest parent without any children
with api.step.nest('lonely parent'):
pass
# funcall successfully produces five
out = api.step.funcall("five", (lambda: 5))
assert out == 5, f'out must be 5 not {out}'
def fail():
raise ValueError("failed exception")
try:
api.step.funcall(None, fail)
except ValueError as e:
pass
else: # pragma: nocover
assert "we expected an exception here, but there was none"
def GenTests(api):
yield (
api.test('basic')
+ api.post_process(StepException, 'inherit status')
+ api.post_process(StepSuccess, 'last status')
+ api.post_check(lambda check, steps: check(
'StepPresentation' in steps['extra_nonsense'].step_text
))
+ api.post_process(StepException, 'exception status')
# TODO(iannucci): switch to build.proto so these can actually be
# differentiated: annotator protocol only has a subset of the possible
# statuses.
+ api.step_data('failure status.I fail', retcode=1)
+ api.post_process(StepFailure, 'failure status')
# TODO(iannucci): switch to build.proto so these can actually be
# differentiated: annotator protocol only has a subset of the possible
# statuses.
+ api.step_data('timeout status.I fail', times_out_after=20)
+ api.post_process(StepFailure, 'timeout status')
)