testsupport

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package testsupport holds helpers shared across the module's test suites. It carries no production call sites — only *_test.go files import it — so it stays out of the production binary's build graph.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HardenGitTestEnv

func HardenGitTestEnv()

HardenGitTestEnv prepares the process environment so test fixtures that shell out to git are insulated from the invoking context and from concurrency hazards. Call it once from a package's TestMain — alongside the GIT identity seeding — before m.Run(). It:

  • Unsets the git locator vars (GIT_DIR/GIT_INDEX_FILE/...) a parent git hook exports, which would otherwise steer fixture git commands into the parent repo's gitdir/index (G-0250).
  • Forces gc.auto=0 / gc.autoDetach=false for every child git via GIT_CONFIG_COUNT, so background auto-gc cannot race fixture commits or TempDir cleanup under load (G-0251).

It is safe to call when the locator vars are already unset (os.Unsetenv on an absent key is a no-op) and safe to call from any package whether or not it shells out to git. os.Setenv/Unsetenv (not t.Setenv) because TestMain has no *testing.T and the changes must apply process-wide for the test binary's lifetime — the same reason the identity vars use os.Setenv (t.Setenv panics under t.Parallel). The enforcement chokepoint (policies.PolicyGitTestEnvHardened) requires this call in every exec-bearing internal/* package's TestMain.

func WriteExecutable added in v0.31.0

func WriteExecutable(path string, data []byte) error

WriteExecutable writes an executable stand-in — a shell script posing as a binary — so that a concurrently forking test process cannot make the subsequent exec of it fail with ETXTBSY ("text file busy").

The hazard is a property of the writing process, not of the file. A plain os.WriteFile holds a writable descriptor on the new file between its open and its close. A fork anywhere else in the process during that window gives the child a copy of that descriptor, and the child keeps it until its own execve closes it. While any descriptor holds a file open for writing, execve on that file fails with ETXTBSY — so a stand-in written by one parallel test can be rejected when a different test execs it. In a package whose tests spawn subprocesses in parallel, the colliding forks are the suite's own (G-0491).

Holding syscall.ForkLock for reading across the write closes the window: syscall's forkExec takes the same lock for writing, so no fork this process starts can overlap the descriptor's lifetime. Measured under deliberate fork pressure, the guarded write reports zero ETXTBSY across 9,600 cycles at baseline pressure and 19,200 more at four times it; the unguarded write reports 12% and 17% respectively.

Writing to a temp name and renaming into place does not help and is not what this does: ETXTBSY is enforced against the inode, and a rename carries the same inode to the new path, leaking descriptor and all. Measured, it is indistinguishable from the unguarded write.

Sustained forking does not starve the write: sync.RWMutex wakes every blocked reader when a writer unlocks, so a write waiting on forks in flight is admitted between them. (Linux and the BSDs additionally reference-count the lock and carry an explicit branch admitting waiting readers; darwin and aix use a plain Lock/Unlock. The property holds on both.)

Point this only at a regular file. Go's own ForkLock documentation lists Open as the operation *not* to hold the lock across, because an open can block for an unbounded time; a target that blocks — a FIFO with no reader, a hung mount — stalls every fork in the process for as long as it blocks. The bound here is that callers write a small regular file into their own t.TempDir.

On Windows ForkLock exists but goes unused, and there is no ETXTBSY either, so the helper is simply a plain write.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL