sh2ju.sh 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. #!/usr/bin/env bash
  2. ### Copyright 2010 Manuel Carrasco Moñino. (manolo at apache.org)
  3. ###
  4. ### Licensed under the Apache License, Version 2.0.
  5. ### You may obtain a copy of it at
  6. ### http://www.apache.org/licenses/LICENSE-2.0
  7. ###
  8. ### A library for shell scripts which creates reports in jUnit format.
  9. ### These reports can be used in Jenkins, or any other CI.
  10. ###
  11. ### Usage:
  12. ### - Include this file in your shell script
  13. ### - Use juLog to call your command any time you want to produce a new report
  14. ### Usage: juLog <options> command arguments
  15. ### options:
  16. ### -class="MyClass" : a class name which will be shown in the junit report
  17. ### -name="TestName" : the test name which will be shown in the junit report
  18. ### -error="RegExp" : a regexp which sets the test as failure when the output matches it
  19. ### -ierror="RegExp" : same as -error but case insensitive
  20. ### -output="Path" : path to output directory, defaults to "./results"
  21. ### - Junit reports are left in the folder 'result' under the directory where the script is executed.
  22. ### - Configure Jenkins to parse junit files from the generated folder
  23. ###
  24. asserts=00; errors=0; total=0; content=""
  25. date="$(which gdate 2>/dev/null || which date)"
  26. # default output folder
  27. juDIR="$(pwd)/results"
  28. # The name of the suite is calculated based in your script name
  29. suite=""
  30. if LANG=C sed --help 2>&1 | grep -q GNU; then
  31. SED="sed"
  32. elif which gsed &>/dev/null; then
  33. SED="gsed"
  34. else
  35. echo "Failed to find GNU sed as sed or gsed. If you are on Mac: brew install gnu-sed." >&2
  36. exit 1
  37. fi
  38. # A wrapper for the eval method witch allows catching seg-faults and use tee
  39. errfile=/tmp/evErr.$$.log
  40. function eVal() {
  41. (eval "$1")
  42. # stdout and stderr may currently be inverted (see below) so echo may write to stderr
  43. echo "$?" 2>&1 | tr -d "\n" > "${errfile}"
  44. }
  45. # Method to clean old tests
  46. function juLogClean() {
  47. echo "+++ Removing old junit reports from: ${juDIR} "
  48. rm -f "${juDIR}"/junit-*
  49. }
  50. # Execute a command and record its results
  51. function juLog() {
  52. suite="";
  53. errfile=/tmp/evErr.$$.log
  54. date="$(which gdate 2>/dev/null || which date)"
  55. asserts=00; errors=0; total=0; content=""
  56. # parse arguments
  57. ya=""; icase=""
  58. while [[ -z "$ya" ]]; do
  59. case "$1" in
  60. -name=*) name="$(echo "$1" | ${SED} -e 's/-name=//')"; shift;;
  61. -class=*) class="$(echo "$1" | ${SED} -e 's/-class=//')"; shift;;
  62. -ierror=*) ereg="$(echo "$1" | ${SED} -e 's/-ierror=//')"; icase="-i"; shift;;
  63. -error=*) ereg="$(echo "$1" | ${SED} -e 's/-error=//')"; shift;;
  64. -output=*) juDIR="$(echo "$1" | ${SED} -e 's/-output=//')"; shift;;
  65. *) ya=1;;
  66. esac
  67. done
  68. # create output directory
  69. mkdir -p "${juDIR}" || exit
  70. # use first arg as name if it was not given
  71. if [[ -z "${name}" ]]; then
  72. name="${asserts}-$1"
  73. shift
  74. fi
  75. if [[ "${class}" = "" ]]; then
  76. class="default"
  77. fi
  78. suite=${class}
  79. # calculate command to eval
  80. [[ -z "$1" ]] && return
  81. cmd="$1"; shift
  82. while [[ -n "${1:-}" ]]
  83. do
  84. cmd="${cmd} \"$1\""
  85. shift
  86. done
  87. # eval the command sending output to a file
  88. outf=/var/tmp/ju$$.txt
  89. errf=/var/tmp/ju$$-err.txt
  90. :>${outf}
  91. echo "" | tee -a ${outf}
  92. echo "+++ Running case: ${class}.${name} " | tee -a ${outf}
  93. echo "+++ working dir: $(pwd)" | tee -a ${outf}
  94. echo "+++ command: ${cmd}" | tee -a ${outf}
  95. ini="$(${date} +%s.%N)"
  96. # execute the command, temporarily swapping stderr and stdout so they can be tee'd to separate files,
  97. # then swapping them back again so that the streams are written correctly for the invoking process
  98. ( (eVal "${cmd}" | tee -a ${outf}) 3>&1 1>&2 2>&3 | tee ${errf}) 3>&1 1>&2 2>&3
  99. evErr="$(cat ${errfile})"
  100. rm -f ${errfile}
  101. end="$(${date} +%s.%N)"
  102. echo "+++ exit code: ${evErr}" | tee -a ${outf}
  103. # set the appropriate error, based in the exit code and the regex
  104. [[ ${evErr} != 0 ]] && err=1 || err=0
  105. out="$(${SED} -e 's/^\([^+]\)/| \1/g' "$outf")"
  106. if [ ${err} = 0 ] && [ -n "${ereg:-}" ]; then
  107. H=$(echo "${out}" | grep -E ${icase} "${ereg}")
  108. [[ -n "${H}" ]] && err=1
  109. fi
  110. [[ ${err} != 0 ]] && echo "+++ error: ${err}" | tee -a ${outf}
  111. rm -f ${outf}
  112. errMsg=$(cat ${errf})
  113. rm -f ${errf}
  114. # calculate vars
  115. asserts=$((asserts+1))
  116. errors=$((errors+err))
  117. time=$(echo "${end} ${ini}" | awk '{print $1 - $2}')
  118. total=$(echo "${total} ${time}" | awk '{print $1 + $2}')
  119. # write the junit xml report
  120. ## failure tag
  121. [[ ${err} = 0 ]] && failure="" || failure="
  122. <failure type=\"ScriptError\" message=\"Script Error\"><![CDATA[${errMsg}]]></failure>
  123. "
  124. ## testcase tag
  125. content="${content}
  126. <testcase assertions=\"1\" name=\"${name}\" time=\"${time}\" classname=\"${class}\">
  127. ${failure}
  128. <system-err><![CDATA[${errMsg}]]></system-err>
  129. </testcase>
  130. "
  131. ## testsuite block
  132. if [[ -e "${juDIR}/junit_${suite}.xml" ]]; then
  133. # file exists. first update the failures count
  134. failCount=$(${SED} -n "s/.*testsuite.*failures=\"\([0-9]*\)\".*/\1/p" "${juDIR}/junit_${suite}.xml")
  135. errors=$((failCount+errors))
  136. ${SED} -i "0,/failures=\"${failCount}\"/ s/failures=\"${failCount}\"/failures=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
  137. ${SED} -i "0,/errors=\"${failCount}\"/ s/errors=\"${failCount}\"/errors=\"${errors}\"/" "${juDIR}/junit_${suite}.xml"
  138. # file exists. Need to append to it. If we remove the testsuite end tag, we can just add it in after.
  139. ${SED} -i "s^</testsuite>^^g" "${juDIR}/junit_${suite}.xml" ## remove testSuite so we can add it later
  140. ${SED} -i "s^</testsuites>^^g" "${juDIR}/junit_${suite}.xml"
  141. cat <<EOF >> "$juDIR/junit_$suite.xml"
  142. ${content:-}
  143. </testsuite>
  144. </testsuites>
  145. EOF
  146. else
  147. # no file exists. Adding a new file
  148. cat <<EOF > "${juDIR}/junit_${suite}.xml"
  149. <?xml version="1.0" encoding="UTF-8"?>
  150. <testsuites>
  151. <testsuite failures="${errors}" assertions="${assertions:-}" name="${suite}" tests="1" errors="${errors}" time="${total}">
  152. ${content:-}
  153. </testsuite>
  154. </testsuites>
  155. EOF
  156. fi
  157. return ${err}
  158. }