runtime_observer_test.go RawHistoryFolder

1package ports
2
3import (
4	"reflect"
5	"testing"
6)
7
8func TestRuntimeObserverContract(t *testing.T) {
9	wantFields := []struct {
10		name string
11		typ  reflect.Type
12	}{
13		{"Schema", reflect.TypeFor[uint16]()},
14		{"ProcessID", reflect.TypeFor[string]()},
15		{"Component", reflect.TypeFor[string]()},
16		{"Scenario", reflect.TypeFor[string]()},
17		{"Run", reflect.TypeFor[uint64]()},
18		{"Sequence", reflect.TypeFor[uint64]()},
19		{"RequestID", reflect.TypeFor[uint64]()},
20		{"Epoch", reflect.TypeFor[uint64]()},
21		{"Kind", reflect.TypeFor[RuntimeMarkKind]()},
22		{"Tick", reflect.TypeFor[int64]()},
23		{"Bytes", reflect.TypeFor[uint64]()},
24		{"Fragments", reflect.TypeFor[uint64]()},
25		{"Retransmits", reflect.TypeFor[uint64]()},
26		{"Pending", reflect.TypeFor[uint64]()},
27		{"AckRTTNanos", reflect.TypeFor[int64]()},
28		{"Valid", reflect.TypeFor[bool]()},
29	}
30
31	typ := reflect.TypeFor[RuntimeMark]()
32	if typ.NumField() != len(wantFields) {
33		t.Fatalf("RuntimeMark has %d fields, want exactly %d", typ.NumField(), len(wantFields))
34	}
35	for i, want := range wantFields {
36		field := typ.Field(i)
37		if field.Name != want.name || field.Type != want.typ {
38			t.Fatalf("RuntimeMark field %d = %s %v, want %s %v", i, field.Name, field.Type, want.name, want.typ)
39		}
40	}
41
42	var _ RuntimeObserver = runtimeObserverFunc(func(RuntimeMark) {})
43}
44
45func TestP4RuntimeScreenMarkKindsValidateSchema1(t *testing.T) {
46	if RuntimeMarkSchema != 1 {
47		t.Fatalf("RuntimeMarkSchema = %d, want 1", RuntimeMarkSchema)
48	}
49	for _, tc := range []struct {
50		kind RuntimeMarkKind
51		want string
52	}{
53		{RuntimeScreenSnapshot, "screen_snapshot"},
54		{RuntimeScreenDelta, "screen_delta"},
55		{RuntimeScreenResetRequested, "screen_reset_requested"},
56	} {
57		if string(tc.kind) != tc.want {
58			t.Fatalf("screen mark kind = %q, want %q", tc.kind, tc.want)
59		}
60		mark := NewRuntimeMark("daemon", tc.kind, 17, true)
61		if !RuntimeMarkValid(mark) {
62			t.Fatalf("screen mark kind %q did not validate: %+v", tc.kind, mark)
63		}
64	}
65}
66
67func TestRuntimeObserverRequiredSpanKinds(t *testing.T) {
68	pairs := []struct {
69		start RuntimeMarkKind
70		end   RuntimeMarkKind
71	}{
72		{RuntimeDiffStart, RuntimeDiffEnd},
73		{RuntimeQueueEnqueued, RuntimeQueueDequeued},
74		{RuntimeACKBlockedStart, RuntimeACKBlockedEnd},
75		{RuntimeAdapterSendStart, RuntimeAdapterSendEnd},
76		{RuntimeAdapterReceiveStart, RuntimeAdapterReceiveEnd},
77	}
78	for _, pair := range pairs {
79		if pair.start == "" || pair.end == "" || pair.start == pair.end {
80			t.Fatalf("invalid required span mapping %#v", pair)
81		}
82	}
83}
84
85func TestRuntimeCorrelationObserverUsesHarnessProcessInputs(t *testing.T) {
86	var got []RuntimeMark
87	observer, err := NewRuntimeCorrelationObserver(runtimeObserverFunc(func(mark RuntimeMark) {
88		got = append(got, mark)
89	}), RuntimeCorrelationInputs{Scenario: "1x4-idle-local", Run: 7})
90	if err != nil {
91		t.Fatal(err)
92	}
93
94	observer.ObserveRuntime(RuntimeMark{Schema: RuntimeMarkSchema, Component: "daemon", Scenario: "runtime", Run: 1, Kind: RuntimeEmitEnd, Valid: true})
95	observer.ObserveRuntime(RuntimeMark{Schema: RuntimeMarkSchema, Component: "daemon", Scenario: "runtime", Run: 1, Sequence: 9, RequestID: 10, Epoch: 11, Kind: RuntimeEmitStart, Valid: true})
96
97	if len(got) != 2 {
98		t.Fatalf("marks=%+v", got)
99	}
100	for _, mark := range got {
101		if mark.Scenario != "1x4-idle-local" || mark.Run != 7 {
102			t.Fatalf("harness correlation was not applied: %+v", mark)
103		}
104		if mark.Sequence == 0 || mark.RequestID == 0 || mark.Epoch == 0 {
105			t.Fatalf("missing deterministic operation identity: %+v", mark)
106		}
107	}
108	if got[1].Sequence != 9 || got[1].RequestID != 10 || got[1].Epoch != 11 {
109		t.Fatalf("explicit operation identity changed: %+v", got[1])
110	}
111}
112
113func TestRuntimeCorrelationObserverAllocatesMissingIDsProcessWide(t *testing.T) {
114	var got []RuntimeMark
115	collect := runtimeObserverFunc(func(mark RuntimeMark) { got = append(got, mark) })
116	first, err := NewRuntimeCorrelationObserver(collect, RuntimeCorrelationInputs{Scenario: "scenario", Run: 1})
117	if err != nil {
118		t.Fatal(err)
119	}
120	second, err := NewRuntimeCorrelationObserver(collect, RuntimeCorrelationInputs{Scenario: "scenario", Run: 1})
121	if err != nil {
122		t.Fatal(err)
123	}
124
125	first.ObserveRuntime(RuntimeMark{Schema: RuntimeMarkSchema, Component: "first", Kind: RuntimeEmitStart, Valid: true})
126	second.ObserveRuntime(RuntimeMark{Schema: RuntimeMarkSchema, Component: "second", Kind: RuntimeEmitEnd, Valid: true})
127
128	if len(got) != 2 {
129		t.Fatalf("marks=%+v", got)
130	}
131	if got[0].Sequence == got[1].Sequence {
132		t.Fatalf("wrapper-local sequence collision: %+v", got)
133	}
134	for _, mark := range got {
135		if mark.RequestID != mark.Sequence || mark.Epoch != mark.RequestID {
136			t.Fatalf("missing-ID fallback changed: %+v", mark)
137		}
138	}
139}
140
141func TestRuntimeCorrelationObserverRejectsInvalidHarnessInputs(t *testing.T) {
142	for _, in := range []RuntimeCorrelationInputs{{}, {Scenario: "scenario"}} {
143		if _, err := NewRuntimeCorrelationObserver(runtimeObserverFunc(func(RuntimeMark) {}), in); err == nil {
144			t.Fatalf("invalid inputs accepted: %+v", in)
145		}
146	}
147}
148
149func TestRuntimeMarkHasNoRenderingPayloadOrPolicyFields(t *testing.T) {
150	typ := reflect.TypeFor[RuntimeMark]()
151	for _, forbidden := range []string{"Cell", "Cells", "Payload", "Frame", "Renderer", "Policy", "Transport"} {
152		if _, ok := typ.FieldByName(forbidden); ok {
153			t.Fatalf("RuntimeMark must not carry %s", forbidden)
154		}
155	}
156}
157
158type runtimeObserverFunc func(RuntimeMark)
159
160func (f runtimeObserverFunc) ObserveRuntime(mark RuntimeMark) { f(mark) }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close