blob: 189458d061575686eb0328555bb4df995e8ad771 [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,
)
self._monitor(
"monitor",
workspace=resp["workspace"],
change_num=resp["change_num"],
presubmit_token=resp["presubmit_token"],
tap_request_id=resp["tap_request_id"],
guitar_project_request_ids=resp["guitar_project_request_ids"],
options=options,
)
# Don't clean up the workspace if the tests failed, so it can be
# inspected to help debug the failures.
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.
presubmit_token (str): Token used to check Piper presubmit status. Empty if
piper_presubmit was not requested.
"""
args = [
"launch",
"-gcs-bucket",
bucket,
"-namespace",
namespace,
"-json-output",
self.m.json.output(),
]
if 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]
if options.piper_presubmit:
args += ["-presubmit"]
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,
workspace,
change_num,
tap_request_id=None,
guitar_project_request_ids=(),
presubmit_token="",
options=None,
):
"""Monitor external tests.
Args:
step_name (str): Name of the step.
workspace (str): Launch request workspace name.
change_num (int): Launch request changelist number.
tap_request_id (str): Launch tap request identifier.
guitar_project_request_ids (seq(str)): Seq of guitar project request identifiers to launch.
presubmit_token (str): Token for checking Piper presubmit status.
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]
if presubmit_token:
args += ["-presubmit-token", presubmit_token, "-workspace", workspace]
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})"
if presubmit_token:
error_msg += (
f"\n\nsee [Fusion UI](http://fusion2/presubmit/{change_num})"
)
error_msg += (
f"\n\nand/or [Critique UI](http://cl/{change_num}/analysis)"
)
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.cipd_ensure(
self.resource("cipd.ensure"),
"fuchsia_internal/infra/fxt/${platform}",
)