| # 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}", |
| ) |