@@ -48,6 +48,10 @@ function bashunit::main::cmd_test() {
4848 --detailed)
4949 export BASHUNIT_SIMPLE_OUTPUT=false
5050 ;;
51+ --output)
52+ export BASHUNIT_OUTPUT_FORMAT=" $2 "
53+ shift
54+ ;;
5155 --debug)
5256 local output_file=" ${2:- } "
5357 if [[ -n " $output_file " && " ${output_file: 0: 1} " != " -" ]]; then
@@ -62,9 +66,17 @@ function bashunit::main::cmd_test() {
6266 -p | --parallel)
6367 export BASHUNIT_PARALLEL_RUN=true
6468 ;;
69+ -j | --jobs)
70+ export BASHUNIT_PARALLEL_RUN=true
71+ export BASHUNIT_PARALLEL_JOBS=" $2 "
72+ shift
73+ ;;
6574 --no-parallel)
6675 export BASHUNIT_PARALLEL_RUN=false
6776 ;;
77+ -w | --watch)
78+ export BASHUNIT_WATCH_MODE=true
79+ ;;
6880 -e | --env | --boot)
6981 # Support: --env "bootstrap.sh arg1 arg2"
7082 local boot_file=" ${2%% * } "
@@ -265,12 +277,19 @@ function bashunit::main::cmd_test() {
265277 export BASHUNIT_COVERAGE=false
266278 bashunit::main::exec_assert " $assert_fn " ${args+" ${args[@]} " }
267279 else
268- # Bash 3.0 compatible: only pass args if we have files
269- # (local args without =() creates a scalar, not an empty array)
270- if [[ " $args_count " -gt 0 ]] ; then
271- bashunit::main::exec_tests " $filter " " $tag_filter " " $exclude_tag_filter " " $ {args[@]}"
280+ if [[ " ${BASHUNIT_WATCH_MODE :- false} " == true ]] ; then
281+ bashunit::main::watch_loop \
282+ " $filter " " $tag_filter " " $exclude_tag_filter " \
283+ ${args+ " $ {args[@]}" }
272284 else
273- bashunit::main::exec_tests " $filter " " $tag_filter " " $exclude_tag_filter "
285+ if [[ " $args_count " -gt 0 ]]; then
286+ bashunit::main::exec_tests \
287+ " $filter " " $tag_filter " " $exclude_tag_filter " \
288+ " ${args[@]} "
289+ else
290+ bashunit::main::exec_tests \
291+ " $filter " " $tag_filter " " $exclude_tag_filter "
292+ fi
274293 fi
275294 fi
276295}
@@ -491,6 +510,79 @@ function bashunit::main::cmd_assert() {
491510 exit $?
492511}
493512
513+ # ############################
514+ # Watch mode
515+ # ############################
516+ function bashunit::main::watch_get_checksum() {
517+ local IFS=$' \t\n '
518+ local -a paths=(" $@ " )
519+
520+ local file checksum=" "
521+ for file in " ${paths[@]+" ${paths[@]} " } " ; do
522+ if [[ -d " $file " ]]; then
523+ local found
524+ found=$( find " $file " -name ' *.sh' -type f \
525+ -exec stat -f ' %m %N' {} + 2> /dev/null ||
526+ find " $file " -name ' *.sh' -type f \
527+ -exec stat -c ' %Y %n' {} + 2> /dev/null) || true
528+ checksum=" ${checksum}${found} "
529+ elif [[ -f " $file " ]]; then
530+ local mtime
531+ mtime=$( stat -f ' %m' " $file " 2> /dev/null ||
532+ stat -c ' %Y' " $file " 2> /dev/null) || true
533+ checksum=" ${checksum}${mtime} ${file} "
534+ fi
535+ done
536+ echo " $checksum "
537+ }
538+
539+ function bashunit::main::watch_loop() {
540+ local filter=" $1 "
541+ local tag_filter=" ${2:- } "
542+ local exclude_tag_filter=" ${3:- } "
543+ shift 3
544+
545+ local IFS=$' \t\n '
546+ local -a watch_paths=(" $@ " )
547+ [[ -d " src" ]] && watch_paths[${# watch_paths[@]} ]=" src"
548+
549+ trap ' printf "\n%sWatch mode stopped.%s\n" \
550+ "${_BASHUNIT_COLOR_SKIPPED}" "${_BASHUNIT_COLOR_DEFAULT}"; \
551+ exit 0' INT
552+
553+ local last_checksum=" "
554+ while true ; do
555+ local current_checksum
556+ current_checksum=$( bashunit::main::watch_get_checksum \
557+ " ${watch_paths[@]} " )
558+
559+ if [[ " $current_checksum " != " $last_checksum " ]]; then
560+ last_checksum=" $current_checksum "
561+ printf ' \033[2J\033[H'
562+ printf " %s[watch] Running tests...%s\n\n" \
563+ " ${_BASHUNIT_COLOR_SKIPPED} " \
564+ " ${_BASHUNIT_COLOR_DEFAULT} "
565+
566+ (
567+ if [[ $# -gt 0 ]]; then
568+ bashunit::main::exec_tests \
569+ " $filter " " $tag_filter " \
570+ " $exclude_tag_filter " " $@ "
571+ else
572+ bashunit::main::exec_tests \
573+ " $filter " " $tag_filter " \
574+ " $exclude_tag_filter "
575+ fi
576+ ) || true
577+
578+ printf " \n%s[watch] Waiting for changes...%s\n" \
579+ " ${_BASHUNIT_COLOR_SKIPPED} " \
580+ " ${_BASHUNIT_COLOR_DEFAULT} "
581+ fi
582+ sleep 1
583+ done
584+ }
585+
494586# ############################
495587# Test execution
496588# ############################
@@ -533,7 +625,11 @@ function bashunit::main::exec_tests() {
533625 bashunit::parallel::init
534626 fi
535627
536- bashunit::console_header::print_version_with_env " $filter " " ${test_files[@]} "
628+ if bashunit::env::is_tap_output_enabled; then
629+ printf " TAP version 13\n"
630+ else
631+ bashunit::console_header::print_version_with_env " $filter " " ${test_files[@]} "
632+ fi
537633
538634 if bashunit::env::is_verbose_enabled; then
539635 if bashunit::env::is_simple_output_enabled; then
@@ -559,9 +655,11 @@ function bashunit::main::exec_tests() {
559655 printf " \r%sStop on failure enabled...%s\n" " ${_BASHUNIT_COLOR_SKIPPED} " " ${_BASHUNIT_COLOR_DEFAULT} "
560656 fi
561657
562- bashunit::console_results::print_failing_tests_and_reset
563- bashunit::console_results::print_incomplete_tests_and_reset
564- bashunit::console_results::print_skipped_tests_and_reset
658+ if ! bashunit::env::is_tap_output_enabled; then
659+ bashunit::console_results::print_failing_tests_and_reset
660+ bashunit::console_results::print_incomplete_tests_and_reset
661+ bashunit::console_results::print_skipped_tests_and_reset
662+ fi
565663 bashunit::console_results::render_result
566664 exit_code=$?
567665
0 commit comments