1#!/usr/bin/env bash 2# 3# Run all etcd tests 4# ./test 5# ./test -v 6# 7# 8# Run specified test pass 9# 10# $ PASSES=unit ./test 11# $ PASSES=integration ./test 12# 13# 14# Run tests for one package 15# Each pass has different default timeout, if you just run tests in one package or 1 test case then you can set TIMEOUT 16# flag for different expectation 17# 18# $ PASSES=unit PKG=./wal TIMEOUT=1m ./test 19# $ PASSES=integration PKG=client/integration TIMEOUT=1m ./test 20# 21# 22# Run specified unit tests in one package 23# To run all the tests with prefix of "TestNew", set "TESTCASE=TestNew "; 24# to run only "TestNew", set "TESTCASE="\bTestNew\b"" 25# 26# $ PASSES=unit PKG=./wal TESTCASE=TestNew TIMEOUT=1m ./test 27# $ PASSES=unit PKG=./wal TESTCASE="\bTestNew\b" TIMEOUT=1m ./test 28# $ PASSES=integration PKG=client/integration TESTCASE="\bTestV2NoRetryEOF\b" TIMEOUT=1m ./test 29# 30# 31# Run code coverage 32# COVERDIR must either be a absolute path or a relative path to the etcd root 33# $ COVERDIR=coverage PASSES="build_cov cov" ./test 34set -e 35 36source ./build 37 38# build before setting up test GOPATH 39if [[ "${PASSES}" == *"functional"* ]]; then 40 ./functional/build 41fi 42 43if [ -z "$PASSES" ]; then 44 PASSES="fmt bom dep build unit" 45fi 46 47USERPKG=${PKG:-} 48 49# Invoke ./tests/cover.test.bash for HTML output 50COVER=${COVER:-"-cover"} 51 52# Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt. 53IGNORE_PKGS="(vendor/|etcdserverpb|rafttest|gopath.proto|v3lockpb|v3electionpb)" 54INTEGRATION_PKGS="(integration|tests/e2e|contrib|functional)" 55 56# all github.com/etcd-io/etcd/whatever pkgs that are not auto-generated / tools 57# shellcheck disable=SC1117 58PKGS=$(find . -name \*.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | grep -vE "(tools/|contrib/|tests/e2e|pb)" | sed "s|\.|${REPO_PATH}|g" | xargs echo) 59# pkg1,pkg2,pkg3 60PKGS_COMMA=${PKGS// /,} 61 62# shellcheck disable=SC1117 63TEST_PKGS=$(find . -name \*_test.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g") 64 65# shellcheck disable=SC1117 66FORMATTABLE=$(find . -name \*.go | while read -r a; do echo "$(dirname "$a")/*.go"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g") 67 68TESTABLE_AND_FORMATTABLE=$(echo "$TEST_PKGS" | grep -vE "$INTEGRATION_PKGS") 69 70# check if user provided PKG override 71if [ -z "${USERPKG}" ]; then 72 TEST=$TESTABLE_AND_FORMATTABLE 73 FMT=$FORMATTABLE 74else 75 # strip out leading dotslashes and trailing slashes from PKG=./foo/ 76 TEST=${USERPKG/#./} 77 TEST=${TEST/#\//} 78 TEST=${TEST/%\//} 79 # only run gofmt on packages provided by user 80 FMT="$TEST" 81fi 82 83# shellcheck disable=SC2206 84FMT=($FMT) 85if [ "${VERBOSE}" == "1" ]; then 86 # shellcheck disable=SC2128 87 echo "Running with FMT:" "${FMT[@]}" 88fi 89 90# prepend REPO_PATH to each local package 91split=$TEST 92TEST="" 93for a in $split; do TEST="$TEST ${REPO_PATH}/${a}"; done 94 95# shellcheck disable=SC2206 96TEST=($TEST) 97if [ "${VERBOSE}" == "1" ]; then 98 # shellcheck disable=SC2128 99 echo "Running with TEST:" "${TEST[@]}" 100fi 101 102# TODO: 'rafttest' is failing with unused 103STATIC_ANALYSIS_PATHS=$(find . -name \*.go ! -path './vendor/*' ! -path './gopath.proto/*' ! -path '*pb/*' | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS") 104# shellcheck disable=SC2206 105STATIC_ANALYSIS_PATHS=($STATIC_ANALYSIS_PATHS) 106if [ "${VERBOSE}" == "1" ]; then 107 # shellcheck disable=SC2128 108 echo "Running with STATIC_ANALYSIS_PATHS:" "${STATIC_ANALYSIS_PATHS[@]}" 109fi 110 111if [ -z "$GOARCH" ]; then 112 GOARCH=$(go env GOARCH); 113fi 114 115# determine the number of CPUs to use for Go tests 116TEST_CPUS="1,2,4" 117if [ -n "${CPU}" ]; then 118 TEST_CPUS="${CPU}" 119fi 120echo "Running with TEST_CPUS:" "${TEST_CPUS}" 121 122# determine whether target supports race detection 123if [ -z "${RACE}" ] ; then 124 if [ "$GOARCH" == "amd64" ]; then 125 RACE="--race" 126 else 127 RACE="--race=false" 128 fi 129else 130 RACE="--race=${RACE:-true}" 131fi 132 133RUN_ARG="" 134if [ -n "${TESTCASE}" ]; then 135 RUN_ARG="-run=${TESTCASE}" 136fi 137 138function unit_pass { 139 echo "Running unit tests..." 140 GO_TEST_FLAG="" 141 if [ "${VERBOSE}" == "1" ]; then 142 GO_TEST_FLAG="-v" 143 fi 144 if [ "${VERBOSE}" == "2" ]; then 145 GO_TEST_FLAG="-v" 146 export CLIENT_DEBUG=1 147 fi 148 149 if [ "${RUN_ARG}" == "" ]; then 150 RUN_ARG="-run=Test" 151 fi 152 153 # check if user provided time out, especially useful when just run one test case 154 # expectation could be different 155 USERTIMEOUT="" 156 if [ -z "${TIMEOUT}" ]; then 157 USERTIMEOUT="3m" 158 else 159 USERTIMEOUT="${TIMEOUT}" 160 fi 161 go test ${GO_TEST_FLAG} -timeout "${USERTIMEOUT}" "${COVER}" ${RACE} -cpu "${TEST_CPUS}" ${RUN_ARG} "$@" "${TEST[@]}" 162} 163 164function integration_pass { 165 echo "Running integration tests..." 166 167 # check if user provided time out, especially useful when just run one test case 168 # expectation could be different 169 USERTIMEOUT="" 170 if [ -z "${TIMEOUT}" ]; then 171 USERTIMEOUT="30m" 172 else 173 USERTIMEOUT="${TIMEOUT}" 174 fi 175 176 # if TESTCASE and PKG set, run specified test case in specified PKG 177 # if TESTCASE set, PKG not set, run specified test case in all integration and integration_extra packages 178 # if TESTCASE not set, PKG set, run all test cases in specified package 179 # if TESTCASE not set, PKG not set, run all tests in all integration and integration_extra packages 180 if [ -z "${TESTCASE}" ] && [ -z "${USERPKG}" ]; then 181 go test -timeout "${USERTIMEOUT}" -v -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/integration" 182 integration_extra "$@" 183 else 184 if [ -z "${USERPKG}" ]; then 185 INTEGTESTPKG=("${REPO_PATH}/integration" 186 "${REPO_PATH}/client/integration" 187 "${REPO_PATH}/clientv3/integration" 188 "${REPO_PATH}/contrib/raftexample" 189 "${REPO_PATH}/store") 190 else 191 INTEGTESTPKG=("${TEST[@]}") 192 fi 193 go test -timeout "${USERTIMEOUT}" -v -cpu "${TEST_CPUS}" "${RUN_ARG}" "$@" "${INTEGTESTPKG[@]}" 194 fi 195} 196 197function integration_extra { 198 go test -timeout 1m -v ${RACE} -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/client/integration" 199 go test -timeout 25m -v ${RACE} -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/clientv3/integration" 200 go test -timeout 1m -v -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/contrib/raftexample" 201 go test -timeout 5m -v ${RACE} -tags v2v3 "$@" "${REPO_PATH}/etcdserver/api/v2store" 202 go test -timeout 1m -v ${RACE} -cpu "${TEST_CPUS}" -run=Example "$@" "${TEST[@]}" 203} 204 205function functional_pass { 206 # Clean up any data and logs from previous runs 207 rm -rf /tmp/etcd-functional-* /tmp/etcd-functional-*.backup 208 209 for a in 1 2 3; do 210 ./bin/etcd-agent --network tcp --address 127.0.0.1:${a}9027 & 211 pid="$!" 212 agent_pids="${agent_pids} $pid" 213 done 214 215 for a in 1 2 3; do 216 echo "Waiting for 'etcd-agent' on ${a}9027..." 217 while ! nc -z localhost ${a}9027; do 218 sleep 1 219 done 220 done 221 222 echo "functional test START!" 223 ./bin/etcd-tester --config ./functional.yaml && echo "'etcd-tester' succeeded" 224 ETCD_TESTER_EXIT_CODE=$? 225 echo "ETCD_TESTER_EXIT_CODE:" ${ETCD_TESTER_EXIT_CODE} 226 227 # shellcheck disable=SC2206 228 agent_pids=($agent_pids) 229 kill -s TERM "${agent_pids[@]}" || true 230 231 if [[ "${ETCD_TESTER_EXIT_CODE}" -ne "0" ]]; then 232 printf "\n" 233 echo "FAILED! 'tail -1000 /tmp/etcd-functional-1/etcd.log'" 234 tail -1000 /tmp/etcd-functional-1/etcd.log 235 236 printf "\n" 237 echo "FAILED! 'tail -1000 /tmp/etcd-functional-2/etcd.log'" 238 tail -1000 /tmp/etcd-functional-2/etcd.log 239 240 printf "\n" 241 echo "FAILED! 'tail -1000 /tmp/etcd-functional-3/etcd.log'" 242 tail -1000 /tmp/etcd-functional-3/etcd.log 243 244 echo "--- FAIL: exit code" ${ETCD_TESTER_EXIT_CODE} 245 exit ${ETCD_TESTER_EXIT_CODE} 246 fi 247 echo "functional test PASS!" 248} 249 250function cov_pass { 251 echo "Running code coverage..." 252 # install gocovmerge before running code coverage from github.com/wadey/gocovmerge 253 # gocovmerge merges coverage files 254 if ! command -v gocovmerge >/dev/null; then 255 echo "gocovmerge not installed" 256 exit 255 257 fi 258 259 if [ -z "$COVERDIR" ]; then 260 echo "COVERDIR undeclared" 261 exit 255 262 fi 263 264 if [ ! -f "bin/etcd_test" ]; then 265 echo "etcd_test binary not found" 266 exit 255 267 fi 268 269 mkdir -p "$COVERDIR" 270 271 # run code coverage for unit and integration tests 272 GOCOVFLAGS="-covermode=set -coverpkg ${PKGS_COMMA} -v -timeout 30m" 273 # shellcheck disable=SC2206 274 GOCOVFLAGS=($GOCOVFLAGS) 275 failed="" 276 for t in $(echo "${TEST_PKGS}" | grep -vE "(tests/e2e|functional)"); do 277 tf=$(echo "$t" | tr / _) 278 # cache package compilation data for faster repeated builds 279 go test "${GOCOVFLAGS[@]}" -i "${REPO_PATH}/$t" || true 280 # uses -run=Test to skip examples because clientv3/ example tests will leak goroutines 281 go test "${GOCOVFLAGS[@]}" -run=Test -coverprofile "$COVERDIR/${tf}.coverprofile" "${REPO_PATH}/$t" || failed="$failed $t" 282 done 283 284 # v2v3 tests 285 go test -tags v2v3 "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/store-v2v3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed store-v2v3" 286 287 # proxy tests 288 go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_integration.coverprofile" "${REPO_PATH}/integration" || failed="$failed proxy-integration" 289 go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_clientv3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed proxy-clientv3/integration" 290 291 # run code coverage for e2e tests 292 # use 30m timeout because e2e coverage takes longer 293 # due to many tests cause etcd process to wait 294 # on leadership transfer timeout during gracefully shutdown 295 echo Testing tests/e2e without proxy... 296 go test -tags cov -timeout 30m -v "${REPO_PATH}/tests/e2e" || failed="$failed tests/e2e" 297 echo Testing tests/e2e with proxy... 298 go test -tags "cov cluster_proxy" -timeout 30m -v "${REPO_PATH}/tests/e2e" || failed="$failed tests/e2e-proxy" 299 300 # incrementally merge to get coverage data even if some coverage files are corrupted 301 # optimistically assume etcdserver package's coverage file is OK since gocovmerge 302 # expects to start with a non-empty file 303 cp "$COVERDIR"/etcdserver.coverprofile "$COVERDIR"/cover.out 304 for f in "$COVERDIR"/*.coverprofile; do 305 echo "merging test coverage file ${f}" 306 gocovmerge "$f" "$COVERDIR"/cover.out >"$COVERDIR"/cover.tmp || failed="$failed $f" 307 if [ -s "$COVERDIR"/cover.tmp ]; then 308 mv "$COVERDIR"/cover.tmp "$COVERDIR"/cover.out 309 fi 310 done 311 # strip out generated files (using GNU-style sed) 312 sed --in-place '/generated.go/d' "$COVERDIR"/cover.out || true 313 314 # held failures to generate the full coverage file, now fail 315 if [ -n "$failed" ]; then 316 for f in $failed; do 317 echo "--- FAIL:" "$f" 318 done 319 exit 255 320 fi 321} 322 323function e2e_pass { 324 echo "Running e2e tests..." 325 326 # check if user provided time out, especially useful when just run one test case 327 # expectation could be different 328 USERTIMEOUT="" 329 if [ -z "${TIMEOUT}" ]; then 330 USERTIMEOUT="30m" 331 else 332 USERTIMEOUT="${TIMEOUT}" 333 fi 334 335 go test -timeout "${USERTIMEOUT}" -v -cpu "${TEST_CPUS}" "${RUN_ARG}" "$@" "${REPO_PATH}/tests/e2e" 336} 337 338function integration_e2e_pass { 339 echo "Running integration and e2e tests..." 340 341 go test -timeout 30m -v -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/tests/e2e" & 342 e2epid="$!" 343 go test -timeout 30m -v -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/integration" & 344 intpid="$!" 345 wait $e2epid 346 wait $intpid 347 integration_extra "$@" 348} 349 350function grpcproxy_pass { 351 go test -timeout 30m -v ${RACE} -tags cluster_proxy -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/integration" 352 go test -timeout 30m -v ${RACE} -tags cluster_proxy -cpu "${TEST_CPUS}" "$@" "${REPO_PATH}/clientv3/integration" 353 go test -timeout 30m -v -tags cluster_proxy "$@" "${REPO_PATH}/tests/e2e" 354} 355 356function release_pass { 357 rm -f ./bin/etcd-last-release 358 # to grab latest patch release; bump this up for every minor release 359 UPGRADE_VER=$(git tag -l --sort=-version:refname "v3.3.*" | head -1) 360 if [ -n "$MANUAL_VER" ]; then 361 # in case, we need to test against different version 362 UPGRADE_VER=$MANUAL_VER 363 fi 364 if [[ -z ${UPGRADE_VER} ]]; then 365 UPGRADE_VER="v3.3.0" 366 echo "fallback to" ${UPGRADE_VER} 367 fi 368 369 local file="etcd-$UPGRADE_VER-linux-$GOARCH.tar.gz" 370 echo "Downloading $file" 371 372 set +e 373 curl --fail -L "https://github.com/etcd-io/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file" 374 local result=$? 375 set -e 376 case $result in 377 0) ;; 378 *) echo "--- FAIL:" ${result} 379 exit $result 380 ;; 381 esac 382 383 tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1 384 mkdir -p ./bin 385 mv /tmp/etcd ./bin/etcd-last-release 386} 387 388function shellcheck_pass { 389 if command -v shellcheck >/dev/null; then 390 shellcheckResult=$(shellcheck -fgcc build test scripts/*.sh 2>&1 || true) 391 if [ -n "${shellcheckResult}" ]; then 392 echo -e "shellcheck checking failed:\\n${shellcheckResult}" 393 exit 255 394 fi 395 fi 396} 397 398function markdown_you_pass { 399 # eschew you 400 yous=$(find . -name \*.md ! -path './vendor/*' ! -path './Documentation/v2/*' ! -path './gopath.proto/*' -exec grep -E --color "[Yy]ou[r]?[ '.,;]" {} + || true) 401 if [ -n "$yous" ]; then 402 echo -e "found 'you' in documentation:\\n${yous}" 403 exit 255 404 fi 405} 406 407function markdown_marker_pass { 408 # TODO: check other markdown files when marker handles headers with '[]' 409 if command -v marker >/dev/null; then 410 markerResult=$(marker --skip-http --root ./Documentation 2>&1 || true) 411 if [ -n "${markerResult}" ]; then 412 echo -e "marker checking failed:\\n${markerResult}" 413 exit 255 414 fi 415 else 416 echo "Skipping marker..." 417 fi 418} 419 420function goword_pass { 421 if command -v goword >/dev/null; then 422 # get all go files to process 423 gofiles=$(find "${FMT[@]}" -iname '*.go' 2>/dev/null) 424 # shellcheck disable=SC2206 425 gofiles_all=($gofiles) 426 # ignore tests and protobuf files 427 # shellcheck disable=SC1117 428 gofiles=$(echo "${gofiles_all[@]}" | sort | uniq | sed "s/ /\n/g" | grep -vE "(\\_test.go|\\.pb\\.go)") 429 # shellcheck disable=SC2206 430 gofiles=($gofiles) 431 # only check for broken exported godocs 432 gowordRes=$(goword -use-spell=false "${gofiles[@]}" | grep godoc-export | sort) 433 if [ -n "$gowordRes" ]; then 434 echo -e "goword checking failed:\\n${gowordRes}" 435 exit 255 436 fi 437 # check some spelling 438 gowordRes=$(goword -ignore-file=.words clientv3/{*,*/*}.go 2>&1 | grep spell | sort) 439 if [ -n "$gowordRes" ]; then 440 echo -e "goword checking failed:\\n${gowordRes}" 441 exit 255 442 fi 443 else 444 echo "Skipping goword..." 445 fi 446} 447 448function gofmt_pass { 449 fmtRes=$(gofmt -l -s -d "${FMT[@]}") 450 if [ -n "${fmtRes}" ]; then 451 echo -e "gofmt checking failed:\\n${fmtRes}" 452 exit 255 453 fi 454} 455 456function govet_pass { 457 vetRes=$(go vet "${TEST[@]}") 458 if [ -n "${vetRes}" ]; then 459 echo -e "govet checking failed:\\n${vetRes}" 460 exit 255 461 fi 462} 463 464function govet_shadow_pass { 465 fmtpkgs=$(for a in "${FMT[@]}"; do dirname "$a"; done | sort | uniq | grep -v "\\.") 466 # shellcheck disable=SC2206 467 fmtpkgs=($fmtpkgs) 468 # Golang 1.12 onwards the experimental -shadow option is no longer available with go vet 469 go get golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow 470 export PATH=${GOPATH}/bin:${PATH} 471 shadow_tool=$(which shadow) 472 vetRes=$(go vet -all -vettool="${shadow_tool}" "${TEST[@]}") 473 if [ -n "${vetRes}" ]; then 474 echo -e "govet -all -shadow checking failed:\\n${vetRes}" 475 exit 255 476 fi 477} 478 479function unparam_pass { 480 if command -v unparam >/dev/null; then 481 unparamResult=$(unparam "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true) 482 if [ -n "${unparamResult}" ]; then 483 echo -e "unparam checking failed:\\n${unparamResult}" 484 exit 255 485 fi 486 else 487 echo "Skipping unparam..." 488 fi 489} 490 491function staticcheck_pass { 492 if command -v staticcheck >/dev/null; then 493 staticcheckResult=$(staticcheck "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true) 494 if [ -n "${staticcheckResult}" ]; then 495 # TODO: resolve these after go1.8 migration 496 # See https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck 497 STATIC_CHECK_MASK="S(A|T)(1002|1005|1006|1008|1012|1019|1032|2002|4003|4006)" 498 if echo "${staticcheckResult}" | grep -vE "$STATIC_CHECK_MASK"; then 499 echo -e "staticcheck checking failed:\\n${staticcheckResult}" 500 exit 255 501 else 502 suppressed=$(echo "${staticcheckResult}" | sed 's/ /\n/g' | grep "(SA" | sort | uniq -c) 503 echo -e "staticcheck suppressed warnings:\\n${suppressed}" 504 fi 505 fi 506 else 507 echo "Skipping staticcheck..." 508 fi 509} 510 511function revive_pass { 512 if command -v revive >/dev/null; then 513 reviveResult=$(revive -config ./tests/revive.toml -exclude "vendor/..." ./... 2>&1 || true) 514 if [ -n "${reviveResult}" ]; then 515 echo -e "revive checking failed:\\n${reviveResult}" 516 exit 255 517 fi 518 else 519 echo "Skipping revive..." 520 fi 521} 522 523function unconvert_pass { 524 if command -v unconvert >/dev/null; then 525 unconvertResult=$(unconvert -v "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true) 526 if [ -n "${unconvertResult}" ]; then 527 echo -e "unconvert checking failed:\\n${unconvertResult}" 528 exit 255 529 fi 530 else 531 echo "Skipping unconvert..." 532 fi 533} 534 535function ineffassign_pass { 536 if command -v ineffassign >/dev/null; then 537 ineffassignResult=$(ineffassign "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true) 538 if [ -n "${ineffassignResult}" ]; then 539 echo -e "ineffassign checking failed:\\n${ineffassignResult}" 540 exit 255 541 fi 542 else 543 echo "Skipping ineffassign..." 544 fi 545} 546 547function nakedret_pass { 548 if command -v nakedret >/dev/null; then 549 nakedretResult=$(nakedret "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true) 550 if [ -n "${nakedretResult}" ]; then 551 echo -e "nakedret checking failed:\\n${nakedretResult}" 552 exit 255 553 fi 554 else 555 echo "Skipping nakedret..." 556 fi 557} 558 559function license_header_pass { 560 licRes="" 561 files=$(find . -type f -iname '*.go' ! -path './vendor/*' ! -path './gopath.proto/*') 562 for file in $files; do 563 if ! head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" ; then 564 licRes="${licRes}"$(echo -e " ${file}") 565 fi 566 done 567 if [ -n "${licRes}" ]; then 568 echo -e "license header checking failed:\\n${licRes}" 569 exit 255 570 fi 571} 572 573function receiver_name_pass { 574 # shellcheck disable=SC1117 575 recvs=$(grep 'func ([^*]' {*,*/*,*/*/*}.go | grep -Ev "(generated|pb/)" | tr ':' ' ' | \ 576 awk ' { print $2" "$3" "$4" "$1 }' | sed "s/[a-zA-Z\.]*go//g" | sort | uniq | \ 577 grep -Ev "(Descriptor|Proto|_)" | awk ' { print $3" "$4 } ' | sort | uniq -c | grep -v ' 1 ' | awk ' { print $2 } ') 578 if [ -n "${recvs}" ]; then 579 # shellcheck disable=SC2206 580 recvs=($recvs) 581 for recv in "${recvs[@]}"; do 582 echo "Mismatched receiver for $recv..." 583 grep "$recv" "${FMT[@]}" | grep 'func (' 584 done 585 exit 255 586 fi 587} 588 589function commit_title_pass { 590 git log --oneline "$(git merge-base HEAD "$(git rev-parse --abbrev-ref --symbolic-full-name "@{u}")")"...HEAD | while read -r l; do 591 commitMsg=$(echo "$l" | cut -f2- -d' ') 592 if [[ "$commitMsg" == Merge* ]]; then 593 # ignore "Merge pull" commits 594 continue 595 fi 596 if [[ "$commitMsg" == Revert* ]]; then 597 # ignore revert commits 598 continue 599 fi 600 601 pkgPrefix=$(echo "$commitMsg" | cut -f1 -d':') 602 spaceCommas=$(echo "$commitMsg" | sed 's/ /\n/g' | grep -c ',$' || echo 0) 603 commaSpaces=$(echo "$commitMsg" | sed 's/,/\n/g' | grep -c '^ ' || echo 0) 604 if [[ $(echo "$commitMsg" | grep -c ":..*") == 0 || "$commitMsg" == "$pkgPrefix" || "$spaceCommas" != "$commaSpaces" ]]; then 605 echo "$l"... 606 echo "Expected commit title format '<package>{\", \"<package>}: <description>'" 607 echo "Got: $l" 608 exit 255 609 fi 610 done 611} 612 613# tools gosimple,unused,staticheck,unconvert,ineffasign,nakedret 614# are not module-aware. See https://github.com/golang/go/issues/24661 615# The module-aware versions need to be used when they become available 616function fmt_pass { 617 toggle_failpoints disable 618 619 # TODO: add "unparam" 620 for p in shellcheck \ 621 markdown_you \ 622 markdown_marker \ 623 goword \ 624 gofmt \ 625 govet \ 626 govet_shadow \ 627 revive \ 628 license_header \ 629 receiver_name \ 630 commit_title \ 631 ; do 632 echo "'$p' started at $(date)" 633 "${p}"_pass "$@" 634 echo "'$p' completed at $(date)" 635 done 636} 637 638function bom_pass { 639 if ! command -v license-bill-of-materials >/dev/null; then 640 return 641 fi 642 if [ "${GO111MODULE}" == "on" ]; then 643 # license-bill-off-materials calls "go list std cmd" which cannot handle modules 644 # Please see https://github.com/golang/go/issues/26924 645 echo "Skipping license-bill-of-materials with go modules..." 646 return 647 fi 648 echo "Checking bill of materials..." 649 license-bill-of-materials \ 650 --override-file bill-of-materials.override.json \ 651 go.etcd.io/etcd go.etcd.io/etcd/etcdctl >bom-now.json || true 652 if ! diff bill-of-materials.json bom-now.json; then 653 echo "vendored licenses do not match given bill of materials" 654 exit 255 655 fi 656 rm bom-now.json 657} 658 659function dep_pass { 660 echo "Checking package dependencies..." 661 # don't pull in etcdserver package 662 pushd clientv3 >/dev/null 663 badpkg="(etcdserver$|mvcc$|backend$|grpc-gateway)" 664 deps=$(go list -f '{{ .Deps }}' | sed 's/ /\n/g' | grep -E "${badpkg}" || echo "") 665 popd >/dev/null 666 if [ -n "$deps" ]; then 667 echo -e "clientv3 has masked dependencies:\\n${deps}" 668 exit 255 669 fi 670} 671 672function build_cov_pass { 673 out="bin" 674 if [ -n "${BINDIR}" ]; then out="${BINDIR}"; fi 675 go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcd_test" 676 go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcdctl_test" "${REPO_PATH}/etcdctl" 677} 678 679# fail fast on static tests 680function build_pass { 681 echo "Checking build..." 682 GO_BUILD_FLAGS="-v" etcd_build 683 GO_BUILD_FLAGS="-v" tools_build 684} 685 686for pass in $PASSES; do 687 echo "Starting '$pass' pass at $(date)" 688 "${pass}"_pass "$@" 689 echo "Finished '$pass' pass at $(date)" 690done 691 692echo "Success" 693 694