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