Skip to content

Conversation

@sin-ack
Copy link
Contributor

@sin-ack sin-ack commented Oct 22, 2025

In bazel test, in macOS/Linux, all test rules are run under a script called test-setup.sh. This script does some extra work related to tests, and one of its tasks is cleaning up the remaining processes in the process group when the main test process exits.

When the user cancels a bazel test (or build for that matter) invocation using Ctrl+C, Bazel sends SIGTERM to all running processes. SIGTERM is then forwarded from test-setup.sh to all processes in the process group, and the script then waits for the test executable to exit. However, these two features interact badly when the test executable is Bash: the default behavior for SIGTERM in Bash is to exit immediately (for SIGINT and SIGQUIT Bash does something called "cooperative exit" where it waits for subprocesses to exit first before exiting itself). Because the Bash process is the test executable that is being waited on, this means test-setup.sh prematurely detects the entire test as having exited and cleans up by sending SIGKILL to the remaining processes in the process group, which means any cleanup actions by the C# process are preempted.

To ensure that test-setup.sh only performs cleanup when the test itself exits, we use exec so that the Bash process is replaced with dotnet exec which will only exit after the C# process itself handles the signal. This is fine since the dotnet exec command is the last command in the template.

I don't know how all of this behaves on Windows so I didn't touch the Windows template.

In `bazel test`, all test rules are run under a script called
`test-setup.sh`. This script does some extra work related to tests, and
one of its tasks is cleaning up the remaining processes in the process
group when the main test process exits.

When the user cancels a `bazel test` (or `build` for that matter)
invocation using Ctrl+C, Bazel sends SIGTERM to all running processes.
SIGTERM is then forwarded from `test-setup.sh` to all processes in the
process group, and the script then waits for the test executable to
exit. However, these two features interact badly when the test
executable is Bash: the default behavior for SIGTERM in Bash is to exit
immediately (for SIGINT and SIGQUIT Bash does something called
"cooperative exit" where it waits for subprocesses to exit first before
exiting itself). Because the Bash process is the test executable that is
being waited on, this means `test-setup.sh` prematurely detects the
entire test as having exited and cleans up by sending SIGKILL to the
remaining processes in the process group, which means any cleanup
actions by the C# process are preempted.

To ensure that `test-setup.sh` only performs cleanup when the test
itself exits, we use `exec` so that the Bash process is replaced with
`dotnet exec` which will only exit after the C# process itself handles
the signal. This is fine since the `dotnet exec` command is the last
command in the template.

I don't know how all of this behaves on Windows so I didn't touch the
Windows template.
@sin-ack sin-ack requested a review from purkhusid as a code owner October 22, 2025 14:06
@sin-ack
Copy link
Contributor Author

sin-ack commented Oct 22, 2025

Test failure is flaky runner it seems.

Copy link
Collaborator

@purkhusid purkhusid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks for the contribution!

@purkhusid purkhusid merged commit 174f452 into bazel-contrib:master Oct 22, 2025
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants