blob: 39221d4593b1cd63817dced2649014dbb9dbee51 [file] [log] [blame]
#!/bin/bash
# Copyright 2025 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.
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/../../lib/platform.sh || exit $?
BT_FILE_DEPS=(
"prebuilt/third_party/jq/${HOST_PLATFORM}/bin/jq"
"scripts/fx"
"scripts/fuchsia-vendored-python"
"scripts/fuchsia-vendored-python3.11"
"scripts/hermetic-env"
"tools/devshell/lib/vars.sh"
"tools/devshell/build"
"tools/devshell/jq.fx"
"tools/devshell/lib/build_api_client.sh"
"tools/devshell/lib/fx-cmd-locator.sh"
"tools/devshell/lib/fx-optional-features.sh"
"tools/devshell/lib/platform.sh"
"tools/devshell/lib/generate-ssh-config.sh"
)
declare fx
BT_MKDIR_DEPS=(
"out/default"
)
MOCK_PYTHON3_DIR="prebuilt/third_party/python3/test"
MOCK_PYTHON3="${MOCK_PYTHON3_DIR}/bin/python3"
BT_MOCKED_TOOLS=(
"build/api/client"
"build/regenerator"
"scripts/fuchsia-vendored-python"
"tools/devshell/lib/bazel_utils.sh"
"tools/devshell/rbe"
"tools/devshell/lib/platform.sh"
"tools/devshell/contrib/lib/count-ninja-actions.py"
"tools/integration/bootstrap.sh"
"${MOCK_PYTHON3}"
)
BT_SET_UP() {
source "${BT_TEMP_DIR}/tools/devshell/tests/lib/fuchsia-mock.sh"
fx="$(btf::setup_fx)"
TEST_BUILD_DIR="${BT_TEMP_DIR}/out/default"
}
# This function must be called by any test that checks the
# behavior of the validate_build_args() function.
setup_parsing_validation_test() {
export FX_BUILD_DEBUG_VALIDATION=1
}
# This function must be called by regular build tests.
setup_build_test() {
# Set up mocked replacements for system utils.
# IMPORTANT: The test suite invokes the current script through "/usr/bin/env -i"
# which appends ":." at the end of PATH, which will result in an error when
# calling fx-run-build-command, so remove this here explicitly.
export PATH="${BT_TEMP_DIR}/bin:${PATH%:.}"
# Misc mock binaries invoked by tools/devshell/build
ninja="$(btf::make_mock_binary bin/ninja)"
build_api_client="${BT_TEMP_DIR}/build/api/client"
bazel="$(btf::make_mock_binary bin/bazel)"
cat > "${BT_TEMP_DIR}/tools/devshell/lib/bazel_utils.sh.mock_side_effects" <<EOF
function fx-get-bazel {
echo "${bazel}"
}
EOF
cat > "${BT_TEMP_DIR}/tools/devshell/lib/platform.sh.mock_side_effects" <<EOF
readonly PREBUILT_ALL_PATHS="${BT_TEMP_DIR}"
readonly PREBUILT_JQ="${BT_TEMP_DIR}/prebuilt/third_party/jq/linux-x64/bin/jq"
readonly PREBUILT_PYTHON3_DIR="${BT_TEMP_DIR}/${MOCK_PYTHON3_DIR}"
readonly PREBUILT_PYTHON3="${BT_TEMP_DIR}/${MOCK_PYTHON3}"
readonly PREBUILT_NINJA="${ninja}"
EOF
if [[ "$1" != "enable_landmines" ]]; then
# Ensure the landmine checks pass by updating the timestamp of various files
# in the test directory.
# LINT.IfChange(landmine_files)
touch "${TEST_BUILD_DIR}/args.gn"
touch "${BT_TEMP_DIR}/tools/devshell/build"
touch "${BT_TEMP_DIR}/tools/devshell/lib/bazel_utils.sh"
touch "${BT_TEMP_DIR}/tools/devshell/lib/vars.sh"
touch "${TEST_BUILD_DIR}/build.ninja" # Must appear last
# LINT.ThenChange(//tools/devshell/build:landmine_files)
fi
# Disable RBE by default.
echo "" > "${TEST_BUILD_DIR}/rbe_settings.json"
# args.json is parsed to look for enable_jobserver in Ninja build mode.
# Ensure this is disabled by default.
touch "${TEST_BUILD_DIR}/args.json"
# Force concurrency, see fx-cpu-count.
mock_getconf=$(btf::make_mock_binary getconf)
echo "8" > "${mock_getconf}.mock_stdout"
btf::add_binary_to_path "${mock_getconf}"
}
# In Ninja build mode, build/api/client is called twice. The first time to
# convert GN labels and Ninja output paths to GN labels (if possible, since
# some GN convenience aliases have no GN labels), the second time to convert
# the labels (and remaining aliases) to Ninja paths.
#
# These two steps are used to print different set of messages in case of
# errors, and could be grouped into a single build/api/client command, but
# for now, both need to be supported.
# $1: The output of the first build/api/client (should be a list of labels)
# $2: The output of the second build/api/client (should be a list of Ninja paths)
setup_build_api_results() {
echo "$1" > "${build_api_client}.mock_stdout.1"
echo "$2" > "${build_api_client}.mock_stdout.2"
}
setup_fint_build() {
# tools/integration/bootstrap.sh is already mocked, populated
# the mock FX_CACHE_DIR with a symlink to a mock fint tool.
fint_bin=$(btf::make_mock_binary bin/fint)
fint="${BT_TEMP_DIR}/.fx/fint"
mkdir -p "$(dirname "${fint}")"
ln -s "${fint_bin}" "${fint}"
}
TEST_fx-build-validate-gn-labels() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-validate-gn-toolchain() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --toolchain=//toolchain:bar //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-validate-gn-alias-default() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --default //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-validate-gn-alias-fuchsia() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --fuchsia //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-validate-gn-alias-fidl() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --fidl //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-validate-gn-alias-host() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --host //src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE gn"
}
TEST_fx-build-reject-gn-toolchain-as-last-argument() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build //src:foo --host "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: Toolchain option --host must be followed by at least one GN label"
}
TEST_fx-build-validate-bazel-single-label() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build @//src:foo ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE bazel"
}
TEST_fx-build-reject-bazel-and-gn-toolchain() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build @//src:foo --toolchain=//toolchain:bar "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: GN option --toolchain=//toolchain:bar cannot be used with Bazel targets: @//src:foo"
}
TEST_fx-build-reject-bazel-and-gn-alias-default() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build @//src:foo --default "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: GN option --default cannot be used with Bazel targets: @//src:foo"
}
TEST_fx-build-reject-bazel-and-gn-alias-fuchsia() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build @//src:foo --fuchsia "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: GN option --fuchsia cannot be used with Bazel targets: @//src:foo"
}
TEST_fx-build-reject-bazel-and-gn-alias-fidl() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build @//src:foo --fidl "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: GN option --fidl cannot be used with Bazel targets: @//src:foo"
}
TEST_fx-build-validate-bazel-with-host-alias() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build @//src:foo --host ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE bazel"
}
TEST_fx-build-reject-bazel-and-gn-labels() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build @//src:foo //src:bar "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: GN label //src:bar cannot be used with Bazel targets: @//src:foo"
}
TEST_fx-build-reject-fint-params-and-bazel-label() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build --fint-params-path foo @//src:foo "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: It's invalid to specify Bazel or GN arguments along with --fint-params-path."
}
TEST_fx-build-reject-fint-params-and-gn-label() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build --fint-params-path foo //src:foo "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: It's invalid to specify Bazel or GN arguments along with --fint-params-path."
}
TEST_fx-build-reject-fint-params-and-extra-args() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build --fint-params-path FILE -- -j20 "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: It's invalid to specify extra Ninja flags along with --fint-params-path."
}
TEST_fx-build-reject-fint-params-missing-arg() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build --fint-params-path "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: Missing --fint-params-path argument, see --help."
}
TEST_fx-build-validate-fint-params() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --fint-params-path FOO ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE fint"
}
TEST_fx-build-validate-fint-params-with-single-arg() {
setup_parsing_validation_test
BT_EXPECT "${fx}" build --fint-params-path=FOO ">" stdout.txt
BT_EXPECT_FILE_CONTAINS stdout.txt "FX_BUILD_MODE fint"
}
TEST_fx-build-reject-log-missing-arg() {
setup_parsing_validation_test
BT_EXPECT_FAIL "${fx}" build --log "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: Missing --log argument, see --help."
}
TEST_fx-build-with-fint() {
setup_build_test
setup_fint_build
BT_EXPECT "${fx}" build --fint-params-path=path/to/build_config.proto
source "${fint}.mock_state"
local expected_fint_args=(
"${fint}"
-log-level=error
build
-static=path/to/build_config.proto
-context=/dev/fd/ANY
)
# The last item in BT_MOCK_ARGS will be -context=/dev/fd/<SOME_NUMBER>
# Replace that with /dev/fd/ANY
local arg mock_args=()
for arg in "${BT_MOCK_ARGS[@]}"; do
case "${arg}" in
-context=/dev/fd/*)
arg="-context=/dev/fd/ANY"
;;
esac
mock_args+=("${arg}")
done
BT_EXPECT_EQ "${mock_args[*]}" "${expected_fint_args[*]}"
}
TEST_fx-build-with-gn-label() {
setup_build_test
setup_build_api_results "//src:foo" "obj/src/foo"
BT_EXPECT "${fx}" build //src:foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -j 8 -C ${TEST_BUILD_DIR} obj/src/foo"
}
TEST_fx-build-with-gn-label-and-j32() {
setup_build_test
setup_build_api_results "//src:foo" "obj/src/foo"
BT_EXPECT "${fx}" build -j32 //src:foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -C ${TEST_BUILD_DIR} -j32 obj/src/foo"
}
TEST_fx-build-with-gn-label-and-enable_jobserver() {
setup_build_test
# Verify that enable_jobserver=true in args.gn sets the Ninja --jobserver flag.
echo "{ \"enable_jobserver\": true }" > "${TEST_BUILD_DIR}/args.json"
setup_build_api_results "//src:foo" "obj/src/foo"
BT_EXPECT "${fx}" build //src:foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -j 8 -C ${TEST_BUILD_DIR} --jobserver obj/src/foo"
}
TEST_fx-build-with-gn-label-and-j-32() {
setup_build_test
setup_build_api_results "//src:foo" "obj/src/foo"
BT_EXPECT "${fx}" build -j 32 //src:foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -C ${TEST_BUILD_DIR} -j 32 obj/src/foo"
}
TEST_fx-build-with-ninja-path() {
setup_build_test
setup_build_api_results "obj/src/foo" "obj/src/foo"
BT_EXPECT "${fx}" build obj/src/foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -j 8 -C ${TEST_BUILD_DIR} obj/src/foo"
}
TEST_fx-build-with-bazel-non-host-label() {
setup_build_test
BT_EXPECT_FAIL "${fx}" build @//src/foo "2>" stderr.txt
BT_EXPECT_FILE_CONTAINS stderr.txt "ERROR: Only host Bazel target labels are supported for now, please use --host flag."
}
TEST_fx-build-with-bazel-host-label() {
setup_build_test
BT_EXPECT "${fx}" build --host @//src/foo
source "${bazel}.mock_state"
local expected_bazel_command=(
"${bazel}"
build
--config=host
--override_repository=gn_targets="${BT_TEMP_DIR}"/build/bazel/local_repositories/empty
@//src/foo
)
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${expected_bazel_command[*]}"
}
TEST_fx-build-with-bazel-host-label-and-j32() {
setup_build_test
BT_EXPECT "${fx}" build --host -j32 @//src/foo
source "${bazel}.mock_state"
local expected_bazel_command=(
"${bazel}"
build
--config=host
-j32
--override_repository=gn_targets="${BT_TEMP_DIR}"/build/bazel/local_repositories/empty
@//src/foo
)
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${expected_bazel_command[*]}"
}
TEST_fx-build-with-bazel-label-with-trailing-host() {
setup_build_test
# Unlike with GN build mode, --host can appear after the Bazel label
BT_EXPECT "${fx}" build @//src/foo --host
source "${bazel}.mock_state"
local expected_bazel_command=(
"${bazel}"
build
--config=host
--override_repository=gn_targets="${BT_TEMP_DIR}"/build/bazel/local_repositories/empty
@//src/foo
)
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${expected_bazel_command[*]}"
}
TEST_fx-build-with-bazel-label-with-config-host() {
setup_build_test
# Unlike GN --toolchain options, --config can appear after the Bazel label.
BT_EXPECT "${fx}" build @//src/foo --config=host
source "${bazel}.mock_state"
local expected_bazel_command=(
"${bazel}"
build
--config=host
--override_repository=gn_targets="${BT_TEMP_DIR}"/build/bazel/local_repositories/empty
@//src/foo
)
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${expected_bazel_command[*]}"
}
TEST_fx-build-produces-build-tracking-events() {
setup_build_test
track_build_event=$(btf::make_mock_binary track-build-event)
cat > "${BT_TEMP_DIR}/tools/devshell/lib/metrics.sh.mock_side_effects" <<EOF
function metrics-init {
echo ""
}
function track-command-execution {
echo ""
}
function track-feature-status {
echo ""
}
function track-command-finished {
echo ""
}
function track-build-event {
"${track_build_event}" "\$@"
}
EOF
setup_build_api_results "//src:foo" "obj/src/foo"
BT_EXPECT "${fx}" build "-j4" //src:foo
source "${ninja}.mock_state"
BT_EXPECT_EQ "${BT_MOCK_ARGS[*]}" "${ninja} -C ${TEST_BUILD_DIR} -j4 obj/src/foo"
source "${track_build_event}.mock_state"
BT_EXPECT_EQ "${#BT_MOCK_ARGS[@]}" "10"
BT_EXPECT_EQ "${BT_MOCK_ARGS[0]}" "${track_build_event}"
# [1] is start time (time-dependent)
# [2] is end time (time-dependent)
BT_EXPECT_EQ "${BT_MOCK_ARGS[3]}" "0" # status
BT_EXPECT_EQ "${BT_MOCK_ARGS[4]}" "-j4" # extra switches
BT_EXPECT_EQ "${BT_MOCK_ARGS[5]}" "obj/src/foo" # ninja targets
BT_EXPECT_EQ "${BT_MOCK_ARGS[6]}" "${TEST_BUILD_DIR}" # fuchsia build dir
# [7] is_no_op (timestamp-dependent)
# [8] is_clean_build (timestamp-dependent)
BT_EXPECT_EQ "${BT_MOCK_ARGS[9]}" "" # quiet
}
BT_RUN_TESTS "$@"