Azure / Azure/azure-functions-core-tools
Flaky test: CompactRendererFunctionBrowserTests alternate-screen test fails ~2 in 5 full-suite runs
- Dominant language
- C#
- Stars
- 1.5k
- Forks
- 498
- Avg merge
- 4d 20h
- Merged PRs (30d)
- 14
Description
## Summary
`CompactRendererFunctionBrowserTests.OnStartAsync_WhenMacOSAndAlternateBufferSupported_UsesAlternateScreen` fails intermittently with:
```
Failed to read input in non-interactive mode
```
It reproduces on a clean `vnext` checkout with no local changes.
## Reproduction
```
dotnet test Azure.Functions.Cli.slnx
```
Roughly 2 in 5 full-suite runs fail. It also fails when the class runs alone, just less often, measured at 1 in 8:
```
dotnet test test/Func.Tests/Func.Tests.csproj --filter "FullyQualifiedName~CompactRendererFunctionBrowserTests"
```
(An earlier revision of this issue said the filtered run always passes. That was too few samples; it fails there too.)
## Cause
The message comes from Spectre.Console, not from our code. `CompactRenderer` starts a background key-reading loop gated on the interaction service:
```csharp
if (_interaction.IsInteractive) // CompactRenderer.cs, OnStartAsync
```
and the loop reads through the injected console:
```csharp
ConsoleKeyInfo? key = await _console.Input.ReadKeyAsync(intercept: true, cancellationToken);
```
The console is properly injected, so this is not an ambient-console leak. The problem is that the *gate* and the console's *input capability* are two independent things. The test builds a Spectre console over a `StringWriter`, whose input profile is non-interactive, so once the gate opens `ReadKeyAsync` throws.
That it fails in both filtered and full-suite runs, at different rates, points at a race rather than a test-ordering dependency: the throw comes off an unobserved background task and only fails the test when it lands before the test finishes. The higher full-suite rate is consistent with more scheduling pressure, not with contamination from another test.
## Suggested fix
Decide the two conditions together instead of separately: don't start the key loop unless the console being rendered to can actually be read from. Whatever the loop does throw should be observed rather than left on a fire-and-forget task, so a real failure surfaces deterministically instead of as a flake in an unrelated test.
## Impact
Every full-suite run is a coin flip, which trains people to re-run on red and makes real regressions easy to wave off. It also costs a CI retry whenever it lands.
## Acceptance criteria
- [ ] The test passes consistently in a full-suite run
- [ ] The key loop's start condition accounts for whether the target console supports input
- [ ] Failures inside the loop are observed rather than swallowed by a detached task
- [ ] No `--filter`-only or serialised-collection workaround; the failure reproduces under `--filter` too, so that wouldn't even hide it
## Context
Hit repeatedly while validating #5543. Unrelated to that PR's changes.
Contributor guide
Assessment
This issue has not been assessed yet.