Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions internal/mutate/tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@ func (t *Tree) in(dir string, run func(at, tmp string)) error {
if err != nil {
return err
}
tmp := filepath.Join(t.dir, "tmp")
if err := os.MkdirAll(tmp, 0o700); err != nil {
// Not under the tree's own directory: a test's t.TempDir() is its name and two levels more
// under this, and a socket there, from a test with a long name, was past the 108 bytes a unix
// socket's path may have - so the test failed with nothing broken, and its lines went unasked.
tmp, err := os.MkdirTemp("", "m")
if err != nil {
return err
}
run(filepath.Join(c.dst, rel), tmp)
Expand Down
38 changes: 38 additions & 0 deletions internal/mutate/tree_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package mutate

import (
"io/fs"
"net"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -62,6 +63,43 @@ func TestATreeIsTheModuleAsGitSeesItPutBackAfterEachRun(t *testing.T) {
}
}

// A run's temporary directory is empty, its own and gone once it returns, and short enough that
// a test with a long name binds a socket in its t.TempDir() there, however deep the tree is.
func TestARunsTemporaryDirectoryTakesASocketFromATestWithALongName(t *testing.T) {
root := gitModule(t)
tree := NewTree(filepath.Join(t.TempDir(), "trees", "12", "a-tree-as-deep-as-a-runner-makes-it"))
var tmps []string
for range 2 {
err := tree.in(filepath.Join(root, "p"), func(_, tmp string) {
tmps = append(tmps, tmp)
if entries, err := os.ReadDir(tmp); err != nil || len(entries) != 0 {
t.Errorf("the run's temporary directory holds %v (%v), not nothing", entries, err)
}
// As t.TempDir() makes one: the test's name and a number, then a directory per call.
dir := filepath.Join(tmp, "TestTheKeepCommandHoldsALeaseForAsLongAsItsCommandRuns1008086323", "001")
if err := os.MkdirAll(dir, 0o700); err != nil {
t.Fatal(err)
}
l, err := net.Listen("unix", filepath.Join(dir, "identity.sock"))
if err != nil {
t.Fatalf("a socket in a long-named test's directory: %v", err)
}
_ = l.Close() // only that it bound is asked
})
if err != nil {
t.Fatal(err)
}
}
if tmps[0] == tmps[1] {
t.Errorf("two runs shared the temporary directory %s", tmps[0])
}
for _, tmp := range tmps {
if _, err := os.Stat(tmp); err == nil {
t.Errorf("%s outlived its run", tmp)
}
}
}

// gitModule is a module in a git work tree: files tracked, one neither tracked nor ignored, an
// ignored file in its package p, and a directory it ignores.
func gitModule(t *testing.T) string {
Expand Down
4 changes: 3 additions & 1 deletion site/src/content/docs/mutate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ edits ran side by side. Instead, every test process runs in a private copy of th
also gets an empty temporary directory of its own as `TMPDIR`, removed after the run. Without it,
two runs of the same tests could share a path under `os.TempDir()`, such as a cache folder named
by the current time. One of them would fail with nothing broken, and the edits only that test
reaches would be let through.
reaches would be let through. The directory is made directly under your own `TMPDIR`, not inside
the copy: a test with a long name that binds a unix socket in its `t.TempDir()` stays under the
108 bytes such a path may have.

The copy holds what git sees: tracked files and files git doesn't ignore. Directories git ignores
(`node_modules`, a build's output) and repositories nested in the module are linked rather than
Expand Down
Loading