| #!/usr/bin/env bash | |
| # Copyright 2026 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. | |
| # | |
| # Tests for the mobile-install command's handling of the deployer process. | |
| # Uses a fake mobile-install aspect so that no Android SDK or device is | |
| # required: mobile-install just builds the requested output groups and then | |
| # executes <bindir>/<target>_mi/launcher as a child process. | |
| CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| source "${CURRENT_DIR}/../integration_test_setup.sh" \ | |
| || { echo "integration_test_setup.sh not found!" >&2; exit 1; } | |
| # Exit code that MobileInstall.Code.NON_ZERO_EXIT maps to in | |
| # failure_details.proto. | |
| readonly MOBILE_INSTALL_NON_ZERO_EXIT=6 | |
| function setup_fake_mi_workspace() { | |
| mkdir -p mi | |
| cat > mi/mi.bzl <<'EOF' | |
| # Minimal stand-in for @rules_android//mobile_install:mi.bzl. The launcher | |
| # emitted for targets whose name starts with "fail" exits with code 1, | |
| # simulating a deployer failure (e.g. an adb install timeout). | |
| def _mi_aspect_impl(target, ctx): | |
| launcher = ctx.actions.declare_file(target.label.name + "_mi/launcher") | |
| if target.label.name.startswith("fail"): | |
| script = "#!/bin/bash\necho 'fake deployer: install failed' >&2\nexit 1\n" | |
| else: | |
| script = "#!/bin/bash\necho 'fake deployer: install finished'\n" | |
| ctx.actions.write(output = launcher, content = script, is_executable = True) | |
| return [OutputGroupInfo( | |
| mobile_install_INTERNAL_ = depset([launcher]), | |
| mobile_install_launcher_INTERNAL_ = depset([launcher]), | |
| )] | |
| MIASPECT = aspect(implementation = _mi_aspect_impl) | |
| def _fake_binary_impl(ctx): | |
| out = ctx.actions.declare_file(ctx.label.name + ".txt") | |
| ctx.actions.write(out, "fake app") | |
| return [DefaultInfo(files = depset([out]))] | |
| fake_binary = rule(implementation = _fake_binary_impl) | |
| EOF | |
| cat > mi/BUILD <<'EOF' | |
| load(":mi.bzl", "fake_binary") | |
| fake_binary(name = "fail_app") | |
| fake_binary(name = "ok_app") | |
| EOF | |
| } | |
| function mobile_install() { | |
| bazel mobile-install \ | |
| --mobile_install_aspect=//mi:mi.bzl \ | |
| --mobile_install_supported_rules=fake_binary \ | |
| "$@" | |
| } | |
| function test_deployer_success() { | |
| setup_fake_mi_workspace | |
| mobile_install //mi:ok_app &> "$TEST_log" \ | |
| || fail "mobile-install should succeed when the deployer exits 0" | |
| expect_log "fake deployer: install finished" | |
| expect_log "Build completed successfully" | |
| } | |
| function test_deployer_failure_sets_exit_code() { | |
| setup_fake_mi_workspace | |
| local exit_code=0 | |
| mobile_install //mi:fail_app &> "$TEST_log" || exit_code=$? | |
| assert_equals "$MOBILE_INSTALL_NON_ZERO_EXIT" "$exit_code" | |
| expect_log "fake deployer: install failed" | |
| expect_log "Non-zero return code '1' from command" | |
| } | |
| function test_deployer_failure_reported_after_build_summary() { | |
| # Regression test: the deployer runs after the build has completed, so its | |
| # failure must be reported after the "Build completed successfully" summary. | |
| # It used to run as a post-build callback inside the build request, which | |
| # made a successful build summary the last thing printed after a deploy | |
| # failure. | |
| setup_fake_mi_workspace | |
| mobile_install //mi:fail_app &> "$TEST_log" \ | |
| && fail "mobile-install should fail when the deployer exits 1" | |
| expect_log "Build completed successfully" | |
| local summary_line error_line | |
| summary_line="$(grep -n "Build completed successfully" "$TEST_log" | head -1 | cut -d: -f1)" | |
| error_line="$(grep -n "Non-zero return code '1' from command" "$TEST_log" | head -1 | cut -d: -f1)" | |
| if [[ -z "$error_line" ]]; then | |
| fail "deployer failure was not reported" | |
| fi | |
| if (( error_line < summary_line )); then | |
| fail "deployer failure (line $error_line) was reported before the build \ | |
| summary (line $summary_line), so the log ends with a success message" | |
| fi | |
| } | |
| run_suite "mobile-install integration tests" |