Skip to content

tear_down_after_script is not run when set_up_before_script fails #1318

Description

@carlfriedrich
Q A
OS Linux (openSUSE Tumbleweed, WSL2)
Shell & version bash 5.3.9
bashunit version 0.50.0

Summary

tear_down_after_script is not run when set_up_before_script fails, so anything the setup hook acquired before a failing statement is leaked, with no framework-supported way to release it.

The per-test pair behaves the opposite way: tear_down is run when set_up fails. Both pairs are documented as symmetric counterparts, so the same code shape leaks at file scope and cleans up at test scope.

This matters because set_up_before_script is the only place to acquire a file-scoped resource needed for all tests in the file, and tear_down_after_script is the only place to release it. A setup hook that does more than one thing leaks whatever it already acquired as soon as any later step fails. Every author then has to duplicate the teardown logic into the setup hook's error paths, which is precisely the duplication the hook pair exists to avoid.

Current behavior

When set_up_before_script fails, bashunit reports every test in the file as failed and moves on to the next file. tear_down_after_script is not called, and nothing in the output says that it was skipped — so whatever the hook had already acquired is left behind silently.

How to reproduce

Two files, each acquiring a resource in its setup hook and then failing.

file_hooks.test.sh:

#!/usr/bin/env bash

RESOURCE=""

function set_up_before_script() {
  RESOURCE="$(mktemp /tmp/bu-repro/file-scoped.XXXXXX)"
  echo "acquired ${RESOURCE}"

  false
}

function tear_down_after_script() {
  echo "tear_down_after_script ran"
  rm -f "${RESOURCE}"
}

function test_anything() {
  assert_same 1 1
}

test_hooks.test.sh:

#!/usr/bin/env bash

RESOURCE=""

function set_up() {
  RESOURCE="$(mktemp /tmp/bu-repro/test-scoped.XXXXXX)"
  echo "acquired ${RESOURCE}"

  false
}

function tear_down() {
  echo "tear_down ran"
  rm -f "${RESOURCE}"
}

function test_anything() {
  assert_same 1 1
}

File scope — no tear_down_after_script ran in the output, and the resource survives:

$ bashunit /tmp/bu-repro/file_hooks.test.sh
Running /tmp/bu-repro/file_hooks.test.sh
✗ Error: Set up before script
    acquired /tmp/bu-repro/file-scoped.WdPcUO

Tests:      1 failed, 1 total

$ ls /tmp/bu-repro/file-scoped.*
/tmp/bu-repro/file-scoped.WdPcUO

Test scope — tear_down ran, and the resource is gone:

$ bashunit /tmp/bu-repro/test_hooks.test.sh
Running /tmp/bu-repro/test_hooks.test.sh
✗ Error: Set up
    acquired /tmp/bu-repro/test-scoped.oRcGOd    Output:      acquired /tmp/bu-repro/test-scoped.oRcGOdtear_down ran

Tests:      1 failed, 1 total

$ ls /tmp/bu-repro/test-scoped.*
ls: cannot access '/tmp/bu-repro/test-scoped.*': No such file or directory

Expected behavior

tear_down_after_script runs after a failing set_up_before_script, matching what the per-test pair already does, so a resource acquired before the failure can be released in the one place meant for it.

docs/test-files.md currently describes the hook as running "only once when all the test functions in the test file have been executed", so the current behaviour seems deliberate. I still find it surprising, though:

  • The asymmetry with set_up/tear_down is invisible at the call site. The same code shape behaves differently depending only on which of the two hook pairs it sits in.
  • tear_down_after_script already has to tolerate partially-built state today, because it runs after tests that potentially failed halfway through. Running it after a failed setup adds no new requirement on it.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

Status
In Progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions