go.etcd.io/etcd@v3.3.27+incompatible/test (about)

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