File: test

package info (click to toggle)
etcd 3.2.26%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 9,620 kB
  • sloc: sh: 2,191; makefile: 729
file content (632 lines) | stat: -rwxr-xr-x 18,642 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
#!/usr/bin/env bash
#
# Run all etcd tests
# ./test
# ./test -v
#
#
# Run specified test pass
#
# $ PASSES=unit ./test
# $ PASSES=integration ./test
#
#
# Run tests for one package
# Each pass has different default timeout, if you just run tests in one package or 1 test case then you can set TIMEOUT
# flag for different expectation
#
# $ PASSES=unit PKG=./wal TIMEOUT=1m ./test
# $ PASSES=integration PKG=client/integration TIMEOUT=1m ./test
#
#
# Run specified unit tests in one package
# To run all the tests with prefix of "TestNew", set "TESTCASE=TestNew ";
# to run only "TestNew", set "TESTCASE="\bTestNew\b""
#
# $ PASSES=unit PKG=./wal TESTCASE=TestNew TIMEOUT=1m ./test
# $ PASSES=unit PKG=./wal TESTCASE="\bTestNew\b" TIMEOUT=1m ./test
# $ PASSES=integration PKG=client/integration TESTCASE="\bTestV2NoRetryEOF\b" TIMEOUT=1m ./test
#
#
# Run code coverage
# COVERDIR must either be a absolute path or a relative path to the etcd root
# $ COVERDIR=coverage PASSES="build_cov cov" ./test
set -e

source ./build

# build before setting up test GOPATH
if [[ "${PASSES}" == *"functional"* ]]; then
	./functional/build
fi

# build tests with vendored dependencies
etcd_setup_gopath

if [ -z "$PASSES" ]; then
	PASSES="fmt bom dep build unit"
fi

USERPKG=${PKG:-}

# Invoke ./tests/cover.test.bash for HTML output
COVER=${COVER:-"-cover"}

# Hack: gofmt ./ will recursively check the .git directory. So use *.go for gofmt.
IGNORE_PKGS="(cmd/|etcdserverpb|rafttest|gopath.proto|v3lockpb|v3electionpb)"
INTEGRATION_PKGS="(integration|e2e|contrib|functional)"

# all github.com/coreos/etcd/whatever pkgs that are not auto-generated / tools
# shellcheck disable=SC1117
PKGS=$(find . -name \*.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | grep -vE "(tools/|contrib/|e2e|pb)" | sed "s|\.|${REPO_PATH}|g" | xargs echo)
# pkg1,pkg2,pkg3
PKGS_COMMA=${PKGS// /,}

# shellcheck disable=SC1117
TEST_PKGS=$(find . -name \*_test.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g")

# shellcheck disable=SC1117
FORMATTABLE=$(find . -name \*.go | while read -r a; do echo "$(dirname "$a")/*.go"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | sed "s|\./||g")

TESTABLE_AND_FORMATTABLE=$(echo "$TEST_PKGS" | grep -vE "$INTEGRATION_PKGS")

# check if user provided PKG override
if [ -z "${USERPKG}" ]; then
	TEST=$TESTABLE_AND_FORMATTABLE
	FMT=$FORMATTABLE
else
	# strip out leading dotslashes and trailing slashes from PKG=./foo/
	TEST=${USERPKG/#./}
	TEST=${TEST/#\//}
	TEST=${TEST/%\//}
	# only run gofmt on packages provided by user
	FMT="$TEST"
fi

# shellcheck disable=SC2206
FMT=($FMT)

# prepend REPO_PATH to each local package
split=$TEST
TEST=""
for a in $split; do TEST="$TEST ${REPO_PATH}/${a}"; done

# shellcheck disable=SC2206
TEST=($TEST)

# TODO: 'client' pkg fails with gosimple from generated files
# TODO: 'rafttest' is failing with unused
STATIC_ANALYSIS_PATHS=$(find . -name \*.go | while read -r a; do dirname "$a"; done | sort | uniq | grep -vE "$IGNORE_PKGS" | grep -v 'client')
# shellcheck disable=SC2206
STATIC_ANALYSIS_PATHS=($STATIC_ANALYSIS_PATHS)

if [ -z "$GOARCH" ]; then
	GOARCH=$(go env GOARCH);
fi

# determine whether target supports race detection
if [ "$GOARCH" == "amd64" ]; then
	RACE="--race"
fi

RUN_ARG=""
if [ ! -z "${TESTCASE}" ]; then
	RUN_ARG="-run=${TESTCASE}"
fi

function unit_pass {
	echo "Running unit tests..."
	GO_TEST_FLAG=""
	if [ "${VERBOSE}" == "1" ]; then
		GO_TEST_FLAG="-v"
	fi
	if [ "${VERBOSE}" == "2" ]; then
		GO_TEST_FLAG="-v"
		export CLIENT_DEBUG=1
	fi

	if [ "${RUN_ARG}" == "" ]; then
	    RUN_ARG="-run=Test"
	fi

	# check if user provided time out, especially useful when just run one test case
	# expectation could be different
	USERTIMEOUT=""
	if [ -z "${TIMEOUT}" ]; then
		USERTIMEOUT="3m"
	else
		USERTIMEOUT="${TIMEOUT}"
	fi
	go test ${GO_TEST_FLAG} -timeout "${USERTIMEOUT}"  "${COVER}" ${RACE} -cpu 4 ${RUN_ARG} "$@" "${TEST[@]}"
}

function integration_pass {
	echo "Running integration tests..."

	# check if user provided time out, especially useful when just run one test case
	# expectation could be different
	USERTIMEOUT=""
	if [ -z "${TIMEOUT}" ]; then
		USERTIMEOUT="20m"
	else
		USERTIMEOUT="${TIMEOUT}"
	fi

	# if TESTCASE and PKG set, run specified test case in specified PKG
	# if TESTCASE set, PKG not set, run specified test case in all integration and integration_extra packages
	# if TESTCASE not set, PKG set, run all test cases in specified package
	# if TESTCASE not set, PKG not set, run all tests in all integration and integration_extra packages
	if [ -z "${TESTCASE}" ] && [ -z "${USERPKG}" ]; then
		go test -timeout "${USERTIMEOUT}" -v -cpu 4 "$@" "${REPO_PATH}/integration"
		integration_extra "$@"
	else
		if [ -z "${USERPKG}" ]; then
			INTEGTESTPKG=("${REPO_PATH}/integration"
						  "${REPO_PATH}/client/integration"
						  "${REPO_PATH}/clientv3/integration"
						  "${REPO_PATH}/store")
		else
			INTEGTESTPKG=("${TEST[@]}")
		fi
		go test -timeout "${USERTIMEOUT}" -v -cpu 4 "${RUN_ARG}"  "$@" "${INTEGTESTPKG[@]}"
	fi
}

function integration_extra {
	go test -timeout 1m -v ${RACE} -cpu 4 "$@" "${REPO_PATH}/client/integration"
	go test -timeout 25m -v ${RACE} -cpu 4 "$@" "${REPO_PATH}/clientv3/integration"
}

function functional_pass {
  	# Clean up any data and logs from previous runs
  	rm -rf /tmp/etcd-functional-* /tmp/etcd-functional-*.backup

	for a in 1 2 3; do
		./bin/etcd-agent --network tcp --address 127.0.0.1:${a}9027 &
		pid="$!"
		agent_pids="${agent_pids} $pid"
	done

	for a in 1 2 3; do
		echo "Waiting for 'etcd-agent' on ${a}9027..."
		while ! nc -z localhost ${a}9027; do
			sleep 1
		done
	done

	echo "functional test START!"
	./bin/etcd-tester --config ./functional.yaml && echo "'etcd-tester' succeeded"
	ETCD_TESTER_EXIT_CODE=$?
	echo "ETCD_TESTER_EXIT_CODE:" ${ETCD_TESTER_EXIT_CODE}

	# shellcheck disable=SC2206
	agent_pids=($agent_pids)
	kill -s TERM "${agent_pids[@]}" || true

	if [[ "${ETCD_TESTER_EXIT_CODE}" -ne "0" ]]; then
		echo "--- FAIL: exit code" ${ETCD_TESTER_EXIT_CODE}
		exit ${ETCD_TESTER_EXIT_CODE}
	fi
	echo "functional test PASS!"
}

function cov_pass {
	echo "Running code coverage..."
	# install gocovmerge before running code coverage from github.com/wadey/gocovmerge
	# gocovmerge merges coverage files
	if ! which gocovmerge >/dev/null; then
		echo "gocovmerge not installed"
		exit 255
	fi

	if [ -z "$COVERDIR" ]; then
		echo "COVERDIR undeclared"
		exit 255
	fi

	if [ ! -f "bin/etcd_test" ]; then
		echo "etcd_test binary not found"
		exit 255
	fi

	mkdir -p "$COVERDIR"

	# run code coverage for unit and integration tests
	GOCOVFLAGS="-covermode=set -coverpkg ${PKGS_COMMA} -v -timeout 20m"
	# shellcheck disable=SC2206
	GOCOVFLAGS=($GOCOVFLAGS)
	failed=""
	for t in $(echo "${TEST_PKGS}" | grep -vE "(e2e|functional)"); do
		tf=$(echo "$t" | tr / _)
		# cache package compilation data for faster repeated builds
		go test "${GOCOVFLAGS[@]}" -i "${REPO_PATH}/$t" || true
		# uses -run=Test to skip examples because clientv3/ example tests will leak goroutines
		go test "${GOCOVFLAGS[@]}" -run=Test -coverprofile "$COVERDIR/${tf}.coverprofile"  "${REPO_PATH}/$t" || failed="$failed $t"
	done

	# v2v3 tests
	go test -tags v2v3 "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/store-v2v3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed store-v2v3"

	# proxy tests
	go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_integration.coverprofile" "${REPO_PATH}/integration" || failed="$failed proxy-integration"
	go test -tags cluster_proxy "${GOCOVFLAGS[@]}" -coverprofile "$COVERDIR/proxy_clientv3.coverprofile" "${REPO_PATH}/clientv3/integration" || failed="$failed proxy-clientv3/integration"

	# run code coverage for e2e tests
	# use 30m timeout because e2e coverage takes longer
	# due to many tests cause etcd process to wait
	# on leadership transfer timeout during gracefully shutdown
	echo Testing e2e without proxy...
	go test -tags cov -timeout 30m -v "${REPO_PATH}/e2e" || failed="$failed e2e"
	echo Testing e2e with proxy...
	go test -tags "cov cluster_proxy" -timeout 30m -v "${REPO_PATH}/e2e" || failed="$failed e2e-proxy"

	# incrementally merge to get coverage data even if some coverage files are corrupted
	# optimistically assume etcdserver package's coverage file is OK since gocovmerge
	# expects to start with a non-empty file
	cp "$COVERDIR"/etcdserver.coverprofile "$COVERDIR"/cover.out
	for f in "$COVERDIR"/*.coverprofile; do
		echo "merging test coverage file ${f}"
		gocovmerge "$f" "$COVERDIR"/cover.out  >"$COVERDIR"/cover.tmp || failed="$failed $f"
		if [ -s "$COVERDIR"/cover.tmp ]; then
			mv "$COVERDIR"/cover.tmp "$COVERDIR"/cover.out
		fi
	done
	# strip out generated files (using GNU-style sed)
	sed --in-place '/generated.go/d' "$COVERDIR"/cover.out || true

	# held failures to generate the full coverage file, now fail
	if [ -n "$failed" ]; then
		for f in $failed; do
			echo "--- FAIL:" "$f"
		done
		exit 255
	fi
}

function e2e_pass {
	echo "Running e2e tests..."

	# check if user provided time out, especially useful when just run one test case
	# expectation could be different
	USERTIMEOUT=""
	if [ -z "${TIMEOUT}" ]; then
		USERTIMEOUT="20m"
	else
		USERTIMEOUT="${TIMEOUT}"
	fi

	go test -timeout "${USERTIMEOUT}" -v -cpu 4 "${RUN_ARG}"  "$@" "${REPO_PATH}/e2e"
}

function integration_e2e_pass {
	echo "Running integration and e2e tests..."

	go test -timeout 20m -v -cpu 4 "$@" "${REPO_PATH}/e2e" &
	e2epid="$!"
	go test -timeout 20m -v -cpu 4 "$@" "${REPO_PATH}/integration" &
	intpid="$!"
	wait $e2epid
	wait $intpid
	integration_extra "$@"
}

function grpcproxy_pass {
	go test -timeout 20m -v ${RACE} -tags cluster_proxy -cpu 4 "$@" "${REPO_PATH}/integration"
	go test -timeout 20m -v ${RACE} -tags cluster_proxy -cpu 4 "$@" "${REPO_PATH}/clientv3/integration"
	go test -timeout 20m -v -tags cluster_proxy "$@" "${REPO_PATH}/e2e"
}

function release_pass {
	rm -f ./bin/etcd-last-release
	# to grab latest patch release; bump this up for every minor release
	UPGRADE_VER=$(git tag -l --sort=-version:refname "v3.2.*" | head -1)
	if [ -n "$MANUAL_VER" ]; then
		# in case, we need to test against different version
		UPGRADE_VER=$MANUAL_VER
	fi
	if [[ -z ${UPGRADE_VER} ]]; then
		UPGRADE_VER="v3.2.0"
		echo "fallback to" ${UPGRADE_VER}
	fi

	local file="etcd-$UPGRADE_VER-linux-$GOARCH.tar.gz"
	echo "Downloading $file"

	set +e
	curl --fail -L "https://github.com/coreos/etcd/releases/download/$UPGRADE_VER/$file" -o "/tmp/$file"
	local result=$?
	set -e
	case $result in
		0)	;;
		*)	echo "--- FAIL:" ${result}
			exit $result
			;;
	esac

	tar xzvf "/tmp/$file" -C /tmp/ --strip-components=1
	mkdir -p ./bin
	mv /tmp/etcd ./bin/etcd-last-release
}

function shellcheck_pass {
	if which shellcheck >/dev/null; then
		shellcheckResult=$(shellcheck -fgcc build test scripts/*.sh 2>&1 || true)
		if [ -n "${shellcheckResult}" ]; then
			echo -e "shellcheck checking failed:\\n${shellcheckResult}"
			exit 255
		fi
	fi
}

function markdown_you_pass {
	# eschew you
	yous=$(find . -name \*.md -exec grep -E --color "[Yy]ou[r]?[ '.,;]" {} + | grep -v /v2/ || true)
	if [ ! -z "$yous" ]; then
		echo -e "found 'you' in documentation:\\n${yous}"
		exit 255
	fi
}

function markdown_marker_pass {
	# TODO: check other markdown files when marker handles headers with '[]'
	if which marker >/dev/null; then
		markerResult=$(marker --skip-http --root ./Documentation 2>&1 || true)
		if [ -n "${markerResult}" ]; then
			echo -e "marker checking failed:\\n${markerResult}"
			exit 255
		fi
	else
		echo "Skipping marker..."
	fi
}

function goword_pass {
	if which goword >/dev/null; then
		# get all go files to process
		gofiles=$(find "${FMT[@]}" -iname '*.go' 2>/dev/null)
		# shellcheck disable=SC2206
		gofiles_all=($gofiles)
		# ignore tests and protobuf files
		# shellcheck disable=SC1117
		gofiles=$(echo "${gofiles_all[@]}" | sort | uniq | sed "s/ /\n/g" | grep -vE "(\\_test.go|\\.pb\\.go)")
		# shellcheck disable=SC2206
		gofiles=($gofiles)
		# only check for broken exported godocs
		gowordRes=$(goword -use-spell=false "${gofiles[@]}" | grep godoc-export | sort)
		if [ ! -z "$gowordRes" ]; then
			echo -e "goword checking failed:\\n${gowordRes}"
			exit 255
		fi
		# check some spelling
		gowordRes=$(goword -ignore-file=.words clientv3/{*,*/*}.go 2>&1 | grep spell | sort)
		if [ ! -z "$gowordRes" ]; then
			echo -e "goword checking failed:\\n${gowordRes}"
			exit 255
		fi
	else
		echo "Skipping goword..."
	fi
}

function gofmt_pass {
	fmtRes=$(gofmt -l -s -d "${FMT[@]}")
	if [ -n "${fmtRes}" ]; then
		echo -e "gofmt checking failed:\\n${fmtRes}"
		exit 255
	fi
}

function govet_pass {
	vetRes=$(go vet "${TEST[@]}")
	if [ -n "${vetRes}" ]; then
		echo -e "govet checking failed:\\n${vetRes}"
		exit 255
	fi
}

function govet_shadow_pass {
	fmtpkgs=$(for a in "${FMT[@]}"; do dirname "$a"; done | sort | uniq | grep -v "\\.")
	# shellcheck disable=SC2206
	fmtpkgs=($fmtpkgs)
	vetRes=$(go tool vet -all -shadow "${fmtpkgs[@]}" 2>&1 | grep -v '/gw/' || true)
	if [ -n "${vetRes}" ]; then
		echo -e "govet -all -shadow checking failed:\\n${vetRes}"
		exit 255
	fi
}

function gosimple_pass {
	if which gosimple >/dev/null; then
		gosimpleResult=$(gosimple "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
		if [ -n "${gosimpleResult}" ]; then
			echo -e "gosimple checking failed:\\n${gosimpleResult}"
			exit 255
		fi
	else
		echo "Skipping gosimple..."
	fi
}

function unused_pass {
	if which unused >/dev/null; then
		unusedResult=$(unused "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
		if [ -n "${unusedResult}" ]; then
			echo -e "unused checking failed:\\n${unusedResult}"
			exit 255
		fi
	else
		echo "Skipping unused..."
	fi
}

function staticcheck_pass {
	if which staticcheck >/dev/null; then
		staticcheckResult=$(staticcheck "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
		if [ -n "${staticcheckResult}" ]; then
			# TODO: resolve these after go1.8 migration
			# See https://github.com/dominikh/go-tools/tree/master/cmd/staticcheck
			STATIC_CHECK_MASK="SA(1012|1019|2002)"
			if echo "${staticcheckResult}" | grep -vE "$STATIC_CHECK_MASK"; then
				echo -e "staticcheck checking failed:\\n${staticcheckResult}"
				exit 255
			else
				suppressed=$(echo "${staticcheckResult}" | sed 's/ /\n/g' | grep "(SA" | sort | uniq -c)
				echo -e "staticcheck suppressed warnings:\\n${suppressed}"
			fi
		fi
	else
		echo "Skipping staticcheck..."
	fi
}

function ineffassign_pass {
	if which ineffassign >/dev/null; then
		ineffassignResult=$(ineffassign "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
		if [ -n "${ineffassignResult}" ]; then
			echo -e "ineffassign checking failed:\\n${ineffassignResult}"
			exit 255
		fi
	else
		echo "Skipping ineffassign..."
	fi
}

function nakedret_pass {
	if which nakedret >/dev/null; then
		nakedretResult=$(nakedret "${STATIC_ANALYSIS_PATHS[@]}" 2>&1 || true)
		if [ -n "${nakedretResult}" ]; then
			echo -e "nakedret checking failed:\\n${nakedretResult}"
			exit 255
		fi
	else
		echo "Skipping nakedret..."
	fi
}

function license_header_pass {
	licRes=""
	files=$(find . -type f -iname '*.go' ! -path './cmd/*' ! -path './gopath.proto/*')
	for file in $files; do
		if ! head -n3 "${file}" | grep -Eq "(Copyright|generated|GENERATED)" ; then
			licRes="${licRes}"$(echo -e "  ${file}")
		fi
	done
	if [ -n "${licRes}" ]; then
		echo -e "license header checking failed:\\n${licRes}"
		exit 255
	fi
}

function receiver_name_pass {
	# shellcheck disable=SC1117
	recvs=$(grep 'func ([^*]' {*,*/*,*/*/*}.go  | grep -Ev "(generated|pb/)" | tr  ':' ' ' |  \
		awk ' { print $2" "$3" "$4" "$1 }' | sed "s/[a-zA-Z\.]*go//g" |  sort  | uniq  | \
		grep -Ev  "(Descriptor|Proto|_)"  | awk ' { print $3" "$4 } ' | sort | uniq -c | grep -v ' 1 ' | awk ' { print $2 } ')
	if [ -n "${recvs}" ]; then
		# shellcheck disable=SC2206
		recvs=($recvs)
		for recv in "${recvs[@]}"; do
			echo "Mismatched receiver for $recv..."
			grep "$recv" "${FMT[@]}" | grep 'func ('
		done
		exit 255
	fi
}

function commit_title_pass {
	git log --oneline "$(git merge-base HEAD master)"...HEAD | while read -r l; do
		commitMsg=$(echo "$l" | cut -f2- -d' ')
		if [[ "$commitMsg" == Merge* ]]; then
			# ignore "Merge pull" commits
			continue
		fi
		if [[ "$commitMsg" == Revert* ]]; then
			# ignore revert commits
			continue
		fi

		pkgPrefix=$(echo "$commitMsg" | cut -f1 -d':')
		spaceCommas=$(echo "$commitMsg" | sed 's/ /\n/g' | grep -c ',$' || echo 0)
		commaSpaces=$(echo "$commitMsg" | sed 's/,/\n/g' | grep -c '^ ' || echo 0)
		if [[ $(echo "$commitMsg" | grep -c ":..*") == 0 || "$commitMsg" == "$pkgPrefix" || "$spaceCommas" != "$commaSpaces" ]]; then
			echo "$l"...
			echo "Expected commit title format '<package>{\", \"<package>}: <description>'"
			echo "Got: $l"
			exit 255
		fi
	done
}

function fmt_pass {
	toggle_failpoints disable

	for p in shellcheck \
			markdown_you \
			markdown_marker \
			goword \
			gofmt \
			govet \
			govet_shadow \
			gosimple \
			unused \
			staticcheck \
			ineffassign \
			nakedret \
			license_header \
			receiver_name \
			commit_title \
			; do
		echo "'$p' started at $(date)"
		"${p}"_pass "$@"
		echo "'$p' completed at $(date)"
	done
}

function bom_pass {
	if ! which license-bill-of-materials >/dev/null; then
		return
	fi
	echo "Checking bill of materials..."
	license-bill-of-materials \
		--override-file bill-of-materials.override.json \
		github.com/coreos/etcd github.com/coreos/etcd/etcdctl >bom-now.json || true
	if ! diff bill-of-materials.json bom-now.json; then
		echo "vendored licenses do not match given bill of materials"
		exit 255
	fi
	rm bom-now.json
}

function dep_pass {
	echo "Checking package dependencies..."
	# don't pull in etcdserver package
	pushd clientv3 >/dev/null
	badpkg="(etcdserver$|mvcc$|backend$|grpc-gateway)"
	deps=$(go list -f '{{ .Deps }}'  | sed 's/ /\n/g' | grep -E "${badpkg}" || echo "")
	popd >/dev/null
	if [ ! -z "$deps" ]; then
		echo -e "clientv3 has masked dependencies:\\n${deps}"
		exit 255
	fi
}

function build_cov_pass {
	out="bin"
	if [ -n "${BINDIR}" ]; then out="${BINDIR}"; fi
	go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcd_test"
	go test -tags cov -c -covermode=set -coverpkg="$PKGS_COMMA" -o "${out}/etcdctl_test" "${REPO_PATH}/etcdctl"
}

# fail fast on static tests
function build_pass {
	echo "Checking build..."
	GO_BUILD_FLAGS="-v" etcd_build
}

for pass in $PASSES; do
	echo "Starting '$pass' pass at $(date)"
	"${pass}"_pass "$@"
	echo "Finished '$pass' pass at $(date)"
done

echo "Success"