1146 lines
30 KiB
Bash
Executable file
1146 lines
30 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
# Copyright (C) 2015, Arpinum
|
|
#
|
|
# shebang-unit is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation, either version 3 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# shebang-unit is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# shebang-unit. If not, see http://www.gnu.org/licenses/lgpl.html.
|
|
|
|
# shebang-unit all in one source file
|
|
|
|
|
|
configuration__load() {
|
|
# yes/no representation used with shebang-unit parameters to activate
|
|
# stuff like colors
|
|
SBU_YES="yes"
|
|
SBU_NO="no"
|
|
|
|
# Colors for outputs
|
|
SBU_GREEN_COLOR_CODE="\\033[1;32m"
|
|
SBU_RED_COLOR_CODE="\\033[1;31m"
|
|
SBU_YELLOW_COLOR_CODE="\\033[1;33m"
|
|
SBU_DEFAULT_COLOR_CODE="\\e[0m"
|
|
|
|
# Functions coding coventions
|
|
SBU_GLOBAL_SETUP_FUNCTION_NAME="global_setup"
|
|
SBU_GLOBAL_TEARDOWN_FUNCTION_NAME="global_teardown"
|
|
SBU_SETUP_FUNCTION_NAME="setup"
|
|
SBU_TEARDOWN_FUNCTION_NAME="teardown"
|
|
SBU_FUNCTION_DECLARATION_REGEX="^[ ]*\(function\)\{0,1\}[ ]*\([A-Za-z0-9_-]\{1,\}\)[ ]*\(([ ]*)\)\{0,1\}[ ]*{"
|
|
SBU_PRIVATE_FUNCTION_NAME_REGEX="^_.*"
|
|
|
|
# Default configuration that can be modified with shebang-unit parameters
|
|
# For more information see shebang-unit usages
|
|
SBU_TEST_FILE_PATTERN="*_test.sh"
|
|
SBU_TEST_FUNCTION_PATTERN="*"
|
|
SBU_USE_COLORS="${SBU_YES}"
|
|
SBU_RANDOM_RUN="${SBU_NO}"
|
|
SBU_REPORTERS="simple"
|
|
SBU_JUNIT_REPORTER_OUTPUT_FILE="./junit_report.xml"
|
|
|
|
# Internal constants
|
|
SBU_SUCCESS_STATUS_CODE=0
|
|
SBU_FAILURE_STATUS_CODE=1
|
|
SBU_VALUE_SEPARATOR=","
|
|
SBU_TEMP_DIR="/tmp/.shebang-unit"
|
|
SBU_LAST_ASSERTION_MSG_KEY="last_assertion_message"
|
|
SBU_NO_RUN="${SBU_NO}"
|
|
SBU_STANDARD_FD=42
|
|
SBU_ERROR_FD=43
|
|
}
|
|
|
|
|
|
assertion__equal() {
|
|
if [[ "$1" != "$2" ]]; then
|
|
_assertion__failed "Actual: <$2>, expected: <$1>."
|
|
fi
|
|
}
|
|
|
|
assertion__different() {
|
|
if [[ "$1" == "$2" ]]; then
|
|
_assertion__failed "Both values are: <$1>."
|
|
fi
|
|
}
|
|
|
|
assertion__string_contains() {
|
|
if ! system__string_contains "$1" "$2"; then
|
|
_assertion__failed "String: <$1> does not contain: <$2>."
|
|
fi
|
|
}
|
|
|
|
assertion__string_does_not_contain() {
|
|
if system__string_contains "$1" "$2"; then
|
|
_assertion__failed "String: <$1> contains: <$2>."
|
|
fi
|
|
}
|
|
|
|
assertion__string_empty() {
|
|
if [[ -n "$1" ]]; then
|
|
_assertion__failed "String: <$1> is not empty."
|
|
fi
|
|
}
|
|
|
|
assertion__string_not_empty() {
|
|
if [[ -z "$1" ]]; then
|
|
_assertion__failed "The string is empty."
|
|
fi
|
|
}
|
|
|
|
assertion__array_contains() {
|
|
local element=$1
|
|
shift 1
|
|
if ! array__contains "${element}" "$@"; then
|
|
local array_as_string="$(system__pretty_print_array "$@")"
|
|
_assertion__failed \
|
|
"Array: <${array_as_string}> does not contain: <${element}>."
|
|
fi
|
|
}
|
|
|
|
assertion__array_does_not_contain() {
|
|
local element=$1
|
|
shift 1
|
|
if array__contains "${element}" "$@"; then
|
|
local array_as_string="$(system__pretty_print_array "$@")"
|
|
_assertion__failed \
|
|
"Array: <${array_as_string}> contains: <${element}>."
|
|
fi
|
|
}
|
|
|
|
assertion__status_code_is_success() {
|
|
if (( $1 != ${SBU_SUCCESS_STATUS_CODE} )); then
|
|
_assertion__failed \
|
|
"Status code is failure instead of success." "$2"
|
|
fi
|
|
}
|
|
|
|
assertion__status_code_is_failure() {
|
|
if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then
|
|
_assertion__failed \
|
|
"Status code is success instead of failure." "$2"
|
|
fi
|
|
}
|
|
|
|
assertion__successful() {
|
|
"$@"
|
|
if (( $? != ${SBU_SUCCESS_STATUS_CODE} )); then
|
|
_assertion__failed "Command is failing instead of successful."
|
|
fi
|
|
}
|
|
|
|
assertion__failing() {
|
|
"$@"
|
|
if (( $? == ${SBU_SUCCESS_STATUS_CODE} )); then
|
|
_assertion__failed "Command is successful instead of failing."
|
|
fi
|
|
}
|
|
|
|
_assertion__failed() {
|
|
local message_to_use="$(_assertion__get_assertion_message_to_use "$1" "$2")"
|
|
system__print_line "Assertion failed. ${message_to_use}"
|
|
exit ${SBU_FAILURE_STATUS_CODE}
|
|
}
|
|
|
|
_assertion__get_assertion_message_to_use() {
|
|
local message=$1
|
|
local custom_messsage=$2
|
|
if [[ -n "${custom_messsage}" ]]; then
|
|
system__print "${message} ${custom_messsage}"
|
|
else
|
|
system__print "${message}"
|
|
fi
|
|
}
|
|
|
|
|
|
mock__make_function_do_nothing() {
|
|
mock__make_function_call "$1" ":"
|
|
}
|
|
|
|
mock__make_function_prints() {
|
|
local function=$1
|
|
local text=$2
|
|
eval "${function}() { printf "${text}"; }"
|
|
}
|
|
|
|
mock__make_function_call() {
|
|
local function_to_mock=$1
|
|
local function_to_call=$2
|
|
shift 2
|
|
eval "${function_to_mock}() { ${function_to_call} \"\$@\"; }"
|
|
}
|
|
|
|
|
|
runner__run_all_test_files() {
|
|
SBU_BASE_TEST_DIRECTORY=$1
|
|
reporter__test_files_start_running
|
|
timer__store_current_time "global_time"
|
|
results__test_files_start_running
|
|
_runner__run_all_test_files_with_pattern_in_directory "$1"
|
|
reporter__test_files_end_running "$(timer__get_time_elapsed "global_time")"
|
|
runner__tests_are_successful
|
|
}
|
|
|
|
_runner__run_all_test_files_with_pattern_in_directory() {
|
|
local file
|
|
local files
|
|
array__from_lines files <<< "$(_runner__get_test_files_in_directory "$1")"
|
|
for file in "${files[@]}"; do
|
|
file_runner__run_test_file "${file}"
|
|
done
|
|
}
|
|
|
|
_runner__get_test_files_in_directory() {
|
|
local files
|
|
array__from_lines files <<< "$(find "$1" -name "${SBU_TEST_FILE_PATTERN}" | sort)"
|
|
if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then
|
|
array__from_lines files <<< "$(system__randomize_array "${files[@]}")"
|
|
fi
|
|
array__print "${files[@]}"
|
|
}
|
|
|
|
runner__tests_are_successful() {
|
|
(( $(results__get_failing_tests_count) == 0 \
|
|
&& $(results__get_skipped_tests_count) == 0 ))
|
|
}
|
|
|
|
|
|
file_runner__run_test_file() {
|
|
local file=$1
|
|
local public_functions=($(parser__get_public_functions_in_file "${file}"))
|
|
local test_functions=($(_file_runner__get_test_functions))
|
|
reporter__test_file_starts_running "${file}" "${#test_functions[@]}"
|
|
( source "${file}"
|
|
_file_runner__run_global_setup_if_exists \
|
|
&& _file_runner__call_all_tests
|
|
_file_runner__run_global_teardown_if_exists )
|
|
_file_runner__check_if_global_setup_has_exited
|
|
reporter__test_file_ends_running
|
|
}
|
|
|
|
_file_runner__run_all_tests_if_global_setup_is_successful() {
|
|
_file_runner__call_all_tests
|
|
}
|
|
|
|
_file_runner__call_all_tests() {
|
|
local i
|
|
for (( i=0; i < ${#test_functions[@]}; i++ )); do
|
|
test_runner__run_test "${test_functions[${i}]}" "${public_functions[@]}"
|
|
done
|
|
}
|
|
|
|
_file_runner__skip_all_tests() {
|
|
local i
|
|
for (( i=0; i < ${#test_functions[@]}; i++ )); do
|
|
test_runner__skip_test "${test_functions[${i}]}" "${public_functions[@]}"
|
|
done
|
|
}
|
|
|
|
_file_runner__get_test_functions() {
|
|
local result=()
|
|
local test_function
|
|
for test_function in "${public_functions[@]}"; do
|
|
if _file_runner__function_is_a_test "${test_function}"\
|
|
&& [[ "${test_function}" == ${SBU_TEST_FUNCTION_PATTERN} ]]; then
|
|
result+=("${test_function}")
|
|
fi
|
|
done
|
|
_file_runner__get_randomized_test_functions_if_needed "${result[@]}"
|
|
}
|
|
|
|
_file_runner__get_randomized_test_functions_if_needed() {
|
|
if [[ "${SBU_RANDOM_RUN}" == "${SBU_YES}" ]]; then
|
|
system__randomize_array "$@"
|
|
else
|
|
array__print "$@"
|
|
fi
|
|
}
|
|
|
|
_file_runner__run_global_setup_if_exists() {
|
|
database__put "sbu_current_global_setup_has_failed" "${SBU_YES}"
|
|
_file_runner__call_function_if_exists "${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \
|
|
&& database__put "sbu_current_global_setup_has_failed" "${SBU_NO}"
|
|
}
|
|
|
|
_file_runner__run_global_teardown_if_exists() {
|
|
_file_runner__call_function_if_exists "${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}"
|
|
}
|
|
|
|
_file_runner__function_is_a_test() {
|
|
! array__contains "$1" \
|
|
"${SBU_GLOBAL_SETUP_FUNCTION_NAME}" \
|
|
"${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}" \
|
|
"${SBU_SETUP_FUNCTION_NAME}" \
|
|
"${SBU_TEARDOWN_FUNCTION_NAME}"
|
|
}
|
|
|
|
_file_runner__call_function_if_exists() {
|
|
local function=$1
|
|
shift 1
|
|
if array__contains "${function}" "${public_functions[@]}"; then
|
|
"${function}"
|
|
fi
|
|
}
|
|
|
|
_file_runner__check_if_global_setup_has_exited() {
|
|
local has_exited="$(database__get "sbu_current_global_setup_has_failed")"
|
|
if [[ "${has_exited}" == "${SBU_YES}" ]]; then
|
|
_file_runner__handle_failure_in_global_setup
|
|
fi
|
|
}
|
|
|
|
_file_runner__handle_failure_in_global_setup() {
|
|
reporter__global_setup_has_failed
|
|
_file_runner__skip_all_tests
|
|
}
|
|
|
|
|
|
parser__get_public_functions_in_file() {
|
|
_parser__find_functions_in_file "$1" \
|
|
| _parser__filter_private_functions \
|
|
| awk '{ print $1 }'
|
|
}
|
|
|
|
_parser__find_functions_in_file() {
|
|
grep -o "${SBU_FUNCTION_DECLARATION_REGEX}" "$1" \
|
|
| _parser__get_function_name_from_declaration
|
|
}
|
|
|
|
_parser__filter_private_functions() {
|
|
grep -v "${SBU_PRIVATE_FUNCTION_NAME_REGEX}"
|
|
}
|
|
|
|
_parser__get_function_name_from_declaration() {
|
|
sed "s/${SBU_FUNCTION_DECLARATION_REGEX}/\2/"
|
|
}
|
|
|
|
|
|
timer__store_current_time() {
|
|
local id=$1
|
|
database__put "sbu_beginning_date_$1" "$(system__get_date_in_seconds)"
|
|
}
|
|
|
|
timer__get_time_elapsed() {
|
|
local id=$1
|
|
local beginning_date="$(database__get "sbu_beginning_date_$1")"
|
|
local ending_date="$(system__get_date_in_seconds)"
|
|
|
|
[[ -n "${beginning_date}" ]] \
|
|
&& system__print "$(( ending_date - beginning_date ))" \
|
|
|| system__print "0"
|
|
}
|
|
|
|
|
|
results__test_files_start_running() {
|
|
database__put "sbu_successful_tests_count" "0"
|
|
database__put "sbu_failing_tests_count" "0"
|
|
database__put "sbu_skipped_tests_count" "0"
|
|
}
|
|
|
|
results__get_successful_tests_count() {
|
|
_results__get_tests_count_of_type "successful"
|
|
}
|
|
|
|
results__increment_successful_tests() {
|
|
_results__increment_tests_of_type "successful"
|
|
}
|
|
|
|
results__get_failing_tests_count() {
|
|
_results__get_tests_count_of_type "failing"
|
|
}
|
|
|
|
results__increment_failing_tests() {
|
|
_results__increment_tests_of_type "failing"
|
|
}
|
|
|
|
results__get_skipped_tests_count() {
|
|
_results__get_tests_count_of_type "skipped"
|
|
}
|
|
|
|
results__increment_skipped_tests() {
|
|
_results__increment_tests_of_type "skipped"
|
|
}
|
|
|
|
results__get_total_tests_count() {
|
|
local successes="$(results__get_successful_tests_count)"
|
|
local failures="$(results__get_failing_tests_count)"
|
|
local skipped="$(results__get_skipped_tests_count)"
|
|
system__print "$(( successes + failures + skipped ))"
|
|
}
|
|
|
|
_results__get_tests_count_of_type() {
|
|
local type=$1
|
|
database__get "sbu_${type}_tests_count"
|
|
}
|
|
|
|
_results__increment_tests_of_type() {
|
|
local type=$1
|
|
local count="$(results__get_${type}_tests_count)"
|
|
database__put "sbu_${type}_tests_count" "$(( count + 1 ))"
|
|
}
|
|
|
|
|
|
test_runner__run_test() {
|
|
local test_function=$1
|
|
shift 1
|
|
reporter__test_starts_running "${test_function}"
|
|
timer__store_current_time "test_time"
|
|
(
|
|
_test_runner__call_setup_if_exists "$@" \
|
|
&& _test_runner__call_test_fonction "${test_function}"
|
|
local setup_and_test_code=$?
|
|
_test_runner__call_teardown_if_exists "$@"
|
|
(( $? == ${SBU_SUCCESS_STATUS_CODE} \
|
|
&& ${setup_and_test_code} == ${SBU_SUCCESS_STATUS_CODE} ))
|
|
)
|
|
_test_runner__parse_test_function_result $?
|
|
reporter__test_ends_running "$(timer__get_time_elapsed "test_time")"
|
|
}
|
|
|
|
_test_runner__call_test_fonction() {
|
|
( "$1" >&${SBU_STANDARD_FD} 2>&${SBU_ERROR_FD} )
|
|
}
|
|
|
|
_test_runner__call_setup_if_exists() {
|
|
_test_runner__call_function_if_exits "${SBU_SETUP_FUNCTION_NAME}" "$@"
|
|
}
|
|
|
|
_test_runner__call_teardown_if_exists() {
|
|
_test_runner__call_function_if_exits "${SBU_TEARDOWN_FUNCTION_NAME}" "$@"
|
|
}
|
|
|
|
_test_runner__parse_test_function_result() {
|
|
if (( $1 == ${SBU_SUCCESS_STATUS_CODE} )); then
|
|
results__increment_successful_tests
|
|
reporter__test_has_succeeded
|
|
else
|
|
results__increment_failing_tests
|
|
reporter__test_has_failed
|
|
fi
|
|
}
|
|
|
|
_test_runner__call_function_if_exits() {
|
|
local function=$1
|
|
shift 1
|
|
if array__contains "${function}" "$@"; then
|
|
"${function}"
|
|
fi
|
|
}
|
|
|
|
test_runner__skip_test() {
|
|
local test_function=$1
|
|
reporter__test_starts_running "${test_function}"
|
|
results__increment_skipped_tests
|
|
reporter__test_is_skipped "${test_function}"
|
|
reporter__test_ends_running 0
|
|
}
|
|
|
|
|
|
reporter__test_files_start_running() {
|
|
_reporter__initialise_file_descriptors
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_files_start_running" "$@"
|
|
}
|
|
|
|
_reporter__initialise_file_descriptors() {
|
|
eval "exec ${SBU_STANDARD_FD}>&1"
|
|
eval "exec ${SBU_ERROR_FD}>&2"
|
|
}
|
|
|
|
reporter__global_setup_has_failed() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "global_setup_has_failed" "$@"
|
|
}
|
|
|
|
reporter__test_file_starts_running() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_file_starts_running" "$@"
|
|
}
|
|
|
|
reporter__test_starts_running() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_starts_running" "$@"
|
|
}
|
|
|
|
reporter__test_has_succeeded() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_has_succeeded" "$@"
|
|
}
|
|
|
|
reporter__test_has_failed() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_has_failed" "$@"
|
|
}
|
|
|
|
reporter__test_is_skipped() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_is_skipped" "$@"
|
|
}
|
|
|
|
reporter__test_ends_running() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_ends_running" "$@"
|
|
}
|
|
|
|
reporter__test_file_ends_running() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_file_ends_running" "$@"
|
|
}
|
|
|
|
reporter__test_files_end_running() {
|
|
reporter__for_each_reporter \
|
|
_reporter__call_function "test_files_end_running" "$@"
|
|
_reporter__release_file_descriptors
|
|
}
|
|
|
|
_reporter__release_file_descriptors() {
|
|
eval "exec 1>&${SBU_STANDARD_FD} ${SBU_STANDARD_FD}>&-"
|
|
eval "exec 2>&${SBU_ERROR_FD} ${SBU_ERROR_FD}>&-"
|
|
}
|
|
|
|
_reporter__call_function() {
|
|
local function=$1
|
|
shift 1
|
|
"${reporter}_reporter__${function}" "$@"
|
|
}
|
|
|
|
reporter__for_each_reporter() {
|
|
local reporter
|
|
for reporter in ${SBU_REPORTERS//${SBU_VALUE_SEPARATOR}/ }; do
|
|
"$@"
|
|
done
|
|
}
|
|
|
|
reporter__print_with_color() {
|
|
system__print_with_color "$@" >&${SBU_STANDARD_FD}
|
|
}
|
|
|
|
reporter__print_line() {
|
|
system__print_line "$@" >&${SBU_STANDARD_FD}
|
|
}
|
|
|
|
reporter__print_line_with_color() {
|
|
system__print_line_with_color "$@" >&${SBU_STANDARD_FD}
|
|
}
|
|
|
|
reporter__print_new_line() {
|
|
system__print_new_line >&${SBU_STANDARD_FD}
|
|
}
|
|
|
|
reporter__get_color_code_for_tests_result() {
|
|
local color_code=${SBU_GREEN_COLOR_CODE}
|
|
if ! runner__tests_are_successful; then
|
|
color_code=${SBU_RED_COLOR_CODE}
|
|
fi
|
|
system__print "${color_code}"
|
|
}
|
|
|
|
reporter__get_test_file_relative_name() {
|
|
system__print "${1#${SBU_BASE_TEST_DIRECTORY}\/}"
|
|
}
|
|
|
|
|
|
simple_reporter__test_files_start_running() {
|
|
:
|
|
}
|
|
|
|
simple_reporter__test_file_starts_running() {
|
|
local relative_name="$(reporter__get_test_file_relative_name "$1")"
|
|
reporter__print_line "[File] ${relative_name}"
|
|
}
|
|
|
|
simple_reporter__global_setup_has_failed() {
|
|
reporter__print_line_with_color \
|
|
"Global setup has failed" ${SBU_YELLOW_COLOR_CODE}
|
|
}
|
|
|
|
simple_reporter__test_starts_running() {
|
|
reporter__print_line "[Test] $1"
|
|
}
|
|
|
|
simple_reporter__test_has_succeeded() {
|
|
reporter__print_line_with_color "OK" ${SBU_GREEN_COLOR_CODE}
|
|
}
|
|
|
|
simple_reporter__test_has_failed() {
|
|
reporter__print_line_with_color "KO" ${SBU_RED_COLOR_CODE}
|
|
}
|
|
|
|
simple_reporter__test_is_skipped() {
|
|
reporter__print_line_with_color "Skipped" ${SBU_YELLOW_COLOR_CODE}
|
|
}
|
|
|
|
simple_reporter__test_ends_running() {
|
|
:
|
|
}
|
|
|
|
simple_reporter__test_file_ends_running() {
|
|
reporter__print_new_line
|
|
}
|
|
|
|
simple_reporter__test_files_end_running() {
|
|
local time="in $1s"
|
|
reporter__print_line "[Results]"
|
|
local color="$(reporter__get_color_code_for_tests_result)"
|
|
local total_count="$(_simple_reporter__get_total_count_message)"
|
|
local failures_count="$(_simple_reporter__get_failures_count_message)"
|
|
local skipped_count="$(results__get_skipped_tests_count) skipped"
|
|
local message="${total_count}, ${failures_count}, ${skipped_count} ${time}"
|
|
reporter__print_line_with_color "${message}" "${color}"
|
|
}
|
|
|
|
_simple_reporter__get_total_count_message() {
|
|
local count="$(results__get_total_tests_count)"
|
|
system__print "${count} test$(_simple_reporter__get_agreement ${count})"
|
|
}
|
|
|
|
_simple_reporter__get_failures_count_message() {
|
|
local count="$(results__get_failing_tests_count)"
|
|
system__print "${count} failure$(_simple_reporter__get_agreement ${count})"
|
|
}
|
|
|
|
_simple_reporter__get_agreement() {
|
|
(( $1 > 1 )) \
|
|
&& system__print "s" \
|
|
|| system__print ""
|
|
}
|
|
|
|
|
|
dots_reporter__test_files_start_running() {
|
|
exec 1>/dev/null
|
|
exec 2>/dev/null
|
|
}
|
|
|
|
dots_reporter__test_file_starts_running() {
|
|
:
|
|
}
|
|
|
|
dots_reporter__global_setup_has_failed() {
|
|
:
|
|
}
|
|
|
|
dots_reporter__test_starts_running() {
|
|
:
|
|
}
|
|
|
|
dots_reporter__test_has_succeeded() {
|
|
reporter__print_with_color "." ${SBU_GREEN_COLOR_CODE}
|
|
}
|
|
|
|
dots_reporter__test_has_failed() {
|
|
reporter__print_with_color "F" ${SBU_RED_COLOR_CODE}
|
|
}
|
|
|
|
dots_reporter__test_is_skipped() {
|
|
reporter__print_with_color "S" ${SBU_YELLOW_COLOR_CODE}
|
|
}
|
|
|
|
dots_reporter__test_ends_running() {
|
|
:
|
|
}
|
|
|
|
dots_reporter__test_file_ends_running() {
|
|
:
|
|
}
|
|
|
|
dots_reporter__test_files_end_running() {
|
|
local color="$(reporter__get_color_code_for_tests_result)"
|
|
local texte="$(runner__tests_are_successful \
|
|
&& system__print "OK" \
|
|
|| system__print "KO")"
|
|
reporter__print_line_with_color "${texte}" "${color}"
|
|
}
|
|
|
|
|
|
junit_reporter__test_files_start_running() {
|
|
_junit_reporter__initialise_report_with \
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
|
|
_junit_reporter__write_line_to_report "<testsuites>"
|
|
}
|
|
|
|
junit_reporter__test_file_starts_running() {
|
|
local file_name=$1
|
|
local test_count=$2
|
|
local suite_name="$(_junit_reporter__get_suite_name "${file_name}")"
|
|
database__put "sbu_current_suite_name" "${suite_name}"
|
|
_junit_reporter__write_line_to_report \
|
|
" <testsuite name=\"${suite_name}\" tests=\"${test_count}\">"
|
|
_junit_reporter__delete_all_outputs_lines "suite"
|
|
_junit_reporter__redirect_outputs_to_database "suite"
|
|
}
|
|
|
|
junit_reporter__global_setup_has_failed() {
|
|
:
|
|
}
|
|
|
|
junit_reporter__test_starts_running() {
|
|
local suite_name="$(database__get "sbu_current_suite_name")"
|
|
local test_name="$(xml__encode_text "$1")"
|
|
_junit_reporter__write_line_to_report \
|
|
" <testcase name=\"${test_name}\" classname=\"${suite_name}\" \
|
|
time=\"\${sbu_current_test_time}\">"
|
|
_junit_reporter__delete_all_outputs_lines "test"
|
|
_junit_reporter__redirect_outputs_to_database "test"
|
|
}
|
|
|
|
junit_reporter__test_has_succeeded() {
|
|
:
|
|
}
|
|
|
|
junit_reporter__test_has_failed() {
|
|
_junit_reporter__write_line_to_report " <failure>"
|
|
_junit_reporter__write_line_to_report " </failure>"
|
|
}
|
|
|
|
junit_reporter__test_is_skipped() {
|
|
_junit_reporter__write_line_to_report " <skipped>"
|
|
_junit_reporter__write_line_to_report " </skipped>"
|
|
}
|
|
|
|
junit_reporter__test_ends_running() {
|
|
_junit_reporter__redirect_outputs_to_database "suite"
|
|
_junit_reporter__write_time_in_current_test_case_tag_in_report "$1"
|
|
_junit_reporter__flush_all_outputs_to_report_if_any "test"
|
|
_junit_reporter__write_line_to_report " </testcase>"
|
|
}
|
|
|
|
_junit_reporter__write_time_in_current_test_case_tag_in_report() {
|
|
local test_time=$1
|
|
local report_content=$(cat "${SBU_JUNIT_REPORTER_OUTPUT_FILE}")
|
|
local content_with_time="$(system__substitute_variable \
|
|
"${report_content}" "sbu_current_test_time" "${test_time}")"
|
|
system__print_line \
|
|
"${content_with_time}" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
|
|
}
|
|
|
|
junit_reporter__test_file_ends_running() {
|
|
_junit_reporter__flush_all_outputs_to_report_if_any "suite"
|
|
_junit_reporter__write_line_to_report " </testsuite>"
|
|
database__put "sbu_current_suite_name" ""
|
|
}
|
|
|
|
junit_reporter__test_files_end_running() {
|
|
_junit_reporter__write_line_to_report "</testsuites>"
|
|
}
|
|
|
|
_junit_reporter__get_suite_name() {
|
|
local relative_name="$(reporter__get_test_file_relative_name "$1")"
|
|
local dots_replaced_by_underscores="${relative_name//./_}"
|
|
local slashes_replaced_by_dots="${dots_replaced_by_underscores//\//.}"
|
|
xml__encode_text "${slashes_replaced_by_dots}"
|
|
}
|
|
|
|
_junit_reporter__initialise_report_with() {
|
|
system__print_line "$1" > "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
|
|
}
|
|
|
|
_junit_reporter__write_line_to_report() {
|
|
system__print_line "$1" >> "${SBU_JUNIT_REPORTER_OUTPUT_FILE}"
|
|
}
|
|
|
|
_junit_reporter__redirect_outputs_to_database() {
|
|
local scope=$1
|
|
exec 1>>\
|
|
"$(database__get_descriptor "sbu_current_${scope}_standard_ouputs_lines")"
|
|
exec 2>>\
|
|
"$(database__get_descriptor "sbu_current_${scope}_error_ouputs_lines")"
|
|
}
|
|
|
|
_junit_reporter__delete_all_outputs_lines() {
|
|
database__put "sbu_current_$1_standard_ouputs_lines"
|
|
database__put "sbu_current_$1_error_ouputs_lines"
|
|
}
|
|
|
|
_junit_reporter__flush_all_outputs_to_report_if_any() {
|
|
_junit_reporter__flush_outputs_to_report_if_any "$1" "standard"
|
|
_junit_reporter__flush_outputs_to_report_if_any "$1" "error"
|
|
}
|
|
|
|
_junit_reporter__flush_outputs_to_report_if_any() {
|
|
local scope=$1
|
|
local outputs_type=$2
|
|
local key="sbu_current_${scope}_${outputs_type}_ouputs_lines"
|
|
local outputs="$(database__get "${key}")"
|
|
if [[ -n "${outputs}" ]]; then
|
|
_junit_reporter__write_outputs_to_report \
|
|
"${scope}" "${outputs_type}" "${outputs}"
|
|
database__put "${key}" ""
|
|
fi
|
|
}
|
|
|
|
_junit_reporter__write_outputs_to_report() {
|
|
local scope=$1
|
|
local outputs_type=$2
|
|
local outputs=$3
|
|
local tag="$(_junit_reporter__get_tag_for_outputs_type "${outputs_type}")"
|
|
local indentation="$(_junit_reporter__get_indentation_for_scope "${scope}")"
|
|
_junit_reporter__write_line_to_report "${indentation}<${tag}>"
|
|
_junit_reporter__write_line_to_report "$(xml__encode_text "${outputs}")"
|
|
_junit_reporter__write_line_to_report "${indentation}</${tag}>"
|
|
}
|
|
|
|
_junit_reporter__get_tag_for_outputs_type() {
|
|
[[ "$1" == "standard" ]] \
|
|
&& system__print "system-out" \
|
|
|| system__print "system-err"
|
|
}
|
|
|
|
_junit_reporter__get_indentation_for_scope() {
|
|
[[ "$1" == "suite" ]] \
|
|
&& system__print " " \
|
|
|| system__print " "
|
|
}
|
|
|
|
|
|
xml__encode_text() {
|
|
local encoded=${1//\&/\&\;}
|
|
encoded=${encoded//\</\<\;}
|
|
encoded=${encoded//\>/\>\;}
|
|
encoded=${encoded//\"/\"\;}
|
|
encoded=${encoded//\'/\&apos\;}
|
|
system__print "${encoded}"
|
|
}
|
|
|
|
|
|
database__initialise() {
|
|
_SBU_DB_TOKEN="$(system__random)"
|
|
_database__ensure_directory_exists
|
|
}
|
|
|
|
database__release() {
|
|
rm -rf "$(_database__get_dir)"
|
|
}
|
|
|
|
database__put() {
|
|
_database__ensure_directory_exists
|
|
system__print "$2" > "$(_database__get_dir)/$1"
|
|
}
|
|
|
|
database__post() {
|
|
_database__ensure_directory_exists
|
|
system__print "$2" >> "$(_database__get_dir)/$1"
|
|
}
|
|
|
|
database__post_line() {
|
|
_database__ensure_directory_exists
|
|
system__print_line "$2" >> "$(_database__get_dir)/$1"
|
|
}
|
|
|
|
database__put_variable() {
|
|
_database__ensure_directory_exists
|
|
database__put "$1" "${!1}"
|
|
}
|
|
|
|
database__get() {
|
|
[[ -e "$(_database__get_dir)/$1" ]] && cat "$(_database__get_dir)/$1"
|
|
}
|
|
|
|
database__get_descriptor() {
|
|
system__print "$(_database__get_dir)/$1"
|
|
}
|
|
|
|
_database__ensure_directory_exists() {
|
|
mkdir -p "$(_database__get_dir)"
|
|
}
|
|
|
|
_database__get_dir() {
|
|
system__print "${SBU_TEMP_DIR}/database/${_SBU_DB_TOKEN}"
|
|
}
|
|
|
|
|
|
system__get_string_or_default() {
|
|
[[ -n "$1" ]] \
|
|
&& system__print "$1" \
|
|
|| system__print "$2"
|
|
}
|
|
|
|
system__get_date_in_seconds() {
|
|
date +%s
|
|
}
|
|
|
|
system__print_line_with_color() {
|
|
system__print_with_color "$@"
|
|
system__print_new_line
|
|
}
|
|
|
|
system__print_with_color() {
|
|
if [[ "${SBU_USE_COLORS}" == "${SBU_YES}" ]]; then
|
|
printf "$2$1${SBU_DEFAULT_COLOR_CODE}"
|
|
else
|
|
system__print "$1"
|
|
fi
|
|
}
|
|
|
|
system__print_line() {
|
|
system__print "$1"
|
|
system__print_new_line
|
|
}
|
|
|
|
system__print() {
|
|
printf "%s" "$1"
|
|
}
|
|
|
|
system__print_new_line() {
|
|
printf "\n"
|
|
}
|
|
|
|
array__contains() {
|
|
local value=$1
|
|
shift 1
|
|
local i
|
|
for (( i=1; i <= $#; i++ )); do
|
|
if [[ "${!i}" == "${value}" ]]; then
|
|
return ${SBU_SUCCESS_STATUS_CODE}
|
|
fi
|
|
done
|
|
return ${SBU_FAILURE_STATUS_CODE}
|
|
}
|
|
|
|
array__from_lines() {
|
|
local IFS=$'\n'
|
|
eval "$1=(\$(</dev/stdin))"
|
|
}
|
|
|
|
array__print() {
|
|
local element
|
|
for element in "$@"; do
|
|
system__print_line "${element}"
|
|
done
|
|
}
|
|
|
|
system__pretty_print_array() {
|
|
local array_as_string=""
|
|
local i
|
|
for (( i=1; i <= $#; i++ )); do
|
|
array_as_string+="${!i}, "
|
|
done
|
|
array_as_string=${array_as_string/%, /}
|
|
printf "[%s]" "${array_as_string}"
|
|
}
|
|
|
|
system__string_contains() {
|
|
[[ "$1" == *"$2"* ]]
|
|
}
|
|
|
|
system__randomize_array() {
|
|
local copy=("$@")
|
|
while (( ${#copy[@]} > 0 )); do
|
|
local random_index=$(( $(system__random) % ${#copy[@]} ))
|
|
system__print_line "${copy[${random_index}]}"
|
|
unset copy[${random_index}]
|
|
copy=("${copy[@]}")
|
|
done
|
|
}
|
|
|
|
system__random() {
|
|
system__print "${RANDOM}"
|
|
}
|
|
|
|
system__substitute_variable() {
|
|
local string=$1
|
|
local key="\$\{$2\}"
|
|
local value=$3
|
|
printf "%s" "${string//${key}/${value}}"
|
|
}
|
|
|
|
|
|
main__main() {
|
|
configuration__load
|
|
_main__initialise
|
|
local parsed_arguments=0
|
|
_main__parse_arguments "$@"
|
|
shift ${parsed_arguments}
|
|
_main__assert_only_one_argument_left $#
|
|
_main__assert_reporters_are_known
|
|
SBU_BASE_TEST_DIRECTORY=$1
|
|
|
|
if [[ "${SBU_NO_RUN}" != "${SBU_YES}" ]]; then
|
|
runner__run_all_test_files "$1"
|
|
return $?
|
|
fi
|
|
}
|
|
|
|
_main__initialise() {
|
|
database__initialise
|
|
trap _main__release EXIT
|
|
}
|
|
|
|
_main__release() {
|
|
database__release
|
|
}
|
|
|
|
_main__parse_arguments() {
|
|
local argument
|
|
for argument in "$@"; do
|
|
case "${argument}" in
|
|
-a|--api-cheat-sheet)
|
|
_main__print_api_cheat_sheet_and_exit
|
|
;;
|
|
-c=*|--colors=*)
|
|
SBU_USE_COLORS="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-d=*|--random-run=*)
|
|
SBU_RANDOM_RUN="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-h|--help)
|
|
_main__print_full_usage
|
|
exit ${SBU_SUCCESS_STATUS_CODE}
|
|
;;
|
|
-f=*|--file-pattern=*)
|
|
SBU_TEST_FILE_PATTERN="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
--no-run)
|
|
SBU_NO_RUN="${SBU_YES}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-o=*|--output-file=*)
|
|
SBU_JUNIT_REPORTER_OUTPUT_FILE="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-t=*|--test-pattern=*)
|
|
SBU_TEST_FUNCTION_PATTERN="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-r=*|--reporters=*)
|
|
SBU_REPORTERS="${argument#*=}"
|
|
(( parsed_arguments++ ))
|
|
;;
|
|
-*|--*)
|
|
_main__print_illegal_option "${argument}"
|
|
_main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE}
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
_main__assert_reporters_are_known() {
|
|
reporter__for_each_reporter _main__fail_if_reporter_unknown
|
|
}
|
|
|
|
_main__fail_if_reporter_unknown() {
|
|
if ! array__contains "${reporter}" "simple" "dots" "junit"; then
|
|
system__print_line \
|
|
"$(_main__get_script_name): unknown reporter <${reporter}>"
|
|
exit ${SBU_FAILURE_STATUS_CODE}
|
|
fi
|
|
}
|
|
|
|
_main__print_illegal_option() {
|
|
local option="${1%=*}"
|
|
option="${option#-}"
|
|
option="${option#-}"
|
|
system__print_line "$(_main__get_script_name): illegal option -- ${option}"
|
|
}
|
|
|
|
_main__assert_only_one_argument_left() {
|
|
if (( $1 > 1 )); then
|
|
system__print_line "$(_main__get_script_name): only one path is allowed"
|
|
_main__print_usage_and_exit_with_code ${SBU_FAILURE_STATUS_CODE}
|
|
fi
|
|
}
|
|
|
|
_main__get_script_name() {
|
|
basename "${BASH_SOURCE[0]}"
|
|
}
|
|
|
|
_main__print_usage_and_exit_with_code() {
|
|
_main__print_usage
|
|
exit $1
|
|
}
|
|
|
|
_main__print_full_usage() {
|
|
_main__print_usage
|
|
local script="$(_main__get_script_name)"
|
|
system__print_new_line
|
|
system__print_line "\
|
|
[options]
|
|
-a, --api-cheat-sheet
|
|
print api cheat sheet like assertions
|
|
-c, --colors=${SBU_YES} or ${SBU_NO}
|
|
tests output with colors or no
|
|
-d, --random-run=${SBU_YES} or ${SBU_NO}
|
|
tests files and functions randomly run or no
|
|
-f, --file-pattern=<pattern>
|
|
pattern to filter test files
|
|
-h
|
|
print usage
|
|
-o, --output-file=<file>
|
|
output file for JUnit reporter
|
|
-r, --reporters=<reporter1,reporter2>
|
|
comma-separated reporters (simple, dots or junit)
|
|
-t, --test-pattern=<pattern>
|
|
pattern to filter test function in files
|
|
|
|
[examples]
|
|
${script} .
|
|
run all tests in current directory
|
|
${script} -p=*test.sh sources/test
|
|
run all tests files ending with test.sh in sources/test"
|
|
}
|
|
|
|
_main__print_usage() {
|
|
system__print_line "\
|
|
usage: $(_main__get_script_name) [options] path
|
|
run all tests in path"
|
|
}
|
|
|
|
_main__print_api_cheat_sheet_and_exit() {
|
|
system__print_line "\
|
|
[assertions]
|
|
assertion__equal (value, other)
|
|
-> assert that <value> is equal to <other>
|
|
assertion__different (value, other)
|
|
-> assert that <value> is different from <other>
|
|
assertion__string_contains (string, substring)
|
|
-> assert that <string> contains <substring>
|
|
assertion__string_does_not_contain (string, substring)
|
|
-> assert that <string> does not contain <substring>
|
|
assertion__string_empty (string)
|
|
-> assert that <string> is empty
|
|
assertion__string_not_empty (string)
|
|
-> assert that <string> is not empty
|
|
assertion__array_contains (element, array[0], array[1], ...)
|
|
-> assert that the <array> contains the <element>
|
|
assertion__array_does_not_contain (element, array elements...)
|
|
-> assert that the <array> does not contain the <element>
|
|
assertion__successful (command)
|
|
-> assert that the <command> is successful
|
|
assertion__failing (command)
|
|
-> assert that the <command> is failing
|
|
assertion__status_code_is_success (code)
|
|
-> assert that the status <code> is 0
|
|
assertion__status_code_is_failure (code)
|
|
-> assert that the status <code> is not 0
|
|
|
|
[special functions]
|
|
${SBU_GLOBAL_SETUP_FUNCTION_NAME}
|
|
-> Executed before all tests in a file
|
|
${SBU_GLOBAL_TEARDOWN_FUNCTION_NAME}
|
|
-> Executed after all tests in a file
|
|
${SBU_SETUP_FUNCTION_NAME}
|
|
-> Executed before each test in a file
|
|
${SBU_TEARDOWN_FUNCTION_NAME}
|
|
-> Executed after each test in a file
|
|
|
|
[mocks]
|
|
mock__make_function_do_nothing (function_to_mock)
|
|
-> make function do nothing
|
|
mock__make_function_prints (function_to_mock, message)
|
|
-> make function prints a message
|
|
mock__make_function_call (function_to_mock, function_to_call)
|
|
-> make function call another function"
|
|
exit ${SBU_SUCCESS_STATUS_CODE}
|
|
}
|
|
|
|
|
|
main__main "$@"
|