k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/hack/make-rules/test.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2014 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 set -o errexit 18 set -o nounset 19 set -o pipefail 20 21 KUBE_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. 22 source "${KUBE_ROOT}/hack/lib/init.sh" 23 24 kube::golang::setup_env 25 kube::golang::setup_gomaxprocs 26 27 # start the cache mutation detector by default so that cache mutators will be found 28 KUBE_CACHE_MUTATION_DETECTOR="${KUBE_CACHE_MUTATION_DETECTOR:-true}" 29 export KUBE_CACHE_MUTATION_DETECTOR 30 31 # panic the server on watch decode errors since they are considered coder mistakes 32 KUBE_PANIC_WATCH_DECODE_ERROR="${KUBE_PANIC_WATCH_DECODE_ERROR:-true}" 33 export KUBE_PANIC_WATCH_DECODE_ERROR 34 35 kube::test::find_dirs() { 36 ( 37 cd "${KUBE_ROOT}" 38 find -L . -not \( \ 39 \( \ 40 -path './_artifacts/*' \ 41 -o -path './_output/*' \ 42 -o -path './cmd/kubeadm/test/*' \ 43 -o -path './contrib/podex/*' \ 44 -o -path './release/*' \ 45 -o -path './target/*' \ 46 -o -path './test/e2e/e2e_test.go' \ 47 -o -path './test/e2e_node/*' \ 48 -o -path './test/e2e_kubeadm/*' \ 49 -o -path './test/integration/*' \ 50 -o -path './third_party/*' \ 51 -o -path './staging/*' \ 52 -o -path './vendor/*' \ 53 \) -prune \ 54 \) -name '*_test.go' -print0 | xargs -0n1 dirname | LC_ALL=C sort -u 55 56 find ./staging -name '*_test.go' -not -path '*/test/integration/*' -prune -print0 | xargs -0n1 dirname | LC_ALL=C sort -u 57 ) 58 } 59 60 # TODO: This timeout should really be lower, this is a *long* time to test one 61 # package, however pkg/api/testing in particular will fail with a lower timeout 62 # currently. We should attempt to lower this over time. 63 KUBE_TIMEOUT=${KUBE_TIMEOUT:--timeout=180s} 64 KUBE_COVER=${KUBE_COVER:-n} # set to 'y' to enable coverage collection 65 KUBE_COVERMODE=${KUBE_COVERMODE:-atomic} 66 # The directory to save test coverage reports to, if generating them. If unset, 67 # a semi-predictable temporary directory will be used. 68 KUBE_COVER_REPORT_DIR="${KUBE_COVER_REPORT_DIR:-}" 69 # How many 'go test' instances to run simultaneously when running tests in 70 # coverage mode. 71 KUBE_COVERPROCS=${KUBE_COVERPROCS:-4} 72 # use KUBE_RACE="" to disable the race detector 73 # this is defaulted to "-race" in make test as well 74 # NOTE: DO NOT ADD A COLON HERE. KUBE_RACE="" is meaningful! 75 KUBE_RACE=${KUBE_RACE-"-race"} 76 # Set to the goveralls binary path to report coverage results to Coveralls.io. 77 KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-} 78 # once we have multiple group supports 79 # Create a junit-style XML test report in this directory if set. 80 KUBE_JUNIT_REPORT_DIR=${KUBE_JUNIT_REPORT_DIR:-} 81 # If KUBE_JUNIT_REPORT_DIR is unset, and ARTIFACTS is set, then have them match. 82 if [[ -z "${KUBE_JUNIT_REPORT_DIR:-}" && -n "${ARTIFACTS:-}" ]]; then 83 export KUBE_JUNIT_REPORT_DIR="${ARTIFACTS}" 84 fi 85 # Set to 'y' to keep the verbose stdout from tests when KUBE_JUNIT_REPORT_DIR is 86 # set. 87 KUBE_KEEP_VERBOSE_TEST_OUTPUT=${KUBE_KEEP_VERBOSE_TEST_OUTPUT:-n} 88 89 kube::test::usage() { 90 kube::log::usage_from_stdin <<EOF 91 usage: $0 [OPTIONS] [TARGETS] 92 93 OPTIONS: 94 -p <number> : number of parallel workers, must be >= 1 95 EOF 96 } 97 98 isnum() { 99 [[ "$1" =~ ^[0-9]+$ ]] 100 } 101 102 PARALLEL="${PARALLEL:-1}" 103 while getopts "hp:i:" opt ; do 104 case ${opt} in 105 h) 106 kube::test::usage 107 exit 0 108 ;; 109 p) 110 PARALLEL="${OPTARG}" 111 if ! isnum "${PARALLEL}" || [[ "${PARALLEL}" -le 0 ]]; then 112 kube::log::usage "'$0': argument to -p must be numeric and greater than 0" 113 kube::test::usage 114 exit 1 115 fi 116 ;; 117 i) 118 kube::log::usage "'$0': use GOFLAGS='-count <num-iterations>'" 119 kube::test::usage 120 exit 1 121 ;; 122 :) 123 kube::log::usage "Option -${OPTARG} <value>" 124 kube::test::usage 125 exit 1 126 ;; 127 ?) 128 kube::test::usage 129 exit 1 130 ;; 131 esac 132 done 133 shift $((OPTIND - 1)) 134 135 # Use eval to preserve embedded quoted strings. 136 testargs=() 137 eval "testargs=(${KUBE_TEST_ARGS:-})" 138 139 # Used to filter verbose test output. 140 go_test_grep_pattern=".*" 141 142 goflags=() 143 # The junit report tool needs full test case information to produce a 144 # meaningful report. 145 if [[ -n "${KUBE_JUNIT_REPORT_DIR}" ]] ; then 146 goflags+=(-v) 147 goflags+=(-json) 148 # Show only summary lines by matching lines like "status package/test" 149 go_test_grep_pattern="^[^[:space:]]\+[[:space:]]\+[^[:space:]]\+/[^[[:space:]]\+" 150 fi 151 152 if [[ -n "${FULL_LOG:-}" ]] ; then 153 go_test_grep_pattern=".*" 154 fi 155 156 # Filter out arguments that start with "-" and move them to goflags. 157 testcases=() 158 for arg; do 159 if [[ "${arg}" == -* ]]; then 160 goflags+=("${arg}") 161 else 162 testcases+=("${arg}") 163 fi 164 done 165 if [[ ${#testcases[@]} -eq 0 ]]; then 166 kube::util::read-array testcases < <(kube::test::find_dirs) 167 fi 168 set -- "${testcases[@]+${testcases[@]}}" 169 170 if [[ -n "${KUBE_RACE}" ]] ; then 171 goflags+=("${KUBE_RACE}") 172 fi 173 174 junitFilenamePrefix() { 175 if [[ -z "${KUBE_JUNIT_REPORT_DIR}" ]]; then 176 echo "" 177 return 178 fi 179 mkdir -p "${KUBE_JUNIT_REPORT_DIR}" 180 echo "${KUBE_JUNIT_REPORT_DIR}/junit_$(kube::util::sortable_date)" 181 } 182 183 produceJUnitXMLReport() { 184 local -r junit_filename_prefix=$1 185 if [[ -z "${junit_filename_prefix}" ]]; then 186 return 187 fi 188 189 local junit_xml_filename 190 junit_xml_filename="${junit_filename_prefix}.xml" 191 192 if ! command -v gotestsum >/dev/null 2>&1; then 193 kube::log::status "gotestsum not found; installing from ./hack/tools" 194 go -C "${KUBE_ROOT}/hack/tools" install gotest.tools/gotestsum 195 fi 196 gotestsum --junitfile "${junit_xml_filename}" --raw-command cat "${junit_filename_prefix}"*.stdout 197 if [[ ! ${KUBE_KEEP_VERBOSE_TEST_OUTPUT} =~ ^[yY]$ ]]; then 198 rm "${junit_filename_prefix}"*.stdout 199 fi 200 201 if ! command -v prune-junit-xml >/dev/null 2>&1; then 202 kube::log::status "prune-junit-xml not found; installing from ./cmd" 203 go -C "${KUBE_ROOT}/cmd/prune-junit-xml" install . 204 fi 205 prune-junit-xml "${junit_xml_filename}" 206 207 kube::log::status "Saved JUnit XML test report to ${junit_xml_filename}" 208 } 209 210 runTests() { 211 local junit_filename_prefix 212 junit_filename_prefix=$(junitFilenamePrefix) 213 214 # Try to normalize input names. 215 local -a targets 216 kube::util::read-array targets < <(kube::golang::normalize_go_targets "$@") 217 218 # If we're not collecting coverage, run all requested tests with one 'go test' 219 # command, which is much faster. 220 if [[ ! ${KUBE_COVER} =~ ^[yY]$ ]]; then 221 kube::log::status "Running tests without code coverage ${KUBE_RACE:+"and with ${KUBE_RACE}"}" 222 # shellcheck disable=SC2031 223 go test "${goflags[@]:+${goflags[@]}}" \ 224 "${KUBE_TIMEOUT}" "${targets[@]}" \ 225 "${testargs[@]:+${testargs[@]}}" \ 226 | tee ${junit_filename_prefix:+"${junit_filename_prefix}.stdout"} \ 227 | grep --binary-files=text "${go_test_grep_pattern}" && rc=$? || rc=$? 228 produceJUnitXMLReport "${junit_filename_prefix}" 229 return "${rc}" 230 fi 231 232 kube::log::status "Running tests with code coverage ${KUBE_RACE:+"and with ${KUBE_RACE}"}" 233 234 # Create coverage report directories. 235 if [[ -z "${KUBE_COVER_REPORT_DIR}" ]]; then 236 cover_report_dir="/tmp/k8s_coverage/$(kube::util::sortable_date)" 237 else 238 cover_report_dir="${KUBE_COVER_REPORT_DIR}" 239 fi 240 cover_profile="coverage.out" # Name for each individual coverage profile 241 kube::log::status "Saving coverage output in '${cover_report_dir}'" 242 mkdir -p "${@+${@/#/${cover_report_dir}/}}" 243 244 # Run all specified tests, collecting coverage results. Go currently doesn't 245 # support collecting coverage across multiple packages at once, so we must issue 246 # separate 'go test' commands for each package and then combine at the end. 247 # To speed things up considerably, we can at least use xargs -P to run multiple 248 # 'go test' commands at once. 249 # To properly parse the test results if generating a JUnit test report, we 250 # must make sure the output from PARALLEL runs is not mixed. To achieve this, 251 # we spawn a subshell for each PARALLEL process, redirecting the output to 252 # separate files. 253 254 printf "%s\n" "${@}" \ 255 | xargs -I{} -n 1 -P "${KUBE_COVERPROCS}" \ 256 bash -c "set -o pipefail; _pkg=\"\$0\"; _pkg_out=\${_pkg//\//_}; \ 257 go test ${goflags[*]:+${goflags[*]}} \ 258 ${KUBE_TIMEOUT} \ 259 -cover -covermode=\"${KUBE_COVERMODE}\" \ 260 -coverprofile=\"${cover_report_dir}/\${_pkg}/${cover_profile}\" \ 261 \"\${_pkg}\" \ 262 ${testargs[*]:+${testargs[*]}} \ 263 | tee ${junit_filename_prefix:+\"${junit_filename_prefix}-\$_pkg_out.stdout\"} \ 264 | grep \"${go_test_grep_pattern}\"" \ 265 {} \ 266 && test_result=$? || test_result=$? 267 268 produceJUnitXMLReport "${junit_filename_prefix}" 269 270 COMBINED_COVER_PROFILE="${cover_report_dir}/combined-coverage.out" 271 { 272 # The combined coverage profile needs to start with a line indicating which 273 # coverage mode was used (set, count, or atomic). This line is included in 274 # each of the coverage profiles generated when running 'go test -cover', but 275 # we strip these lines out when combining so that there's only one. 276 echo "mode: ${KUBE_COVERMODE}" 277 278 # Include all coverage reach data in the combined profile, but exclude the 279 # 'mode' lines, as there should be only one. 280 while IFS='' read -r x; do 281 grep -h -v "^mode:" < "${x}" || true 282 done < <(find "${cover_report_dir}" -name "${cover_profile}") 283 } >"${COMBINED_COVER_PROFILE}" 284 285 coverage_html_file="${cover_report_dir}/combined-coverage.html" 286 go tool cover -html="${COMBINED_COVER_PROFILE}" -o="${coverage_html_file}" 287 kube::log::status "Combined coverage report: ${coverage_html_file}" 288 289 return "${test_result}" 290 } 291 292 reportCoverageToCoveralls() { 293 if [[ ${KUBE_COVER} =~ ^[yY]$ ]] && [[ -x "${KUBE_GOVERALLS_BIN}" ]]; then 294 kube::log::status "Reporting coverage results to Coveralls for service ${CI_NAME:-}" 295 ${KUBE_GOVERALLS_BIN} -coverprofile="${COMBINED_COVER_PROFILE}" \ 296 ${CI_NAME:+"-service=${CI_NAME}"} \ 297 ${COVERALLS_REPO_TOKEN:+"-repotoken=${COVERALLS_REPO_TOKEN}"} \ 298 || true 299 fi 300 } 301 302 checkFDs() { 303 # several unittests panic when httptest cannot open more sockets 304 # due to the low default files limit on OS X. Warn about low limit. 305 local fileslimit 306 fileslimit="$(ulimit -n)" 307 if [[ ${fileslimit} -lt 1000 ]]; then 308 echo "WARNING: ulimit -n (files) should be at least 1000, is ${fileslimit}, may cause test failure"; 309 fi 310 } 311 312 checkFDs 313 314 runTests "$@" 315 316 # We might run the tests for multiple versions, but we want to report only 317 # one of them to coveralls. Here we report coverage from the last run. 318 reportCoverageToCoveralls