Description
go test -race -shuffle=on ./pkg/tui (the test-race CI job added in #4221) fails intermittently, roughly 1 run in 5, on TestLoadSessionThenClickEditLabel. The race detector reports a write/read race on App.session in pkg/app:
App.ReplaceSession writes a.session (called from the bubbletea update loop when a past session is loaded).
- The goroutine started by
App.Start reads a.session to call runtime.EmitStartupInfo. reEmitStartupInfo has the same shape: its closure reads a.session from inside the goroutine pumpToEvents spawns.
This is a production data race, not a test artifact: startup info is emitted in the background while the UI can already replace the session. It was not part of the four causes fixed in #4228 (it did not fire in run 34493297831), which is why it is tracked separately.
Expected Behavior
go test -race -shuffle=on ./pkg/tui passes consistently.
Actual Behavior
TestLoadSessionThenClickEditLabel/new_tab (or another subtest, depending on timing) fails with WARNING: DATA RACE and the package fails.
Steps to Reproduce
go test -race -count=5 -run TestLoadSessionThenClickEditLabel ./pkg/tui
Fails about once per 5 iterations on an M-series Mac with Go 1.27.
Error output
WARNING: DATA RACE
Write at 0x00c000f845b8 by goroutine 1314:
github.com/docker/docker-agent/pkg/app.(*App).ReplaceSession()
pkg/app/app.go:1705 +0x9c
github.com/docker/docker-agent/pkg/tui.(*appModel).handleLoadSession()
pkg/tui/tui.go:1723 +0x7e8
github.com/docker/docker-agent/pkg/tui.(*appModel).update()
pkg/tui/tui.go:1260 +0x5688
github.com/docker/docker-agent/pkg/tui.(*appModel).Update()
pkg/tui/tui.go:857 +0x3c
github.com/docker/docker-agent/pkg/tui/tuitest.(*captureModel).Update()
pkg/tui/tuitest/driver.go:357 +0x8c
charm.land/bubbletea/v2.(*Program).eventLoop()
charm.land/bubbletea/v2@v2.0.9/tea.go:880 +0xed0
charm.land/bubbletea/v2.(*Program).Run()
charm.land/bubbletea/v2@v2.0.9/tea.go:1153 +0xe10
github.com/docker/docker-agent/pkg/tui/tuitest.New.func1()
pkg/tui/tuitest/driver.go:168 +0x84
Previous read at 0x00c000f845b8 by goroutine 1333:
github.com/docker/docker-agent/pkg/app.(*App).Start.func1.1.1()
pkg/app/app.go:168 +0x98
Goroutine 1314 (running) created at:
github.com/docker/docker-agent/pkg/tui/tuitest.New()
pkg/tui/tuitest/driver.go:166 +0x520
github.com/docker/docker-agent/pkg/tui.testLoadSessionThenClickEditLabel()
pkg/tui/session_load_click_test.go:85 +0x7b4
github.com/docker/docker-agent/pkg/tui.TestLoadSessionThenClickEditLabel.func2()
pkg/tui/session_load_click_test.go:35 +0x30
testing.tRunner()
GOROOT/src/testing/testing.go:2193 +0x164
testing.(*T).Run.gowrap1()
GOROOT/src/testing/testing.go:2258 +0x34
Goroutine 1333 (finished) created at:
github.com/docker/docker-agent/pkg/app.(*App).Start.func1.1()
pkg/app/app.go:166 +0x11c
==================
Additional context
Proposed fix: snapshot the session on the caller's goroutine (sess := a.session) before spawning the startup-info goroutines in Start and reEmitStartupInfo, so the background work never reads the field. ReplaceSession re-emits startup info for the new session anyway, so emitting for the snapshotted session is the intended behavior.
Description
go test -race -shuffle=on ./pkg/tui(thetest-raceCI job added in #4221) fails intermittently, roughly 1 run in 5, onTestLoadSessionThenClickEditLabel. The race detector reports a write/read race onApp.sessioninpkg/app:App.ReplaceSessionwritesa.session(called from the bubbletea update loop when a past session is loaded).App.Startreadsa.sessionto callruntime.EmitStartupInfo.reEmitStartupInfohas the same shape: its closure readsa.sessionfrom inside the goroutinepumpToEventsspawns.This is a production data race, not a test artifact: startup info is emitted in the background while the UI can already replace the session. It was not part of the four causes fixed in #4228 (it did not fire in run 34493297831), which is why it is tracked separately.
Expected Behavior
go test -race -shuffle=on ./pkg/tuipasses consistently.Actual Behavior
TestLoadSessionThenClickEditLabel/new_tab(or another subtest, depending on timing) fails withWARNING: DATA RACEand the package fails.Steps to Reproduce
Fails about once per 5 iterations on an M-series Mac with Go 1.27.
Error output
Additional context
Proposed fix: snapshot the session on the caller's goroutine (
sess := a.session) before spawning the startup-info goroutines inStartandreEmitStartupInfo, so the background work never reads the field.ReplaceSessionre-emits startup info for the new session anyway, so emitting for the snapshotted session is the intended behavior.