| #!/bin/bash |
| # Copyright 2019 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. |
| |
| ### Test expected behavior of fx test |
| |
| BT_LINKED_DEPS=( |
| "third_party" |
| "prebuilt/third_party/dart" |
| "scripts/fxtest" |
| "scripts/fxutils" |
| ) |
| |
| BT_FILE_DEPS=( |
| "scripts/fx" |
| "tools/devshell/tests/subcommands/data/fx_test_test/tests_hashfile" |
| "tools/devshell/tests/subcommands/data/fx_test_test/tests_multiple_in_package.json" |
| "tools/devshell/tests/subcommands/data/fx_test_test/tests_package_server_integration.json" |
| "tools/devshell/lib/fx-cmd-locator.sh" |
| "tools/devshell/lib/fx-optional-features.sh" |
| "tools/devshell/lib/vars.sh" |
| "tools/devshell/lib/platform.sh" |
| "tools/devshell/test" |
| ) |
| |
| BT_MOCKED_TOOLS=( |
| "tools/devshell/build" |
| "tools/devshell/is-package-server-running" |
| "tools/devshell/update-if-in-base" |
| "tools/devshell/shell" |
| "tools/devshell/symbolize" |
| "tools/devshell/lib/metrics_custom_report.sh" |
| ) |
| |
| declare fx DATA_DIR |
| |
| BT_SET_UP() { |
| source "${BT_TEMP_DIR}/tools/devshell/tests/lib/fuchsia-mock.sh" |
| fx="$(btf::setup_fx)" |
| btf::make_installed_hosttools_mock "ffx" > /dev/null |
| btf::make_installed_hosttools_mock "symbolizer" > /dev/null |
| btf::make_installed_hosttools_mock "symbol-index" > /dev/null |
| DATA_DIR="${BT_TEMP_DIR}/tools/devshell/tests/subcommands/data/fx_test_test" |
| } |
| |
| # Test that the "fx test --info" outputs in the format expected by other |
| # commands, eg `fx run-test` |
| TEST_fxtest_info() { |
| cp "${DATA_DIR}/tests_multiple_in_package.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| BT_EXPECT ${fx} test --info --exact > "${out}" |
| BT_EXPECT_EQ "$(sed -n 's/^package_url: \(.*\)/\1/p' "${out}" | wc -l)" 7 |
| } |
| |
| # Test that `fx test` calls `fx update-if-in-base` and `fx is-package-server-running` properly |
| TEST_fxtest_package_server_integration() { |
| cp "${DATA_DIR}/tests_package_server_integration.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname="overflow_fuzzer_test" |
| BT_EXPECT ${fx} test --no-use-package-hash ${testname} > ${out} |
| # ensure that is-package-server-running was called |
| BT_ASSERT_FILE_EXISTS "${BT_TEMP_DIR}/tools/devshell/is-package-server-running.mock_state" |
| # ensure that update-if-in-base was called with the proper testname |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/update-if-in-base" "${testname}" |
| } |
| |
| # Ensure that `fx build` is called by default |
| TEST_fxtest_build() { |
| cp "${DATA_DIR}/tests_package_server_integration.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname="overflow_fuzzer_test" |
| BT_EXPECT ${fx} test --no-use-package-hash ${testname} > ${out} |
| # ensure that fx build was called |
| # TODO: once fx test calls fx build with a specific target, check it here as well |
| BT_ASSERT_FILE_EXISTS "${BT_TEMP_DIR}/tools/devshell/build.mock_state" |
| } |
| |
| # Ensure that `fx test` exits with a non-zero status if `fx build` fails. |
| TEST_fxtest_build_failure_exits_with_nonzero_status() { |
| cp "${DATA_DIR}/tests_package_server_integration.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname="overflow_fuzzer_test" |
| echo 1 > "${BT_TEMP_DIR}/tools/devshell/build.mock_status" |
| BT_EXPECT_FAIL ${fx} test --no-use-package-hash ${testname} > ${out} |
| # ensure that fx build was called |
| # TODO: once fx test calls fx build with a specific target, check it here as well |
| BT_ASSERT_FILE_EXISTS "${BT_TEMP_DIR}/tools/devshell/build.mock_state" |
| } |
| |
| # Ensure that `fx build` is not called when "--no-build" option is given |
| TEST_fxtest_nobuild() { |
| cp "${DATA_DIR}/tests_package_server_integration.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname="overflow_fuzzer_test" |
| BT_EXPECT ${fx} test --no-use-package-hash --no-build ${testname} > ${out} |
| # ensure that fx build was called |
| # TODO: once fx test calls fx build with a specific target, check it here as well |
| BT_ASSERT_FILE_DOES_NOT_EXIST "${BT_TEMP_DIR}/tools/devshell/build.mock_state" |
| } |
| |
| # Ensure that `fx test` exits with a non-zero status if |
| # `fx # is-package-server-running` fails. |
| TEST_fxtest_fails_with_no_package_server() { |
| cp "${DATA_DIR}/tests_package_server_integration.json" "${BT_TEMP_DIR}/out/default/tests.json" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname1="overflow_fuzzer_test" |
| echo 1 > "${BT_TEMP_DIR}/tools/devshell/is-package-server-running.mock_status" |
| BT_EXPECT_FAIL ${fx} test --no-use-package-hash --no-build ${testname} > ${out} |
| # ensure that fx is-package-server-running was called |
| BT_ASSERT_FILE_EXISTS "${BT_TEMP_DIR}/tools/devshell/is-package-server-running.mock_state" |
| } |
| |
| # Test that "fx test" runs a component test pinning it to the hash (merkleroot) of |
| # the component package, so that the user has confidence that if a test runs, it is |
| # running the exact same version that has been built |
| TEST_fxtest_hashpinnning() { |
| cp -R "${DATA_DIR}/tests_hashfile/out" "${BT_TEMP_DIR}" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname1="overflow_fuzzer_test" |
| local hash1="913cdd63ab4aa794694448450505efaa2a8fe27fb33888e5156da9db60ac0a29" |
| local testname2="hello_world_cpp_unittests" |
| local hash2="7a604498e05fa012391b6b51da9cc74ff6a6a9d25b1376de98125c194232bfa1" |
| |
| # expect that "fx shell run-test-component URL-WITH-HASH" was executed |
| BT_EXPECT ${fx} test --no-build ${testname1} >> "${out}" |
| local packageUrl1="fuchsia-pkg://fuchsia.com/example-fuzzers?hash=${hash1}#meta/overflow_fuzzer_test.cmx" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/shell" "run-test-component" "${packageUrl1}" |
| |
| # expect that "fx shell run-test-component URL-WITH-HASH" was executed |
| BT_EXPECT ${fx} test --no-build ${testname2} >> "${out}" |
| local packageUrl2="fuchsia-pkg://fuchsia.com/hello_world_cpp_tests?hash=${hash2}#meta/hello_world_cpp_unittests.cmx" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/shell.mock_state.2" "run-test-component" "${packageUrl2}" |
| } |
| |
| # Test that when the merkle root of a package is changed as a result of the |
| # 'fx build' executed by fx test, the new hash is used when pinning the |
| # package in fx shell run-test-component |
| TEST_fxtest_updated_hash() { |
| cp -R "${DATA_DIR}/tests_hashfile/out" "${BT_TEMP_DIR}" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local testname1="overflow_fuzzer_test" |
| local hash_old="913cdd63ab4aa794694448450505efaa2a8fe27fb33888e5156da9db60ac0a29" |
| local hash_new="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" |
| |
| # create a build script that, when called, changes hash_old to hash_new in the |
| # package repository |
| cat > "${BT_TEMP_DIR}/tools/devshell/build.mock_side_effects" <<EOF |
| sed -i 's/${hash_old}/${hash_new}/g' ${BT_TEMP_DIR}/out/default/amber-files/repository/targets.json |
| EOF |
| # expect that "fx shell run-test-component URL-WITH-HASH" executes with the new hash, not the old one |
| BT_EXPECT ${fx} test ${testname1} >> "${out}" |
| local packageUrl="fuchsia-pkg://fuchsia.com/example-fuzzers?hash=${hash_new}#meta/overflow_fuzzer_test.cmx" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/shell" "run-test-component" "${packageUrl}" |
| } |
| |
| # Test that "fx test" fails if a component test doesn't have an entry in the |
| # package repository |
| TEST_fxtest_no_hashfile() { |
| cp -R "${DATA_DIR}/tests_hashfile/out" "${BT_TEMP_DIR}" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [], |
| "test": { |
| "cpu": "arm64", |
| "label": "//examples/fuzzer:fuzzing-examples_pkg(//build/toolchain/fuchsia:arm64)", |
| "name": "overflow_fuzzer_test", |
| "os": "fuchsia", |
| "package_url": "fuchsia-pkg://fuchsia.com/example_not_in_repository#meta/overflow_fuzzer_test.cmx", |
| "path": "" |
| } |
| }] |
| EOF |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| |
| # expect that fx test fails because the repository doesn't have an entry for |
| # this test's package (example_not_in_repository) |
| BT_EXPECT_FAIL ${fx} test --no-build overflow_fuzzer_test >> "${out}" |
| } |
| |
| # Test that "fx test" builds only the minimal target for device tests |
| TEST_fxtest_build_device_only_package() { |
| mkdir -p "${BT_TEMP_DIR}/out/default" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [], |
| "test": { |
| "cpu": "arm64", |
| "label": "//examples/fuzzer:fuzzing-examples_pkg(//build/toolchain/fuchsia:arm64)", |
| "package_label": "//examples/fuzzer:fuzzing_pkg(//build/toolchain/fuchsia:arm64)", |
| "name": "overflow_fuzzer_test", |
| "os": "fuchsia", |
| "package_url": "fuchsia-pkg://fuchsia.com/example-fuzzers#meta/overflow_fuzzer_test.cmx", |
| "path": "" |
| } |
| }] |
| EOF |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| |
| # with incremental enabled, expect "fx shell build <test_package>" |
| BT_EXPECT ${fx} --enable=incremental test --no-use-package-hash overflow_fuzzer_test >> "${out}" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/build" "examples/fuzzer:fuzzing_pkg" |
| # with incremental disabled, expect "fx shell build updates" |
| BT_EXPECT ${fx} --disable=incremental test --no-use-package-hash overflow_fuzzer_test >> "${out}" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/build.mock_state.2" "updates" |
| } |
| |
| # Test that "fx test" builds the default target for an e2e test |
| TEST_fxtest_build_e2e() { |
| mkdir -p "${BT_TEMP_DIR}/out/default" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [{"dimensions": {"device_type": "qemu_x64"}}], |
| "test": { |
| "cpu": "x64", |
| "label": "//examples/example_host_test:host_tools_example_pkg(//build/toolchain/host_x64)", |
| "name": "example_host_test", |
| "os": "linux", |
| "path": "host_x64/example_host_test" |
| } |
| }] |
| EOF |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| |
| btf::make_hosttools_mock "example_host_test" > /dev/null |
| |
| # expect "fx shell build" |
| BT_EXPECT ${fx} test --e2e example_host_test >> "${out}" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/build" |
| } |
| |
| # Test that "fx test" only builds the host tool for a host test |
| TEST_fxtest_build_host() { |
| mkdir -p "${BT_TEMP_DIR}/out/default" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [], |
| "test": { |
| "cpu": "x64", |
| "label": "//examples/example_host_test:host_tools_example_pkg(//build/toolchain/host_x64)", |
| "name": "example_host_test", |
| "os": "linux", |
| "path": "host_x64/example_host_test" |
| } |
| }] |
| EOF |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| |
| btf::make_hosttools_mock "example_host_test" > /dev/null |
| |
| # expect "fx shell build" |
| BT_EXPECT ${fx} test example_host_test >> "${out}" |
| btf::expect-mock-args "${BT_TEMP_DIR}/tools/devshell/build" "host_x64/example_host_test" |
| } |
| |
| # Test that "fx test" executes an e2e test with the proper env variables set |
| TEST_fxtest_e2e_env() { |
| mkdir -p "${BT_TEMP_DIR}/out/default" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [{"dimensions": {"device_type": "qemu_x64"}}], |
| "test": { |
| "cpu": "x64", |
| "label": "//examples/example_host_test:host_tools_example_pkg(//build/toolchain/host_x64)", |
| "name": "example_host_test", |
| "os": "linux", |
| "path": "host_x64/example_host_test" |
| } |
| }] |
| EOF |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local test_exec="$(btf::make_hosttools_mock "example_host_test")" |
| # some 'fx test' features call this function to track test execution if the |
| # user has metrics enabled. |
| function track-subcommand-custom-event { |
| : |
| } |
| export -f track-subcommand-custom-event |
| |
| # when the test is executed, print out the e2e variables as they are given to the test |
| cat > "${test_exec}.mock_side_effects" <<'EOF' |
| printVar() { |
| local n="$1" |
| # print var if it's set, even if it's empty |
| [[ -n ${!n+x} ]] && echo "TESTING_E2E_VARS: $n=${!n}" > /dev/stderr |
| } |
| printVar FUCHSIA_DEVICE_ADDR |
| printVar FUCHSIA_SSH_KEY |
| printVar FUCHSIA_SSH_PORT |
| printVar FUCHSIA_TEST_OUTDIR |
| printVar SL4F_HTTP_PORT |
| printVar FUCHSIA_IPV4_ADDR |
| exit 0 |
| EOF |
| # check if environemnt is as expected for a user-defined IPV4 device and SSH port |
| BT_EXPECT ${fx} -d 1.1.1.1:8123 test -o --e2e example_host_test | grep "TESTING_E2E_VARS" > "${out}" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_DEVICE_ADDR=1.1.1.1" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_SSH_PORT=8123" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_SSH_KEY=..*" # ensures no empty definition |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_TEST_OUTDIR=..*" # ensures no empty definition |
| |
| # check if environemnt is as expected for a user-defined IPV6 device and SSH port |
| BT_EXPECT ${fx} -d '[::1]:1111' test -o --e2e example_host_test | grep "TESTING_E2E_VARS" > "${out}" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_DEVICE_ADDR=[::1]" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_SSH_PORT=1111" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_SSH_KEY=..*" # ensures no empty definition |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "FUCHSIA_TEST_OUTDIR=..*" # ensures no empty definition |
| |
| # no device set, test should not be given a fuchsia_ssh_port, and fuchsia_device_addr must be defined as empty |
| BT_EXPECT ${fx} test -o --e2e example_host_test | grep "TESTING_E2E_VARS" > "${out}" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" 'FUCHSIA_DEVICE_ADDR=$' # ensures empty definition |
| BT_EXPECT_EMPTY "$(grep FUCHSIA_SSH_PORT "${out}")" "unexpected FUCHSIA_SSH_PORT" |
| |
| # if defined in the caller's environment, sl4f_http_port is passed as is |
| BT_EXPECT SL4F_HTTP_PORT=9099 ${fx} test -o --e2e example_host_test | grep "TESTING_E2E_VARS" > "${out}" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING "${out}" "SL4F_HTTP_PORT=9099" |
| |
| # fuchsia_test_outdir must be a valid and writable directory |
| BT_EXPECT ${fx} test -o --e2e example_host_test > "${out}" |
| local test_out_dir="$(sed -n "s/.* FUCHSIA_TEST_OUTDIR=\(.*\)/\1/p" "${out}")" |
| BT_EXPECT touch "${test_out_dir}/test_file" |
| } |
| |
| # Ensure that if `fx build` changes `tests.json`, `fx test` loads the updated |
| # tests.json before running the test. |
| TEST_fxtest_build_changes_testsjson() { |
| mkdir -p "${BT_TEMP_DIR}/out/default" |
| local out="${BT_TEMP_DIR}/_fx_test_output" |
| local test_exec="$(btf::make_hosttools_mock "example_host_test")" |
| cat > "${BT_TEMP_DIR}/out/default/tests.json" <<EOF |
| [{"environments": [], |
| "test": { |
| "command": [ |
| "host_x64/example_host_test", |
| "beforefxbuild" |
| ], |
| "cpu": "x64", |
| "label": "//examples/example_host_test:host_tools_example_pkg(//build/toolchain/host_x64)", |
| "name": "example_host_test", |
| "os": "linux", |
| "path": "host_x64/example_host_test" |
| } |
| }] |
| EOF |
| # make 'fx build' change the command argument in tests.json from "beforefxbuild" |
| # to "afterfxbuild" |
| cat > "${BT_TEMP_DIR}/tools/devshell/build.mock_side_effects" <<EOF |
| sed -i 's/beforefxbuild/afterfxbuild/' "${BT_TEMP_DIR}/out/default/tests.json" |
| EOF |
| |
| BT_EXPECT ${fx} test example_host_test > ${out} |
| |
| btf::expect-mock-args "${test_exec}.mock_state" "afterfxbuild" |
| } |
| |
| |
| |
| BT_RUN_TESTS "$@" |