hack/test-go.sh
2c49e079
 #!/bin/bash
f3cf4a9e
 #
 # This script runs Go language unit tests for the Origin repository. Arguments to this script
 # are parsed as a list of packages to test until the first argument starting with '-' or '--' is
 # found. That argument and all following arguments are interpreted as flags to be passed directly
 # to `go test`. If no arguments are given, then "all" packages are tested.
f7a7cebd
 #
f3cf4a9e
 # Coverage reports and jUnit XML reports can be generated by this script as well, but both cannot
 # be generated at once.
 #
 # This script consumes the following parameters as environment variables:
 #  - DRY_RUN:             prints all packages that would be tested with the args that would be used and exits
 #  - TEST_KUBE:           toggles testing of non-essential Kubernetes unit tests
 #  - TIMEOUT:             the timeout for any one unit test (default '60s')
 #  - DETECT_RACES:        toggles the 'go test' race detector (defaults '-race')
 #  - COVERAGE_OUTPUT_DIR: locates the directory in which coverage output files will be placed
 #  - COVERAGE_SPEC:       a set of flags for 'go test' that specify the coverage behavior (default '-cover -covermode=atomic')
 #  - GOTEST_FLAGS:        any other flags to be sent to 'go test'
 #  - JUNIT_REPORT:        toggles the creation of jUnit XML from the test output and changes this script's output behavior
 #                         to use the 'junitreport' tool for summarizing the tests.
dfde1e63
 
e3bb13a1
 set -o errexit
 set -o nounset
 set -o pipefail
2c49e079
 
f3cf4a9e
 function exit_trap() {
     local return_code=$?
e3bb13a1
 
f3cf4a9e
     end_time=$(date +%s)
f7a7cebd
 
f3cf4a9e
     if [[ "${return_code}" -eq "0" ]]; then
         verb="succeeded"
     else
         verb="failed"
     fi
 
b8d8076e
     echo "$0 ${verb} after $(( end_time - start_time )) seconds"
f3cf4a9e
     exit "${return_code}"
 }
e3bb13a1
 
f3cf4a9e
 trap exit_trap EXIT
d7385467
 
f3cf4a9e
 start_time=$(date +%s)
 OS_ROOT=$(dirname "${BASH_SOURCE}")/..
 source "${OS_ROOT}/hack/common.sh"
 source "${OS_ROOT}/hack/util.sh"
 source "${OS_ROOT}/hack/lib/util/environment.sh"
 cd "${OS_ROOT}"
 os::log::install_errexit
e3bb13a1
 os::build::setup_env
f3cf4a9e
 os::util::environment::setup_tmpdir_vars "test-go"
2c49e079
 
f3cf4a9e
 # TODO(skuznets): remove these once we've migrated all tools to the new vars
 if [[ -n "${KUBE_TIMEOUT+x}" ]]; then
     TIMEOUT="${KUBE_TIMEOUT}"
     echo "[WARNING] The flag \$KUBE_TIMEOUT for $0 is deprecated, use \$TIMEOUT instead."
 fi
2c49e079
 
f3cf4a9e
 if [[ -n "${KUBE_COVER+x}" ]]; then
     COVERAGE_SPEC="${KUBE_COVER}"
     echo "[WARNING] The flag \$KUBE_COVER for $0 is deprecated, use \$COVERAGE_SPEC instead."
 fi
f828ee5a
 
f3cf4a9e
 if [[ -n "${OUTPUT_COVERAGE+x}" ]]; then
     COVERAGE_OUTPUT_DIR="${OUTPUT_COVERAGE}"
     echo "[WARNING] The flag \$OUTPUT_COVERAGE for $0 is deprecated, use \$COVERAGE_OUTPUT_DIR instead."
 fi
d7385467
 
f3cf4a9e
 if [[ -n "${KUBE_RACE+x}" ]]; then
     DETECT_RACES="${KUBE_RACE}"
     echo "[WARNING] The flag \$KUBE_RACE for $0 is deprecated, use \$DETECT_RACES instead."
fcabd4de
 fi
 
f3cf4a9e
 if [[ -n "${PRINT_PACKAGES+x}" ]]; then
     DRY_RUN="${PRINT_PACKAGES}"
     echo "[WARNING] The flag \$PRINT_PACKAGES for $0 is deprecated, use \$DRY_RUN instead."
 fi
fcabd4de
 
f3cf4a9e
 # Internalize environment variables we consume and default if they're not set
 dry_run="${DRY_RUN:-}"
 test_kube="${TEST_KUBE:-}"
8e40a79d
 test_timeout="${TIMEOUT:-120s}"
f3cf4a9e
 detect_races="${DETECT_RACES:-true}"
 coverage_output_dir="${COVERAGE_OUTPUT_DIR:-}"
 coverage_spec="${COVERAGE_SPEC:--cover -covermode atomic}"
 gotest_flags="${GOTEST_FLAGS:-}"
 junit_report="${JUNIT_REPORT:-}"
 
 if [[ -n "${junit_report}" && -n "${coverage_output_dir}" ]]; then
     echo "$0 cannot create jUnit XML reports and coverage reports at the same time."
     exit 1
56c2952e
 fi
2c49e079
 
f3cf4a9e
 # determine if user wanted verbosity
 verbose=
 if [[ "${gotest_flags}" =~ -v( |$) ]]; then
     verbose=true
651d5cb0
 fi
2cb02738
 
f3cf4a9e
 # Build arguments for 'go test'
 if [[ -z "${verbose}" && -n "${junit_report}" ]]; then
f7a7cebd
     # verbosity can be set explicitly by the user or set implicitly by asking for the jUnit
     # XML report, so we only want to add the flag if it hasn't been added by a user already
f3cf4a9e
     # and is being implicitly set by jUnit report generation
     gotest_flags+=" -v"
f828ee5a
 fi
 
f7a7cebd
 if [[ "${detect_races}" == "true" ]]; then
f3cf4a9e
     gotest_flags+=" -race"
 fi
f828ee5a
 
f3cf4a9e
 # check to see if user has not disabled coverage mode
 if [[ -n "${coverage_spec}" ]]; then
     # if we have a coverage spec set, we add it. '-race' implies '-cover -covermode atomic'
     # but specifying both at the same time does not lead to an error so we can add both specs
     gotest_flags+=" ${coverage_spec}"
dfde1e63
 fi
651d5cb0
 
f3cf4a9e
 # check to see if user has not disabled test timeouts
 if [[ -n "${test_timeout}" ]]; then
     gotest_flags+=" -timeout ${test_timeout}"
 fi
991da56e
 
f3cf4a9e
 # list_test_packages_under lists all packages containing Golang test files that we want to run as unit tests
 # under the given base dir in the OpenShift Origin tree
 function list_test_packages_under() {
b8d8076e
     local basedir=$*
f3cf4a9e
 
     # we do not quote ${basedir} to allow for multiple arguments to be passed in as well as to allow for
     # arguments that use expansion, e.g. paths containing brace expansion or wildcards
     find ${basedir} -not \(                   \
         \(                                    \
               -path 'Godeps'                  \
               -o -path '*_output'             \
               -o -path '*_tools'              \
               -o -path '*.git'                \
               -o -path '*openshift.local.*'   \
               -o -path '*Godeps/*'            \
               -o -path '*assets/node_modules' \
               -o -path '*test/*'              \
         \) -prune                             \
e30a37f4
     \) -name '*_test.go' | xargs -n1 dirname | sort -u | xargs -n1 printf "${OS_GO_PACKAGE}/%s\n"
f3cf4a9e
 }
519ad100
 
f3cf4a9e
 # Break up the positional arguments into packages that need to be tested and arguments that need to be passed to `go test`
 package_args=
 for arg in "$@"; do
     if [[ "${arg}" =~ -.* ]]; then
f7a7cebd
         # we found an arg that begins with a dash, so we stop interpreting arguments
f3cf4a9e
         # henceforth as packages and instead interpret them as flags to give to `go test`
         break
     fi
     # an arg found before the first flag is a package
     package_args+=" ${arg}"
     shift
 done
 gotest_flags+=" $*"
 
 # Determine packages to test
4d925d57
 godeps_package_prefix="Godeps/_workspace/src/"
f3cf4a9e
 test_packages=
 if [[ -n "${package_args}" ]]; then
     for package in ${package_args}; do
4d925d57
         # If we're trying to recursively test a package under Godeps, strip the Godeps prefix so go test can find the packages correctly
         if [[ "${package}" == "${godeps_package_prefix}"*"/..." ]]; then
             test_packages="${test_packages} ${package:${#godeps_package_prefix}}"
         else
             test_packages="${test_packages} ${OS_GO_PACKAGE}/${package}"
         fi
f3cf4a9e
     done
 else
     # If no packages are given to test, we need to generate a list of all packages with unit tests
f7a7cebd
     openshift_test_packages="$(list_test_packages_under '*')"
f3cf4a9e
 
     kubernetes_path="Godeps/_workspace/src/k8s.io/kubernetes"
     mandatory_kubernetes_packages="${OS_GO_PACKAGE}/${kubernetes_path}/pkg/api ${OS_GO_PACKAGE}/${kubernetes_path}/pkg/api/v1"
 
     test_packages="${openshift_test_packages} ${mandatory_kubernetes_packages}"
 
     if [[ -n "${test_kube}" ]]; then
         # we need to find all of the kubernetes test suites, excluding those we directly whitelisted before, the end-to-end suite, and
         # the go2idl tests which we currently do not support
         optional_kubernetes_packages="$(find "${kubernetes_path}" -not \(                             \
           \(                                                                                          \
             -path "${kubernetes_path}/pkg/api"                                                        \
             -o -path "${kubernetes_path}/pkg/api/v1"                                                  \
             -o -path "${kubernetes_path}/test/e2e"                                                    \
             -o -path "${kubernetes_path}/cmd/libs/go2idl/client-gen/testoutput/testgroup/unversioned" \
           \) -prune                                                                                   \
e30a37f4
         \) -name '*_test.go' | xargs -n1 dirname | sort -u | xargs -n1 printf "${OS_GO_PACKAGE}/%s\n")"
