detached 9a4b27f

perf(harness): drive proxy workload shapes

commit
9a4b27fd904918a3e81d827339f941ec13ffc109
author
brice · · 2026-08-01 04:53 UTC
parent
b54b862
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +161 -9
M cmd/vev-perf-harness/launcher.go +20 -6
@@ -116,6 +116,8 @@ type terminalOutput interface {
116116 Close() error
117117 }
118118
119+var setPTYWinsize = rawterm.SetWinsize
120+
119121 type cliProcess struct {
120122 cmd *exec.Cmd
121123 pty io.ReadWriteCloser
@@ -135,19 +137,31 @@ type cliProcess struct {
135137 closeResult error
136138 }
137139
140+func (p *cliProcess) Resize(cols, rows uint16) error {
141+ fd, ok := p.pty.(interface{ Fd() uintptr })
142+ if !ok {
143+ return errors.New("client PTY does not expose a file descriptor")
144+ }
145+ return setPTYWinsize(int(fd.Fd()), cols, rows)
146+}
147+
138148 func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProcess, error) {
139149 if len(role.Args) == 0 {
140150 return nil, errors.New("public CLI role has no arguments")
141151 }
142- if info, err := os.Stat(l.bin); err != nil {
152+ bin, err := filepath.Abs(l.bin)
153+ if err != nil {
154+ return nil, err
155+ }
156+ if info, err := os.Stat(bin); err != nil {
143157 return nil, err
144158 } else if info.IsDir() {
145- return nil, fmt.Errorf("public CLI binary %q is a directory", l.bin)
159+ return nil, fmt.Errorf("public CLI binary %q is a directory", bin)
146160 }
147161 if m.Role == "ssh_stdio_peer" || m.Role == "udp_peer" {
148162 return l.preparePeer(m, role)
149163 }
150- cmd := exec.Command(l.bin, role.Args...)
164+ cmd := exec.Command(bin, role.Args...)
151165 runDir := filepath.Dir(m.TracePath)
152166 runtimeDir, err := l.runtimeDir(runDir)
153167 if err != nil {
@@ -157,9 +171,9 @@ func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProces
157171 // a role working directory: daemon-owned subprocess cleanup may otherwise
158172 // treat the evidence directory as its working tree and remove a preallocated
159173 // trace while a later repetition is being merged.
160- cmd.Env = append(withoutEnv(os.Environ(), "VEV", "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN", "VEV_REMOTE_TRANSPORT", "XDG_RUNTIME_DIR", "XDG_STATE_HOME"), traceEnvironment(m)...)
174+ cmd.Env = append(withoutEnv(os.Environ(), "VEV", "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN", "VEV_PERF_BIN", "VEV_REMOTE_TRANSPORT", "XDG_RUNTIME_DIR", "XDG_STATE_HOME"), traceEnvironment(m)...)
161175 cmd.Env = append(cmd.Env, "XDG_RUNTIME_DIR="+runtimeDir,
162- "XDG_STATE_HOME="+filepath.Join(runDir, "state"), "TERM=xterm-256color")
176+ "XDG_STATE_HOME="+filepath.Join(runDir, "state"), "TERM=xterm-256color", "VEV_PERF_BIN="+bin)
163177 if m.Role == "client" {
164178 l.mu.Lock()
165179 peer, routed := l.peers[runDir]
@@ -236,7 +250,7 @@ func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProces
236250 p.shutdown = func() error {
237251 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
238252 defer cancel()
239- shutdown := exec.CommandContext(ctx, l.bin, "kill", "--daemon")
253+ shutdown := exec.CommandContext(ctx, bin, "kill", "--daemon")
240254 shutdown.Env = withoutEnv(cmd.Env, "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN")
241255 return shutdown.Run()
242256 }
@@ -116,6 +116,8 @@ type terminalOutput interface {
116 Close() error116 Close() error
117 }117 }
118 118
119+var setPTYWinsize = rawterm.SetWinsize
120+
119 type cliProcess struct {121 type cliProcess struct {
120 cmd *exec.Cmd122 cmd *exec.Cmd
121 pty io.ReadWriteCloser123 pty io.ReadWriteCloser
@@ -135,19 +137,31 @@ type cliProcess struct {
135 closeResult error137 closeResult error
136 }138 }
137 139
140+func (p *cliProcess) Resize(cols, rows uint16) error {
141+ fd, ok := p.pty.(interface{ Fd() uintptr })
142+ if !ok {
143+ return errors.New("client PTY does not expose a file descriptor")
144+ }
145+ return setPTYWinsize(int(fd.Fd()), cols, rows)
146+}
147+
138 func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProcess, error) {148 func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProcess, error) {
139 if len(role.Args) == 0 {149 if len(role.Args) == 0 {
140 return nil, errors.New("public CLI role has no arguments")150 return nil, errors.New("public CLI role has no arguments")
141 }151 }
142- if info, err := os.Stat(l.bin); err != nil {152+ bin, err := filepath.Abs(l.bin)
153+ if err != nil {
154+ return nil, err
155+ }
156+ if info, err := os.Stat(bin); err != nil {
143 return nil, err157 return nil, err
144 } else if info.IsDir() {158 } else if info.IsDir() {
145- return nil, fmt.Errorf("public CLI binary %q is a directory", l.bin)159+ return nil, fmt.Errorf("public CLI binary %q is a directory", bin)
146 }160 }
147 if m.Role == "ssh_stdio_peer" || m.Role == "udp_peer" {161 if m.Role == "ssh_stdio_peer" || m.Role == "udp_peer" {
148 return l.preparePeer(m, role)162 return l.preparePeer(m, role)
149 }163 }
150- cmd := exec.Command(l.bin, role.Args...)164+ cmd := exec.Command(bin, role.Args...)
151 runDir := filepath.Dir(m.TracePath)165 runDir := filepath.Dir(m.TracePath)
152 runtimeDir, err := l.runtimeDir(runDir)166 runtimeDir, err := l.runtimeDir(runDir)
153 if err != nil {167 if err != nil {
@@ -157,9 +171,9 @@ func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProces
157 // a role working directory: daemon-owned subprocess cleanup may otherwise171 // a role working directory: daemon-owned subprocess cleanup may otherwise
158 // treat the evidence directory as its working tree and remove a preallocated172 // treat the evidence directory as its working tree and remove a preallocated
159 // trace while a later repetition is being merged.173 // trace while a later repetition is being merged.
160- cmd.Env = append(withoutEnv(os.Environ(), "VEV", "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN", "VEV_REMOTE_TRANSPORT", "XDG_RUNTIME_DIR", "XDG_STATE_HOME"), traceEnvironment(m)...)174+ cmd.Env = append(withoutEnv(os.Environ(), "VEV", "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN", "VEV_PERF_BIN", "VEV_REMOTE_TRANSPORT", "XDG_RUNTIME_DIR", "XDG_STATE_HOME"), traceEnvironment(m)...)
161 cmd.Env = append(cmd.Env, "XDG_RUNTIME_DIR="+runtimeDir,175 cmd.Env = append(cmd.Env, "XDG_RUNTIME_DIR="+runtimeDir,
162- "XDG_STATE_HOME="+filepath.Join(runDir, "state"), "TERM=xterm-256color")176+ "XDG_STATE_HOME="+filepath.Join(runDir, "state"), "TERM=xterm-256color", "VEV_PERF_BIN="+bin)
163 if m.Role == "client" {177 if m.Role == "client" {
164 l.mu.Lock()178 l.mu.Lock()
165 peer, routed := l.peers[runDir]179 peer, routed := l.peers[runDir]
@@ -236,7 +250,7 @@ func (l *cliLauncher) Launch(m processMapping, role roleCommand) (launchedProces
236 p.shutdown = func() error {250 p.shutdown = func() error {
237 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)251 ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
238 defer cancel()252 defer cancel()
239- shutdown := exec.CommandContext(ctx, l.bin, "kill", "--daemon")253+ shutdown := exec.CommandContext(ctx, bin, "kill", "--daemon")
240 shutdown.Env = withoutEnv(cmd.Env, "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN")254 shutdown.Env = withoutEnv(cmd.Env, "VEV_PERF_TRACE", "VEV_PERF_PROCESS_ID", "VEV_PERF_SCENARIO", "VEV_PERF_RUN")
241 return shutdown.Run()255 return shutdown.Run()
242 }256 }
M cmd/vev-perf-harness/launcher_test.go +30 -0
@@ -24,6 +24,36 @@ func (discardTerminalOutput) Write(p []byte) (int, error) { return len(p), nil }
2424 func (discardTerminalOutput) Sync() error { return nil }
2525 func (discardTerminalOutput) Close() error { return nil }
2626
27+type fakeFDPTY struct {
28+ *fakePTY
29+ fd uintptr
30+}
31+
32+func (p *fakeFDPTY) Fd() uintptr { return p.fd }
33+
34+func TestCLIProcessResize(t *testing.T) {
35+ original := setPTYWinsize
36+ t.Cleanup(func() { setPTYWinsize = original })
37+
38+ var gotFD int
39+ var gotCols, gotRows uint16
40+ setPTYWinsize = func(fd int, cols, rows uint16) error {
41+ gotFD, gotCols, gotRows = fd, cols, rows
42+ return nil
43+ }
44+ p := &cliProcess{pty: &fakeFDPTY{fakePTY: &fakePTY{}, fd: 42}}
45+ if err := p.Resize(119, 40); err != nil {
46+ t.Fatal(err)
47+ }
48+ if gotFD != 42 || gotCols != 119 || gotRows != 40 {
49+ t.Fatalf("winsize setter got fd=%d cols=%d rows=%d", gotFD, gotCols, gotRows)
50+ }
51+
52+ if err := (&cliProcess{pty: &fakePTY{}}).Resize(119, 40); err == nil || !strings.Contains(err.Error(), "file descriptor") {
53+ t.Fatalf("Resize without FD error = %v", err)
54+ }
55+}
56+
2757 func TestCLIProcessCloseDrainsQueuedTerminalOutputBeforeClientExit(t *testing.T) {
2858 harnessPTY, clientPTY := net.Pipe()
2959 t.Cleanup(func() { _ = clientPTY.Close() })
@@ -24,6 +24,36 @@ func (discardTerminalOutput) Write(p []byte) (int, error) { return len(p), nil }
24 func (discardTerminalOutput) Sync() error { return nil }24 func (discardTerminalOutput) Sync() error { return nil }
25 func (discardTerminalOutput) Close() error { return nil }25 func (discardTerminalOutput) Close() error { return nil }
26 26
27+type fakeFDPTY struct {
28+ *fakePTY
29+ fd uintptr
30+}
31+
32+func (p *fakeFDPTY) Fd() uintptr { return p.fd }
33+
34+func TestCLIProcessResize(t *testing.T) {
35+ original := setPTYWinsize
36+ t.Cleanup(func() { setPTYWinsize = original })
37+
38+ var gotFD int
39+ var gotCols, gotRows uint16
40+ setPTYWinsize = func(fd int, cols, rows uint16) error {
41+ gotFD, gotCols, gotRows = fd, cols, rows
42+ return nil
43+ }
44+ p := &cliProcess{pty: &fakeFDPTY{fakePTY: &fakePTY{}, fd: 42}}
45+ if err := p.Resize(119, 40); err != nil {
46+ t.Fatal(err)
47+ }
48+ if gotFD != 42 || gotCols != 119 || gotRows != 40 {
49+ t.Fatalf("winsize setter got fd=%d cols=%d rows=%d", gotFD, gotCols, gotRows)
50+ }
51+
52+ if err := (&cliProcess{pty: &fakePTY{}}).Resize(119, 40); err == nil || !strings.Contains(err.Error(), "file descriptor") {
53+ t.Fatalf("Resize without FD error = %v", err)
54+ }
55+}
56+
27 func TestCLIProcessCloseDrainsQueuedTerminalOutputBeforeClientExit(t *testing.T) {57 func TestCLIProcessCloseDrainsQueuedTerminalOutputBeforeClientExit(t *testing.T) {
28 harnessPTY, clientPTY := net.Pipe()58 harnessPTY, clientPTY := net.Pipe()
29 t.Cleanup(func() { _ = clientPTY.Close() })59 t.Cleanup(func() { _ = clientPTY.Close() })
M cmd/vev-perf-harness/runner.go +35 -3
@@ -307,6 +307,15 @@ func (h *harness) runOne(o options, mat manifest, s scenario, run int, raw io.Wr
307307 flushedRecorded = true
308308 return nil
309309 }
310+ if cols, rows, resize := workloadResize(s, sequence); resize {
311+ resizer, ok := p.process.(processResizer)
312+ if !ok {
313+ return res, errors.New("client process does not support resize workload")
314+ }
315+ if resizeErr := resizer.Resize(cols, rows); resizeErr != nil {
316+ return res, fmt.Errorf("resize client PTY: %w", resizeErr)
317+ }
318+ }
310319 if measureErr := p.process.Measure(workloadInput(s, run, fmt.Sprintf("measured-%d", sequence)), injected, flushed); measureErr != nil {
311320 if injectedRecorded && !flushedRecorded {
312321 diagnosticErr := recordMark(harnessMark{Scenario: s.ID, Run: run, Sequence: sequence, Kind: "terminal_flushed", Tick: h.clock.Now(), Valid: false})
@@ -418,6 +427,20 @@ func launchOrder(mappings []processMapping) []processMapping {
418427 return out
419428 }
420429
430+type processResizer interface {
431+ Resize(cols, rows uint16) error
432+}
433+
434+func workloadResize(s scenario, sequence uint64) (uint16, uint16, bool) {
435+ if s.Workload != "resize_sweep" && s.Workload != "snapshot_output_resize" {
436+ return 0, 0, false
437+ }
438+ if sequence%2 == 1 {
439+ return 119, 40, true
440+ }
441+ return 120, 40, true
442+}
443+
421444 // workloadInput is shell input delivered through the client PTY. It avoids any
422445 // private daemon API and makes every topology/workload/transport manifest row
423446 // execute a real terminal workload. The marker is the output boundary awaited
@@ -426,16 +449,25 @@ func workloadInput(s scenario, run int, phase string) []byte {
426449 marker := fmt.Sprintf("__VEV_HARNESS_%s_r%d_%s__", safeName(s.ID), run, safeName(phase))
427450 body := "printf 'vev perf %s\\n'"
428451 switch s.Workload {
429- case "active_output", "all_output", "inactive_output", "interactive_flood":
452+ case "active_output":
453+ body = `printf '\033[10;20HX'; : '%s'`
454+ case "all_output":
455+ body = `printf '%%-120s' 'vev perf line %s'`
456+ case "inactive_output":
457+ body = `printf '\033[38;2;20;120;220mred'; printf '\033[38;2;220;80;40mgreen'; printf '\033[38;2;80;220;120m%s'; printf '\033[0m'`
458+ case "interactive_flood":
430459 body = "i=0; while [ $i -lt 128 ]; do printf 'vev perf output %s\\n'; i=$((i+1)); done"
431460 case "resize_sweep":
432461 body = "printf 'vev perf resize-sweep %s\\n'"
433462 case "copy_search":
434463 body = "printf 'vev perf copy-search %s\\n'"
435464 case "snapshot_output_resize":
436- body = "printf 'vev perf snapshot-output-resize %s\\n'"
465+ body = `printf '\033[2J\033[Hvev perf snapshot-output-resize %s\n'`
437466 case "attach_restore_tab_switch":
438- body = "printf 'vev perf attach-restore-tab-switch %s\\n'"
467+ body = `"$VEV_PERF_BIN" cmd next-tab; "$VEV_PERF_BIN" cmd previous-tab; printf 'vev perf attach-restore-tab-switch %s\n'`
468+ if phase == "warmup" {
469+ body = `"$VEV_PERF_BIN" cmd new-tab; "$VEV_PERF_BIN" cmd previous-tab; printf 'vev perf attach-restore-tab-switch %s\n'`
470+ }
439471 }
440472 return []byte(fmt.Sprintf(body+"; printf '%s\\n'\n", s.ID, marker))
441473 }
@@ -307,6 +307,15 @@ func (h *harness) runOne(o options, mat manifest, s scenario, run int, raw io.Wr
307 flushedRecorded = true307 flushedRecorded = true
308 return nil308 return nil
309 }309 }
310+ if cols, rows, resize := workloadResize(s, sequence); resize {
311+ resizer, ok := p.process.(processResizer)
312+ if !ok {
313+ return res, errors.New("client process does not support resize workload")
314+ }
315+ if resizeErr := resizer.Resize(cols, rows); resizeErr != nil {
316+ return res, fmt.Errorf("resize client PTY: %w", resizeErr)
317+ }
318+ }
310 if measureErr := p.process.Measure(workloadInput(s, run, fmt.Sprintf("measured-%d", sequence)), injected, flushed); measureErr != nil {319 if measureErr := p.process.Measure(workloadInput(s, run, fmt.Sprintf("measured-%d", sequence)), injected, flushed); measureErr != nil {
311 if injectedRecorded && !flushedRecorded {320 if injectedRecorded && !flushedRecorded {
312 diagnosticErr := recordMark(harnessMark{Scenario: s.ID, Run: run, Sequence: sequence, Kind: "terminal_flushed", Tick: h.clock.Now(), Valid: false})321 diagnosticErr := recordMark(harnessMark{Scenario: s.ID, Run: run, Sequence: sequence, Kind: "terminal_flushed", Tick: h.clock.Now(), Valid: false})
@@ -418,6 +427,20 @@ func launchOrder(mappings []processMapping) []processMapping {
418 return out427 return out
419 }428 }
420 429
430+type processResizer interface {
431+ Resize(cols, rows uint16) error
432+}
433+
434+func workloadResize(s scenario, sequence uint64) (uint16, uint16, bool) {
435+ if s.Workload != "resize_sweep" && s.Workload != "snapshot_output_resize" {
436+ return 0, 0, false
437+ }
438+ if sequence%2 == 1 {
439+ return 119, 40, true
440+ }
441+ return 120, 40, true
442+}
443+
421 // workloadInput is shell input delivered through the client PTY. It avoids any444 // workloadInput is shell input delivered through the client PTY. It avoids any
422 // private daemon API and makes every topology/workload/transport manifest row445 // private daemon API and makes every topology/workload/transport manifest row
423 // execute a real terminal workload. The marker is the output boundary awaited446 // execute a real terminal workload. The marker is the output boundary awaited
@@ -426,16 +449,25 @@ func workloadInput(s scenario, run int, phase string) []byte {
426 marker := fmt.Sprintf("__VEV_HARNESS_%s_r%d_%s__", safeName(s.ID), run, safeName(phase))449 marker := fmt.Sprintf("__VEV_HARNESS_%s_r%d_%s__", safeName(s.ID), run, safeName(phase))
427 body := "printf 'vev perf %s\\n'"450 body := "printf 'vev perf %s\\n'"
428 switch s.Workload {451 switch s.Workload {
429- case "active_output", "all_output", "inactive_output", "interactive_flood":452+ case "active_output":
453+ body = `printf '\033[10;20HX'; : '%s'`
454+ case "all_output":
455+ body = `printf '%%-120s' 'vev perf line %s'`
456+ case "inactive_output":
457+ body = `printf '\033[38;2;20;120;220mred'; printf '\033[38;2;220;80;40mgreen'; printf '\033[38;2;80;220;120m%s'; printf '\033[0m'`
458+ case "interactive_flood":
430 body = "i=0; while [ $i -lt 128 ]; do printf 'vev perf output %s\\n'; i=$((i+1)); done"459 body = "i=0; while [ $i -lt 128 ]; do printf 'vev perf output %s\\n'; i=$((i+1)); done"
431 case "resize_sweep":460 case "resize_sweep":
432 body = "printf 'vev perf resize-sweep %s\\n'"461 body = "printf 'vev perf resize-sweep %s\\n'"
433 case "copy_search":462 case "copy_search":
434 body = "printf 'vev perf copy-search %s\\n'"463 body = "printf 'vev perf copy-search %s\\n'"
435 case "snapshot_output_resize":464 case "snapshot_output_resize":
436- body = "printf 'vev perf snapshot-output-resize %s\\n'"465+ body = `printf '\033[2J\033[Hvev perf snapshot-output-resize %s\n'`
437 case "attach_restore_tab_switch":466 case "attach_restore_tab_switch":
438- body = "printf 'vev perf attach-restore-tab-switch %s\\n'"467+ body = `"$VEV_PERF_BIN" cmd next-tab; "$VEV_PERF_BIN" cmd previous-tab; printf 'vev perf attach-restore-tab-switch %s\n'`
468+ if phase == "warmup" {
469+ body = `"$VEV_PERF_BIN" cmd new-tab; "$VEV_PERF_BIN" cmd previous-tab; printf 'vev perf attach-restore-tab-switch %s\n'`
470+ }
439 }471 }
440 return []byte(fmt.Sprintf(body+"; printf '%s\\n'\n", s.ID, marker))472 return []byte(fmt.Sprintf(body+"; printf '%s\\n'\n", s.ID, marker))
441 }473 }
M cmd/vev-perf-harness/runner_test.go +76 -0
@@ -309,6 +309,82 @@ func TestHarnessFakeRunnerRoutesClientToPeerAndCleansEveryRole(t *testing.T) {
309309 }
310310 }
311311
312+func TestWorkloadInputShapes(t *testing.T) {
313+ tests := []struct {
314+ workload string
315+ contains []string
316+ }{
317+ {"active_output", []string{`printf '\033[10;20HX'`}},
318+ {"all_output", []string{"120", "vev perf line"}},
319+ {"inactive_output", []string{
320+ `printf '\033[38;2;20;120;220mred'`,
321+ `printf '\033[38;2;220;80;40mgreen'`,
322+ `printf '\033[38;2;80;220;120ms'`,
323+ }},
324+ {"interactive_flood", []string{"while [ $i -lt 128 ]"}},
325+ {"snapshot_output_resize", []string{`\033[2J`, "vev perf snapshot-output-resize"}},
326+ {"attach_restore_tab_switch", []string{`"$VEV_PERF_BIN" cmd next-tab`, `"$VEV_PERF_BIN" cmd previous-tab`}},
327+ }
328+ for _, tt := range tests {
329+ t.Run(tt.workload, func(t *testing.T) {
330+ got := string(workloadInput(scenario{ID: "s", Workload: tt.workload}, 1, "measured-1"))
331+ for _, want := range tt.contains {
332+ if !strings.Contains(got, want) {
333+ t.Errorf("workload input %q does not contain %q", got, want)
334+ }
335+ }
336+ if !strings.Contains(got, "__VEV_HARNESS_s_r1_measured-1__") {
337+ t.Errorf("workload input has no terminal marker: %q", got)
338+ }
339+ })
340+ }
341+
342+ active := string(workloadInput(scenario{ID: "s", Workload: "active_output"}, 1, "measured-1"))
343+ if strings.Contains(active, `\r`) || strings.Contains(active, "vev perf") {
344+ t.Fatalf("active output is not an absolute-cursor one-cell write: %q", active)
345+ }
346+ inactive := string(workloadInput(scenario{ID: "s", Workload: "inactive_output"}, 1, "measured-1"))
347+ if count := strings.Count(inactive, `\033[38;2;`); count != 3 {
348+ t.Fatalf("inactive output truecolor runs = %d, want 3: %q", count, inactive)
349+ }
350+
351+ warmup := string(workloadInput(scenario{ID: "s", Workload: "attach_restore_tab_switch"}, 1, "warmup"))
352+ if count := strings.Count(warmup, `"$VEV_PERF_BIN" cmd new-tab`); count != 1 {
353+ t.Fatalf("attach warmup new-tab commands = %d, want 1: %q", count, warmup)
354+ }
355+ if strings.Contains(warmup, "cmd next-tab") || !strings.Contains(warmup, `cmd previous-tab; printf 'vev perf attach`) {
356+ t.Fatalf("attach warmup does not return to the marker-producing tab: %q", warmup)
357+ }
358+ measured := string(workloadInput(scenario{ID: "s", Workload: "attach_restore_tab_switch"}, 1, "measured-1"))
359+ if strings.Contains(measured, "cmd new-tab") {
360+ t.Fatalf("measured attach workload creates another tab: %q", measured)
361+ }
362+}
363+
364+func TestWorkloadResize(t *testing.T) {
365+ for _, tt := range []struct {
366+ workload string
367+ sequence uint64
368+ wantCols uint16
369+ wantOK bool
370+ }{
371+ {"active_output", 1, 0, false},
372+ {"resize_sweep", 1, 119, true},
373+ {"resize_sweep", 2, 120, true},
374+ {"snapshot_output_resize", 1, 119, true},
375+ } {
376+ t.Run(fmt.Sprintf("%s-%d", tt.workload, tt.sequence), func(t *testing.T) {
377+ cols, rows, ok := workloadResize(scenario{Workload: tt.workload}, tt.sequence)
378+ if ok != tt.wantOK || cols != tt.wantCols {
379+ t.Fatalf("workloadResize() = (%d, %d, %t), want cols=%d ok=%t", cols, rows, ok, tt.wantCols, tt.wantOK)
380+ }
381+ if ok && rows != 40 {
382+ t.Fatalf("workloadResize() rows = %d, want 40", rows)
383+ }
384+ })
385+ }
386+}
387+
312388 func TestHarnessAggregatesRawEventsSeparatelyFromRunDispersion(t *testing.T) {
313389 first := append([]int64(nil), make([]int64, minimumInIntervalEventSamples+5)...)
314390 for i := range first {
@@ -309,6 +309,82 @@ func TestHarnessFakeRunnerRoutesClientToPeerAndCleansEveryRole(t *testing.T) {
309 }309 }
310 }310 }
311 311
312+func TestWorkloadInputShapes(t *testing.T) {
313+ tests := []struct {
314+ workload string
315+ contains []string
316+ }{
317+ {"active_output", []string{`printf '\033[10;20HX'`}},
318+ {"all_output", []string{"120", "vev perf line"}},
319+ {"inactive_output", []string{
320+ `printf '\033[38;2;20;120;220mred'`,
321+ `printf '\033[38;2;220;80;40mgreen'`,
322+ `printf '\033[38;2;80;220;120ms'`,
323+ }},
324+ {"interactive_flood", []string{"while [ $i -lt 128 ]"}},
325+ {"snapshot_output_resize", []string{`\033[2J`, "vev perf snapshot-output-resize"}},
326+ {"attach_restore_tab_switch", []string{`"$VEV_PERF_BIN" cmd next-tab`, `"$VEV_PERF_BIN" cmd previous-tab`}},
327+ }
328+ for _, tt := range tests {
329+ t.Run(tt.workload, func(t *testing.T) {
330+ got := string(workloadInput(scenario{ID: "s", Workload: tt.workload}, 1, "measured-1"))
331+ for _, want := range tt.contains {
332+ if !strings.Contains(got, want) {
333+ t.Errorf("workload input %q does not contain %q", got, want)
334+ }
335+ }
336+ if !strings.Contains(got, "__VEV_HARNESS_s_r1_measured-1__") {
337+ t.Errorf("workload input has no terminal marker: %q", got)
338+ }
339+ })
340+ }
341+
342+ active := string(workloadInput(scenario{ID: "s", Workload: "active_output"}, 1, "measured-1"))
343+ if strings.Contains(active, `\r`) || strings.Contains(active, "vev perf") {
344+ t.Fatalf("active output is not an absolute-cursor one-cell write: %q", active)
345+ }
346+ inactive := string(workloadInput(scenario{ID: "s", Workload: "inactive_output"}, 1, "measured-1"))
347+ if count := strings.Count(inactive, `\033[38;2;`); count != 3 {
348+ t.Fatalf("inactive output truecolor runs = %d, want 3: %q", count, inactive)
349+ }
350+
351+ warmup := string(workloadInput(scenario{ID: "s", Workload: "attach_restore_tab_switch"}, 1, "warmup"))
352+ if count := strings.Count(warmup, `"$VEV_PERF_BIN" cmd new-tab`); count != 1 {
353+ t.Fatalf("attach warmup new-tab commands = %d, want 1: %q", count, warmup)
354+ }
355+ if strings.Contains(warmup, "cmd next-tab") || !strings.Contains(warmup, `cmd previous-tab; printf 'vev perf attach`) {
356+ t.Fatalf("attach warmup does not return to the marker-producing tab: %q", warmup)
357+ }
358+ measured := string(workloadInput(scenario{ID: "s", Workload: "attach_restore_tab_switch"}, 1, "measured-1"))
359+ if strings.Contains(measured, "cmd new-tab") {
360+ t.Fatalf("measured attach workload creates another tab: %q", measured)
361+ }
362+}
363+
364+func TestWorkloadResize(t *testing.T) {
365+ for _, tt := range []struct {
366+ workload string
367+ sequence uint64
368+ wantCols uint16
369+ wantOK bool
370+ }{
371+ {"active_output", 1, 0, false},
372+ {"resize_sweep", 1, 119, true},
373+ {"resize_sweep", 2, 120, true},
374+ {"snapshot_output_resize", 1, 119, true},
375+ } {
376+ t.Run(fmt.Sprintf("%s-%d", tt.workload, tt.sequence), func(t *testing.T) {
377+ cols, rows, ok := workloadResize(scenario{Workload: tt.workload}, tt.sequence)
378+ if ok != tt.wantOK || cols != tt.wantCols {
379+ t.Fatalf("workloadResize() = (%d, %d, %t), want cols=%d ok=%t", cols, rows, ok, tt.wantCols, tt.wantOK)
380+ }
381+ if ok && rows != 40 {
382+ t.Fatalf("workloadResize() rows = %d, want 40", rows)
383+ }
384+ })
385+ }
386+}
387+
312 func TestHarnessAggregatesRawEventsSeparatelyFromRunDispersion(t *testing.T) {388 func TestHarnessAggregatesRawEventsSeparatelyFromRunDispersion(t *testing.T) {
313 first := append([]int64(nil), make([]int64, minimumInIntervalEventSamples+5)...)389 first := append([]int64(nil), make([]int64, minimumInIntervalEventSamples+5)...)
314 for i := range first {390 for i := range first {

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 2

Tags 13

↑↓ moveenter openesc close