bats_require_minimum_version 1.5.0

load 'test_helper'
fixtures file_setup_teardown

setup_file() {
  export SETUP_FILE_EXPORT_TEST=true
}

@test "setup_file is run once per file" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/setup_file_once.log"
  bats "$FIXTURE_ROOT/setup_file.bats"
}

@test "teardown_file is run once per file" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/teardown_file_once.log"
  reentrant_run bats "$FIXTURE_ROOT/teardown_file.bats"
  [[ $status -eq 0 ]]
  # output the log for faster debugging
  cat "$LOG"
  # expect to find an entry for the tested file
  grep 'teardown_file.bats' "$LOG"
  # it should be the only entry
  run wc -l <"$LOG"
  [[ $output -eq 1 ]]
}

@test "setup_file is called correctly in multi file suite" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/setup_file_multi_file_suite.log"
  reentrant_run bats "$FIXTURE_ROOT/setup_file.bats" "$FIXTURE_ROOT/no_setup_file.bats" "$FIXTURE_ROOT/setup_file2.bats"
  [[ $status -eq 0 ]]
  run wc -l <"$LOG"
  # each setup_file[2].bats is in the log exactly once!
  [[ $output -eq 2 ]]
  grep setup_file.bats "$LOG"
  grep setup_file2.bats "$LOG"
}

@test "teardown_file is called correctly in multi file suite" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/teardown_file_multi_file_suite.log"
  reentrant_run bats "$FIXTURE_ROOT/teardown_file.bats" "$FIXTURE_ROOT/no_teardown_file.bats" "$FIXTURE_ROOT/teardown_file2.bats"
  [[ $status -eq 0 ]]
  run wc -l <"$LOG"
  # each teardown_file[2].bats is in the log exactly once!
  [[ $output -eq 2 ]]
  grep teardown_file.bats "$LOG"
  grep teardown_file2.bats "$LOG"

}

@test "setup_file failure aborts tests for this file" {
  # this might need to mark them as skipped as the test count is already determined at this point
  reentrant_run bats "$FIXTURE_ROOT/setup_file_failed.bats"
  echo "$output"
  [[ "${lines[0]}" == "1..2" ]]
  [[ "${lines[1]}" == "not ok 1 setup_file failed" ]]
  [[ "${lines[2]}" == "# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/setup_file_failed.bats, line 2)" ]]
  [[ "${lines[3]}" == "#   \`false' failed" ]]
  [[ "${lines[4]}" == "# bats warning: Executed 1 instead of expected 2 tests" ]] # this warning is expected
  # to appease the count validator, we would have to reduce the expected number of tests (retroactively?) or
  # output even those tests that should be skipped due to a failed setup_file.
  # Since we are already in a failure mode, the additional error does not hurt and is less verbose than
  # printing all the failed/skipped tests due to the setup failure.
}

@test "teardown_file failure fails at least one test from the file" {
  reentrant_run bats "$FIXTURE_ROOT/teardown_file_failed.bats"
  [[ $status -ne 0 ]]
  echo "$output"
  [[ "${lines[0]}" == "1..1" ]]
  [[ "${lines[1]}" == "ok 1 test" ]]
  [[ "${lines[2]}" == "not ok 2 teardown_file failed" ]]
  [[ "${lines[3]}" == "# (from function \`teardown_file' in test file $RELATIVE_FIXTURE_ROOT/teardown_file_failed.bats, line 2)" ]]
  [[ "${lines[4]}" == "#   \`false' failed" ]]
  [[ "${lines[5]}" == "# bats warning: Executed 2 instead of expected 1 tests" ]] # for now this warning is expected
  # for a failed teardown_file not to change the number of tests being reported, we would have to alter at least one previous test result report
  # this would require arbitrary amounts of buffering so we simply add our own line with a fake test number
  # tripping the count validator won't change the overall result, as we already are in a failure mode
}

@test "teardown_file runs even if any test in the file failed" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/teardown_file_failed.log"
  reentrant_run bats "$FIXTURE_ROOT/teardown_file_after_failing_test.bats"
  [[ $status -ne 0 ]]
  grep teardown_file_after_failing_test.bats "$LOG"
  echo "$output"
  [[ $output == "1..1
not ok 1 failing test
# (in test file $RELATIVE_FIXTURE_ROOT/teardown_file_after_failing_test.bats, line 6)
#   \`false' failed" ]]
}

@test "teardown_file should run even after user abort via CTRL-C" {
  if [[ "${BATS_NUMBER_OF_PARALLEL_JOBS:-1}" -gt 1 ]]; then
    skip "Aborts don't work in parallel mode"
  fi
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/teardown_file_abort.log"
  # guarantee that background processes get their own process group -> pid=pgid
  set -m
  SECONDS=0
  # run testsubprocess in background to not avoid blocking this test
  bats "$FIXTURE_ROOT/teardown_file_after_long_test.bats" &
  SUBPROCESS_PID=$!
  # wait until we enter the test
  sleep 2
  # fake sending SIGINT (CTRL-C) to the process group of the background subprocess
  kill -SIGINT -- -$SUBPROCESS_PID
  wait # for the test to finish either way (SIGINT or normal execution)
  echo "Waited: $SECONDS seconds"
  [[ $SECONDS -lt 10 ]] # make sure we really cut it short with SIGINT
  # check that teardown_file ran and created the log file
  [[ -f "$LOG" ]]
  grep teardown_file_after_long_test.bats "$LOG"
  # but the test must not have run to the end!
  run ! grep "test finished successfully" "$LOG"
}

@test "setup_file runs even if all tests in the file are skipped" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/setup_file_skipped.log"
  reentrant_run bats "$FIXTURE_ROOT/setup_file_even_if_all_tests_are_skipped.bats"
  [[ -f "$LOG" ]]
  grep setup_file_even_if_all_tests_are_skipped.bats "$LOG"
}

@test "teardown_file runs even if all tests in the file are skipped" {
  # shellcheck disable=SC2031,SC2030
  export LOG="$BATS_TEST_TMPDIR/teardown_file_skipped.log"
  reentrant_run bats "$FIXTURE_ROOT/teardown_file_even_if_all_tests_are_skipped.bats"
  [[ $status -eq 0 ]]
  [[ -f "$LOG" ]]
  grep teardown_file_even_if_all_tests_are_skipped.bats "$LOG"
}

@test "setup_file must not leak context between tests in the same suite" {
  # example: BATS_ROOT was unset in one test but used in others, therefore, the suite failed
  # Simulate leaking env var from first to second test by: export SETUP_FILE_VAR="LEAK!"
  reentrant_run bats "$FIXTURE_ROOT/setup_file_does_not_leak_env.bats" "$FIXTURE_ROOT/setup_file_does_not_leak_env2.bats"
  echo "$output"
  [[ $status -eq 0 ]]
}

@test "teardown_file must not leak context between tests in the same suite" {
  # example: BATS_ROOT was unset in one test but used in others, therefore, the suite failed
  reentrant_run bats "$FIXTURE_ROOT/teardown_file_does_not_leak.bats" "$FIXTURE_ROOT/teardown_file_does_not_leak2.bats"
  echo "$output"
  [[ $status -eq 0 ]]
  [[ $output == "1..2
ok 1 test
ok 2 must not see variable from first run" ]]
}

@test "halfway setup_file errors are caught and reported" {
  reentrant_run bats "$FIXTURE_ROOT/setup_file_halfway_error.bats"
  [ $status -ne 0 ]
  echo "$output"
  [ "${lines[0]}" == "1..1" ]
  [ "${lines[1]}" == "not ok 1 setup_file failed" ]
  [ "${lines[2]}" == "# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/setup_file_halfway_error.bats, line 3)" ]
  [ "${lines[3]}" == "#   \`false' failed" ]
}

@test "halfway teardown_file errors are ignored" {
  reentrant_run -0 bats "$FIXTURE_ROOT/teardown_file_halfway_error.bats"
  [ "${lines[0]}" == "1..1" ]
  [ "${lines[1]}" == "ok 1 empty" ]
  [ "${#lines[@]}" -eq 2 ]

}

@test "variables exported in setup_file are visible in tests" {
  [[ $SETUP_FILE_EXPORT_TEST == "true" ]]
}

@test "Don't run setup_file for files without tests" {
  # shellcheck disable=SC2031
  export LOG="$BATS_TEST_TMPDIR/setup_file.log"
  # only select the test from no_setup_file
  reentrant_run bats -f test "$FIXTURE_ROOT/setup_file.bats" "$FIXTURE_ROOT/no_setup_file.bats"

  [ ! -f "$LOG" ]             # setup_file must not have been executed!
  [ "${lines[0]}" == '1..1' ] # but at least one test should have been run
}

@test "Failure in setup_file and teardown_file still prints error message" {
  reentrant_run ! bats "$FIXTURE_ROOT/error_in_setup_and_teardown_file.bats"
  [ "${lines[0]}" == '1..1' ]
  [ "${lines[1]}" == 'not ok 1 setup_file failed' ]
  [ "${lines[2]}" == "# (from function \`setup_file' in test file $RELATIVE_FIXTURE_ROOT/error_in_setup_and_teardown_file.bats, line 2)" ]
  [ "${lines[3]}" == "#   \`false' failed" ]
  [ "${#lines[@]}" -eq 4 ]
}
