igr_functions.sh 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. # Included from run-test.sh files. DO NOT run independently.
  2. ### This file contain basic variables & functions for running Integration tests of Dinit.
  3. # Input variables:
  4. # Directory containing executable binaries. If unset defaults to two-levels up from current
  5. # working directory
  6. DINIT_BINDIR="${DINIT_BINDIR-../..}"
  7. # After sourcing this script, the following output variables are set:
  8. #
  9. # IGR_OUTPUT - directory for test output files (also exported to environment)
  10. # TEST_NAME - name of the current test
  11. # QUIET - will be set to "--quiet" unless DEBUG is set
  12. #
  13. # Also, the various functions defined below may be used.
  14. ## Basic functions
  15. # According to POSIX, echo has unspecified behavior in some cases, for example
  16. # when its first argument is "-n" or backslash ("\").
  17. # So we replace the shell built-in echo with a printf-based function.
  18. # For more information see: http://www.etalabs.net/sh_tricks.html
  19. echo() {
  20. IFS=" " printf %s\\n "$*"
  21. }
  22. # Issue an error message and exit.
  23. # $1 - the main error message
  24. # $2 - (optional) detail/additional info
  25. # $TEST_NAME - name of the current test
  26. # $STAGE - (optional) test stage description
  27. error() {
  28. >&2 echo "${TEST_NAME:-}: Error: $1"
  29. if [ -n "${2:-}" ]; then
  30. >&2 echo " ... $2"
  31. fi
  32. if [ -n "${STAGE:-}" ]; then
  33. >&2 echo "${TEST_NAME:-}: Failed at stage $STAGE."
  34. fi
  35. if [ -e "${SOCKET:-}" ]; then
  36. stop_dinit
  37. fi
  38. exit 1
  39. }
  40. # Issue a warning message.
  41. # $1 - the main warning message
  42. # $2 - (optional) detail/additional info
  43. # $TEST_NAME - name of the current test
  44. warning() {
  45. echo
  46. >&2 echo "${TEST_NAME:-}: Warning: $1"
  47. if [ -n "${2:-}" ]; then
  48. >&2 echo " ... $2"
  49. fi
  50. echo
  51. }
  52. ## Executable path resolvers functions
  53. # These return 0 and set suitable variable when program found, or return 1 on failure.
  54. # Utility / implementation for find_xxx()
  55. # $1 - name of the executable to locate
  56. # $2 - name of the variable to set with the path to the executable
  57. find_executable() {
  58. exename=$1
  59. varname=$2
  60. if [ -z "$(eval "echo \${${varname}:-}")" ]; then
  61. if [ -x "$DINIT_BINDIR/$exename" ]; then
  62. export "$varname"="$DINIT_BINDIR/$exename"
  63. else
  64. return 1
  65. fi
  66. fi
  67. }
  68. find_dinit() { find_executable dinit DINIT; }
  69. find_dinitctl() { find_executable dinitctl DINITCTL; }
  70. find_dinitcheck() { find_executable dinitcheck DINITCHECK; }
  71. find_dinitmonitor() { find_executable dinit-monitor DINITMONITOR; }
  72. # Prepare IGR_OUTPUT.
  73. # Three basic modes:
  74. # - IGR_OUTPUT is already set
  75. # - IGR_OUTPUT_BASE is set, append test name and use result as IGR_OUTPUT
  76. # - neither, use "output" within current directory as IGR_OUTPUT
  77. TEST_NAME="${PWD##*/}"
  78. [ -n "$TEST_NAME" ] || error "Failed to guess test name."
  79. if [ -z "${IGR_OUTPUT:-}" ]; then
  80. if [ -n "${IGR_OUTPUT_BASE:-}" ]; then
  81. export IGR_OUTPUT="${IGR_OUTPUT_BASE}/${TEST_NAME}"
  82. else
  83. export IGR_OUTPUT="$PWD"/output
  84. fi
  85. fi
  86. export SOCKET="$IGR_OUTPUT/socket"
  87. mkdir -p "$IGR_OUTPUT"
  88. if [ -n "${DEBUG:-}" ]; then
  89. QUIET=""
  90. else
  91. QUIET="--quiet"
  92. fi
  93. ## Integration tests helper functions
  94. # spawn_dinit: spawn a dinit daemon using "$SOCKET" as socket path.
  95. # Any arguments are passed on to dinit.
  96. # RESULT: Return 0 & sets DINITPID variable on success.
  97. # Message and exit on failure.
  98. spawn_dinit() {
  99. find_dinit || error "Cannot find dinit exec path." "Specify 'DINIT_BINDIR' and/or ensure dinit is compiled."
  100. "$DINIT" $QUIET -u -d sd -p "$SOCKET" -l /dev/null "$@" &
  101. DINITPID=$!
  102. # Wait for Dinit socket to show up.
  103. TIMEOUT=0
  104. while [ ! -e "$SOCKET" ]; do
  105. if [ $TIMEOUT -le 600 ]; then
  106. sleep 0.1
  107. TIMEOUT=$((TIMEOUT+1))
  108. else
  109. error "Starting dinit: reached timeout without detecting socket creation."
  110. fi
  111. done
  112. return 0
  113. }
  114. # Stop the current Dinit instance.
  115. # Takes no arguments.
  116. # RESULT: Returns 0 if Dinit stops.
  117. # Returns 0 and issue a warning if Dinit already stopped.
  118. stop_dinit() {
  119. # NOTE: We can't use error() within, using error() will result in an infinite cycle as
  120. # error() calls this function.
  121. if [ ! -e "$SOCKET" ]; then
  122. warning "stop_dinit() called but cannot find any running dinit instance!"
  123. return 0
  124. fi
  125. if find_dinitctl && $DINITCTL $QUIET shutdown -p "$SOCKET"; then
  126. wait "$DINITPID"
  127. return 0
  128. else
  129. warning "Cannot stop dinit via dinitctl." "Falling back to killing dinit (pid=$DINITPID) via signal."
  130. kill "$DINITPID" || { echo "${TEST_NAME:-}: Cannot stop Dinit instance!" && exit 1; } >&2
  131. wait "$DINITPID"
  132. fi
  133. }
  134. # Spawns a dinit daemon and waits until it exits.
  135. # Arguments are passed on to dinit.
  136. # RESULT: Returns exit code of dinit.
  137. spawn_dinit_oneshot() {
  138. find_dinit || error "Cannot find dinit exec path." "Specify 'DINIT_BINDIR' and/or ensure dinit is compiled."
  139. exit_code=0
  140. "$DINIT" $QUIET -u -d sd -p "$SOCKET" -l /dev/null "$@" || exit_code=$?
  141. return $exit_code
  142. }
  143. # Run a dinitctl command against the currently running dinit instance.
  144. # Any arguments are passed to dinitctl.
  145. # RESULT: Returns exit code from dinitctl.
  146. run_dinitctl() {
  147. find_dinitctl || error "Cannot find dinitctl exec path." "Specify 'DINIT_BINDIR' and/or ensure dinit is compiled."
  148. exit_code=0
  149. "$DINITCTL" -p "$SOCKET" "$@" || exit_code=$?
  150. return $exit_code
  151. }
  152. # Run dinitcheck.
  153. # Any arguments are passed to dinitcheck.
  154. # RESULT: Return exit code from dinitcheck.
  155. run_dinitcheck() {
  156. find_dinitcheck || error "Cannot find dinitcheck exec path." "Specify 'DINIT_BINDIR' and/or ensure dinit is compiled."
  157. exit_code=0
  158. "$DINITCHECK" -d sd "$@" || exit_code=$?
  159. return $exit_code
  160. }
  161. # Compares the contents of a given file with an expected result (text).
  162. # Final newlines are stripped from file contents before comparison.
  163. # Accepts file as $1 and a text as $2.
  164. # RESULT: Returns 0 if content of file is the same with given text.
  165. # Returns 1 if content of file is not the same with given text.
  166. # Exits with an error if the file doesn't exist.
  167. compare_text() {
  168. if [ ! -e "$1" ]; then
  169. error "$1 file doesn't exist!"
  170. fi
  171. FILE="$(cat "$1")" || error "Cannot read given file!"
  172. if [ "$FILE" = "$2" ]; then
  173. return 0
  174. else
  175. return 1
  176. fi
  177. }
  178. # Compares the contents of a given file with an expected result (text).
  179. # Newlines in file contents are preserved and compared.
  180. # Accepts file as $1 and a text as $2.
  181. # RESULT: Returns 0 if content of file is the same with given text.
  182. # Returns 1 if content of file is not the same with given text.
  183. # Exits with an error if the file doesn't exist.
  184. compare_text_nl() {
  185. if [ ! -e "$1" ]; then
  186. error "$1 file doesn't exist!"
  187. fi
  188. # capture file contents and preserve final newline (by appending an 'x' and removing it after)
  189. FILE="$(cat "$1" && echo "x")" || error "Cannot read given file!"
  190. FILE="${FILE%?}"
  191. if [ "$FILE" = "$2" ]; then
  192. return 0
  193. else
  194. return 1
  195. fi
  196. }
  197. # Compare the contents of a given file with the expected result (in another file).
  198. # Accepts actual result file as $1 and expected results file as $2.
  199. # RESULT: Return 0 if content of file is the same as the other file.
  200. # Return 1 otherwise.
  201. # Exits with an error if either file doesn't exist.
  202. compare_file() {
  203. if [ ! -e "$1" ]; then
  204. error "$1 file doesn't exist!"
  205. fi
  206. if [ ! -e "$2" ]; then
  207. error "$2 file doesn't exist!"
  208. fi
  209. if cmp -s "$1" "$2"; then
  210. return 0
  211. else
  212. return 1
  213. fi
  214. }
  215. # Compare output from a command with the contents of a file.
  216. # Accepts command (with arguments) as $1 and file with expected result as $2.
  217. # To capture stderr as well as stdout, set $3 equal to "err".
  218. # RESULT: CMD_OUT variable contains the command output.
  219. # return 0 if command output is the same as the file contents.
  220. # return 1 otherwise.
  221. # Exits with an error if the file doesn't exist.
  222. compare_cmd() {
  223. if [ ! -e "$2" ]; then
  224. error "$2 file doesn't exist!"
  225. fi
  226. if [ "${3:-}" = "err" ]; then
  227. CMD_OUT="$($1 2>&1 || true)"
  228. elif [ -z "${3:-}" ]; then
  229. CMD_OUT="$($1 || true)"
  230. else
  231. warning "compare_cmd(): Invalid argument for capturing stderr: $3" "Ignoring..."
  232. CMD_OUT="$($1 || true)"
  233. fi
  234. if [ "$CMD_OUT" = "$(cat "$2")" ]; then
  235. return 0
  236. else
  237. return 1
  238. fi
  239. }
  240. # Capture the exact output of a command, including any final newlines.
  241. # $1 - variable name to store output in
  242. # $2... command to run (with arguments following)
  243. capture_exact_output() {
  244. name=$1; shift
  245. cmd=$1; shift
  246. # execute the command and output an additional '/' which is then stripped
  247. # (the '/' inhibits the usual stripping of trailing newlines)
  248. r=0
  249. output="$(r=0; "$cmd" "$@" || r=$?; echo /; exit $r)" || r=$?
  250. output="${output%/}"
  251. eval "$name=\$output"
  252. return $r
  253. }