charmbracelet / charmbracelet/bubbletea
TestViewModel is flaky under the Taskfile's test flags
- Dominant language
- Go
- Stars
- 44.9k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
`task test` fails most of the time on `main` for me, currently `fc707bb`. Over 15 consecutive runs of `go test -race -count 4 -cpu 1,4 ./...` I get 13 failures. It's always `TestViewModel`, but a different subset of subtests each run, and the race detector doesn't report a data race.
Smallest reproducer I found, which fails consistently:
```
go test -race -cpu 1 -run TestViewModel ./...
```
Both flags matter. `-race` alone passes and `-cpu 1` alone passes; it needs the two together, presumably because `-race` slows things down enough and `GOMAXPROCS=1` removes the parallelism that was hiding the ordering.
A representative diff, from `mouse_allmotion`:
```
-\x1b[?25l\x1b[?2004h\x1b[?1003h\x1b[?1006h\x1b[>4;2m\x1b[=1;1u\r\x1b[Jsuccess...
+\x1b[?25l\x1b[?2004h\x1b[>4;2m\x1b[=1;1u\x1b[?u\r\x1b[Jsuccess\x1b[?1003h\x1b[?1006h...
```
As far as I can tell the renderer's flush ticker in `startRenderer` runs at the program's FPS, so an intermediate `flush(false)` can land before `Quit` is handled. When that happens the output picks up the `\x1b[?u` keyboard enhancements request, which `cursed_renderer.go` only writes when `closing` is false, and the mouse mode sequences move into a later frame. The golden files record the ordering where no intermediate flush happened.
This doesn't look like a recent regression: it reproduces the same way at c60f0c5, where the `-race`/`-count`/`-cpu` flags were added in #1691.
Setting `WithFPS(1)` in the test keeps the ticker from firing while these tests run, which fixes it for me at 15/15 with the golden files unchanged. Happy to send that as a PR if it's the direction you'd want, though you may prefer something that removes the timing dependency outright rather than widening the window.
Contributor guide
Research direction
Start by running `go test -race -cpu 1 -run TestViewModel ./...` and locate `TestViewModel`. Read `startRenderer` and `cursed_renderer.go`, focusing on the flush ticker, `Quit`, and the `closing`-dependent output ordering. Done means the test no longer depends on ticker timing and passes with the existing golden files under the reproducer flags.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli, testing
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100