From 92531af9e7962bd5455234ac23354a23d8a11f86 Mon Sep 17 00:00:00 2001 From: "Bland, Mike" Date: Tue, 5 Dec 2017 07:28:40 -0600 Subject: [PATCH] bats/background-process: Close file descriptor 3 Closes #226. From the comment within `run_in_background`: Bats duplicates standard output as file descriptor 3 so that output from its framework functions isn't captured along with any output from the code under test. If the code under test contains a `sleep` or other blocking operation, this file descriptor will be held open until the process becomes unblocked, preventing Bats from exiting. Hence, we explicitly close file descriptor 3. Any other code running under Bats that opens a background process should close this file descriptor as well. See: https://github.com/sstephenson/bats/issues/80 Much thanks to @marascio for discovering and researching the problem, and proposing the actual fix. --- lib/bats/background-process | 26 ++++++++++++++++++++++++-- tests/bats-background-process.bats | 24 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/lib/bats/background-process b/lib/bats/background-process index e39241c..0af678e 100644 --- a/lib/bats/background-process +++ b/lib/bats/background-process @@ -5,7 +5,7 @@ # These functions make it easier to write Bats test cases that validate the # behavior of long-running processes such as servers: # -# @test '$SUITE: my-server should start successfully' { +# @test "$SUITE: my-server should start successfully" { # skip_if_missing_background_utilities # run_in_background 'my-server' # wait_for_background_output 'my-server is now ready' @@ -13,6 +13,17 @@ # assert_... # } # +# @test "$SUITE: my-test-script should start successfully" { +# skip_if_missing_background_utilities +# run_in_test_script_in_background 'my-test-script' \ +# 'sleep 1' \ +# 'echo "Hello, World!"' \ +# 'sleep 10' +# wait_for_background_output 'Hello, World!' +# stop_background_run +# assert_... +# } +# # Call `skip_if_missing_background_utilities` at the beginning of each test case # to skip it if the host system lacks any of the process management utilities # required by the functions from this file. @@ -64,7 +75,18 @@ run_in_background() { export BATS_BACKGROUND_RUN_OUTPUT BATS_BACKGROUND_RUN_OUTPUT="$BATS_TEST_ROOTDIR/background-run-output.txt" printf '' >"$BATS_BACKGROUND_RUN_OUTPUT" - "$@" >"$BATS_BACKGROUND_RUN_OUTPUT" 2>&1 & + + # Bats duplicates standard output as file descriptor 3 so that output from its + # framework functions isn't captured along with any output from the code under + # test. If the code under test contains a `sleep` or other blocking operation, + # this file descriptor will be held open until the process becomes unblocked, + # preventing Bats from exiting. Hence, we explicitly close file descriptor 3. + # + # Any other code running under Bats that opens a background process should + # close this file descriptor as well. See: + # - https://github.com/sstephenson/bats/issues/80 + # - https://github.com/mbland/go-script-bash/issues/226 + "$@" >"$BATS_BACKGROUND_RUN_OUTPUT" 2>&1 3>&- & export BATS_BACKGROUND_RUN_PID="$!" restore_bats_shell_options } diff --git a/tests/bats-background-process.bats b/tests/bats-background-process.bats index a0fc749..328731b 100644 --- a/tests/bats-background-process.bats +++ b/tests/bats-background-process.bats @@ -163,3 +163,27 @@ kill_background_test_script() { stop_background_run 'HUP' assert_status "$((128 + $(kill -l HUP)))" } + +@test "$SUITE: bash -c command passes under run_in_background" { + skip_if_missing_background_utilities + mkdir "$BATS_TEST_ROOTDIR" + + run_in_background bash -c 'echo "Oh hai, Mark."; sleep 60' + run wait_for_background_output 'Oh hai, Mark.' '0.25' + assert_success +} + +@test "$SUITE: bash -c command fails under run_in_background without hanging" { + skip_if_missing_background_utilities + mkdir "$BATS_TEST_ROOTDIR" + + run_in_background bash -c 'echo "Oh hai, Mark."; sleep 60' + run wait_for_background_output 'I did not do it! I did not!' '0.25' + assert_failure \ + 'Output did not match regular expression:' \ + " 'I did not do it! I did not!'" \ + '' \ + 'OUTPUT:' \ + '------' \ + 'Oh hai, Mark.' +}