f3cf4a9e
 
         test_packages="${test_packages} ${optional_kubernetes_packages}"
     fi
 fi
519ad100
 
f3cf4a9e
 if [[ -n "${dry_run}" ]]; then
     echo "The following base flags for \`go test\` will be used by $0:"
     echo "go test ${gotest_flags}"
     echo "The following packages will be tested by $0:"
     for package in ${test_packages}; do
         echo "${package}"
     done
     exit 0
 fi
6c29435b
 
f3cf4a9e
 # Run 'go test' with the accumulated arguments and packages:
 if [[ -n "${junit_report}" ]]; then
     # we need to generate jUnit xml
     hack/build-go.sh tools/junitreport
     junitreport="$(os::build::find-binary junitreport)"
 
     if [[ -z "${junitreport}" ]]; then
         echo "It looks as if you don't have a compiled junitreport binary"
         echo
         echo "If you are running from a clone of the git repo, please run"
         echo "'./hack/build-go.sh tools/junitreport'."
         exit 1
     fi
 
     test_output_file="${LOG_DIR}/test-go.log"
b8d8076e
     test_error_file="${LOG_DIR}/test-go-err.log"
f3cf4a9e
     junit_report_file="${ARTIFACT_DIR}/report.xml"
 
4dbd9fd4
     echo "[INFO] Running \`go test\`..."
     # we don't care if the `go test` fails in this pipe, as we want to generate the report and summarize the output anyway
     set +o pipefail
 
b8d8076e
     go test ${gotest_flags} ${test_packages} 2>"${test_error_file}" \
         | tee "${test_output_file}"                                 \
         | "${junitreport}" --type gotest                            \
                            --suites nested                          \
                            --roots github.com/openshift/origin      \
                            --stream                                 \
4dbd9fd4
                            --output "${junit_report_file}"
 
b8d8076e
     test_return_code="${PIPESTATUS[0]}"
 
4dbd9fd4
     set -o pipefail
f7a7cebd
 
4dbd9fd4
     echo
b8d8076e
     summary="$( "${junitreport}" summarize < "${junit_report_file}" )"
     echo "${summary}"
     
     if echo "${summary}" | grep -q ', 0 failed,'; then
         if [[ "${test_return_code}" -ne "0" ]]; then
             echo "[WARNING] While the jUnit report found no failed tests, the \`go test\` process failed."
             echo "[WARNING] This usually means that the unit test suite failed to compile."
         fi
     fi
 
     if [[ -s "${test_error_file}" ]]; then
         echo "[WARNING] \`go test\` had the following output to stderr:"
         cat "${test_error_file}"
     fi
 
4dbd9fd4
     echo "[INFO] Full output from \`go test\` logged at ${test_output_file}"
     echo "[INFO] jUnit XML report placed at ${junit_report_file}"
b8d8076e
     exit "${test_return_code}"
f3cf4a9e
 
 elif [[ -n "${coverage_output_dir}" ]]; then
     # we need to generate coverage reports
     for test_package in ${test_packages}; do
         mkdir -p "${coverage_output_dir}/${test_package}"
         local_gotest_flags="${gotest_flags} -coverprofile=${coverage_output_dir}/${test_package}/profile.out"
 
         go test ${local_gotest_flags} ${test_package}
     done
 
     # assemble all profiles and generate a coverage report
     echo 'mode: atomic' > "${coverage_output_dir}/profiles.out"
     find "${coverage_output_dir}" -name profile.out | xargs sed '/^mode: atomic$/d' >> "${coverage_output_dir}/profiles.out"
 
     go tool cover "-html=${coverage_output_dir}/profiles.out" -o "${coverage_output_dir}/coverage.html"
     echo "[INFO] Coverage profile written to ${coverage_output_dir}/coverage.html"
 
     # clean up all of the individual coverage reports as they have been subsumed into the report at ${coverage_output_dir}/coverage.html
     # we can clean up all of the coverage reports at once as they all exist in subdirectories of ${coverage_output_dir}/${OS_GO_PACKAGE}
     # and they are the only files found in those subdirectories
     rm -rf "${coverage_output_dir}/${OS_GO_PACKAGE}"
dfde1e63
 else
f3cf4a9e
     # we need to generate neither jUnit XML nor coverage reports
     go test ${gotest_flags} ${test_packages}
2c49e079
 fi