microsoft / microsoft/microsoft-ui-reactor
[Bug] Row-edit backward test: two entailed assertions, and its "differential isolation" block compares two arms that converge
- Dominant language
- C#
- Stars
- 646
- Forks
- 54
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 84
Description
### What happened?
`tests/Reactor.Tests/DataGridRowEditKeyboardTests.cs` →
`FocusPrevRowEditColumn_WalksBackwardAndWraps` (`:254-282` on `main`) contains two assertions
that **cannot fail independently**, and a comment that names the wrong pair as the test's
direction oracle.
The test's real oracle is sound — this is not a false green, and `FocusPrevRowEditColumn` is
genuinely covered. It is filed because both problems make the test *read* as more rigorous than
it is, which is how vacuous assertions survive review.
Constants (`:47-49`): `IdCol = 0`, `NameCol = 1`, `ScoreCol = 2`.
**1. Two entailed assertions.**
```csharp
:270 Assert.Equal(NameCol, state.FocusedColIndex); // x must be 1
:271 Assert.NotEqual(IdCol, state.FocusedColIndex); // asserts x != 0 <- ENTAILED
:280 Assert.Equal(NameCol, forward.FocusedColIndex); // x must be 1
:281 Assert.NotEqual(ScoreCol, forward.FocusedColIndex); // asserts x != 2 <- ENTAILED
```
Exhaustive over all `int x`: no value satisfies the `Equal` and fails the `NotEqual` beneath it.
Deleting `:271` and `:281` cannot change the test's verdict on any input.
`:271`'s comment — *"read-only column is skipped both ways"* — states a real and important
property, but the assertion under it does not test that property. The property is already proven,
by the two `Equal`s: they pin **every** position the backward walk visits (`Score` at `:267`,
then `Name` at `:270`), so `Id`'s absence from that sequence is the proof. There is no position
left for a further assertion to constrain.
**2. The sharper one: the "differential isolation" block compares arms that converge.**
```csharp
:275 // Differential isolation: same grid, same start, only the direction differs.
:266/:269 backward: Id -> Score -> Name (TWO steps, ends NameCol)
:279 forward: Id -> Name (ONE step, ends NameCol)
```
The two arms take **different step counts** and land on the **same column**. Their endpoint
values are both `NameCol`, so at the point the comment claims a differential, the two directions
are *indistinguishable*. An endpoint comparison between these arms would be direction-blind.
The genuine discriminator is already in the file, one step earlier, and is unlabelled:
```csharp
:267 Assert.Equal(ScoreCol, state.FocusedColIndex); // backward, step 1
:280 Assert.Equal(NameCol, forward.FocusedColIndex); // forward, step 1
```
Same start, one step each, different results. **That** is the pair that cannot both hold if the
two directions collapse — and it is what makes the test non-vacuous today.
### Steps to reproduce
Both parts are decidable statically; each also has a mutation that confirms it.
1. **Entailment.** Delete `:271` and `:281`, then
`dotnet test tests/Reactor.Tests --filter "FullyQualifiedName~FocusPrevRowEditColumn_WalksBackwardAndWraps" -p:Platform=x64 -p:SkipSignaturesGen=true`
→ still passes. Detection power is unchanged, because no `int` satisfies the `Equal` above
each and fails the `NotEqual`.
2. **Converging arms.** Compare `:270` and `:280` by inspection: both assert `NameCol`. The two
arms report the same value, so no comparison between them can distinguish direction.
3. **Where the power actually is.** Mutate `DataGridState.FocusPrevRowEditColumn()` to call
`MoveRowEditFocus(+1)` and re-run → the failure is at **`:267`** (`Expected: 2, Actual: 1`),
not at any of the assertions in the block labelled "differential isolation".
### Suggested change
- Delete `:271` and `:281`.
- Retarget the comment at `:275` so it names `:267` vs `:280` — the first step of each arm — as
the discriminating pair, and states why the endpoints are *not* comparable (unequal step
counts, converging on `NameCol`).
- Keep `:279-280`: pinning forward-from-read-only is worth asserting on its own merits. It just
is not a differential against the backward arm's endpoint.
General form, for the surrounding tests: `Assert.NotEqual(armA, armB)` earns its place only when
the expectations are **computed from the fixture**, where both arms can drift to the same value
while each still matches its own derived expectation. Against **distinct literal constants**, the
paired `Equal`s are the stronger construction and already fail loudly if the fixture degenerates.
### Related context
The 2-column editable ring is the underlying reason this test is delicate. With only `Name` and
`Score` editable, prev and next are the **same element** from any in-ring start, so direction is
observable *only* from a start outside the ring (the read-only column, or the `-1` sentinel) —
which is exactly the origin `:262` chooses, and why that choice is correct and load-bearing.
The same 2-element ring independently hid a real product bug in #1016 (*"a single Tab went
first → last → wrapped back to first and focus never appeared to move"*). A **third editable
column** in this fixture would make the directions diverge from every start and remove the
constraint from both. Worth considering alongside this change rather than after it.
### Reactor version / commit
`e0bab27f` introduced both assertions (`git log -S`), and is contained in `origin/main`.
Verified against `origin/main` @ `3f85ca49`.
### Platform
Both — headless xUnit, architecture-independent.
### .NET SDK version
10.0.302
### Windows version
N/A — headless unit test, no live WinUI objects constructed.
### Windows App SDK version
N/A — headless unit test.
### Logs / stack trace
N/A — the test passes. The finding is that two of its assertions cannot fail, and that a comment
attributes the test's discriminating power to the wrong pair of lines.
### Confirmation
- [x] I have searched existing issues and this isn't a duplicate.
- [x] This bug reproduces against the current `main` branch.
Contributor guide
Research direction
Start in tests/Reactor.Tests/DataGridRowEditKeyboardTests.cs at FocusPrevRowEditColumn_WalksBackwardAndWraps, then run the provided dotnet test filter. Remove the two entailed NotEqual assertions and retarget the differential-isolation comment to the first-step assertions at :267 and :280, while keeping the forward assertion. Done means the focused test still passes and the comment accurately describes the converging endpoints.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- testing-qa
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100