| """This module defines the adb_test rule.""" |
| |
| load("@local_config_platform//:constraints.bzl", "HOST_CONSTRAINTS") |
| load("//bazel:remove_indentation.bzl", "remove_indentation") |
| |
| def _adb_test_runner_transition_impl(settings, attr): # buildifier: disable=unused-variable |
| platform = settings["//tests/adb_test_runner:adb_platform"] |
| |
| # If no platform was specified via --adb_platform, use the host platform. This allows us to |
| # "bazel test" a skia_android_unit_test on a developer workstation without passing said flag to |
| # Bazel. |
| if platform == "": |
| # The HOST_CONSTRAINTS list should always be of the form [cpu, os], e.g.: |
| # |
| # HOST_CONSTRAINTS = ["@platforms//cpu:x86_64", "@platforms//os:linux"] |
| # |
| # The CPU and OS constraints will be omitted from HOST_CONSTRAINTS in the rare case that |
| # they cannot be determined. |
| # |
| # Reference: |
| # https://github.com/bazelbuild/bazel/blob/30ca122db02d953068ebb2b036b015e6b375c9ce/src/main/java/com/google/devtools/build/lib/bazel/repository/LocalConfigPlatformFunction.java#L186 |
| if len(HOST_CONSTRAINTS) != 2 or \ |
| not HOST_CONSTRAINTS[0].startswith("@platforms//cpu:") or \ |
| not HOST_CONSTRAINTS[1].startswith("@platforms//os:"): |
| fail( |
| "Expected HOST_CONSTRAINTS to be of the form " + |
| """["@platforms//cpu:<cpu>", "@platforms//os:<os>"], got""", |
| HOST_CONSTRAINTS, |
| ) |
| |
| # Map the Bazel constants to GOARCH constants. More can be added as needed. See |
| # https://github.com/bazelbuild/rules_go/blob/5933b6ed063488472fc14ceca232b3115e8bc39f/go/private/platforms.bzl#LL30C9-L30C9. |
| cpu = HOST_CONSTRAINTS[0].removeprefix("@platforms//cpu:") |
| os = HOST_CONSTRAINTS[1].removeprefix("@platforms//os:") |
| cpu = { |
| "x86_64": "amd64", |
| "aarch64": "arm64", |
| }.get(cpu, cpu) # Defaults to the original CPU if not in the dictionary. |
| os = { |
| "osx": "darwin", |
| }.get(os, os) # Default to the original OS if not in the dictionary. |
| |
| platform = os + "_" + cpu |
| else: |
| # Map the --adb_platform CPU part to GOARCH style, which we differ from for readability. |
| platform = platform.replace("x86", "amd64") |
| |
| return {"//command_line_option:platforms": "@io_bazel_rules_go//go/toolchain:" + platform} |
| |
| # This transition allows us to cross-compile the Go test runner (i.e. the program that issues adb |
| # commands) for a different platform, for example when "bazel build"-ing on an x86 GCE machine and |
| # running the compiled artifact on a Raspberry Pi in a subsequent CI task. |
| adb_test_runner_transition = transition( |
| implementation = _adb_test_runner_transition_impl, |
| inputs = ["//tests/adb_test_runner:adb_platform"], |
| outputs = ["//command_line_option:platforms"], |
| ) |
| |
| def _adb_test_impl(ctx): |
| template = remove_indentation(""" |
| #!/bin/bash |
| |
| # Print commands and expand variables for easier debugging. |
| set -x |
| |
| # List the test runner binary for debugging purposes. |
| ls -l $(rootpath {adb_test_runner}) |
| |
| $(rootpath {adb_test_runner}) \ |
| --device {device} \ |
| --archive $(rootpath {archive}) \ |
| --test-runner $(rootpath {test_runner}) |
| """) |
| |
| if ctx.attr.device == "unknown": |
| template = remove_indentation(""" |
| #!/bin/bash |
| |
| echo "FAILED: No Android device was specified. Try re-running with a Bazel flag that" |
| echo " specifies an Android device under test, such as --config=pixel_5." |
| |
| exit 1 |
| """) |
| |
| # Expand variables. |
| template = ctx.expand_location(template.format( |
| device = ctx.attr.device, |
| archive = ctx.attr.archive.label, |
| test_runner = ctx.attr.test_runner.label, |
| adb_test_runner = ctx.attr._adb_test_runner[0].label, |
| ), targets = [ |
| ctx.attr.archive, |
| ctx.attr.test_runner, |
| ctx.attr._adb_test_runner[0], |
| ]) |
| |
| output_file = ctx.actions.declare_file(ctx.attr.name) |
| ctx.actions.write(output_file, template, is_executable = True) |
| |
| runfiles = ctx.runfiles(files = [ctx.file.archive]) |
| runfiles = runfiles.merge(ctx.attr._adb_test_runner[0][DefaultInfo].default_runfiles) |
| |
| return [DefaultInfo( |
| executable = output_file, |
| runfiles = runfiles, |
| )] |
| |
| adb_test = rule( |
| doc = """Runs an Android test on device via `adb`. |
| |
| This test rule produces a wrapper shell script that invokes a Go proram that issues adb |
| commands to interact with the device under test. |
| |
| When building a test that should run on a different host (e.g. a Skolo Raspberry Pi), invoke |
| Bazel with flag --adb_platform to set the adb_test_runner target platform accordingly, for |
| example --adb_platform=linux_arm64. |
| """, |
| implementation = _adb_test_impl, |
| attrs = { |
| "device": attr.string( |
| doc = "Device under test.", |
| mandatory = True, |
| values = [ |
| "pixel_5", |
| "pixel_7", |
| "unknown", |
| ], |
| ), |
| "test_runner": attr.label( |
| doc = ( |
| "Test runner script that calls the compiled C++ binary with any necessary " + |
| "command-line arguments. This script will be executed on the Android device." |
| ), |
| allow_single_file = [".sh"], |
| mandatory = True, |
| ), |
| "archive": attr.label( |
| doc = ( |
| "Tarball containing the test runner script, the compiled C++ binary and any" + |
| "necessary static resources such as fonts, images, etc." |
| ), |
| allow_single_file = [".tar.gz"], |
| mandatory = True, |
| ), |
| "_adb_test_runner": attr.label( |
| default = Label("//tests/adb_test_runner"), |
| allow_single_file = True, |
| executable = True, |
| cfg = adb_test_runner_transition, |
| ), |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| test = True, |
| ) |