tui tests: the package runs in UTC from TestMain, so no test swaps time.Local under the gateway's loops
TestSessionsPage raced about 1 in 20-40 runs under -race: sessionsHome set time.Local = time.UTC (and back in its cleanup) while goroutines an earlier test left running read it. gateway.ListenAndServe starts its Keep* loops (check-ins, credits, history, quotas) on its ctx and returns once the server is shut down, without waiting for them, and those loops call .Local() and time.Date(..., time.Local). TestMain now sets time.Local to UTC once, before any test starts a goroutine, and sessionsHome no longer touches it; the sessions fixtures' days are counted in UTC as before. A probe test with a goroutine calling time.Now().Local() while sessionsHome ran reported DATA RACE at sessions_test.go:30 on the old code and passes -race -count=3 on the new one (probe not committed). The reported race itself didn't reproduce here before the change (-race -count=40 of the package, -count=150 of TestTUI*|TestSessionsPage). go test -race -count=40 -run TestSessionsPage ./internal/tui and go test -race -count=3 ./internal/tui pass; go vet and gofmt are clean.
This commit is contained in:
1 parent
5d8717239d
commit
1caea63804
2 files changed
+14
-7
No files matched your search
@@ -2,9 +2,16 @@ package tui
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/yetone/magpie/internal/testenv"
|
||||
)
|
||||
|
||||
// The tests run in a home of their own, never the user's real files.
|
||||
func TestMain(m *testing.M) { testenv.Main(m) }
|
||||
// The tests run in a home of their own, never the user's real files, and in
|
||||
// UTC, the zone the sessions fixtures' days are counted in. The zone is set
|
||||
// here, before any test starts a goroutine: a test that swapped it would race
|
||||
// the gateway's loops an earlier test left running, which read time.Local.
|
||||
func TestMain(m *testing.M) {
|
||||
time.Local = time.UTC
|
||||
testenv.Main(m)
|
||||
}
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
)
|
||||
|
||||
// sessionsHome puts the sessions package's fixtures where Claude Code and
|
||||
// Codex keep their sessions, in a sandbox HOME, their models priced.
|
||||
// Codex keep their sessions, in a sandbox HOME, their models priced. Their
|
||||
// days are counted in UTC, which TestMain sets for the package.
|
||||
func sessionsHome(t *testing.T) {
|
||||
t.Helper()
|
||||
h := home(t)
|
||||
@@ -26,8 +27,7 @@ func sessionsHome(t *testing.T) {
|
||||
}
|
||||
t.Setenv(env, dir)
|
||||
}
|
||||
oldZone, oldPrice := time.Local, sessions.PriceOf
|
||||
time.Local = time.UTC
|
||||
oldPrice := sessions.PriceOf
|
||||
sessions.PriceOf = func(_ settings.Settings, m string) (catalog.Price, bool) {
|
||||
switch m {
|
||||
case "claude-opus-5-5":
|
||||
@@ -39,9 +39,9 @@ func sessionsHome(t *testing.T) {
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
// the page's index is written behind it: let that write finish before
|
||||
// the zone it reads (a stat of the file it writes) goes back
|
||||
// the prices it reads go back
|
||||
sessions.Reset()
|
||||
time.Local, sessions.PriceOf = oldZone, oldPrice
|
||||
sessions.PriceOf = oldPrice
|
||||
})
|
||||
sessions.Reset()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user