Browse code

rewrite hack/test-go.sh

Steve Kuznetsov authored on 2016/01/09 13:05:09
Showing 3 changed files
... ...
@@ -57,7 +57,7 @@ Run the unit tests with:
57 57
 
58 58
     $ hack/test-go.sh
59 59
 
60
-or an individual package unit test with:
60
+or an individual package using its relative path with:
61 61
 
62 62
     $ hack/test-go.sh pkg/build
63 63
 
... ...
@@ -69,37 +69,53 @@ To run only a certain regex of tests in a package, use:
69 69
 
70 70
     $ hack/test-go.sh pkg/build -test.run=SynchronizeBuildRunning
71 71
 
72
-To get verbose output add `-v` to the end:
72
+To get verbose output for the above example:
73 73
 
74 74
     $ hack/test-go.sh pkg/build -test.run=SynchronizeBuildRunning -v
75 75
 
76 76
 To run all tests with verbose output:
77 77
 
78
-    $ hack/test-go.sh "" -v
78
+    $ hack/test-go.sh -v
79
+
80
+To change the timeout for individual unit tests, which defaults to one minute, use:
81
+
82
+    $ TIMEOUT=<timeout> hack/test-go.sh
79 83
 
80 84
 To enable running the kubernetes unit tests:
81 85
 
82
-    $ TEST_KUBE=1 hack/test-go.sh
86
+    $ TEST_KUBE=true hack/test-go.sh
83 87
 
84 88
 To run unit test for an individual kubernetes package:
85 89
 
86
-    $ TEST_KUBE=1 hack/test-go.sh Godeps/_workspace/src/k8s.io/kubernetes/examples
90
+    $ hack/test-go.sh Godeps/_workspace/src/k8s.io/kubernetes/examples
91
+
92
+To change the coverage mode, which is `-cover -covermode=atomic` by default, use:
87 93
 
88
-To turn off or change the coverage mode, which is `-cover -covermode=atomic` by default, use:
94
+    $ COVERAGE_SPEC="<some coverage specification>" hack/test-go.sh
89 95
 
90
-    $ KUBE_COVER="" hack/test-go.sh
96
+To turn off coverage calculation, which is on by default, use:
97
+
98
+    $ COVERAGE_SPEC= hack/test-go.sh
91 99
 
92 100
 To run tests without the go race detector, which is on by default, use:
93 101
 
94
-    $ KUBE_RACE="" hack/test-go.sh
102
+    $ DETECT_RACES= hack/test-go.sh
95 103
 
96 104
 To create a line coverage report, set `OUTPUT_COVERAGE` to a path where the
97 105
 report should be stored. For example:
98 106
 
99
-    $ OUTPUT_COVERAGE=/path/to/dir hack/test-go.sh pkg/build
107
+    $ COVERAGE_OUTPUT_DIR='/path/to/dir' hack/test-go.sh
100 108
 
101 109
 After that you can open `/path/to/dir/coverage.html` in the browser.
102 110
 
111
+To generate a jUnit XML report from the output of the tests, and see a summary of the test output 
112
+instead of the full test output, use:
113
+    
114
+    $ JUNIT_REPORT=true hack/test-go.sh
115
+
116
+`hack/test-go.sh` cannot generate jUnit XML and a coverage report for all packages at once. If you
117
+require both, you must call `hack/test-go.sh` twice.
118
+
103 119
 ### Integration tests
104 120
 
105 121
 Integration tests cover multiple components acting together (generally, 2 or 3). These tests should
... ...
@@ -70,7 +70,7 @@ verify: build
70 70
 #   make test-unit
71 71
 #   make test-unit WHAT=pkg/build GOFLAGS=-v
72 72
 test-unit:
73
-	TEST_KUBE=1 hack/test-go.sh $(WHAT) $(TESTS) $(TESTFLAGS)
73
+	TEST_KUBE=true GOTEST_FLAGS="$(TESTFLAGS)" hack/test-go.sh $(WHAT) $(TESTS) 
74 74
 .PHONY: test-unit
