Files
kjol/tools/tsgo/internal/tracing/tracing_test.go
2026-07-09 16:50:43 -04:00

141 lines
4.3 KiB
Go

package tracing
import (
"io/fs"
"testing"
"testing/fstest"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/vfs/vfstest"
"gotest.tools/v3/assert"
)
func TestConcurrentDurationEventsUseSeparateThreadIDs(t *testing.T) {
t.Parallel()
fsys := vfstest.FromMap(fstest.MapFS{
"/trace": &fstest.MapFile{Mode: fs.ModeDir},
}, true)
tr, err := StartTracing(fsys, "/trace", "", true /*deterministic*/)
assert.NilError(t, err)
endA := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": "/a.ts"}, true)
endB := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": "/b.ts"}, true)
endA()
endB()
endCheck := tr.Push(PhaseCheck, "checkSourceFile", map[string]any{"checkerId": 0, "path": "/a.ts"}, true)
endVariance := tr.Push(PhaseCheckTypes, "getVariancesWorker", map[string]any{"checkerId": 0, "id": 1}, true)
endVariance()
endCheck()
assert.NilError(t, tr.StopTracing())
traceText, ok := fsys.ReadFile("/trace/trace.json")
assert.Assert(t, ok)
var events []traceEvent
assert.NilError(t, json.Unmarshal([]byte(traceText), &events))
aBegin := findEvent(t, events, "B", "createSourceFile", "path", "/a.ts")
aEnd := findEvent(t, events, "E", "createSourceFile", "path", "/a.ts")
bBegin := findEvent(t, events, "B", "createSourceFile", "path", "/b.ts")
bEnd := findEvent(t, events, "E", "createSourceFile", "path", "/b.ts")
assert.Equal(t, aBegin.TID, aEnd.TID)
assert.Equal(t, bBegin.TID, bEnd.TID)
assert.Assert(t, aBegin.TID != bBegin.TID)
assertThreadName(t, events, aBegin.TID, "file:/a.ts")
assertThreadName(t, events, bBegin.TID, "file:/b.ts")
checkBegin := findEvent(t, events, "B", "checkSourceFile", "path", "/a.ts")
varianceBegin := findEvent(t, events, "B", "getVariancesWorker", "id", float64(1))
assert.Equal(t, checkBegin.TID, varianceBegin.TID)
assertThreadName(t, events, checkBegin.TID, "checker:0")
assertDurationEventsAreWellNestedByThread(t, events)
}
func TestThreadIDsAreStableAcrossFirstSeenOrder(t *testing.T) {
t.Parallel()
first := traceThreadIDsForPaths(t, []string{"/a.ts", "/b.ts"})
second := traceThreadIDsForPaths(t, []string{"/b.ts", "/a.ts"})
assert.DeepEqual(t, first, second)
}
func traceThreadIDsForPaths(t *testing.T, paths []string) map[string]int {
t.Helper()
fsys := vfstest.FromMap(fstest.MapFS{
"/trace": &fstest.MapFile{Mode: fs.ModeDir},
}, true)
tr, err := StartTracing(fsys, "/trace", "", true /*deterministic*/)
assert.NilError(t, err)
for _, path := range paths {
end := tr.Push(PhaseParse, "createSourceFile", map[string]any{"path": path}, true)
end()
}
assert.NilError(t, tr.StopTracing())
traceText, ok := fsys.ReadFile("/trace/trace.json")
assert.Assert(t, ok)
var events []traceEvent
assert.NilError(t, json.Unmarshal([]byte(traceText), &events))
threadIDs := make(map[string]int)
for _, path := range paths {
threadIDs[path] = findEvent(t, events, "B", "createSourceFile", "path", path).TID
}
return threadIDs
}
func findEvent(t *testing.T, events []traceEvent, phase string, name string, argName string, argValue any) traceEvent {
t.Helper()
for _, event := range events {
if event.PH == phase && event.Name == name && event.Args[argName] == argValue {
return event
}
}
t.Fatalf("failed to find %s event %q with %s=%v", phase, name, argName, argValue)
return traceEvent{}
}
func assertThreadName(t *testing.T, events []traceEvent, tid int, name string) {
t.Helper()
for _, event := range events {
if event.PH == "M" && event.Name == "thread_name" && event.TID == tid && event.Args["name"] == name {
return
}
}
t.Fatalf("failed to find thread_name metadata for thread %d named %q", tid, name)
}
func assertDurationEventsAreWellNestedByThread(t *testing.T, events []traceEvent) {
t.Helper()
stacks := make(map[int][]traceEvent)
for _, event := range events {
switch event.PH {
case "B":
stacks[event.TID] = append(stacks[event.TID], event)
case "E":
stack := stacks[event.TID]
assert.Assert(t, len(stack) > 0, "unmatched end event %q on thread %d", event.Name, event.TID)
begin := stack[len(stack)-1]
assert.Equal(t, begin.Cat, event.Cat)
assert.Equal(t, begin.Name, event.Name)
stacks[event.TID] = stack[:len(stack)-1]
}
}
for tid, stack := range stacks {
assert.Assert(t, len(stack) == 0, "thread %d has %d unterminated events", tid, len(stack))
}
}