blob: e9a99fef8604dafb31d4aef435ab4a8889e2053b [file] [log] [blame]
# Copyright 2020 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 import recipe_api
from PB.recipe_modules.fuchsia.fxt.options import Options
class FxtApi(recipe_api.RecipeApi):
"""APIs for the Fuchsia external tests client."""
Options = Options
def orchestrate_fxt_tests(
self, bucket, namespace, options, guitar_test_filter_exprs=()
):
with self.m.step.nest("run external tests") as presentation:
resp = self._launch(
"launch",
bucket,
namespace,
options=options,
guitar_test_filter_exprs=guitar_test_filter_exprs,
presentation=presentation,
)
try:
self._monitor(
"monitor",
tap_request_id=resp["tap_request_id"],
guitar_project_request_ids=resp["guitar_project_request_ids"],
options=options,
)
finally:
if resp["workspace"]:
self._cleanup(
"cleanup", workspace=resp["workspace"], options=options
)
def _launch(
self,
step_name,
bucket,
namespace,
options,
guitar_test_filter_exprs,
presentation,
):
"""Launch tests in external infrastructure.
Args:
step_name (str): Name of the step.
bucket (str): GCS bucket containing Fuchsia build artifacts.
namespace (str): Unique identifier for a build.
options (fxt.Options): fxt tool options.
guitar_test_filter_exprs (seq(str)): Guitar test filter expressions.
See go/bqr for examples.
presentation (Step): Add links to this step.
Returns:
dict:
tap_request_id (str): Launch request identifier for Tap tests.
guitar_project_request_ids (list(str)): List of Guitar Project request identifiers to launch.
change_num (int): Launch request changelist number.
workspace (str): Launch request workspace name.
"""
args = [
"launch",
"-gcs-bucket",
bucket,
"-namespace",
namespace,
"-json-output",
self.m.json.output(),
]
assert (
bool(options.image_name) != options.sdk_mode
), "exactly one of image_name and sdk_mode must be set"
if options.image_name:
args += ["-name", options.image_name]
elif options.sdk_mode:
args.append("-sdk")
for project in options.tap_projects:
args += ["-tap-project", project]
if options.guitar_config:
args += ["-guitar-config-json", self.m.json.input(options.guitar_config)]
for expr in guitar_test_filter_exprs:
args += ["-guitar-test-filter-expr", expr]
step = self._run(step_name, args, options)
resp = step.json.output
# The changelist gets deleted after the tests complete, so we should
# only attach it to the launch step since it will be useless when
# attached to the top-level presentation.
#
# Not all types of external tests require creating a CL, so `change_num`
# may be zero or unset.
if resp.get("change_num"):
step.presentation.links["changelist"] = f"http://cl/{resp['change_num']}"
if options.tap_projects:
presentation.links["tap_tests"] = f"http://test/{resp['tap_request_id']}"
step.presentation.links[
"tap_tests"
] = f"http://test/{resp['tap_request_id']}"
if options.guitar_config:
for i, request_id in enumerate(resp["guitar_project_request_ids"]):
presentation.links[f"guitar_tests_{i}"] = f"http://fusion2/{request_id}"
step.presentation.links[
f"guitar_tests_{i}"
] = f"http://fusion2/{request_id}"
return resp
def _monitor(
self,
step_name,
tap_request_id=None,
guitar_project_request_ids=(),
options=None,
):
"""Monitor external tests.
Args:
step_name (str): Name of the step.
tap_request_id (str): Launch tap request identifier.
guitar_project_request_ids (seq(str)): Seq of guitar project request identifiers to launch.
options (fxt.Options): Configuration.
"""
args = [
"monitor",
"-timeout",
options.timeout_secs or 40 * 60,
]
if tap_request_id:
args += ["-tap-request-id", tap_request_id]
for request_id in guitar_project_request_ids:
args += ["-guitar-project-request-id", request_id]
try:
self._run(step_name, args, options)
except self.m.step.StepFailure:
error_msg = "external tests failed"
if tap_request_id:
error_msg += f"\n\nsee [Tap tests UI](http://test/{tap_request_id})"
for request_id in guitar_project_request_ids:
error_msg += f"\n\nsee [Guitar tests UI](http://fusion2/{request_id})"
raise self.m.step.StepFailure(error_msg)
def _cleanup(self, step_name, workspace, options):
"""Cleanup external workspace."""
args = [
"cleanup",
"-workspace",
workspace,
]
self._run(step_name, args, options, infra_step=True)
def _run(self, step_name, args, options, **kwargs):
cmd = [self._fxt_client] + args
if options.use_staging_host:
cmd.append("-use-staging-host")
return self.m.step(step_name, cmd, **kwargs)
@property
def _fxt_client(self):
return self.m.ensure_tool("fxt", self.resource("tool_manifest.json"))