75 75
 
76 76
 # Run integration tests. Compiles its own tests, cannot be run
... ...
@@ -1,129 +1,250 @@
1 1
 #!/bin/bash
2
-
3
-# See HACKING.md for usage
2
+#
3
+# This script runs Go language unit tests for the Origin repository. Arguments to this script
4
+# are parsed as a list of packages to test until the first argument starting with '-' or '--' is
5
+# found. That argument and all following arguments are interpreted as flags to be passed directly
6
+# to `go test`. If no arguments are given, then "all" packages are tested.
7
+# 
8
+# Coverage reports and jUnit XML reports can be generated by this script as well, but both cannot
9
+# be generated at once.
10
+#
11
+# This script consumes the following parameters as environment variables:
12
+#  - DRY_RUN:             prints all packages that would be tested with the args that would be used and exits
13
+#  - TEST_KUBE:           toggles testing of non-essential Kubernetes unit tests
14
+#  - TIMEOUT:             the timeout for any one unit test (default '60s')
15
+#  - DETECT_RACES:        toggles the 'go test' race detector (defaults '-race')
16
+#  - COVERAGE_OUTPUT_DIR: locates the directory in which coverage output files will be placed
17
+#  - COVERAGE_SPEC:       a set of flags for 'go test' that specify the coverage behavior (default '-cover -covermode=atomic')
18
+#  - GOTEST_FLAGS:        any other flags to be sent to 'go test'
19
+#  - JUNIT_REPORT:        toggles the creation of jUnit XML from the test output and changes this script's output behavior
20
+#                         to use the 'junitreport' tool for summarizing the tests.
4 21
 
5 22
 set -o errexit
6 23
 set -o nounset
7 24
 set -o pipefail
8 25
 
9
-STARTTIME=$(date +%s)
10
-OS_ROOT=$(dirname "${BASH_SOURCE}")/..
11
-source "${OS_ROOT}/hack/common.sh"
26
+function exit_trap() {
27
+    local return_code=$?
12 28
 
13
-# Go to the top of the tree.
14
-cd "${OS_ROOT}"
29
+    end_time=$(date +%s)
30
+    
31
+    if [[ "${return_code}" -eq "0" ]]; then
32
+        verb="succeeded"
33
+    else
34
+        verb="failed"
35
+    fi
36
+
37
+    echo "$0 ${verb} after $((${end_time} - ${start_time})) seconds"
38
+    exit "${return_code}"
39
+}
15 40
 
16
-PRINT_PACKAGES=${PRINT_PACKAGES:-""}
17
-TEST_KUBE=${TEST_KUBE:-""}
18
-KUBE_GODEP_PATH="./Godeps/_workspace/src/k8s.io/kubernetes/pkg"
41
+trap exit_trap EXIT
19 42
 
43
+start_time=$(date +%s)
44
+OS_ROOT=$(dirname "${BASH_SOURCE}")/..
45
+source "${OS_ROOT}/hack/common.sh"
46
+source "${OS_ROOT}/hack/util.sh"
47
+source "${OS_ROOT}/hack/lib/util/environment.sh"
48
+cd "${OS_ROOT}"
49
+os::log::install_errexit
20 50
 os::build::setup_env
51
+os::util::environment::setup_tmpdir_vars "test-go"
21 52
 
22
-find_test_dirs() {
23
-  cd "${OS_ROOT}"
24
-  find $1 -not \( \
25
-      \( \
26
-        -wholename './Godeps' \
27
-        -o -wholename './_output' \
28
-        -o -wholename './_tools' \
29
-        -o -wholename './.git' \
30
-        -o -wholename './openshift.local.*' \
31
-        -o -wholename '*/Godeps/*' \
32
-        -o -wholename './assets/node_modules' \
33
-        -o -wholename './test/extended' \
34
-      \) -prune \
35
-    \) -name '*_test.go' -print0 | xargs -0n1 dirname | sort -u | xargs -n1 printf "${OS_GO_PACKAGE}/%s\n"
36
-}
53
+# TODO(skuznets): remove these once we've migrated all tools to the new vars
54
+if [[ -n "${KUBE_TIMEOUT+x}" ]]; then
55
+    TIMEOUT="${KUBE_TIMEOUT}"
56
+    echo "[WARNING] The flag \$KUBE_TIMEOUT for $0 is deprecated, use \$TIMEOUT instead."
57
+fi
37 58
 
38
-special_upstream_test_dirs() {
39
-  echo "${OS_GO_PACKAGE}/${KUBE_GODEP_PATH}/api"
40
-  echo "${OS_GO_PACKAGE}/${KUBE_GODEP_PATH}/api/v1"
41
-}
59
+if [[ -n "${KUBE_COVER+x}" ]]; then
60
+    COVERAGE_SPEC="${KUBE_COVER}"
61
+    echo "[WARNING] The flag \$KUBE_COVER for $0 is deprecated, use \$COVERAGE_SPEC instead."
62
+fi
42 63
 
43
-# find the upstream test directories, excluding special-case directories and the upstream runtime package.
44
-# The tests for the upstream runtime package are not solvent currently due to a patch for:
45
-# https://github.com/kubernetes/kubernetes/pull/9971
46
-#
47
-# k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned  - ignored because it is
48
-#   for testing types.generated.go which we are not currently using and will fail.
49
-find_upstream_test_dirs() {
50
-  cd "${OS_ROOT}"
51
-  find ./Godeps/_workspace/src/k8s.io/kubernetes -not \( \
52
-      \( \
53
-        -wholename "${KUBE_GODEP_PATH}/api" \
54
-        -o -wholename "${KUBE_GODEP_PATH}/api/v1" \
55
-        -o -wholename './Godeps/_workspace/src/k8s.io/kubernetes/pkg/runtime' \
56
-        -o -wholename './Godeps/_workspace/src/k8s.io/kubernetes/test/e2e' \
57
-        -o -wholename './Godeps/_workspace/src/k8s.io/kubernetes/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned' \
58
-      \) -prune \
59
-    \) -name '*_test.go' -print0 | xargs -0n1 dirname | sort -u | xargs -n1 printf "${OS_GO_PACKAGE}/%s\n"
60
-}
64
+if [[ -n "${OUTPUT_COVERAGE+x}" ]]; then
65
+    COVERAGE_OUTPUT_DIR="${OUTPUT_COVERAGE}"
66
+    echo "[WARNING] The flag \$OUTPUT_COVERAGE for $0 is deprecated, use \$COVERAGE_OUTPUT_DIR instead."
67
+fi
61 68
 
62
-# -covermode=atomic becomes default with -race in Go >=1.3
63
-if [ -z ${KUBE_COVER+x} ]; then
64
-  KUBE_COVER=""
69
+if [[ -n "${KUBE_RACE+x}" ]]; then
70
+    DETECT_RACES="${KUBE_RACE}"
71
+    echo "[WARNING] The flag \$KUBE_RACE for $0 is deprecated, use \$DETECT_RACES instead."
65 72
 fi
66 73
 
67
-OUTPUT_COVERAGE=${OUTPUT_COVERAGE:-""}
74
+if [[ -n "${PRINT_PACKAGES+x}" ]]; then
75
+    DRY_RUN="${PRINT_PACKAGES}"
76
+    echo "[WARNING] The flag \$PRINT_PACKAGES for $0 is deprecated, use \$DRY_RUN instead."
77
+fi
68 78
 
69
-if [ -n "${OUTPUT_COVERAGE}" ]; then
70
-  if [ -z ${KUBE_RACE+x} ]; then
71
-    KUBE_RACE="-race"
72
-  fi
73
-  if [ -z "${KUBE_COVER}" ]; then
74
-    KUBE_COVER="-cover -covermode=atomic"
75
-  fi
79
+# Internalize environment variables we consume and default if they're not set
80
+dry_run="${DRY_RUN:-}"
81
+test_kube="${TEST_KUBE:-}"
82
+test_timeout="${TIMEOUT:-60s}"
83
+detect_races="${DETECT_RACES:-true}"
84
+coverage_output_dir="${COVERAGE_OUTPUT_DIR:-}"
85
+coverage_spec="${COVERAGE_SPEC:--cover -covermode atomic}"
86
+gotest_flags="${GOTEST_FLAGS:-}"
87
+junit_report="${JUNIT_REPORT:-}"
88
+
89
+if [[ -n "${junit_report}" && -n "${coverage_output_dir}" ]]; then
90
+    echo "$0 cannot create jUnit XML reports and coverage reports at the same time."
91
+    exit 1
76 92
 fi
77 93
 
78
-if [ -z ${KUBE_RACE+x} ]; then
79
-  KUBE_RACE=""
94
+# determine if user wanted verbosity
95
+verbose=
96
+if [[ "${gotest_flags}" =~ -v( |$) ]]; then
97
+    verbose=true
80 98
 fi
81 99
 
82
-KUBE_TIMEOUT=${KUBE_TIMEOUT:-'60s'}
83
-
84
-if [ "${1-}" != "" ]; then
85
-  suffix=/...
86
-  if [[ "${1}" == *"${suffix}" ]]; then
87
-    # if the package name ends with /... they are intending a recursive test
88
-    test_packages=$(find_test_dirs "${1%$suffix}")
89
-  else
90
-    test_packages="$OS_GO_PACKAGE/$1"
91
-  fi
92
-elif [ -n "$TEST_KUBE" ]; then
93
-  test_packages=`find_test_dirs "."; special_upstream_test_dirs; find_upstream_test_dirs`
94
-else
95
-  test_packages=`find_test_dirs "."; special_upstream_test_dirs`
100
+# Build arguments for 'go test'
101
+if [[ -z "${verbose}" && -n "${junit_report}" ]]; then
102
+    # verbosity can be set explicitly by the user or set implicitly by asking for the jUnit 
103
+    # XML report, so we only want to add the flag if it hasn't been added by a user already 
104
+    # and is being implicitly set by jUnit report generation
105
+    gotest_flags+=" -v"
96 106
 fi
97 107
 
98
-if [ -n "$PRINT_PACKAGES" ]; then
99
-  for package in $test_packages
100
-  do
101
-    echo $package
102
-  done
108
+if [[ -n "${detect_races}" ]]; then
109
+    gotest_flags+=" -race"
110
+fi
103 111
 
104
-  exit 0
112
+# check to see if user has not disabled coverage mode
113
+if [[ -n "${coverage_spec}" ]]; then
114
+    # if we have a coverage spec set, we add it. '-race' implies '-cover -covermode atomic'
115
+    # but specifying both at the same time does not lead to an error so we can add both specs
116
+    gotest_flags+=" ${coverage_spec}"
105 117
 fi
106 118
 
107
-export OPENSHIFT_ON_PANIC=crash
119
+# check to see if user has not disabled test timeouts
120
+if [[ -n "${test_timeout}" ]]; then
121
+    gotest_flags+=" -timeout ${test_timeout}"
122
+fi
108 123
 
109
-if [[ -n "${KUBE_COVER}" && -n "${OUTPUT_COVERAGE}" ]]; then
110
-  # Iterate over packages to run coverage
111
-  test_packages=( $test_packages )
112
-  for test_package in "${test_packages[@]}"
113
-  do
114
-    mkdir -p "$OUTPUT_COVERAGE/$test_package"
115
-    KUBE_COVER_PROFILE="-coverprofile=$OUTPUT_COVERAGE/$test_package/profile.out"
124
+# list_test_packages_under lists all packages containing Golang test files that we want to run as unit tests
125
+# under the given base dir in the OpenShift Origin tree
126
+function list_test_packages_under() {
127
+    local basedir=$@
128
+
129
+    # we do not quote ${basedir} to allow for multiple arguments to be passed in as well as to allow for
130
+    # arguments that use expansion, e.g. paths containing brace expansion or wildcards
131
+    find ${basedir} -not \(                   \
132
+        \(                                    \
133
+              -path 'Godeps'                  \
134
+              -o -path '*_output'             \
135
+              -o -path '*_tools'              \
136
+              -o -path '*.git'                \
137
+              -o -path '*openshift.local.*'   \
138
+              -o -path '*Godeps/*'            \
139
+              -o -path '*assets/node_modules' \
140
+              -o -path '*test/*'              \
141
+        \) -prune                             \
142
+    \) -name '*_test.go' -print0 | xargs -0n1 dirname -z | sort -uz | xargs -0n1 printf "${OS_GO_PACKAGE}/%s\n"
143
+}
116 144
 
117
-    go test $KUBE_RACE -timeout $KUBE_TIMEOUT $KUBE_COVER "$KUBE_COVER_PROFILE" "$test_package" "${@:2}"
118
-  done
145
+# Break up the positional arguments into packages that need to be tested and arguments that need to be passed to `go test`
146
+package_args=
147
+for arg in "$@"; do
148
+    if [[ "${arg}" =~ -.* ]]; then
149
+        # we found an arg that begins with a dash, so we stop interpreting arguments 
150
+        # henceforth as packages and instead interpret them as flags to give to `go test`
151
+        break
152
+    fi
153
+    # an arg found before the first flag is a package
154
+    package_args+=" ${arg}"
155
+    shift
156
+done
157
+gotest_flags+=" $*"
158
+
159
+# Determine packages to test
160
+test_packages=
161
+if [[ -n "${package_args}" ]]; then
162
+    for package in ${package_args}; do
163
+        test_packages="${test_packages} ${OS_GO_PACKAGE}/${package}"
164
+    done
165
+else
166
+    # If no packages are given to test, we need to generate a list of all packages with unit tests
167
+    openshift_test_packages="$(list_test_packages_under '*')"  
168
+
169
+    kubernetes_path="Godeps/_workspace/src/k8s.io/kubernetes"
170
+    mandatory_kubernetes_packages="${OS_GO_PACKAGE}/${kubernetes_path}/pkg/api ${OS_GO_PACKAGE}/${kubernetes_path}/pkg/api/v1"
171
+
172
+    test_packages="${openshift_test_packages} ${mandatory_kubernetes_packages}"
173
+
174
+    if [[ -n "${test_kube}" ]]; then
175
+        # we need to find all of the kubernetes test suites, excluding those we directly whitelisted before, the end-to-end suite, and
176
+        # the go2idl tests which we currently do not support
177
+        optional_kubernetes_packages="$(find "${kubernetes_path}" -not \(                             \
178
+          \(                                                                                          \
179
+            -path "${kubernetes_path}/pkg/api"                                                        \
180
+            -o -path "${kubernetes_path}/pkg/api/v1"                                                  \
181
+            -o -path "${kubernetes_path}/test/e2e"                                                    \
182
+            -o -path "${kubernetes_path}/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned" \
183
+          \) -prune                                                                                   \
184
+        \) -name '*_test.go' -print0 | xargs -0n1 dirname -z | sort -uz | xargs -0n1 printf "${OS_GO_PACKAGE}/%s\n")"
185
+
186
+        test_packages="${test_packages} ${optional_kubernetes_packages}"
187
+    fi
188
+fi
119 189
 
120
-  echo 'mode: atomic' > ${OUTPUT_COVERAGE}/profiles.out
121
-  find $OUTPUT_COVERAGE -name profile.out | xargs sed '/^mode: atomic$/d' >> ${OUTPUT_COVERAGE}/profiles.out
122
-  go tool cover "-html=${OUTPUT_COVERAGE}/profiles.out" -o "${OUTPUT_COVERAGE}/coverage.html"
190
+if [[ -n "${dry_run}" ]]; then
191
+    echo "The following base flags for \`go test\` will be used by $0:"
192
+    echo "go test ${gotest_flags}"
193
+    echo "The following packages will be tested by $0:"
194
+    for package in ${test_packages}; do
195
+        echo "${package}"
196
+    done
197
+    exit 0
198
+fi
123 199
 
124
-  rm -rf $OUTPUT_COVERAGE/$OS_GO_PACKAGE
200
+# Run 'go test' with the accumulated arguments and packages:
201
+if [[ -n "${junit_report}" ]]; then
202
+    # we need to generate jUnit xml
203
+    hack/build-go.sh tools/junitreport
204
+    junitreport="$(os::build::find-binary junitreport)"
205
+
206
+    if [[ -z "${junitreport}" ]]; then
207
+        echo "It looks as if you don't have a compiled junitreport binary"
208
+        echo
209
+        echo "If you are running from a clone of the git repo, please run"
210
+        echo "'./hack/build-go.sh tools/junitreport'."
211
+        exit 1
212
+    fi
213
+
214
+    test_output_file="${LOG_DIR}/test-go.log"
215
+    junit_report_file="${ARTIFACT_DIR}/report.xml"
216
+
217
+    # we don't care if 'go test' fails, we want to summarize the output anyway.
218
+    return_code=0 # default to 0, overwrite only if the following line fails
219
+    go test ${gotest_flags} ${test_packages} > ${test_output_file} 2>&1 || return_code=$?
220
+    echo "[INFO] Output from \`go test\` redirected to ${test_output_file}"
221
+
222
+    cat "${test_output_file}" | "${junitreport}" --type gotest --suites nested --roots github.com/openshift/origin > "${junit_report_file}"
223
+    echo "[INFO] jUnit XML report placed at ${junit_report_file}"
224
+    cat "${junit_report_file}" | "${junitreport}" summarize
225
+    exit "${return_code}"
226
+
227
+elif [[ -n "${coverage_output_dir}" ]]; then
228
+    # we need to generate coverage reports
229
+    for test_package in ${test_packages}; do
230
+        mkdir -p "${coverage_output_dir}/${test_package}"
231
+        local_gotest_flags="${gotest_flags} -coverprofile=${coverage_output_dir}/${test_package}/profile.out"
232
+
233
+        go test ${local_gotest_flags} ${test_package}
234
+    done
235
+
236
+    # assemble all profiles and generate a coverage report
237
+    echo 'mode: atomic' > "${coverage_output_dir}/profiles.out"
238
+    find "${coverage_output_dir}" -name profile.out | xargs sed '/^mode: atomic$/d' >> "${coverage_output_dir}/profiles.out"
239
+
240
+    go tool cover "-html=${coverage_output_dir}/profiles.out" -o "${coverage_output_dir}/coverage.html"
241
+    echo "[INFO] Coverage profile written to ${coverage_output_dir}/coverage.html"
242
+
243
+    # clean up all of the individual coverage reports as they have been subsumed into the report at ${coverage_output_dir}/coverage.html
244
+    # we can clean up all of the coverage reports at once as they all exist in subdirectories of ${coverage_output_dir}/${OS_GO_PACKAGE}
245
+    # and they are the only files found in those subdirectories
246
+    rm -rf "${coverage_output_dir}/${OS_GO_PACKAGE}"
125 247
 else
126
-  nice go test $KUBE_RACE -timeout $KUBE_TIMEOUT $KUBE_COVER "${@:2}" $test_packages
248
+    # we need to generate neither jUnit XML nor coverage reports
249
+    go test ${gotest_flags} ${test_packages}
127 250
 fi
128
-
129
-ret=$?; ENDTIME=$(date +%s); echo "$0 took $(($ENDTIME - $STARTTIME)) seconds"; exit "$ret"