03_test_script.sh raw

   1  #!/usr/bin/env bash
   2  #
   3  # Copyright (c) 2018-present The Bitcoin Core developers
   4  # Distributed under the MIT software license, see the accompanying
   5  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   6  
   7  export LC_ALL=C.UTF-8
   8  
   9  set -o errexit -o xtrace -o pipefail
  10  
  11  if [ "${DANGER_RUN_CI_ON_HOST}" != "1" ]; then
  12    echo "This script will make unsafe local and global modifications, so it can only be run inside a container and requires DANGER_RUN_CI_ON_HOST=1"
  13    exit 1
  14  fi
  15  
  16  cd "${BASE_ROOT_DIR}"
  17  
  18  export PATH="/path_with space:${PATH}"
  19  export ASAN_OPTIONS="detect_leaks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1"
  20  export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan"
  21  export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:halt_on_error=1:second_deadlock_stack=1"
  22  export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1"
  23  
  24  echo "Number of available processing units: $(nproc)"
  25  if [ "$CI_OS_NAME" == "macos" ]; then
  26    top -l 1 -s 0 | awk ' /PhysMem/ {print}'
  27  else
  28    free -m -h
  29    echo "System info: $(uname --kernel-name --kernel-release)"
  30    lscpu
  31  fi
  32  echo "Free disk space:"
  33  df -h
  34  
  35  # We force an install of linux-headers again here via $PACKAGES to fix any
  36  # kernel mismatch between a cached docker image and the underlying host.
  37  # This can happen occasionally on hosted runners if the runner image is updated.
  38  if [[ "$CONTAINER_NAME" == "ci_native_asan" ]]; then
  39    $CI_RETRY_EXE apt-get update
  40    ${CI_RETRY_EXE} bash -c "apt-get install --no-install-recommends --no-upgrade -y $PACKAGES"
  41  fi
  42  
  43  # What host to compile for. See also ./depends/README.md
  44  # Tests that need cross-compilation export the appropriate HOST.
  45  # Tests that run natively guess the host
  46  export HOST=${HOST:-$("$BASE_ROOT_DIR/depends/config.guess")}
  47  
  48  echo "=== BEGIN env ==="
  49  env
  50  echo "=== END env ==="
  51  
  52  # The CI framework should be flexible where it is run from. For example, from
  53  # a git-archive, a git-worktree, or a normal git repo.
  54  # The iwyu task requires a working git repo, which may not always be
  55  # available, so initialize one with force.
  56  if [[ "${RUN_IWYU}" == true ]]; then
  57    mv .git .git_ci_backup || true
  58    git init
  59    git add ./src  # the git diff command used later for iwyu only cares about ./src
  60    git config user.email "ci@ci"
  61    git config user.name "CI"
  62    git commit -m "dummy CI ./src init for IWYU"
  63  fi
  64  
  65  if [ "$RUN_FUZZ_TESTS" = "true" ]; then
  66    export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_corpora/
  67    if [ ! -d "$DIR_FUZZ_IN" ]; then
  68      ${CI_RETRY_EXE} git clone --depth=1 https://github.com/bitcoin-core/qa-assets "${DIR_QA_ASSETS}"
  69    fi
  70    (
  71      cd "${DIR_QA_ASSETS}"
  72      echo "Using qa-assets repo from commit ..."
  73      git log -1
  74    )
  75  elif [ "$RUN_UNIT_TESTS" = "true" ]; then
  76    export DIR_UNIT_TEST_DATA=${DIR_QA_ASSETS}/unit_test_data/
  77    if [ ! -d "$DIR_UNIT_TEST_DATA" ]; then
  78      mkdir -p "$DIR_UNIT_TEST_DATA"
  79      ${CI_RETRY_EXE} curl --location --fail https://github.com/bitcoin-core/qa-assets/raw/main/unit_test_data/script_assets_test.json -o "${DIR_UNIT_TEST_DATA}/script_assets_test.json"
  80    fi
  81  fi
  82  
  83  # Make sure default datadir does not exist and is never read by creating a dummy file
  84  if [ "$CI_OS_NAME" == "macos" ]; then
  85    echo > "${HOME}/Library/Application Support/Bitcoin"
  86  else
  87    echo > "${HOME}/.bitcoin"
  88  fi
  89  
  90  if [ -z "$NO_DEPENDS" ]; then
  91    if [[ $CI_IMAGE_NAME_TAG == *alpine* ]]; then
  92      SHELL_OPTS="CONFIG_SHELL=/usr/bin/dash"
  93    else
  94      SHELL_OPTS="CONFIG_SHELL="
  95    fi
  96    bash -c "$SHELL_OPTS make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS LOG=1"
  97  fi
  98  if [ "$DOWNLOAD_PREVIOUS_RELEASES" = "true" ]; then
  99    test/get_previous_releases.py --target-dir "$PREVIOUS_RELEASES_DIR"
 100  fi
 101  
 102  BITCOIN_CONFIG_ALL="-DCMAKE_COMPILE_WARNING_AS_ERROR=ON -DBUILD_BENCH=ON -DBUILD_FUZZ_BINARY=ON"
 103  if [ -z "$NO_DEPENDS" ]; then
 104    BITCOIN_CONFIG_ALL="${BITCOIN_CONFIG_ALL} -DCMAKE_TOOLCHAIN_FILE=$DEPENDS_DIR/$HOST/toolchain.cmake"
 105  fi
 106  
 107  ccache --zero-stats
 108  
 109  # Folder where the build is done.
 110  BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build-$HOST}
 111  
 112  BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL '-DCMAKE_INSTALL_PREFIX=$BASE_OUTDIR' -Werror=dev"
 113  
 114  if [[ "${RUN_IWYU}" == true || "${RUN_TIDY}" == true ]]; then
 115    BITCOIN_CONFIG_ALL="$BITCOIN_CONFIG_ALL -DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
 116  fi
 117  
 118  eval "CMAKE_ARGS=($BITCOIN_CONFIG_ALL $BITCOIN_CONFIG)"
 119  cmake -S "$BASE_ROOT_DIR" -B "$BASE_BUILD_DIR" "${CMAKE_ARGS[@]}" || (
 120    cd "${BASE_BUILD_DIR}"
 121    # shellcheck disable=SC2046
 122    cat $(cmake -P "${BASE_ROOT_DIR}/ci/test/GetCMakeLogFiles.cmake")
 123    false
 124  )
 125  
 126  if [[ "${GOAL}" != all && "${GOAL}" != codegen ]]; then
 127    GOAL="all ${GOAL}"
 128  fi
 129  
 130  # shellcheck disable=SC2086
 131  cmake --build "${BASE_BUILD_DIR}" "$MAKEJOBS" --target $GOAL || (
 132    echo "Build failure. Verbose build follows."
 133    # shellcheck disable=SC2086
 134    cmake --build "${BASE_BUILD_DIR}" -j1 --target $GOAL --verbose
 135    false
 136  )
 137  
 138  ccache --version | head -n 1 && ccache --show-stats --verbose
 139  ccache --print-stats | python3 -c '
 140  import os
 141  import sys
 142  
 143  for line in sys.stdin:
 144      key, value = line.split("\t", 1)
 145      # "primary storage" fallback only needed for ccache version 4.5.1
 146      if key in ("local_storage_hit", "primary_storage_hit"):
 147          hits = int(value)
 148      elif key in ("local_storage_miss", "primary_storage_miss"):
 149          miss = int(value)
 150  
 151  calls = hits + miss
 152  # codegen has no calls, so skip that here
 153  if calls:
 154      rate = (hits / calls * 100)
 155      print(f"{rate:.2f}")
 156      if rate < 75:
 157          container = os.environ["CONTAINER_NAME"]
 158          print(
 159              "::notice title=low ccache hitrate::"
 160              f"Ccache hit-rate in {container} was {rate:.2f}%"
 161          )
 162  '
 163  du -sh "${DEPENDS_DIR}"/*/
 164  du -sh "${PREVIOUS_RELEASES_DIR}"
 165  
 166  if [ -n "${CI_LIMIT_STACK_SIZE}" ]; then
 167    ulimit -s 512
 168  fi
 169  
 170  if [[ ${BARE_METAL_RISCV} == "true" ]]; then
 171      export BASE_BUILD_DIR
 172      "${BASE_ROOT_DIR}/ci/test/link-riscv.sh"
 173  fi
 174  
 175  if [ -n "$USE_VALGRIND" ]; then
 176    "${BASE_ROOT_DIR}/ci/test/wrap-valgrind.py"
 177  fi
 178  
 179  if [ "$RUN_CHECK_DEPS" = "true" ]; then
 180    "${BASE_ROOT_DIR}/contrib/devtools/check-deps.sh" "${BASE_BUILD_DIR}"
 181  fi
 182  
 183  if [[ "$CI_OS_NAME" == "macos" && "${GOAL}" = "install deploy" ]]; then
 184    unzip "${BASE_BUILD_DIR}/bitcoin-macos-app.zip" -d "${BASE_BUILD_DIR}/deploy"
 185    if ! ( codesign --verify "${BASE_BUILD_DIR}/deploy/Bitcoin-Qt.app" ); then
 186      echo "Codesigning failed."
 187      false
 188    fi
 189  fi
 190  
 191  if [ "$RUN_UNIT_TESTS" = "true" ]; then
 192    DIR_UNIT_TEST_DATA="${DIR_UNIT_TEST_DATA}" \
 193    LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
 194    CTEST_OUTPUT_ON_FAILURE=ON \
 195    ctest --test-dir "${BASE_BUILD_DIR}" \
 196      --stop-on-failure \
 197      "${MAKEJOBS}" \
 198      --timeout $(( TEST_RUNNER_TIMEOUT_FACTOR * 60 ))
 199  fi
 200  
 201  if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then
 202    # parses TEST_RUNNER_EXTRA as an array which allows for multiple arguments such as TEST_RUNNER_EXTRA='--exclude "rpc_bind.py --ipv6"'
 203    eval "TEST_RUNNER_EXTRA=($TEST_RUNNER_EXTRA)"
 204    LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
 205    "${BASE_BUILD_DIR}/test/functional/test_runner.py" \
 206      "${MAKEJOBS}" \
 207      --tmpdirprefix "${BASE_SCRATCH_DIR}/test_runner/" \
 208      --ansi \
 209      --combinedlogslen=99999999 \
 210      --timeout-factor="${TEST_RUNNER_TIMEOUT_FACTOR}" \
 211      "${TEST_RUNNER_EXTRA[@]}" \
 212      --quiet \
 213      --failfast
 214  fi
 215  
 216  if [ "${RUN_TIDY}" = "true" ]; then
 217    cmake -B /tidy-build -DLLVM_DIR=/usr/lib/llvm-"${TIDY_LLVM_V}"/cmake -DCMAKE_BUILD_TYPE=Release -S "${BASE_ROOT_DIR}"/contrib/devtools/bitcoin-tidy
 218    cmake --build /tidy-build "$MAKEJOBS"
 219    cmake --build /tidy-build --target bitcoin-tidy-tests "$MAKEJOBS"
 220  
 221    set -eo pipefail
 222    # Filter out:
 223    # * qt qrc and moc generated files
 224    jq 'map(select(.file | test("src/qt/.*_autogen/.*\\.cpp$") | not))' "${BASE_BUILD_DIR}/compile_commands.json" > tmp.json
 225    mv tmp.json "${BASE_BUILD_DIR}/compile_commands.json"
 226  
 227    cd "${BASE_BUILD_DIR}/src/"
 228    if ! ( run-clang-tidy-"${TIDY_LLVM_V}" -config-file="${BASE_ROOT_DIR}/src/.clang-tidy" -quiet -load="/tidy-build/libbitcoin-tidy.so" "${MAKEJOBS}" | tee tmp.tidy-out.txt ); then
 229      grep -C5 "error: " tmp.tidy-out.txt
 230      echo "^^^ ⚠️ Failure generated from clang-tidy"
 231      false
 232    fi
 233  fi
 234  
 235  if [[ "${RUN_IWYU}" == true ]]; then
 236    # TODO: Consider enforcing IWYU across the entire codebase.
 237    FILES_WITH_ENFORCED_IWYU="/src/(((bench|crypto|index|kernel|primitives|script|univalue/(lib|test)|util|zmq)/.*|common/license_info|node/(blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp)"
 238    jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns)))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_errors.json"
 239    jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns) | not))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_warnings.json"
 240  
 241    cd "${BASE_ROOT_DIR}"
 242  
 243    run_iwyu() {
 244      mv "${BASE_BUILD_DIR}/$1" "${BASE_BUILD_DIR}/compile_commands.json"
 245      {
 246        python3 "/include-what-you-use/iwyu_tool.py" \
 247               -p "${BASE_BUILD_DIR}" "${MAKEJOBS}" \
 248               -- -Xiwyu --cxx17ns -Xiwyu --mapping_file="${BASE_ROOT_DIR}/contrib/devtools/iwyu/bitcoin.core.imp" \
 249               -Xiwyu --max_line_length=160 \
 250               -Xiwyu --check_also="*/primitives/*.h" \
 251               2>&1 || true
 252      } | tee /tmp/iwyu_ci.out
 253      python3 "/include-what-you-use/fix_includes.py" --nosafe_headers < /tmp/iwyu_ci.out
 254      git diff -U1 | ./contrib/devtools/clang-format-diff.py -binary="clang-format-${IWYU_LLVM_V}" -p1 -i -v
 255    }
 256  
 257    run_iwyu "compile_commands_iwyu_errors.json"
 258    if ! ( git --no-pager diff --exit-code ); then
 259      echo "^^^ ⚠️ Failure generated from IWYU"
 260      false
 261    fi
 262  
 263    run_iwyu "compile_commands_iwyu_warnings.json"
 264    git --no-pager diff
 265  fi
 266  
 267  if [ "$RUN_FUZZ_TESTS" = "true" ]; then
 268    # shellcheck disable=SC2086
 269    LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \
 270    "${BASE_BUILD_DIR}/test/fuzz/test_runner.py" \
 271      ${FUZZ_TESTS_CONFIG} \
 272      "${MAKEJOBS}" \
 273      -l DEBUG \
 274      "${DIR_FUZZ_IN}" \
 275      --empty_min_time=60
 276  fi
 277