Skip to content
internal/testutil
Edit
On this page

Package testutil provides shared test helpers for creating temporary git repos, used across internal/*_test.go packages to avoid duplicating boilerplate.

#internal/testutil

#internal/testutil

Package testutil provides shared test helpers for creating temporary git repos, used across internal/*_test.go packages to avoid duplicating boilerplate.

This package intentionally does NOT import internal/repo (or any other internal/* package) to avoid import cycles -- internal/git is at the bottom of the dependency graph and its tests need these helpers too.

#PathologicalCommName

Go go
const PathologicalCommName = "sg (weird) name"

PathologicalCommName is an executable basename that reproduces the /proc//stat parsing trap: the kernel writes the comm field unescaped between parentheses, so a name containing spaces and parentheses shifts every subsequent field for any parser that splits on whitespace. It is exactly 15 bytes, the largest comm Linux stores without truncation, so the spawned process's comm is this string verbatim.

#IdentityName

Go go
const IdentityName  = "Test"

The deterministic identity every repo these helpers build commits under.

#IdentityEmail

Go go
const IdentityEmail = "test@test.com"

#Process

Go go
type Process struct

Process is a child process spawned by a test, used by suites that need a PID whose liveness they control.

#SubmoduleRepo

Go go
type SubmoduleRepo struct

SubmoduleRepo holds paths for a test repo with a submodule.

#Git

Go go
func Git(t *testing.T, dir string, args ...string) string

Git runs git in dir, fails the test on a nonzero exit, and returns stdout and stderr interleaved (combined output) with surrounding whitespace trimmed. Because the result can carry stderr, do not feed it back to git; use GitOut for a value that has to be exact.

#GitRaw

Go go
func GitRaw(t *testing.T, dir string, args ...string) string

GitRaw is Git without the trim: stdout and stderr interleaved, exactly as git wrote them. Use it wherever the trailing newline (or its absence) is part of what the test asserts.

#GitOut

Go go
func GitOut(t *testing.T, dir string, args ...string) string

GitOut runs git in dir, fails the test on a nonzero exit, and returns stdout verbatim -- stderr never enters the result, so it is the runner to use for a value the test hands back to git. On failure the fatal message carries git's stderr, which is where the explanation lives.

#GitTry

Go go
func GitTry(t *testing.T, dir string, args ...string) (string, int)

GitTry runs git in dir and returns its combined output (stdout and stderr interleaved, verbatim) and exit code without failing the test: fixtures routinely depend on commands that exit nonzero by design (a conflicting merge, a refused amend), and the refusal itself is on stderr. Only a failure to start the process at all fails the test.

#GitTryEnv

Go go
func GitTryEnv(t *testing.T, dir string, extraEnv []string, args ...string) (string, int)

GitTryEnv is GitTry with extra "KEY=value" entries appended to the inherited environment -- needed for calls that would otherwise open an editor. Same combined-output contract as GitTry.

#GitStdin

Go go
func GitStdin(t *testing.T, dir, stdin string, args ...string) string

GitStdin runs git in dir with stdin data, fails the test on a nonzero exit, and returns trimmed stdout -- stderr is inherited and lands in the test's own output rather than in the result, so the result is safe to feed back to git.

#GitTryOut

Go go
func GitTryOut(t *testing.T, dir string, args ...string) (string, bool)

GitTryOut runs git in dir and returns its verbatim stdout -- stderr is discarded -- plus whether git exited zero. Use it to probe for something that may legitimately be absent.

#WriteFile

Go go
func WriteFile(t *testing.T, dir, rel, content string)

WriteFile writes content at a repo-relative path, creating parent directories. It is the one spelling for "put this file in the repo".

#WriteFileAt

Go go
func WriteFileAt(t *testing.T, path, content string)

WriteFileAt is WriteFile for a path the caller has already joined.

#TreePaths

Go go
func TreePaths(t *testing.T, dir, rev string) []string

TreePaths returns every path in a revision's tree, repo-relative and recursive. --full-tree keeps the answer independent of the directory git is invoked from. An empty tree yields nil.

#SplitLines

Go go
func SplitLines(s string) []string

SplitLines splits s on newlines, dropping empty lines. An empty string yields nil, so a caller can range over the result without a length check.

#Contains

Go go
func Contains(haystack []string, needle string) bool

Contains reports whether haystack holds needle.

#Rev

Go go
func Rev(t *testing.T, dir, rev string) string

Rev resolves a revision to its full SHA, failing the test when it does not resolve. Read from stdout only, like RevTry: a SHA a test compares or hands back to git must never have a stderr line mixed into it.

#RevTry

Go go
func RevTry(t *testing.T, dir, rev string) string

RevTry resolves a revision to its full SHA, returning "" when the revision does not exist (an unborn branch, a ref another test has yet to create).

#Parents

Go go
func Parents(t *testing.T, dir, ref string) []string

Parents returns the parent SHAs of a commit, in order, read from stdout only for the same reason as Rev. A commit that cannot be read at all fails the test; a root commit yields an empty slice.

#Show

Go go
func Show(t *testing.T, dir, rev, path string) (string, bool)

Show returns the verbatim content of a repo-relative path at rev, and whether that path exists in that revision.

#MustShow

Go go
func MustShow(t *testing.T, dir, rev, path string) string

MustShow is Show for a path the test requires to be present.

#GitDir

Go go
func GitDir(t *testing.T, dir string) string

GitDir is git's own answer to where this checkout's git directory is, rather than the

/.git a fixture can usually assume. A SUBMODULE checkout's .git is a FILE pointing into the parent's modules directory, so every helper that reads a state file by path goes through here.

#MergeStateGone

Go go
func MergeStateGone(t *testing.T, dir string) bool

MergeStateGone reports whether git considers a merge concluded, i.e. whether MERGE_HEAD is absent from the git directory.

#AssertMergeHead

Go go
func AssertMergeHead(t *testing.T, dir, want, context string)

AssertMergeHead fails unless the repository is mid-merge with MERGE_HEAD naming want. context names the moment being asserted, so a failure says which step of a fixture or which post-condition broke.

#FileExists

Go go
func FileExists(path string) bool

FileExists reports whether path exists.

#SpawnSleeper

Go go
func SpawnSleeper(t *testing.T) *Process

SpawnSleeper starts a long-lived child process and returns a handle to it. The process is killed when the test ends unless the test kills it earlier.

#SpawnPathologicalNameSleeper

Go go
func SpawnPathologicalNameSleeper(t *testing.T) *Process

SpawnPathologicalNameSleeper starts a long-lived child process whose executable basename -- and therefore its /proc comm field -- is PathologicalCommName.

#InitRepoWithSubmodule

Go go
func InitRepoWithSubmodule(t *testing.T) SubmoduleRepo

InitRepoWithSubmodule creates a test repo containing one submodule.

#InitRepoWithTwoSubmodules

Go go
func InitRepoWithTwoSubmodules(t *testing.T) (SubmoduleRepo, SubmoduleRepo)

InitRepoWithTwoSubmodules creates a test repo containing two submodules.

#InitRepo

Go go
func InitRepo(t *testing.T, safegitInit func(ctx context.Context, gitDir string) error) (repoDir, gitDir, safegitDir string)

InitRepo creates a temp git repo with a seed file ("seed.txt") and initial commit, runs the provided safegitInit function to set up .git/safegit/, and returns (repoDir, gitDir, safegitDir).

Callers pass repo.Init as safegitInit:

dir, gitDir, sgDir := testutil.InitRepo(t, repo.Init)

#InitUnbornRepo

Go go
func InitUnbornRepo(t *testing.T) (repoDir, gitDir string)

InitUnbornRepo creates a temp git repo with NO commits at all, so HEAD names a branch that does not exist yet -- the state a repository is in between git init and its first commit. Returns (repoDir, gitDir).

It is the fixture for everything that has to keep working before there is a HEAD to resolve: git rev-parse HEAD fails there, and so does every git command that takes HEAD as a treeish.

#InitBareRepo

Go go
func InitBareRepo(t *testing.T) string

InitBareRepo creates a temp git repo with an allow-empty initial commit (no seed file, no safegit init). Returns the repo directory. Suitable for packages like git and index that don't need safegit infrastructure.

#Chdir

Go go
func Chdir(t *testing.T, dir string)

Chdir changes into dir for the duration of the test, restoring the original working directory on cleanup. A failed restore fails the test: leaving a suite in the wrong directory corrupts every test after it.

#Process.Kill

Go go
func (p *Process) Kill(t *testing.T)

Kill terminates the process and reaps it, so the PID is genuinely dead (not a zombie) when Kill returns. It is safe to call more than once.

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • rlsbl Release orchestration and project scaffolding CLI that bumps versions, validates a structured JSONL changelog, tags only the commit CI verified, and publishes to npm, PyPI, Go and more
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search