You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1146 lines
30 KiB

  1. #!/usr/bin/env bash
  2. # Copyright (C) 2015, Arpinum
  3. #
  4. # shebang-unit is free software: you can redistribute it and/or modify it under
  5. # the terms of the GNU General Public License as published by the Free Software
  6. # Foundation, either version 3 of the License, or (at your option) any later
  7. # version.
  8. #
  9. # shebang-unit is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  12. # details.
  13. #
  14. # You should have received a copy of the GNU General Public License along with
  15. # shebang-unit. If not, see http://www.gnu.org/licenses/lgpl.html.
  16. # shebang-unit all in one source file
  17. configuration__load() {
  18. # yes/no representation used with shebang-unit parameters to activate
  19. # stuff like colors
  20. SBU_YES="yes"
  21. SBU_NO="no"
  22. # Colors for outputs
  23. SBU_GREEN_COLOR_CODE="\\033[1;32m"
  24. SBU_RED_COLOR_CODE="\\033[1;31m"
  25. SBU_YELLOW_COLOR_CODE="\\033[1;33m"
  26. SBU_DEFAULT_COLOR_CODE="\\e[0m"
  27. # Functions coding coventions
  28. SBU_GLOBAL_SETUP_FUNCTION_NAME="global_setup"
  29. SBU_GLOBAL_TEARDOWN_FUNCTION_NAME="global_teardown"
  30. SBU_SETUP_FUNCTION_NAME="setup"
  31. SBU_TEARDOWN_FUNCTION_NAME="teardown"
  32. SBU_FUNCTION_DECLARATION_REGEX="^[ ]*\(function\)\{0,1\}[ ]*\([A-Za-z0-9_-]\{1,\}\)[ ]*\(([ ]*)\)\{0,1\}[ ]*{"
  33. SBU_PRIVATE_FUNCTION_NAME_REGEX="^_.*"
  34. # Default configuration that can be modified with shebang-unit parameters
  35. # For more information see shebang-unit usages
  36. SBU_TEST_FILE_PATTERN="*_test.sh"
  37. SBU_TEST_FUNCTION_PATTERN="*"
  38. SBU_USE_COLORS="${SBU_YES}"
  39. SBU_RANDOM_RUN="${SBU_NO}"
  40. SBU_REPORTERS="simple"
  41. SBU_JUNIT_REPORTER_OUTPUT_FILE="./junit_report.xml"
  42. # Internal constants
  43. SBU_SUCCESS_STATUS_CODE=0
  44. SBU_FAILURE_STATUS_CODE=1
  45. SBU_VALUE_SEPARATOR=","
  46. SBU_TEMP_DIR="/tmp/.shebang-unit"
  47. SBU_LAST_ASSERTION_MSG_KEY="last_assertion_message"
  48. SBU_NO_RUN="${SBU_NO}"
  49. SBU_STANDARD_FD=42
  50. SBU_ERROR_FD=43
  51. }
  52. assertion__equal() {
  53. if [[ "$1" != "$2" ]]; then
  54. _assertion__failed "Actual: <$2>, expected: <$1>."
  55. fi
  56. }
  57. assertion__different() {
  58. if [[ "$1" == "$2" ]]; then
  59. _assertion__failed "Both values are: <$1>."
  60. fi
  61. }
  62. assertion__string_contains() {
  63. if ! system__string_contains "$1" "$2"; then
  64. _assertion__failed "String: <$1> does not contain: <$2>."
  65. fi
  66. }
  67. assertion__string_does_not_contain() {
  68. if system__string_contains "$1" "$2"; then
  69. _assertion__failed "String: <$1> contains: <$2>."
  70. fi
  71. }
  72. assertion__string_empty() {
  73. if [[ -n "$1" ]]; then
  74. _assertion__failed "String: <$1> is not empty."
  75. fi
  76. }
  77. assertion__string_not_empty() {
  78. if [[ -z "$1" ]]; then
  79. _assertion__failed "The string is empty."
  80. fi
  81. }
  82. assertion__array_contains() {
  83. local element=$1
  84. shift 1
  85. if ! array__contains "${element}" "$@"; then
  86. local array_as_string="$(system__pretty_print_array "$@")"
  87. _assertion__failed \
  88. "Array: <${array_as_string}> does not contain: <${element}>."
  89. fi
  90. }
  91. assertion__array_does_not_contain() {
  92. local element=$1
  93. shift 1
  94. if array__contains "${element}" "$@"; then
  95. local array_as_string="$(system__pretty_print_array "$@")"
  96. _assertion__failed \
  97. "Array: <${array_as_string}> contains: <${element}>."
  98. fi
  99. }
  100. assertion__status_code_is_success() {
  101. if (( $1 != ${SBU_SUCCESS_STATUS_CODE} )); then
  102. _assertion__failed \
  103. "Status code is failure instead of success." "$2"
  104. fi
  105. }
  106. assertion__status_code_is_failure() {
  107. if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then
  108. _assertion__failed \
  109. "Status code is success instead of failure." "$2"
  110. fi
  111. }
  112. assertion__successful() {
  113. "$@"
  114. if (( $? != ${SBU_SUCCESS_STATUS_CODE} )); then
  115. _assertion__failed "Command is failing instead of successful."
  116. fi
  117. }
  118. assertion__failing() {
  119. "$@"
  120. if (( $? == ${SBU_SUCCESS_STATUS_CODE} )); then
  121. _assertion__failed "Command is successful instead of failing."
  122. fi
  123. }
  124. _assertion__failed() {
  125. local message_to_use="$(_assertion__get_assertion_message_to_use "$1" "$2")"
  126. system__print_line "Assertion failed. ${message_to_use}"
  127. exit ${SBU_FAILURE_STATUS_CODE}
  128. }
  129. _assertion__get_assertion_message_to_use() {
  130. local message=$1
  131. local custom_messsage=$2
  132. if [[ -n "${custom_messsage}" ]]; then
  133. system__print "${message} ${custom_messsage}"
  134. else
  135. system__print "${message}"
  136. fi
  137. }
  138. mock__make_function_do_nothing() {
  139. mock__make_function_call "$1" ":"
  140. }
  141. mock__make_function_prints() {
  142. local function=$1
  143. local text=$2
  144. eval "${function}() { printf "${text}"; }"
  145. }
  146. mock__make_function_call() {
  147. local function_to_mock=$1
  148. local function_to_call=$2
  149. shift 2
  150. eval "${function_to_mock}() { ${function_to_call} \"\$@\"; }"
  151. }
  152. runner__run_all_test_files() {
  153. SBU_BASE_TEST_DIRECTORY=$1
  154. reporter__test_files_start_running
  155. timer__store_current_time "global_time"
  156. results__test_files_start_running
  157. _runner__run_all_test_files_with_pattern_in_directory "$1"
  158. reporter__test_files_end_running "$(timer__get_time_elapsed "global_time")"
  159. runner__tests_are_successful
  160. }
  161. _runner__run_all_test_files_with_pattern_in_directory() {
  162. local file
  163. local files
  164. array__from_lines files <<< "$(_runner__get_test_files_in_directory "$1")"
  165. for file in "${files[@]}"; do
  166. file_runner__run_test_file "${file}"
  167. done
  168. }
  169. _runner__get_test_files_in_directory() {
  170. local files
  171. array__from_lines files <<< "$(find "$1" -name "${SBU_TEST_FILE_PATTERN}" | sort)"
  172. if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then
  173. array__from_lines files <<< "$(system__randomize_array "${files[@]}")"
  174. fi
  175. array__print "${files[@]}"
  176. }
  177. runner__tests_are_successful() {
  178. (( $(results__get_failing_tests_count) == 0 \
  179. && $(results__get_skipped_tests_count) == 0 ))
  180. }
  181. file_runner__run_test_file() {
  182. local file=$1
  183. local public_functions=($(parser__get_public_functions_in_file "${file}"))
  184. local test_functions=($(_file_runner__get_test_functions))
  185. reporter__test_file_starts_running "${file}" "${#test_functions[@]}"
  186. ( source "${file}"
  187. _file_runner__run_global_setup_if_exists \
  188. && _file_runner__call_all_tests
  189. _file_runner__run_global_teardown_if_exists )
  190. _file_runner__check_if_global_setup_has_exited
  191. reporter__test_file_ends_running
  192. }
  193. _file_runner__run_all_tests_if_global_setup_is_successful() {
  194. _file_runner__call_all_tests
  195. }
  196. _file_runner__call_all_tests() {
  197. local i
  198. for (( i=0; i < ${#test_functions[@]}; i++ )); do
  199. test_runner__run_test "${test_functions[${i}]}" "${public_functions[@]}"
  200. done
  201. }
  202. _file_runner__skip_all_tests() {
  203. local i
  204. for (( i=0; i < ${#test_functions[@]}; i++ )); do
  205. test_runner__skip_test "${test_functions[${i}]}" "${public_functions[@]}"
  206. done
  207. }
  208. _file_runner__get_test_functions() {
  209. local result=()
  210. local test_function
  211. for test_function in "${public_functions[@]}"; do
  212. if _file_runner__function_is_a_test "${test_function}"\
  213. && [[ "${test_function}" == ${SBU_TEST_FUNCTION_PATTERN} ]]; then
  214. result+=("${test_function}")
  215. fi
  216. done
  217. _file_runner__get_randomized_test_functions_if_needed "${result[@]}"
  218. }
  219. _file_runner__get_randomized_test_functions_if_needed() {
  220. if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then
  221. system__randomize_array "$@"
  222. else
  223. array__print "$@"
  224. fi
  225. }
  226. _file_runner__run_global_setup_if_exists() {
  227. database__put "sbu_current_global_setup_has_failed" "${SBU_YES}"
  228. _file_runner__call_function_if_exists "${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \
  229. && database__put "sbu_current_global_setup_has_failed" "${SBU_NO}"
  230. }
  231. _file_runner__run_global_teardown_if_exists() {
  232. _file_runner__call_function_if_exists "${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}"
  233. }
  234. _file_runner__function_is_a_test() {
  235. ! array__contains "$1" \
  236. "${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \
  237. "${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}" \
  238. "${SBU_SETUP_FUNCTION_NAME}" \
  239. "${SBU_TEARDOWN_FUNCTION_NAME}"
  240. }
  241. _file_runner__call_function_if_exists() {
  242. local function=$1
  243. shift 1
  244. if array__contains "${function}" "${public_functions[@]}"; then
  245. "${function}"
  246. fi
  247. }
  248. _file_runner__check_if_global_setup_has_exited() {
  249. local has_exited="$(database__get "sbu_current_global_setup_has_failed")"
  250. if [[ "${has_exited}" == "${SBU_YES}" ]]; then
  251. _file_runner__handle_failure_in_global_setup
  252. fi
  253. }
  254. _file_runner__handle_failure_in_global_setup() {
  255. reporter__global_setup_has_failed
  256. _file_runner__skip_all_tests
  257. }
  258. parser__get_public_functions_in_file() {
  259. _parser__find_functions_in_file "$1" \
  260. | _parser__filter_private_functions \
  261. | awk '{ print $1 }'
  262. }
  263. _parser__find_functions_in_file() {
  264. grep -o "${SBU_FUNCTION_DECLARATION_REGEX}" "$1" \
  265. | _parser__get_function_name_from_declaration
  266. }
  267. _parser__filter_private_functions() {
  268. grep -v "${SBU_PRIVATE_FUNCTION_NAME_REGEX}"
  269. }
  270. _parser__get_function_name_from_declaration() {
  271. sed "s/${SBU_FUNCTION_DECLARATION_REGEX}/\2/"
  272. }
  273. timer__store_current_time() {
  274. local id=$1
  275. database__put "sbu_beginning_date_$1" "$(system__get_date_in_seconds)"
  276. }
  277. timer__get_time_elapsed() {
  278. local id=$1
  279. local beginning_date="$(database__get "sbu_beginning_date_$1")"
  280. local ending_date="$(system__get_date_in_seconds)"
  281. [[ -n "${beginning_date}" ]] \
  282. && system__print "$(( ending_date - beginning_date ))" \
  283. || system__print "0"
  284. }
  285. results__test_files_start_running() {
  286. database__put "sbu_successful_tests_count" "0"
  287. database__put "sbu_failing_tests_count" "0"
  288. database__put "sbu_skipped_tests_count" "0"
  289. }
  290. results__get_successful_tests_count() {
  291. _results__get_tests_count_of_type "successful"
  292. }
  293. results__increment_successful_tests() {
  294. _results__increment_tests_of_type "successful"
  295. }
  296. results__get_failing_tests_count() {
  297. _results__get_tests_count_of_type "failing"
  298. }
  299. results__increment_failing_tests() {
  300. _results__increment_tests_of_type "failing"
  301. }
  302. results__get_skipped_tests_count() {
  303. _results__get_tests_count_of_type "skipped"
  304. }
  305. results__increment_skipped_tests() {
  306. _results__increment_tests_of_type "skipped"
  307. }
  308. results__get_total_tests_count() {
  309. local successes="$(results__get_successful_tests_count)"
  310. local failures="$(results__get_failing_tests_count)"
  311. local skipped="$(results__get_skipped_tests_count)"
  312. system__print "$(( successes + failures + skipped ))"
  313. }
  314. _results__get_tests_count_of_type() {
  315. local type=$1
  316. database__get "sbu_${type}_tests_count"
  317. }
  318. _results__increment_tests_of_type() {
  319. local type=$1
  320. local count="$(results__get_${type}_tests_count)"
  321. database__put "sbu_${type}_tests_count" "$(( count + 1 ))"
  322. }
  323. test_runner__run_test() {
  324. local test_function=$1
  325. shift 1
  326. reporter__test_starts_running "${test_function}"
  327. timer__store_current_time "test_time"
  328. (
  329. _test_runner__call_setup_if_exists "$@" \
  330. && _test_runner__call_test_fonction "${test_function}"
  331. local setup_and_test_code=$?
  332. _test_runner__call_teardown_if_exists "$@"
  333. (( $? == ${SBU_SUCCESS_STATUS_CODE} \
  334. && ${setup_and_test_code} == ${SBU_SUCCESS_STATUS_CODE} ))
  335. )
  336. _test_runner__parse_test_function_result $?
  337. reporter__test_ends_running "$(timer__get_time_elapsed "test_time")"
  338. }
  339. _test_runner__call_test_fonction() {
  340. ( "$1" >&${SBU_STANDARD_FD} 2>&${SBU_ERROR_FD} )
  341. }
  342. _test_runner__call_setup_if_exists() {
  343. _test_runner__call_function_if_exits "${SBU_SETUP_FUNCTION_NAME}" "$@"
  344. }
  345. _test_runner__call_teardown_if_exists() {
  346. _test_runner__call_function_if_exits "${SBU_TEARDOWN_FUNCTION_NAME}" "$@"
  347. }
  348. _test_runner__parse_test_function_result() {
  349. if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then
  350. results__increment_successful_tests
  351. reporter__test_has_succeeded
  352. else
  353. results__increment_failing_tests
  354. reporter__test_has_failed
  355. fi
  356. }
  357. _test_runner__call_function_if_exits() {
  358. local function=$1
  359. shift 1
  360. if array__contains "${function}" "$@"; then
  361. "${function}"
  362. fi
  363. }
  364. test_runner__skip_test() {
  365. local test_function=$1
  366. reporter__test_starts_running "${test_function}"
  367. results__increment_skipped_tests
  368. reporter__test_is_skipped "${test_function}"
  369. reporter__test_ends_running 0
  370. }
  371. reporter__test_files_start_running() {
  372. _reporter__initialise_file_descriptors
  373. reporter__for_each_reporter \
  374. _reporter__call_function "test_files_start_running" "$@"
  375. }
  376. _reporter__initialise_file_descriptors() {
  377. eval "exec ${SBU_STANDARD_FD}>&1"
  378. eval "exec ${SBU_ERROR_FD}>&2"
  379. }
  380. reporter__global_setup_has_failed() {
  381. reporter__for_each_reporter \
  382. _reporter__call_function "global_setup_has_failed" "$@"
  383. }
  384. reporter__test_file_starts_running() {
  385. reporter__for_each_reporter \
  386. _reporter__call_function "test_file_starts_running" "$@"
  387. }
  388. reporter__test_starts_running() {
  389. reporter__for_each_reporter \
  390. _reporter__call_function "test_starts_running" "$@"
  391. }
  392. reporter__test_has_succeeded() {
  393. reporter__for_each_reporter \
  394. _reporter__call_function "test_has_succeeded" "$@"
  395. }
  396. reporter__test_has_failed() {
  397. reporter__for_each_reporter \
  398. _reporter__call_function "test_has_failed" "$@"
  399. }
  400. reporter__test_is_skipped() {
  401. reporter__for_each_reporter \
  402. _reporter__call_function "test_is_skipped" "$@"
  403. }
  404. reporter__test_ends_running() {
  405. reporter__for_each_reporter \
  406. _reporter__call_function "test_ends_running" "$@"
  407. }
  408. reporter__test_file_ends_running() {
  409. reporter__for_each_reporter \
  410. _reporter__call_function "test_file_ends_running" "$@"
  411. }
  412. reporter__test_files_end_running() {
  413. reporter__for_each_reporter \
  414. _reporter__call_function "test_files_end_running" "$@"
  415. _reporter__release_file_descriptors
  416. }
  417. _reporter__release_file_descriptors() {
  418. eval "exec 1>&${SBU_STANDARD_FD} ${SBU_STANDARD_FD}>&-"
  419. eval "exec 2>&${SBU_ERROR_FD} ${SBU_ERROR_FD}>&-"
  420. }
  421. _reporter__call_function() {
  422. local function=$1
  423. shift 1
  424. "${reporter}_reporter__${function}" "$@"
  425. }
  426. reporter__for_each_reporter() {
  427. local reporter
  428. for reporter in ${SBU_REPORTERS//${SBU_VALUE_SEPARATOR}/ }; do
  429. "$@"
  430. done
  431. }
  432. reporter__print_with_color() {
  433. system__print_with_color "$@" >&${SBU_STANDARD_FD}
  434. }
  435. reporter__print_line() {
  436. system__print_line "$@" >&${SBU_STANDARD_FD}
  437. }
  438. reporter__print_line_with_color() {
  439. system__print_line_with_color "$@" >&${SBU_STANDARD_FD}
  440. }
  441. reporter__print_new_line() {
  442. system__print_new_line >&${SBU_STANDARD_FD}
  443. }
  444. reporter__get_color_code_for_tests_result() {
  445. local color_code=${SBU_GREEN_COLOR_CODE}
  446. if ! runner__tests_are_successful; then
  447. color_code=${SBU_RED_COLOR_CODE}
  448. fi
  449. system__print "${color_code}"
  450. }
  451. reporter__get_test_file_relative_name() {
  452. system__print "${1#${SBU_BASE_TEST_DIRECTORY}\/}"
  453. }
  454. simple_reporter__test_files_start_running() {
  455. :
  456. }
  457. simple_reporter__test_file_starts_running() {
  458. local relative_name="$(reporter__get_test_file_relative_name "$1")"
  459. reporter__print_line "[File] ${relative_name}"
  460. }
  461. simple_reporter__global_setup_has_failed() {
  462. reporter__print_line_with_color \
  463. "Global setup has failed" ${SBU_YELLOW_COLOR_CODE}
  464. }
  465. simple_reporter__test_starts_running() {
  466. reporter__print_line "[Test] $1"
  467. }
  468. simple_reporter__test_has_succeeded() {
  469. reporter__print_line_with_color "OK" ${SBU_GREEN_COLOR_CODE}
  470. }
  471. simple_reporter__test_has_failed() {
  472. reporter__print_line_with_color "KO" ${SBU_RED_COLOR_CODE}
  473. }
  474. simple_reporter__test_is_skipped() {
  475. reporter__print_line_with_color "Skipped" ${SBU_YELLOW_COLOR_CODE}
  476. }
  477. simple_reporter__test_ends_running() {
  478. :
  479. }
  480. simple_reporter__test_file_ends_running() {
  481. reporter__print_new_line
  482. }
  483. simple_reporter__test_files_end_running() {
  484. local time="in $1s"
  485. reporter__print_line "[Results]"
  486. local color="$(reporter__get_color_code_for_tests_result)"
  487. local total_count="$(_simple_reporter__get_total_count_message)"
  488. local failures_count="$(_simple_reporter__get_failures_count_message)"
  489. local skipped_count="$(results__get_skipped_tests_count) skipped"
  490. local message="${total_count}, ${failures_count}, ${skipped_count} ${time}"
  491. reporter__print_line_with_color "${message}" "${color}"
  492. }
  493. _simple_reporter__get_total_count_message() {
  494. local count="$(results__get_total_tests_count)"
  495. system__print "${count} test$(_simple_reporter__get_agreement ${count})"
  496. }
  497. _simple_reporter__get_failures_count_message() {
  498. local count="$(results__get_failing_tests_count)"
  499. system__print "${count} failure$(_simple_reporter__get_agreement ${count})"
  500. }
  501. _simple_reporter__get_agreement() {
  502. (( $1 > 1 )) \
  503. && system__print "s" \
  504. || system__print ""
  505. }
  506. dots_reporter__test_files_start_running() {
  507. exec 1>/dev/null
  508. exec 2>/dev/null
  509. }
  510. dots_reporter__test_file_starts_running() {
  511. :
  512. }
  513. dots_reporter__global_setup_has_failed() {
  514. :
  515. }
  516. dots_reporter__test_starts_running() {
  517. :
  518. }
  519. dots_reporter__test_has_succeeded() {
  520. reporter__print_with_color "." ${SBU_GREEN_COLOR_CODE}
  521. }
  522. dots_reporter__test_has_failed() {
  523. reporter__print_with_color "F" ${SBU_RED_COLOR_CODE}
  524. }
  525. dots_reporter__test_is_skipped() {
  526. reporter__print_with_color "S" ${SBU_YELLOW_COLOR_CODE}
  527. }
  528. dots_reporter__test_ends_running() {
  529. :
  530. }
  531. dots_reporter__test_file_ends_running() {
  532. :
  533. }
  534. dots_reporter__test_files_end_running() {
  535. local color="$(reporter__get_color_code_for_tests_result)"
  536. local texte="$(runner__tests_are_successful \
  537. && system__print "OK" \
  538. || system__print "KO")"
  539. reporter__print_line_with_color "${texte}" "${color}"
  540. }
  541. junit_reporter__test_files_start_running() {
  542. _junit_reporter__initialise_report_with \
  543. "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
  544. _junit_reporter__write_line_to_report "<testsuites>"
  545. }
  546. junit_reporter__test_file_starts_running() {
  547. local file_name=$1
  548. local test_count=$2
  549. local suite_name="$(_junit_reporter__get_suite_name "${file_name}")"
  550. database__put "sbu_current_suite_name" "${suite_name}"
  551. _junit_reporter__write_line_to_report \
  552. " <testsuite name=\"${suite_name}\" tests=\"${test_count}\">"
  553. _junit_reporter__delete_all_outputs_lines "suite"
  554. _junit_reporter__redirect_outputs_to_database "suite"
  555. }
  556. junit_reporter__global_setup_has_failed() {
  557. :
  558. }
  559. junit_reporter__test_starts_running() {
  560. local suite_name="$(database__get "sbu_current_suite_name")"
  561. local test_name="$(xml__encode_text "$1")"
  562. _junit_reporter__write_line_to_report \
  563. " <testcase name=\"${test_name}\" classname=\"${suite_name}\" \
  564. time=\"\${sbu_current_test_time}\">"
  565. _junit_reporter__delete_all_outputs_lines "test"
  566. _junit_reporter__redirect_outputs_to_database "test"
  567. }
  568. junit_reporter__test_has_succeeded() {
  569. :
  570. }
  571. junit_reporter__test_has_failed() {
  572. _junit_reporter__write_line_to_report " <failure>"
  573. _junit_reporter__write_line_to_report " </failure>"
  574. }
  575. junit_reporter__test_is_skipped() {
  576. _junit_reporter__write_line_to_report " <skipped>"
  577. _junit_reporter__write_line_to_report " </skipped>"
  578. }
  579. junit_reporter__test_ends_running() {
  580. _junit_reporter__redirect_outputs_to_database "suite"
  581. _junit_reporter__write_time_in_current_test_case_tag_in_report "$1"
  582. _junit_reporter__flush_all_outputs_to_report_if_any "test"
  583. _junit_reporter__write_line_to_report " </testcase>"
  584. }
  585. _junit_reporter__write_time_in_current_test_case_tag_in_report() {
  586. local test_time=$1
  587. local report_content=$(cat "${SBU_JUNIT_REPORTER_OUTPUT_FILE}")
  588. local content_with_time="$(system__substitute_variable \
  589. "${report_content}" "sbu_current_test_time" "${test_time}")"
  590. system__print_line \
  591. "${content_with_time}" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
  592. }
  593. junit_reporter__test_file_ends_running() {
  594. _junit_reporter__flush_all_outputs_to_report_if_any "suite"
  595. _junit_reporter__write_line_to_report " </testsuite>"
  596. database__put "sbu_current_suite_name" ""
  597. }
  598. junit_reporter__test_files_end_running() {
  599. _junit_reporter__write_line_to_report "</testsuites>"
  600. }
  601. _junit_reporter__get_suite_name() {
  602. local relative_name="$(reporter__get_test_file_relative_name "$1")"
  603. local dots_replaced_by_underscores="${relative_name//./_}"
  604. local slashes_replaced_by_dots="${dots_replaced_by_underscores//\//.}"
  605. xml__encode_text "${slashes_replaced_by_dots}"
  606. }
  607. _junit_reporter__initialise_report_with() {
  608. system__print_line "$1" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
  609. }
  610. _junit_reporter__write_line_to_report() {
  611. system__print_line "$1" >> "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
  612. }
  613. _junit_reporter__redirect_outputs_to_database() {
  614. local scope=$1
  615. exec 1>>\
  616. "$(database__get_descriptor "sbu_current_${scope}_standard_ouputs_lines")"
  617. exec 2>>\
  618. "$(database__get_descriptor "sbu_current_${scope}_error_ouputs_lines")"
  619. }
  620. _junit_reporter__delete_all_outputs_lines() {
  621. database__put "sbu_current_$1_standard_ouputs_lines"
  622. database__put "sbu_current_$1_error_ouputs_lines"
  623. }
  624. _junit_reporter__flush_all_outputs_to_report_if_any() {
  625. _junit_reporter__flush_outputs_to_report_if_any "$1" "standard"
  626. _junit_reporter__flush_outputs_to_report_if_any "$1" "error"
  627. }
  628. _junit_reporter__flush_outputs_to_report_if_any() {
  629. local scope=$1
  630. local outputs_type=$2
  631. local key="sbu_current_${scope}_${outputs_type}_ouputs_lines"
  632. local outputs="$(database__get "${key}")"
  633. if [[ -n "${outputs}" ]]; then
  634. _junit_reporter__write_outputs_to_report \
  635. "${scope}" "${outputs_type}" "${outputs}"
  636. database__put "${key}" ""
  637. fi
  638. }
  639. _junit_reporter__write_outputs_to_report() {
  640. local scope=$1
  641. local outputs_type=$2
  642. local outputs=$3
  643. local tag="$(_junit_reporter__get_tag_for_outputs_type "${outputs_type}")"
  644. local indentation="$(_junit_reporter__get_indentation_for_scope "${scope}")"
  645. _junit_reporter__write_line_to_report "${indentation}<${tag}>"
  646. _junit_reporter__write_line_to_report "$(xml__encode_text "${outputs}")"
  647. _junit_reporter__write_line_to_report "${indentation}</${tag}>"
  648. }
  649. _junit_reporter__get_tag_for_outputs_type() {
  650. [[ "$1" == "standard" ]] \
  651. && system__print "system-out" \
  652. || system__print "system-err"
  653. }
  654. _junit_reporter__get_indentation_for_scope() {
  655. [[ "$1" == "suite" ]] \
  656. && system__print " " \
  657. || system__print " "
  658. }
  659. xml__encode_text() {
  660. local encoded=${1//\&/\&amp\;}
  661. encoded=${encoded//\</\&lt\;}
  662. encoded=${encoded//\>/\&gt\;}
  663. encoded=${encoded//\"/\&quot\;}
  664. encoded=${encoded//\'/\&apos\;}
  665. system__print "${encoded}"
  666. }
  667. database__initialise() {
  668. _SBU_DB_TOKEN="$(system__random)"
  669. _database__ensure_directory_exists
  670. }
  671. database__release() {
  672. rm -rf "$(_database__get_dir)"
  673. }
  674. database__put() {
  675. _database__ensure_directory_exists
  676. system__print "$2" > "$(_database__get_dir)/$1"
  677. }
  678. database__post() {
  679. _database__ensure_directory_exists
  680. system__print "$2" >> "$(_database__get_dir)/$1"
  681. }
  682. database__post_line() {
  683. _database__ensure_directory_exists
  684. system__print_line "$2" >> "$(_database__get_dir)/$1"
  685. }
  686. database__put_variable() {
  687. _database__ensure_directory_exists
  688. database__put "$1" "${!1}"
  689. }
  690. database__get() {
  691. [[ -e "$(_database__get_dir)/$1" ]] && cat "$(_database__get_dir)/$1"
  692. }
  693. database__get_descriptor() {
  694. system__print "$(_database__get_dir)/$1"
  695. }
  696. _database__ensure_directory_exists() {
  697. mkdir -p "$(_database__get_dir)"
  698. }
  699. _database__get_dir() {
  700. system__print "${SBU_TEMP_DIR}/database/${_SBU_DB_TOKEN}"
  701. }
  702. system__get_string_or_default() {
  703. [[ -n "$1" ]] \
  704. && system__print "$1" \
  705. || system__print "$2"
  706. }
  707. system__get_date_in_seconds() {
  708. date +%s
  709. }
  710. system__print_line_with_color() {
  711. system__print_with_color "$@"
  712. system__print_new_line
  713. }
  714. system__print_with_color() {
  715. if [[ "${SBU_USE_COLORS}" == "${SBU_YES}" ]]; then
  716. printf "$2$1${SBU_DEFAULT_COLOR_CODE}"
  717. else
  718. system__print "$1"
  719. fi
  720. }
  721. system__print_line() {
  722. system__print "$1"
  723. system__print_new_line
  724. }
  725. system__print() {
  726. printf "%s" "$1"
  727. }
  728. system__print_new_line() {
  729. printf "\n"
  730. }
  731. array__contains() {
  732. local value=$1
  733. shift 1
  734. local i
  735. for (( i=1; i <= $#; i++ )); do
  736. if [[ "${!i}" == "${value}" ]]; then
  737. return ${SBU_SUCCESS_STATUS_CODE}
  738. fi
  739. done
  740. return ${SBU_FAILURE_STATUS_CODE}
  741. }
  742. array__from_lines() {
  743. local IFS=$'\n'
  744. eval "$1=(\$(</dev/stdin))"
  745. }
  746. array__print() {
  747. local element
  748. for element in "$@"; do
  749. system__print_line "${element}"
  750. done
  751. }
  752. system__pretty_print_array() {
  753. local array_as_string=""
  754. local i
  755. for (( i=1; i <= $#; i++ )); do
  756. array_as_string+="${!i}, "
  757. done
  758. array_as_string=${array_as_string/%, /}
  759. printf "[%s]" "${array_as_string}"
  760. }
  761. system__string_contains() {
  762. [[ "$1" == *"$2"* ]]
  763. }
  764. system__randomize_array() {
  765. local copy=("$@")
  766. while (( ${#copy[@]} > 0 )); do
  767. local random_index=$(( $(system__random) % ${#copy[@]} ))
  768. system__print_line "${copy[${random_index}]}"
  769. unset copy[${random_index}]
  770. copy=("${copy[@]}")
  771. done
  772. }
  773. system__random() {
  774. system__print "${RANDOM}"
  775. }
  776. system__substitute_variable() {
  777. local string=$1
  778. local key="\$\{$2\}"
  779. local value=$3
  780. printf "%s" "${string//${key}/${value}}"
  781. }
  782. main__main() {
  783. configuration__load
  784. _main__initialise
  785. local parsed_arguments=0
  786. _main__parse_arguments "$@"
  787. shift ${parsed_arguments}
  788. _main__assert_only_one_argument_left $#
  789. _main__assert_reporters_are_known
  790. SBU_BASE_TEST_DIRECTORY=$1
  791. if [[ "${SBU_NO_RUN}" != "${SBU_YES}" ]]; then
  792. runner__run_all_test_files "$1"
  793. return $?
  794. fi
  795. }
  796. _main__initialise() {
  797. database__initialise
  798. trap _main__release EXIT
  799. }
  800. _main__release() {
  801. database__release
  802. }
  803. _main__parse_arguments() {
  804. local argument
  805. for argument in "$@"; do
  806. case "${argument}" in
  807. -a|--api-cheat-sheet)
  808. _main__print_api_cheat_sheet_and_exit
  809. ;;
  810. -c=*|--colors=*)
  811. SBU_USE_COLORS="${argument#*=}"
  812. (( parsed_arguments++ ))
  813. ;;
  814. -d=*|--random-run=*)
  815. SBU_RANDOM_RUN="${argument#*=}"
  816. (( parsed_arguments++ ))
  817. ;;
  818. -h|--help)
  819. _main__print_full_usage
  820. exit ${SBU_SUCCESS_STATUS_CODE}
  821. ;;
  822. -f=*|--file-pattern=*)
  823. SBU_TEST_FILE_PATTERN="${argument#*=}"
  824. (( parsed_arguments++ ))
  825. ;;
  826. --no-run)
  827. SBU_NO_RUN="${SBU_YES}"
  828. (( parsed_arguments++ ))
  829. ;;
  830. -o=*|--output-file=*)
  831. SBU_JUNIT_REPORTER_OUTPUT_FILE="${argument#*=}"
  832. (( parsed_arguments++ ))
  833. ;;
  834. -t=*|--test-pattern=*)
  835. SBU_TEST_FUNCTION_PATTERN="${argument#*=}"
  836. (( parsed_arguments++ ))
  837. ;;
  838. -r=*|--reporters=*)
  839. SBU_REPORTERS="${argument#*=}"
  840. (( parsed_arguments++ ))
  841. ;;
  842. -*|--*)
  843. _main__print_illegal_option "${argument}"
  844. _main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE}
  845. ;;
  846. esac
  847. done
  848. }
  849. _main__assert_reporters_are_known() {
  850. reporter__for_each_reporter _main__fail_if_reporter_unknown
  851. }
  852. _main__fail_if_reporter_unknown() {
  853. if ! array__contains "${reporter}" "simple" "dots" "junit"; then
  854. system__print_line \
  855. "$(_main__get_script_name): unknown reporter <${reporter}>"
  856. exit ${SBU_FAILURE_STATUS_CODE}
  857. fi
  858. }
  859. _main__print_illegal_option() {
  860. local option="${1%=*}"
  861. option="${option#-}"
  862. option="${option#-}"
  863. system__print_line "$(_main__get_script_name): illegal option -- ${option}"
  864. }
  865. _main__assert_only_one_argument_left() {
  866. if (( $1 > 1 )); then
  867. system__print_line "$(_main__get_script_name): only one path is allowed"
  868. _main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE}
  869. fi
  870. }
  871. _main__get_script_name() {
  872. basename "${BASH_SOURCE[0]}"
  873. }
  874. _main__print_usage_and_exit_with_code() {
  875. _main__print_usage
  876. exit $1
  877. }
  878. _main__print_full_usage() {
  879. _main__print_usage
  880. local script="$(_main__get_script_name)"
  881. system__print_new_line
  882. system__print_line "\
  883. [options]
  884. -a, --api-cheat-sheet
  885. print api cheat sheet like assertions
  886. -c, --colors=${SBU_YES} or ${SBU_NO}
  887. tests output with colors or no
  888. -d, --random-run=${SBU_YES} or ${SBU_NO}
  889. tests files and functions randomly run or no
  890. -f, --file-pattern=<pattern>
  891. pattern to filter test files
  892. -h
  893. print usage
  894. -o, --output-file=<file>
  895. output file for JUnit reporter
  896. -r, --reporters=<reporter1,reporter2>
  897. comma-separated reporters (simple, dots or junit)
  898. -t, --test-pattern=<pattern>
  899. pattern to filter test function in files
  900. [examples]
  901. ${script} .
  902. run all tests in current directory
  903. ${script} -p=*test.sh sources/test
  904. run all tests files ending with test.sh in sources/test"
  905. }
  906. _main__print_usage() {
  907. system__print_line "\
  908. usage: $(_main__get_script_name) [options] path
  909. run all tests in path"
  910. }
  911. _main__print_api_cheat_sheet_and_exit() {
  912. system__print_line "\
  913. [assertions]
  914. assertion__equal (value, other)
  915. -> assert that <value> is equal to <other>
  916. assertion__different (value, other)
  917. -> assert that <value> is different from <other>
  918. assertion__string_contains (string, substring)
  919. -> assert that <string> contains <substring>
  920. assertion__string_does_not_contain (string, substring)
  921. -> assert that <string> does not contain <substring>
  922. assertion__string_empty (string)
  923. -> assert that <string> is empty
  924. assertion__string_not_empty (string)
  925. -> assert that <string> is not empty
  926. assertion__array_contains (element, array[0], array[1], ...)
  927. -> assert that the <array> contains the <element>
  928. assertion__array_does_not_contain (element, array elements...)
  929. -> assert that the <array> does not contain the <element>
  930. assertion__successful (command)
  931. -> assert that the <command> is successful
  932. assertion__failing (command)
  933. -> assert that the <command> is failing
  934. assertion__status_code_is_success (code)
  935. -> assert that the status <code> is 0
  936. assertion__status_code_is_failure (code)
  937. -> assert that the status <code> is not 0
  938. [special functions]
  939. ${SBU_GLOBAL_SETUP_FUNCTION_NAME}
  940. -> Executed before all tests in a file
  941. ${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}
  942. -> Executed after all tests in a file
  943. ${SBU_SETUP_FUNCTION_NAME}
  944. -> Executed before each test in a file
  945. ${SBU_TEARDOWN_FUNCTION_NAME}
  946. -> Executed after each test in a file
  947. [mocks]
  948. mock__make_function_do_nothing (function_to_mock)
  949. -> make function do nothing
  950. mock__make_function_prints (function_to_mock, message)
  951. -> make function prints a message
  952. mock__make_function_call (function_to_mock, function_to_call)
  953. -> make function call another function"
  954. exit ${SBU_SUCCESS_STATUS_CODE}
  955. }
  956. main__main "$@"