1package cef
2
3import (
4 "bytes"
5 "testing"
6 "time"
7
8 "github.com/rs/zerolog"
9 "github.com/stretchr/testify/require"
10)
11
12func TestActivateStartupTraceSeedsNeutralProcessAndConfigTiming(t *testing.T) {
13 processStartupTrace.Lock()
14 previous := processStartupTrace.trace
15 processStartupTrace.trace = nil
16 processStartupTrace.Unlock()
17 t.Cleanup(func() {
18 processStartupTrace.Lock()
19 processStartupTrace.trace = previous
20 processStartupTrace.Unlock()
21 })
22
23 processEntry := time.Unix(100, 0)
24 configComplete := processEntry.Add(15 * time.Millisecond)
25 var output bytes.Buffer
26 logger := zerolog.New(&output).Level(zerolog.DebugLevel)
27
28 ActivateStartupTrace(processEntry, configComplete, &logger)
29 trace := activeStartupTrace()
30
31 require.NotNil(t, trace)
32 require.Equal(t, []string{"process_entry", "config_complete"}, []string{trace.milestones[0].Name, trace.milestones[1].Name})
33 require.Equal(t, int64(0), trace.milestones[0].Elapsed.Milliseconds())
34 require.Equal(t, int64(15), trace.milestones[1].Elapsed.Milliseconds())
35 require.Contains(t, output.String(), `"milestone":"process_entry"`)
36 require.Contains(t, output.String(), `"milestone":"config_complete"`)
37 require.NotContains(t, output.String(), `"message":"startup_trace: first presentation"`)
38}
39
40func TestStartupTraceAcceptsOnlyOrderedOneShotMilestones(t *testing.T) {
41 now := time.Unix(100, 0)
42 trace := newStartupTrace(func() time.Time { return now }, now)
43
44 require.True(t, trace.Mark("process_entry"))
45 require.False(t, trace.Mark("cef_initialized"), "out-of-order transition must be rejected")
46 require.False(t, trace.Mark("process_entry"), "duplicate transition must be rejected")
47
48 for _, name := range []string{
49 "config_complete",
50 "cef_library_load_begin",
51 "cef_initialized",
52 "browser_create_requested",
53 "first_accelerated_paint_received",
54 "first_dmabuf_texture_swap",
55 "first_gtk_presentation",
56 } {
57 now = now.Add(time.Millisecond)
58 if name == "first_gtk_presentation" {
59 require.Truef(t, trace.MarkGTKAfterPaint(), "milestone %s should be accepted", name)
60 } else {
61 require.Truef(t, trace.Mark(name), "milestone %s should be accepted", name)
62 }
63 }
64 require.False(t, trace.Mark("first_gtk_presentation"))
65}
66
67func TestStartupTracePreservesProcessAndConfigTimestamps(t *testing.T) {
68 processEntry := time.Unix(100, 0)
69 configComplete := processEntry.Add(1500 * time.Microsecond)
70 now := configComplete.Add(900 * time.Microsecond)
71 trace := newStartupTrace(func() time.Time { return now }, processEntry)
72
73 require.True(t, trace.markAt("process_entry", processEntry))
74 require.True(t, trace.markAt("config_complete", configComplete))
75
76 require.Equal(t, int64(0), trace.milestones[0].Elapsed.Milliseconds())
77 require.Equal(t, int64(0), trace.milestones[0].Delta.Milliseconds())
78 require.Equal(t, int64(1), trace.milestones[1].Elapsed.Milliseconds())
79 require.Equal(t, int64(1), trace.milestones[1].Delta.Milliseconds())
80}
81
82func TestStartupTraceReservesFirstGTKPresentationForAfterPaint(t *testing.T) {
83 now := time.Unix(100, 0)
84 trace := newStartupTrace(func() time.Time { return now }, now)
85
86 for _, name := range startupMilestoneOrder[:len(startupMilestoneOrder)-1] {
87 now = now.Add(time.Millisecond)
88 require.True(t, trace.Mark(name))
89 }
90
91 require.Len(t, trace.milestones, len(startupMilestoneOrder)-1)
92 require.False(t, trace.Mark("first_gtk_presentation"), "only the CEF-to-GTK after-paint hook can record the reserved milestone")
93 require.False(t, trace.summaryEmitted)
94 require.True(t, trace.MarkGTKAfterPaint(), "only the after-paint hook may record this milestone")
95}
96
97func TestStartupTraceEmitsOneNormalSummaryAtFirstPresentation(t *testing.T) {
98 var output bytes.Buffer
99 logger := zerolog.New(&output)
100 now := time.Unix(100, 0)
101 trace := newStartupTrace(func() time.Time { return now }, now)
102 trace.SetBackend("gdk-dmabuf")
103 trace.SetLogger(&logger)
104
105 for _, name := range startupMilestoneOrder[:len(startupMilestoneOrder)-1] {
106 now = now.Add(time.Millisecond)
107 require.True(t, trace.Mark(name))
108 }
109 now = now.Add(time.Millisecond)
110 require.True(t, trace.MarkGTKAfterPaint())
111 trace.MarkGTKAfterPaint()
112
113 require.Contains(t, output.String(), `"message":"startup_trace: first presentation"`)
114 require.Contains(t, output.String(), `"backend":"gdk-dmabuf"`)
115 require.Equal(t, 1, bytes.Count(output.Bytes(), []byte(`"message":"startup_trace: first presentation"`)))
116}