123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- #!/usr/bin/env bash
- ### Copyright 2010 Manuel Carrasco Moñino. (manolo at apache.org)
- ###
- ### Licensed under the Apache License, Version 2.0.
- ### You may obtain a copy of it at
- ### http://www.apache.org/licenses/LICENSE-2.0
- ###
- ### A library for shell scripts which creates reports in jUnit format.
- ### These reports can be used in Jenkins, or any other CI.
- ###
- ### Usage:
- ### - Include this file in your shell script
- ### - Use juLog to call your command any time you want to produce a new report
- ### Usage: juLog <options> command arguments
- ### options:
- ### -class="MyClass" : a class name which will be shown in the junit report
- ### -name="TestName" : the test name which will be shown in the junit report
- ### -error="RegExp" : a regexp which sets the test as failure when the output matches it
- ### -ierror="RegExp" : same as -error but case insensitive
- ### -output="Path" : path to output directory, defaults to "./results"
- ### - Junit reports are left in the folder 'result' under the directory where the script is executed.
- ### - Configure Jenkins to parse junit files from the generated folder
- ###
- asserts=00; errors=0; total=0; content=""
- date="$(which gdate 2>/dev/null || which date)"
- # default output folder
- juDIR="$(pwd)/results"
- # The name of the suite is calculated based in your script name
- suite=""
- if LANG=C sed --help 2>&1 | grep -q GNU; then
- SED="sed"
- elif which gsed &>/dev/null; then
- SED="gsed"
- else
- echo "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2
- exit 1
- fi
- # A wrapper for the eval method witch allows catching seg-faults and use tee
- errfile=/tmp/evErr.$$.log
- function eVal() {
- (eval "$1")
- # stdout and stderr may currently be inverted (see below) so echo may write to stderr
- echo "$?" 2>&1 | tr -d "\n" > "${errfile}"
- }
- # Method to clean old tests
- function juLogClean() {
- echo "+++ Removing old junit reports from: ${juDIR} "
- rm -f "${juDIR}"/junit-*
- }
- # Execute a command and record its results
- function juLog() {
- suite="";
- errfile=/tmp/evErr.$$.log
- date="$(which gdate 2>/dev/null || which date)"
- asserts=00; errors=0; total=0; content=""
- # parse arguments
- ya=""; icase=""
- while [[ -z "$ya" ]]; do
- case "$1" in
- -name=*) name="$(echo "$1" | ${SED} -e 's/-name=//')"; shift;;
- -class=*) class="$(echo "$1" | ${SED} -e 's/-class=//')"; shift;;
- -ierror=*) ereg="$(echo "$1" | ${SED} -e 's/-ierror=//')"; icase="-i"; shift;;
- -error=*) ereg="$(echo "$1" | ${SED} -e 's/-error=//')"; shift;;
- -output=*) juDIR="$(echo "$1" | ${SED} -e 's/-output=//')"; shift;;
- *) ya=1;;
- esac
- done
- # create output directory
- mkdir -p "${juDIR}" || exit
- # use first arg as name if it was not given
- if [[ -z "${name}" ]]; then
- name="${asserts}-$1"
- shift
- fi
- if [[ "${class}" = "" ]]; then
- class="default"
- fi
- suite=${class}
- # calculate command to eval
- [[ -z "$1" ]] && return
- cmd="$1"; shift
- while [[ -n "${1:-}" ]]
- do
- cmd="${cmd} \"$1\""
- shift
- done
- # eval the command sending output to a file
- outf=/var/tmp/ju$$.txt
- errf=/var/tmp/ju$$-err.txt
- :>${outf}
- echo "" | tee -a ${outf}
- echo "+++ Running case: ${class}.${name} " | tee -a ${outf}
- echo "+++ working dir: $(pwd)" | tee -a ${outf}
- echo "+++ command: ${cmd}" | tee -a ${outf}
- ini="$(${date} +%s.%N)"
- # execute the command, temporarily swapping stderr and stdout so they can be tee'd to separate files,
- # then swapping them back again so that the streams are written correctly for the invoking process
- ( (eVal "${cmd}" | tee -a ${outf}) 3>&1 1>&2 2>&3 | tee ${errf}) 3>&1 1>&2 2>&3
- evErr="$(cat ${errfile})"
- rm -f ${errfile}
- end="$(${date} +%s.%N)"
- echo "+++ exit code: ${evErr}" | tee -a ${outf}
- # set the appropriate error, based in the exit code and the regex
- [[ ${evErr} != 0 ]] && err=1 || err=0
- out="$(${SED} -e 's/^\([^+]\)/| \1/g' "$outf")"
- if [ ${err} = 0 ] && [ -n "${ereg:-}" ]; then
- H=$(echo "${out}" | grep -E ${icase} "${ereg}")
- [[ -n "${H}" ]] && err=1
- fi
- [[ ${err} != 0 ]] && echo "+++ error: ${err}" | tee -a ${outf}
- rm -f ${outf}
- errMsg=$(cat ${errf})
- rm -f ${errf}
- # calculate vars
- asserts=$((asserts+1))
- errors=$((errors+err))
- time=$(echo "${end} ${ini}" | awk '{print $1 - $2}')
- total=$(echo "${total} ${time}" | awk '{print $1 + $2}')
- # write the junit xml report
- ## failure tag
- [[ ${err} = 0 ]] && failure="" || failure="
- <failure type=\"ScriptError\" message=\"Script Error\"><![CDATA[${errMsg}]]></failure>
- "
- ## testcase tag
- content="${content}
- <testcase assertions=\"1\" name=\"${name}\" time=\"${time}\" classname=\"${class}\">
- ${failure}
- <system-err><![CDATA[${errMsg}]]></system-err>
- </testcase>
- "
- ## testsuite block
- if [[ -e "${juDIR}/junit_${suite}.xml" ]]; then
- # file exists. first update the failures count
- failCount=$(${SED} -n "s/.*testsuite.*failures=\"\([0-9]*\)\".*/\1/p" "${juDIR}/junit_${suite}.xml")
- errors=$((failCount+errors))
- ${SED} -i "0,/failures=\"${failCount}\"/ s/failures=\"${failCount}\"/failures=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
- ${SED} -i "0,/errors=\"${failCount}\"/ s/errors=\"${failCount}\"/errors=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
- # file exists. Need to append to it. If we remove the testsuite end tag, we can just add it in after.
- ${SED} -i "s^</testsuite>^^g" "${juDIR}/junit_${suite}.xml" ## remove testSuite so we can add it later
- ${SED} -i "s^</testsuites>^^g" "${juDIR}/junit_${suite}.xml"
- cat <<EOF >> "$juDIR/junit_$suite.xml"
- ${content:-}
- </testsuite>
- </testsuites>
- EOF
- else
- # no file exists. Adding a new file
- cat <<EOF > "${juDIR}/junit_${suite}.xml"
- <?xml version="1.0" encoding="UTF-8"?>
- <testsuites>
- <testsuite failures="${errors}" assertions="${assertions:-}" name="${suite}" tests="1" errors="${errors}" time="${total}">
- ${content:-}
- </testsuite>
- </testsuites>
- EOF
- fi
- return ${err}
- }
|