mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
test(trace): guard writer mutex via WriteStep/Close race on w.file
This commit is contained in:
1 parent
db3857a913
commit
e21cd44089
1 file changed
+12
-18
@@ -289,16 +289,16 @@ func TestWriteStep_MultipleStepsAppend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bug class: dropping the writer mutex would let concurrent encoder.Encode
|
// Bug class: dropping the writer mutex unsynchronizes the w.file field that
|
||||||
// calls interleave, producing torn JSONL lines. Run under -race: every line
|
// WriteStep reads and Close nils out. Run under -race with WriteStep racing
|
||||||
// must be a complete, parseable Step and all N must arrive.
|
// Close: a missing mutex is a reported data race on w.file, and any step that
|
||||||
func TestWriteStep_ConcurrentWritesAreWellFormed(t *testing.T) {
|
// survives Close must still be a complete, parseable JSONL line.
|
||||||
|
func TestWriteStep_RacesCloseSafely(t *testing.T) {
|
||||||
directory := t.TempDir()
|
directory := t.TempDir()
|
||||||
writer, err := NewWriter(directory)
|
writer, err := NewWriter(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer writer.Close()
|
|
||||||
|
|
||||||
const n = 50
|
const n = 50
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
@@ -306,27 +306,21 @@ func TestWriteStep_ConcurrentWritesAreWellFormed(t *testing.T) {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(index int) {
|
go func(index int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if err := writer.WriteStep(Step{Index: index, Screen: "s"}); err != nil {
|
// Tolerate "writer is closed": the contract under test is that the
|
||||||
t.Errorf("WriteStep: %v", err)
|
// w.file access is synchronized, not that every write lands.
|
||||||
}
|
_ = writer.WriteStep(Step{Index: index, Screen: "s"})
|
||||||
}(index)
|
}(index)
|
||||||
}
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
t.Fatalf("Close: %v", err)
|
||||||
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
lines := readLines(t, filepath.Join(directory, "trace.jsonl"))
|
for _, line := range readLines(t, filepath.Join(directory, "trace.jsonl")) {
|
||||||
if len(lines) != n {
|
|
||||||
t.Fatalf("expected %d lines, got %d", n, len(lines))
|
|
||||||
}
|
|
||||||
seen := make(map[int]bool, n)
|
|
||||||
for _, line := range lines {
|
|
||||||
var got Step
|
var got Step
|
||||||
if err := json.Unmarshal([]byte(line), &got); err != nil {
|
if err := json.Unmarshal([]byte(line), &got); err != nil {
|
||||||
t.Fatalf("torn JSONL line: %v\n%s", err, line)
|
t.Fatalf("torn JSONL line: %v\n%s", err, line)
|
||||||
}
|
}
|
||||||
seen[got.Index] = true
|
|
||||||
}
|
|
||||||
if len(seen) != n {
|
|
||||||
t.Errorf("expected %d distinct steps, got %d", n, len(seen))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user