diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc index 55b2b35..fe8a059 100644 --- a/.github/workflows/ci.bazelrc +++ b/.github/workflows/ci.bazelrc @@ -4,6 +4,13 @@ common --experimental_google_legacy_api build --verbose_failures build --worker_verbose +# Required by @rules_android +build --experimental_enable_android_migration_apis +build --incompatible_java_common_parameters +build --android_databinding_use_v3_4_args +build --experimental_android_databinding_v2 + # Don't rely on test logs being easily accessible from the test runner, # though it makes the log noisier. test --test_output=errors +test --sandbox_debug diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f1ca5b..9c210b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,3 +64,56 @@ jobs: - name: "Running unit tests" run: | bazel --bazelrc=${{ github.workspace }}/.github/workflows/ci.bazelrc test --enable_bzlmod=${{ matrix.bzlmod }} //test/... + + instrumentation-tests: + strategy: + # Allow tests to continue on other devices if they fail on one device. + fail-fast: false + matrix: + os: [ubuntu-latest] + api-level: [29] + bzlmod: [false] + runs-on: ${{ matrix.os }} + needs: build + timeout-minutes: 60 + steps: + - name: "Checkout the sources" + uses: actions/checkout@v4 + + - name: "Enable KVM" + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: "Init AVD cache" + uses: actions/cache@v3 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-${{ matrix.api-level }} + + - name: "Create AVD and generate snapshot for caching" + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + disable-animations: false + script: echo "Generated AVD snapshot for caching." + + - name: "Run instrumentation test" + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + force-avd-creation: false + emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none + avd-name: android_avd + disable-animations: true + working-directory: ./example/ait + script: | + $ANDROID_HOME/platform-tools/adb get-serialno + bazel --bazelrc=${{ github.workspace }}/.github/workflows/ci.bazelrc test --enable_bzlmod=${{ matrix.bzlmod }} //:sample_instrumentation diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3819313 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.swp +*.swo diff --git a/MODULE.bazel b/MODULE.bazel index 96c8fcb..db14b45 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -17,7 +17,7 @@ maven.install( name = "maven", # To generate the maven lockfile, run this command: # bazel run --noenable_bzlmod @maven//:pin - lock_file = "@//:maven_install.json", + lock_file = "//:maven_install.json", repositories = [ "https://maven.google.com", "https://repo1.maven.org/maven2", @@ -33,8 +33,8 @@ use_repo( RULES_ANDROID_COMMIT = "f49f2e3af10a32f813ca294a0f025fd27294861a" RULES_ANDROID_SHA = "EGpnoftEKL6mc6QfSi7kAGqzhwtY8wsyBl5mAK84AQM=" archive_override( - module_name = "rules_android", - urls = ["https://github.com/bazelbuild/rules_android/archive/%s.zip" % RULES_ANDROID_COMMIT], - integrity = "sha256-%s" % RULES_ANDROID_SHA, - strip_prefix = "rules_android-%s" % RULES_ANDROID_COMMIT + module_name = "rules_android", + urls = ["https://github.com/bazelbuild/rules_android/archive/%s.zip" % RULES_ANDROID_COMMIT], + integrity = "sha256-%s" % RULES_ANDROID_SHA, + strip_prefix = "rules_android-%s" % RULES_ANDROID_COMMIT ) diff --git a/WORKSPACE.bzlmod b/WORKSPACE.bzlmod index 3aa901a..c1076c1 100644 --- a/WORKSPACE.bzlmod +++ b/WORKSPACE.bzlmod @@ -1,8 +1,9 @@ workspace(name = "rules_utp") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("@rules_android//rules:rules.bzl", "android_sdk_repository") maybe( android_sdk_repository, name = "androidsdk", -) \ No newline at end of file +) diff --git a/defs.bzl b/defs.bzl index d29b4b2..7e3c9c5 100644 --- a/defs.bzl +++ b/defs.bzl @@ -19,7 +19,7 @@ load("@rules_android//:defs.bzl", "rules_android_workspace") load("@rules_jvm_external//:defs.bzl", "maven_install") def rules_utp_workspace(): - """ Sets up workspace dependencies for rules_android.""" + """ Sets up workspace dependencies for rules_utp.""" bazel_skylib_workspace() UTP_VERSION = "0.0.9-alpha01" diff --git a/example/ait/BUILD b/example/ait/BUILD new file mode 100644 index 0000000..1a27b4d --- /dev/null +++ b/example/ait/BUILD @@ -0,0 +1,109 @@ +load("@rules_android//rules:rules.bzl", "android_binary", "android_library") +load( + "@rules_utp//launcher:rules.bzl", + "UTP_RELEASE", + "android_environment", + "environment", + "environment_variable", + "instrumentation", + "instrumentation_args", + "utp_entry_point", +) +load(":rule.bzl", "android_instrumentation_test") + +package( + default_applicable_licenses = ["@rules_utp//:license"], + default_visibility = ["//visibility:public"], +) + +android_instrumentation_test( + name = "sample_instrumentation", + device_serial = "emulator-5554", # Default serial-no + entry_point = ":entry_point", + environment = ":default_environment", + instrumentation = ":device_instrumentation", + instrumentation_args = ":device_instrumentation_args", + support_apps = [":sample_app"], + test_app = ":test_app", + utp_release = "@rules_utp" + UTP_RELEASE, +) + +android_binary( + name = "test_app", + instruments = ":sample_app", + manifest = "//lib:AndroidManifest.xml", + multidex = "native", + deps = [ + ":test_src", + "@android_maven//:com_android_support_multidex", + ], +) + +android_library( + name = "test_src", + srcs = ["//lib:SampleTest.java"], + deps = [ + ":sample_lib", + "@androidx_maven//:androidx_test_core", + "@androidx_maven//:androidx_test_espresso_espresso_core", + "@androidx_maven//:androidx_test_ext_junit", + "@androidx_maven//:androidx_test_runner", + "@bazel_tools//tools/jdk:TestRunner", + ], +) + +android_binary( + name = "sample_app", + custom_package = "com.sample", + manifest = "//lib:SampleAndroidManifest.xml", + multidex = "native", + deps = [ + ":sample_lib", + "@android_maven//:com_android_support_multidex", + ], +) + +android_library( + name = "sample_lib", + srcs = ["//lib:SampleActivity.java"], + custom_package = "com.sample", + manifest = "//lib:SampleAndroidManifest.xml", + resource_files = ["//lib:resource_files"], +) + +## instruments + +instrumentation( + name = "device_instrumentation", + app_package = "com.sample", + test_package = "com.sample.test", + test_runner_class = "androidx.test.runner.AndroidJUnitRunner", +) + +instrumentation_args( + name = "device_instrumentation_args", + # enable_debug = True, # Need to launch debugger otherwise test will hang +) + +## environment + +android_environment( + name = "android_environment", + test_log_dir = "/tmp/ait/testlog", + test_run_log = "test-results.log", +) + +environment( + name = "default_environment", + android_environment = ":android_environment", + output_dir = environment_variable("TEST_UNDECLARED_OUTPUTS_DIR"), + runfiles_dir = "{}/test_local_repo".format(environment_variable("TEST_SRCDIR")), + tmp_dir = environment_variable("TEST_TMPDIR"), +) + +### UTP entry point +utp_entry_point( + name = "entry_point", + testonly = True, + utp_release = "@rules_utp" + UTP_RELEASE, +) diff --git a/example/ait/MODULE.bazel b/example/ait/MODULE.bazel new file mode 100644 index 0000000..0be7fad --- /dev/null +++ b/example/ait/MODULE.bazel @@ -0,0 +1,68 @@ +module( + name = "example_ait", + version = "TODO", +) + +bazel_dep(name = "rules_android", version = "0.1.1") +bazel_dep(name = "rules_utp", version = "0.0.1") +bazel_dep(name = "rules_jvm_external", version = "5.3") + + +bazel_dep(name = "rules_java", version = "7.3.2") +rules_java_toolchains = use_extension("@rules_java//java:extensions.bzl", "toolchains") +use_repo(rules_java_toolchains, "remote_java_tools") +bazel_dep(name = "bazel_skylib", version = "1.4.1") + + +UTP_VERSION = "0.0.9-alpha01" +SERVICES_VERSION = "1.4.2" +androidxLibVersion = "1.0.0" +coreVersion = "1.6.0-alpha05" +extJUnitVersion = "1.2.0-alpha03" +espressoVersion = "3.6.0-alpha03" +runnerVersion = "1.6.0-alpha06" +rulesVersion = "1.6.0-alpha03" + +maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven") +maven.install( + name = "android_maven", + artifacts = [ + "com.android.support:multidex:1.0.3", + "com.android.support.test:runner:1.0.2", + ], + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], +) +maven.install( + name = "androidx_maven", + artifacts = [ + "androidx.test:core:" + coreVersion, + "androidx.test.espresso:espresso-core:" + espressoVersion, + "androidx.test.ext:junit:" + extJUnitVersion, + "androidx.test:runner:" + runnerVersion, + "androidx.test:rules:" + rulesVersion, + ], + fetch_sources = True, + # To generate the maven lockfile, run this command: + # bazel run --noenable_bzlmod @rules_androidx_maven//:pin + # lock_file = "//:rules_androidx_maven_install.json", + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], +) +use_repo( + maven, + "android_maven", "androidx_maven" +) + + +remote_android_extensions = use_extension("@bazel_tools//tools/android:android_extensions.bzl", "remote_android_tools_extensions") +use_repo(remote_android_extensions, "android_gmaven_r8", "android_tools") + +local_path_override( + module_name = "rules_utp", + path = "../../", +) diff --git a/example/ait/WORKSPACE b/example/ait/WORKSPACE new file mode 100644 index 0000000..edd81eb --- /dev/null +++ b/example/ait/WORKSPACE @@ -0,0 +1,29 @@ +workspace(name = "example_ait") + +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +local_repository( + name = "rules_utp", + path = "../../", +) +load("@rules_utp//:prereqs.bzl", "rules_utp_prereqs") +rules_utp_prereqs() + +load("@rules_utp//:depprereqs.bzl", "rules_utp_dep_prereqs") +rules_utp_dep_prereqs() + +load("@rules_utp//:defs.bzl", "rules_utp_workspace") +rules_utp_workspace() + +load("defs.bzl", "example_workspace") +example_workspace() + +maybe( + android_sdk_repository, + name = "androidsdk", +) + + +register_toolchains("@rules_android//toolchains/android:all") +register_toolchains("@rules_android//toolchains/android_sdk:all") +register_toolchains("@rules_android//toolchains/emulator:all") diff --git a/example/ait/WORKSPACE.bzlmod b/example/ait/WORKSPACE.bzlmod new file mode 100644 index 0000000..b02aad9 --- /dev/null +++ b/example/ait/WORKSPACE.bzlmod @@ -0,0 +1,9 @@ +workspace(name = "example_ait") + +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("@rules_android//rules:rules.bzl", "android_sdk_repository") + +maybe( + android_sdk_repository, + name = "androidsdk", +) diff --git a/example/ait/defs.bzl b/example/ait/defs.bzl new file mode 100644 index 0000000..0e3afad --- /dev/null +++ b/example/ait/defs.bzl @@ -0,0 +1,55 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Workspace setup macro for rules_android.""" + +load("@rules_jvm_external//:defs.bzl", "maven_install") + +def example_workspace(): + """ Sets up workspace dependencies for rules_android.""" + + CORE_VERSION = "1.6.0-alpha05" + EXT_JUNIT_VERSION = "1.2.0-alpha03" + ESPRESSO_VERSION = "3.6.0-alpha03" + RUNNER_VERSION = "1.6.0-alpha06" + RULES_VERSION = "1.6.0-alpha03" + + maven_install( + name = "android_maven", + artifacts = [ + "com.android.support:multidex:1.0.3", + "com.android.support.test:runner:1.0.2", + ], + fetch_sources = True, + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + ) + + maven_install( + name = "androidx_maven", + artifacts = [ + "androidx.test:core:" + CORE_VERSION, + "androidx.test.espresso:espresso-core:" + ESPRESSO_VERSION, + "androidx.test.ext:junit:" + EXT_JUNIT_VERSION, + "androidx.test:runner:" + RUNNER_VERSION, + "androidx.test:rules:" + RULES_VERSION, + ], + fetch_sources = True, + repositories = [ + "https://maven.google.com", + "https://repo1.maven.org/maven2", + ], + ) diff --git a/example/ait/lib/AndroidManifest.xml b/example/ait/lib/AndroidManifest.xml new file mode 100644 index 0000000..4be44b5 --- /dev/null +++ b/example/ait/lib/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + diff --git a/example/ait/lib/BUILD b/example/ait/lib/BUILD new file mode 100644 index 0000000..d66bb15 --- /dev/null +++ b/example/ait/lib/BUILD @@ -0,0 +1,16 @@ +package( + default_applicable_licenses = ["@rules_utp//:license"], + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "resource_files", + srcs = glob(["res/**"]), +) + +exports_files([ + "AndroidManifest.xml", + "SampleAndroidManifest.xml", + "SampleTest.java", + "SampleActivity.java", +]) diff --git a/example/ait/lib/SampleActivity.java b/example/ait/lib/SampleActivity.java new file mode 100644 index 0000000..575d553 --- /dev/null +++ b/example/ait/lib/SampleActivity.java @@ -0,0 +1,29 @@ +/** + * Copyright 2024 The Bazel Authors. All rights reserved. + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.sample; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.util.Log; + +/** Skeleton activity used for testing Unified Test Platform e2e. */ +public class SampleActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.sample_activity); + } +} diff --git a/example/ait/lib/SampleAndroidManifest.xml b/example/ait/lib/SampleAndroidManifest.xml new file mode 100644 index 0000000..1bffb8d --- /dev/null +++ b/example/ait/lib/SampleAndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/example/ait/lib/SampleTest.java b/example/ait/lib/SampleTest.java new file mode 100644 index 0000000..f9ade49 --- /dev/null +++ b/example/ait/lib/SampleTest.java @@ -0,0 +1,50 @@ +/** + * Copyright 2024 The Bazel Authors. All rights reserved. + * + *

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + *

http://www.apache.org/licenses/LICENSE-2.0 + * + *

Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.sample; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.core.app.ActivityScenario; +import com.sample.SampleActivity; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Sanity Espresso tests for the Unified Test Platform to use. */ +@RunWith(AndroidJUnit4.class) +public class SampleTest { + + @Before + public void setUp() {} + + @Test + public void helloWorldDoesNotBlowUp() { + ActivityScenario.launch(SampleActivity.class); + } + + @Test + public void helloWorldIsDisplayed() { + ActivityScenario.launch(SampleActivity.class); + System.out.println( + "helloWorldIsDisplayed... " + + withId(R.string.hello_world) + + " / " + + withId(R.string.app_name)); + onView(withId(R.id.text_hello)).check(matches(withText("Hello world!"))); + } +} diff --git a/example/ait/lib/res/drawable-hdpi/ic_launcher.png b/example/ait/lib/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..72ebaad Binary files /dev/null and b/example/ait/lib/res/drawable-hdpi/ic_launcher.png differ diff --git a/example/ait/lib/res/drawable-mdpi/ic_launcher.png b/example/ait/lib/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..5ad8cb4 Binary files /dev/null and b/example/ait/lib/res/drawable-mdpi/ic_launcher.png differ diff --git a/example/ait/lib/res/drawable-xhdpi/ic_launcher.png b/example/ait/lib/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..a734148 Binary files /dev/null and b/example/ait/lib/res/drawable-xhdpi/ic_launcher.png differ diff --git a/example/ait/lib/res/drawable-xxhdpi/ic_launcher.png b/example/ait/lib/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..585e71d Binary files /dev/null and b/example/ait/lib/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/example/ait/lib/res/layout/sample_activity.xml b/example/ait/lib/res/layout/sample_activity.xml new file mode 100644 index 0000000..2451aef --- /dev/null +++ b/example/ait/lib/res/layout/sample_activity.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/example/ait/lib/res/menu/menu.xml b/example/ait/lib/res/menu/menu.xml new file mode 100644 index 0000000..2b6d2bc --- /dev/null +++ b/example/ait/lib/res/menu/menu.xml @@ -0,0 +1,8 @@ +

