| #!/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. |
| |
| # Source platform.sh so that we can point to jq and include it as a runtime dependency. We need to |
| # do it in a subfunction since platform.sh defines readonly variables, and it gets re-sourced to |
| # avoid conflicts. |
| function read_host_platform() { |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)/../../lib/platform.sh" || exit $? |
| echo "$HOST_PLATFORM" |
| } |
| |
| BT_FILE_DEPS=( |
| "out/default/host_x64/pm" |
| "prebuilt/third_party/jq/$(read_host_platform)/bin/jq" |
| "scripts/fx" |
| "tools/devshell/lib/fx-cmd-locator.sh" |
| "tools/devshell/lib/fx-optional-features.sh" |
| "tools/devshell/lib/generate-ssh-config.sh" |
| "tools/devshell/lib/updates.sh" |
| "tools/devshell/lib/vars.sh" |
| "tools/devshell/lib/platform.sh" |
| "tools/devshell/serve" |
| "tools/devshell/jq.fx" |
| "tools/devshell/tests/subcommands/data/fx_serve_update_test/testpackage.json" |
| "tools/devshell/tests/subcommands/data/fx_serve_update_test/testpackage.manifest" |
| ) |
| |
| # Build a fake gn to satisfy vars.sh and some other commands to satisfy |
| # serve. |
| BT_MOCKED_TOOLS=( |
| "tools/devshell/build" |
| "tools/devshell/add-update-source" |
| "tools/devshell/shell" |
| ) |
| |
| declare fx |
| |
| BT_SET_UP() { |
| source "${BT_TEMP_DIR}/tools/devshell/tests/lib/fuchsia-mock.sh" |
| fx="$(btf::setup_fx)" |
| source "${BT_TEMP_DIR}/tools/devshell/lib/vars.sh" |
| fx-config-read |
| build="${BT_TEMP_DIR}/tools/devshell/build" |
| |
| export FUCHSIA_DEVICE_NAME="foo-bar-baz-quux" |
| |
| FUCHSIA_DIR="${BT_TEMP_DIR}" |
| FUCHSIA_BUILD_DIR="${FUCHSIA_DIR}/out/default" |
| FUCHSIA_DEVSHELL_DIR="${FUCHSIA_DIR}/tools/devshell" |
| readonly PM="${FUCHSIA_BUILD_DIR}/host_x64/pm" |
| readonly REPODIR="${FUCHSIA_BUILD_DIR}/amber-files" |
| |
| # Fake fuchsia dir configuration. |
| echo "out/default" > "${FUCHSIA_DIR}/.fx-build-dir" |
| touch "${FUCHSIA_BUILD_DIR}/args.gn" |
| touch "${FUCHSIA_BUILD_DIR}/fx.config" |
| } |
| |
| BT_TEAR_DOWN() { |
| if [[ -n "${serve_updates_pid}" ]]; then |
| if kill -0 "${serve_updates_pid}" 2> /dev/null; then |
| kill -TERM "${serve_updates_pid}" 2> /dev/null |
| wait "${serve_updates_pid}" 2> /dev/null |
| fi |
| fi |
| } |
| |
| # Create a TUF repository for testing. |
| _create_repo() { |
| # The pm tool needs to exist. |
| BT_ASSERT_FILE_EXISTS "${PM}" |
| |
| # Build the package from the manifest. |
| "${PM}" -o testpackage -m testpackage.manifest build |
| |
| # Archive the package to a .far. |
| (cd testpackage; "${PM}" archive) |
| |
| # Generate an empty repo and fill it with the .far. |
| "${PM}" newrepo -repo "${REPODIR}" |
| "${PM}" publish -a -f testpackage/testpackage-0.far -r "${REPODIR}" |
| BT_ASSERT_FILE_EXISTS "${REPODIR}/repository/targets.json" |
| } |
| |
| # Create a mock pm instance that will serve a file, then block until the test |
| # is complete. |
| _create_mock_pm() { |
| local serve_updates_port=$1 |
| |
| local nc_opts=( -l "${serve_updates_port}" ) |
| if [[ "$(uname -s)" != "Darwin" ]]; then |
| nc_opts+=( -q 0 ) |
| fi |
| |
| local pm="$(btf::make_installed_hosttools_mock pm)" |
| cat > "${pm}.mock_side_effects" <<EOF |
| # Run a server in the background. |
| nc ${nc_opts[@]} 2>&1 >/dev/null <<EOHTTP & |
| HTTP/1.1 200 OK |
| Content-Type: text/html; charset=UTF-8 |
| Server: netcat! |
| |
| \$(cat ${REPODIR}/repository/targets.json) |
| EOHTTP |
| pid=\$! |
| |
| # Give the server some time to get a request. Otherwise kill it and error out. |
| c=0 |
| while (( c < 10 )); do |
| if kill -0 "\${pid}"; then |
| wait "\${pid}" |
| err=\$? |
| if [[ "\${err}" -eq 0 ]]; then |
| break |
| else |
| echo "nc died with \${err}" 1>&2 |
| exit "\${err}" |
| fi |
| fi |
| |
| sleep 1 |
| done |
| |
| if (( c == 10 )); then |
| echo "timed out waiting for request" 1>&2 |
| exit 1 |
| fi |
| |
| # Even though serve will try to kill the pm process when it is shut |
| # down, it won't actually kill the mock pm, because serve kills only |
| # kills the pm mock wrapper, not the mock_side_effects. So in order to |
| # actually shut down the mock pm, wait for the real binary to be deleted. |
| # Wait for the mock pm to be removed, then shut down. |
| c=0 |
| while (( c < 10 )); do |
| if [[ ! -f "${pm}" ]]; then |
| exit 0 |
| fi |
| |
| sleep 1 |
| (( c++ )) |
| done |
| |
| echo "timed out waiting for ${pm} to be removed" 1>&2 |
| exit 1 |
| EOF |
| } |
| |
| # Wait for serve to report it is ready to push packages. |
| _wait_for_serve_updates_to_start() { |
| local serve_updates_pid=$1 |
| local stdout=$2 |
| |
| local tries=0 |
| while (( tries < 10 )); do |
| # Error out if serve failed. |
| if ! kill -0 "${serve_updates_pid}" 2> /dev/null; then |
| BT_FAIL "serve failed" |
| fi |
| |
| if [[ -e "${stdout}" ]]; then |
| if grep "Ready to push packages!" "${stdout}" 1> /dev/null; then |
| break |
| fi |
| fi |
| |
| sleep 0.1 |
| (( tries++ )) |
| done |
| |
| if [[ "${tries}" -eq 10 ]]; then |
| BT_FAIL "timed out waiting for serve to start" |
| fi |
| |
| return 0 |
| } |
| |
| _get_targets() { |
| local port=$1 |
| curl -s http://127.0.0.1:${port}/targets.json > targets.json |
| return $? |
| } |
| |
| _check_targets() { |
| local port=$1 |
| local -i max_tries=10 |
| local -i try_count=1 |
| local -i status=0 |
| _get_targets "${port}" |
| status=$? |
| while (( "${status}" != 0 && try_count < max_tries )); do |
| sleep 0.1 |
| _get_targets "${port}" |
| status=$? |
| : $(( try_count++ )) |
| done |
| |
| BT_ASSERT_GOOD_STATUS "${status}" "Failed to get targets.json after ${max_tries} attempts. Last status was '${status}'." |
| |
| # Confirm testpackage is one of the targets. |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING targets.json "testpackage/0" |
| } |
| |
| TEST_fx_serve_updates_with_pm() { |
| cd data/fx_serve_update_test |
| BT_ASSERT _create_repo |
| |
| # Listen on a unique port. |
| local serve_updates_port=9083 |
| |
| BT_ASSERT _create_mock_pm "${serve_updates_port}" |
| |
| local ffx="$(btf::make_installed_hosttools_mock ffx)" |
| cat > "${ffx}.mock_side_effects" << "EOF" |
| if [[ "$@" == "config get repository.server.mode" ]]; then |
| echo "\"pm\"" |
| elif [[ "$@" == "config get repository.server.listen" ]]; then |
| echo "\"[::]:${serve_update_port}\"" |
| else |
| echo "127.0.0.1" |
| fi |
| EOF |
| |
| # Fire up fx serve. |
| "${FUCHSIA_DEVSHELL_DIR}/serve" -l "${serve_updates_port}" 1>stdout & |
| local serve_updates_pid=$! |
| |
| # Give fx serve some time to come up. |
| BT_ASSERT _wait_for_serve_updates_to_start "${serve_updates_pid}" stdout |
| |
| # Request the list of targets. Confirm testpackage is one of the targets. |
| BT_ASSERT _check_targets "${serve_updates_port}" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING targets.json "testpackage/0" |
| |
| # Shut down our fake pm, which should stop serve. |
| kill "${serve_updates_pid}" |
| wait "${serve_updates_pid}" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stdout "Ready to push packages!" |
| |
| return 0 |
| } |
| |
| TEST_fx_serve_updates_with_pm_with_incremental() { |
| cd data/fx_serve_update_test |
| BT_ASSERT _create_repo |
| |
| # Listen on a unique port. |
| local serve_updates_port=9084 |
| |
| BT_ASSERT _create_mock_pm "${serve_updates_port}" |
| |
| local ffx="$(btf::make_installed_hosttools_mock ffx)" |
| cat > "${ffx}.mock_side_effects" <<EOF |
| if [[ "\$@" == "config get repository.server.mode" ]]; then |
| echo "\"pm\"" |
| elif [[ "$@" == "config get repository.server.listen" ]]; then |
| echo "\"[::]:${serve_update_port}\"" |
| else |
| echo "127.0.0.1" |
| fi |
| EOF |
| |
| # Fire up fx serve. Note, test output is captured, so re-route background task output |
| # to stderr to avoid hanging. |
| FUCHSIA_DISABLED_incremental_legacy=0 "${FUCHSIA_DEVSHELL_DIR}/serve" -v -l "${serve_updates_port}" 1>stdout 2>stderr & |
| local serve_updates_pid=$! |
| |
| # Give fx serve some time to come up. |
| BT_ASSERT _wait_for_serve_updates_to_start "${serve_updates_pid}" stdout |
| |
| # Request the list of targets. Confirm testpackage is one of the targets. |
| BT_ASSERT _check_targets "${serve_updates_port}" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING targets.json "testpackage/0" |
| |
| # Shut down our fake pm, which should stop serve. |
| kill "${serve_updates_pid}" |
| wait "${serve_updates_pid}" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stdout "Ready to push packages!" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stderr "Incremental package auto-publishing is enabled for pm." |
| } |
| |
| TEST_fx_serve_updates_exits_if_pm_fails_to_start() { |
| cd data/fx_serve_update_test |
| BT_ASSERT _create_repo |
| |
| # Listen on a unique port. |
| local serve_updates_port=9085 |
| |
| local pm="$(btf::make_installed_hosttools_mock pm)" |
| local lockfile="${BT_TEMP_DIR}/.pm.lock" |
| echo 5 > "${pm}.mock_status" |
| |
| local ffx="$(btf::make_installed_hosttools_mock ffx)" |
| cat > "${ffx}.mock_side_effects" << "EOF" |
| if [[ "$@" == "config get repository.server.mode" ]]; then |
| echo "\"pm\"" |
| else |
| echo "127.0.0.1" |
| fi |
| EOF |
| |
| # Fire up fx serve. Note, test output is captured, so re-route background task output |
| # to stderr to avoid hanging. |
| BT_EXPECT_FAIL "${FUCHSIA_DEVSHELL_DIR}/serve" -l ":${serve_updates_port}" 2> stderr |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stderr "The repository server died." |
| } |
| |
| TEST_fx_serve_updates_with_ffx() { |
| cd data/fx_serve_update_test |
| BT_ASSERT _create_repo |
| |
| # Listen on a unique port. |
| local serve_updates_port=9086 |
| |
| local shell="${FUCHSIA_DIR}/tools/devshell/shell" |
| btf::make_mock "${shell}" |
| cat > "${shell}.mock_side_effects" <<EOF |
| if [[ "\$@" == "clock --monotonic" ]]; then |
| echo "0" |
| fi |
| EOF |
| |
| # Create a mock ffx, which runs in ffx mode, and writes to files when the server is started and stopped. |
| local ffx="$(btf::make_installed_hosttools_mock ffx)" |
| local started_server="${BT_TEMP_DIR}/started-server" |
| local stopped_server="${BT_TEMP_DIR}/stopped-server" |
| cat > "${ffx}.mock_side_effects" <<EOF |
| if [[ "\$@" == "config get repository.server.mode" ]]; then |
| echo "\"ffx\"" |
| elif [[ "\$@" == "config get repository.server.listen" ]]; then |
| echo "\"[::]:${serve_updates_port}\"" |
| elif [[ "\$@" == "repository server start --address [::]:${serve_updates_port}" ]]; then |
| touch "${started_server}" |
| exit 0 |
| elif [[ "\$@" == "repository server stop" ]]; then |
| touch "${stopped_server}" |
| exit 0 |
| elif [[ "\$@" == "--config ffx_repository=true --machine json repository server status" ]]; then |
| if [ -e "${started_server}" ]; then |
| echo "{\"state\": \"running\", \"address\": \"[::]:${serve_updates_port}\"}" |
| else |
| echo "{\"state\": \"stopped\"}" |
| fi |
| else |
| echo "127.0.0.1" |
| fi |
| EOF |
| |
| # Fire up fx serve. Note, test output is captured, so re-route background task output |
| # to stderr to avoid hanging. |
| "${FUCHSIA_DEVSHELL_DIR}/serve" -l ":${serve_updates_port}" 1>stdout & |
| local serve_updates_pid=$! |
| |
| _wait_for_serve_updates_to_start "${serve_updates_pid}" stdout |
| |
| # Shut down our fake ffx server, which should stop serve. |
| kill "${serve_updates_pid}" |
| wait "${serve_updates_pid}" |
| |
| BT_ASSERT_FILE_EXISTS "${started_server}" |
| BT_ASSERT_FILE_EXISTS "${stopped_server}" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stdout "Ready to push packages!" |
| } |
| |
| TEST_fx_serve_updates_with_ffx_with_incremental() { |
| cd data/fx_serve_update_test |
| BT_ASSERT _create_repo |
| |
| # Listen on a unique port. |
| local serve_updates_port=9087 |
| |
| local shell="${FUCHSIA_DIR}/tools/devshell/shell" |
| btf::make_mock "${shell}" |
| cat > "${shell}.mock_side_effects" <<EOF |
| if [[ "\$@" == "clock --monotonic" ]]; then |
| echo "0" |
| fi |
| EOF |
| |
| BT_ASSERT _create_mock_pm "${serve_updates_port}" |
| |
| # `package-tool` monitors filesystem and publishes package updates. |
| local package_tool="$(btf::make_installed_hosttools_mock package-tool)" |
| |
| local shell="${FUCHSIA_DIR}/tools/devshell/shell" |
| btf::make_mock "${shell}" |
| cat > "${shell}.mock_side_effects" <<EOF |
| if [[ "\$@" == "clock --monotonic" ]]; then |
| echo "0" |
| fi |
| EOF |
| |
| # ffx repository server is used, but the package updates are handled by the `package-tool`. |
| local ffx="$(btf::make_installed_hosttools_mock ffx)" |
| local started_server="${BT_TEMP_DIR}/started-server" |
| local stopped_server="${BT_TEMP_DIR}/stopped-server" |
| cat > "${ffx}.mock_side_effects" <<EOF |
| if [[ "\$@" == "config get repository.server.mode" ]]; then |
| echo "\"ffx\"" |
| elif [[ "\$@" == "config get repository.server.listen" ]]; then |
| echo "\"[::]:${serve_updates_port}\"" |
| elif [[ "\$@" == "repository server start --address [::]:${serve_updates_port}" ]]; then |
| touch "${started_server}" |
| exit 0 |
| elif [[ "\$@" == "repository server stop" ]]; then |
| touch "${stopped_server}" |
| exit 0 |
| elif [[ "\$@" == "--config ffx_repository=true --machine json repository server status" ]]; then |
| if [ -e "${started_server}" ]; then |
| echo "{\"state\": \"running\", \"address\": \"[::]:${serve_updates_port}\"}" |
| else |
| echo "{\"state\": \"stopped\"}" |
| fi |
| else |
| echo "127.0.0.1" |
| fi |
| EOF |
| |
| # Fire up fx serve. Note, test output is captured, so re-route background task output |
| # to stderr to avoid hanging. |
| FUCHSIA_DISABLED_incremental=0 "${FUCHSIA_DEVSHELL_DIR}/serve" -v -l "${serve_updates_port}" 1>stdout 2>stderr & |
| local serve_updates_pid=$! |
| |
| BT_ASSERT _wait_for_serve_updates_to_start "${serve_updates_pid}" stdout |
| |
| # Shut down our fake ffx server, which should stop serve. |
| kill "${serve_updates_pid}" |
| wait "${serve_updates_pid}" |
| |
| BT_ASSERT_FILE_EXISTS "${started_server}" |
| BT_ASSERT_FILE_EXISTS "${stopped_server}" |
| |
| btf::expect-mock-args \ |
| "${package_tool}" "package-tool" "repository" \ |
| "publish" "${REPODIR}" \ |
| "--watch" \ |
| "--package-list" "all_package_manifests.list" \ |
| "--trusted-root" "${REPODIR}/repository/9.root.json" \ |
| "--ignore-missing-packages" \ |
| "--time-versioning" |
| |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stdout "Ready to push packages!" |
| BT_EXPECT_FILE_CONTAINS_SUBSTRING stderr "Incremental package auto-publishing is enabled for ffx." |
| } |
| |
| BT_RUN_TESTS "$@" |