+ + diff --git a/example/ait/lib/res/values/dimens.xml b/example/ait/lib/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/example/ait/lib/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/example/ait/lib/res/values/strings.xml b/example/ait/lib/res/values/strings.xml new file mode 100644 index 0000000..3e0e615 --- /dev/null +++ b/example/ait/lib/res/values/strings.xml @@ -0,0 +1,8 @@ + + + Hello World! + + sampleapp + Hello world! + Settings + diff --git a/example/ait/rule.bzl b/example/ait/rule.bzl new file mode 100644 index 0000000..da2fee7 --- /dev/null +++ b/example/ait/rule.bzl @@ -0,0 +1,186 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""An example of instrumentation test implemented on utp_test""" + +load( + "@rules_utp//launcher:rules.bzl", + "android_instrumentation_driver", + "enum", + "local_android_device_provider", + "utp_test", +) + +def android_instrumentation_test( + name, + test_app, + device_serial, + entry_point, + environment, + instrumentation, + instrumentation_args, + utp_release, + support_apps = [], + size = "medium", + timeout = None, + data = None, + tags = None): + """Runs an APK test on an Android emulator. + + Args: + name: (str) Name of the test, and stem for additional targets created. + test_app: (Label) The `android_binary` containing the test classes. + device_serial: (str) A serial number of a running emulator, needed by adb. + entry_point: ([Label]) An utp_entry_point target, passing to utp_test. + environment: ([Label]) An environment target, passing to utp_test. + instrumentation: ([Label]) An instrumentation target. + instrumentation_args: ([Label]) An instrumentation_args target. + utp_release: ([Label]) An utp_release target. + support_apps: ([Label]) List of APKs to install. + size: (str) Size of the test ("small", "medium", "large", "enormous"). + timeout: (str) Test timeout ("short", "moderate", "long", "eternal"). + data: ([Label]) List of targets needed at runtime. APK targets will *not* be installed; + use support_apps for that. + tags: ([str]) Tags to apply to the generated test target. + """ + names = struct( + device_provider = "{}_device_provider".format(name), + test_driver = "{}_test_driver".format(name), + utp_test = "{}_utp_test".format(name), + install_script = "{}_install_script".format(name), + installable = "{}_installable".format(name), + ) + + # UTP plugin install_plugins.jar is publicly available until UTP can install installables + installables = [] + + local_android_device_provider( + name = names.device_provider, + testonly = True, + serial = device_serial, + instrumentation = instrumentation, + instrumentation_args = instrumentation_args, + custom_adb = "@androidsdk//:adb", + utp_release = utp_release, + visibility = [ + "//visibility:private", + ], + ) + + android_instrumentation_driver( + name = names.test_driver, + testonly = True, + instrumentation = instrumentation, + instrumentation_args = instrumentation_args, + utp_release = utp_release, + # We need to install test_services to run shell or use orchestrator on devices + shell_execution = enum("NO_SHELL_EXECUTION"), + use_orchestrator = False, + visibility = [ + "//visibility:private", + ], + ) + host_plugins = [] + + utp_test( + name = names.utp_test, + size = size, + timeout = timeout, + data = data, + tags = tags, + installables = installables, + device_provider = ":{}".format(names.device_provider), + test_driver = ":{}".format(names.test_driver), + entry_point = entry_point, + test_app = test_app, + environment = environment, + host_plugins = host_plugins, + diagnostic_exporters = [], + logging = None, + port_picker = None, + test_result_listeners = [], + ) + + # installables should be installed to devices by UTP + # However, UTP hasn't publish plugin binaries that handle the installation. + # As a work around, we add a script to finish the installation. + # Once UTP publish the android_install_plugin jar, we can remove this workaround. + _install_artifacts_test( + name = name, + device_serial = device_serial, + apps_to_install = [test_app] + support_apps, + utp_test = names.utp_test, + ) + +def _install_artifacts_impl(ctx): + apks_to_install = [] + for app in ctx.attr.apps_to_install: + apks_to_install.append(app[ApkInfo].signed_apk) + + install_output = ctx.actions.declare_file(ctx.label.name + "_install.output") + ctx.actions.run_shell( + inputs = ctx.files.apps_to_install, + outputs = [install_output], + command = "{adb} {serial_flag} install-multi-package {targets} >& {out}".format( + serial_flag = ("-s %s" % ctx.attr.device_serial) if ctx.attr.device_serial else "", + adb = ctx.executable._adb.path, + out = install_output.path, + targets = " ".join([f.path for f in apks_to_install]), + ), + tools = [ctx.executable._adb], + mnemonic = "InstallAPKs", + ) + + # Need to copy executable from dependency + output = ctx.actions.declare_file(ctx.label.name + ".output") + ctx.actions.run_shell( + inputs = [ctx.file.utp_test, install_output], + outputs = [output], + command = "cp '%s' '%s'" % (ctx.file.utp_test.path, output.path), + ) + utps = ctx.attr.utp_test[DefaultInfo] + utps_files = [utps.files] + utps_files.append(utps.data_runfiles.files) + utps_files.append(utps.default_runfiles.files) + return [ + DefaultInfo( + executable = output, + runfiles = ctx.runfiles( + files = [install_output, ctx.file.utp_test, output] + apks_to_install, + transitive_files = depset(transitive = utps_files), + ), + ), + ] + +_install_artifacts_test = rule( + implementation = _install_artifacts_impl, + test = True, + attrs = dict( + _adb = attr.label( + default = Label("@androidsdk//:adb"), + allow_files = True, + executable = True, + cfg = "exec", + ), + device_serial = attr.string( + ), + apps_to_install = attr.label_list(mandatory = True), + utp_test = attr.label( + mandatory = True, + allow_single_file = True, + executable = True, + cfg = "exec", + ), + ), +) diff --git a/launcher/entry_point.bzl b/launcher/entry_point.bzl index b0c0601..4650c39 100644 --- a/launcher/entry_point.bzl +++ b/launcher/entry_point.bzl @@ -81,4 +81,4 @@ def launcher_classpath(entry_point): Result: (str) Argument suitable for "java -jar". """ - return entry_point.launcher.short_path, + return entry_point.launcher.short_path diff --git a/launcher/launcher.sh b/launcher/launcher.sh index 20fd082..07f1c0f 100644 --- a/launcher/launcher.sh +++ b/launcher/launcher.sh @@ -34,6 +34,13 @@ SED_SCRIPT=${TEST_TMPDIR}/runner_config.sed # enums. echo 's#\\*\"@!([^!]+)!@\\*\"#\1#g' > ${SED_SCRIPT} +# These functions are created by bazel during test. The grep expressions below +# don't take care of multi-line functions, hence resulting incorrectly formatted +# sed expressions if there're multi-line functions in the output of printenv. +# To keep grep expressions simple, unset these functions in advance. +unset rlocation +unset is_absolute + # Replace environment variables. Except LD_LIBRARY_PATH, it's huge and makes # the script hard to read. printenv | \ @@ -75,7 +82,7 @@ sed -r \ # Dump the config with line numbers, to make it easy to diagnose any error # messages. -#cat -n ${RUNNER_CONFIG} +# cat -n ${RUNNER_CONFIG} %extra_setup_commands% diff --git a/tools/android/BUILD b/tools/android/BUILD index 9c295cc..7e854e3 100644 --- a/tools/android/BUILD +++ b/tools/android/BUILD @@ -9,7 +9,7 @@ package( alias( name = "adb", - actual = "@androidsdk//:platform-tools/adb", + actual = "@androidsdk//:adb", ) alias(