Follow-up to #92 step 2 (#113). Not a conversion task — a counting task, and it should come before the next conversion.
Why this exists
#92 named three flaky tests in a table. #113 converted two of them and then found two more, each one by turning its own CI red:
| found |
test |
how |
| #92 |
ISO-TP STmin pacing |
listed |
| #92 |
J1939 fixed-rate BAM |
listed |
| #113 round 3 |
DiscardPendingPdus_Drains_Completed_Pdus_And_Abort_Faults |
macOS went red |
| #113 round 5 |
J1939TpTests.Parallel_Bam_And_TwoCm_Sessions_Do_Not_Interfere |
macOS went red |
Each was measured against main before being attributed, so none was caused by the branch:
| test |
base |
branch |
DiscardPendingPdus_… |
4 / 6 |
4 / 6 |
Parallel_Bam_And_TwoCm_… |
6 / 6 |
4 / 6 |
Six runs each under 2× load on a four-core box.
The problem is the discovery method. Converting members as CI trips over them makes any pull request unbounded, and it works through the population in the order the runner happens to lose its bets rather than in any order that reflects risk. #113 spent three of its five CI rounds on tests it did not touch.
What to produce
There are 117 Task.Delay / Thread.Sleep call sites under tests/CanKit.Pro.Tests/TestCases, across twenty files:
J1939/J1939NodeTests.cs 21 ProtocolActorTimerTests.cs 4
CANopen/CanOpenNodeIntegrationTests.cs 17 IsoTp/IsoTpFunctionalClientTests 4
IsoTp/IsoTpChannelIntegrationTests.cs 14 BusStateMonitorTests.cs 4
ProtocolActorTests.cs 10 RawCanSubscriptionTests.cs 3
DeadlineTests.cs 9 Uds/SimulatedUdsEcu.cs 2
J1939TpTests.cs 8 IsoTp/IsoTpBusOffTests.cs 2
Uds/UdsClientTests.cs 5 …and six files with one each
CANopen/CanOpenDynamicMappingTests.cs 5
Classify each into:
- Waits for an effect — a bounded poll for something to become observable. Fine as it stands: a slow runner makes these slower, never wrong.
- Assumes work completed in a window —
await Task.Delay(50); /* it has surely arrived */. These are the defects. All four found so far are here.
- Whole-test budgets — a
WithTimeout(5s) around work whose duration a loaded runner controls. Parallel_Bam_And_TwoCm_Sessions_Do_Not_Interfere is this shape, and it is not a sleep, so grepping for sleeps alone will miss it.
Output is a list, not a fix: which tests are in category 2 or 3, and for each, whether the interval it depends on runs on an actor that could be put on a VirtualClock.
What #113 already provides
VirtualClock with AdvanceAsync, AdvanceToAsync, RunUntilAsync and WaitUntilTimerArmedAsync; ProtocolActor.NextTimerDelayAsync as a sound arm-before-you-advance barrier; and injectable actors on IsoTpChannel and J1939NodeImpl.
J1939TpChannel and CanOpenNode still build their own actors, so anything in category 2 or 3 that lives in those layers needs the same seam first. Worth knowing before scoping, not after.
Two lessons from #113 worth carrying into any conversion
- A jump over a deadline cannot distinguish it from any shorter one. Both converted tests initially verified that pacing existed and not that it had the configured value; halving the interval left them passing. Bracket from both sides.
- The wire does not prove a timer is armed. A receiver sends flow control several statements before
ArmNCr; a sender arms STmin from a thread-pool post. Move the clock in that window and the interval is armed from the new reading and never elapses.
Both were caught in review rather than by the tests failing, which is the argument for auditing rather than converting from memory.
Follow-up to #92 step 2 (#113). Not a conversion task — a counting task, and it should come before the next conversion.
Why this exists
#92 named three flaky tests in a table. #113 converted two of them and then found two more, each one by turning its own CI red:
DiscardPendingPdus_Drains_Completed_Pdus_And_Abort_FaultsJ1939TpTests.Parallel_Bam_And_TwoCm_Sessions_Do_Not_InterfereEach was measured against
mainbefore being attributed, so none was caused by the branch:DiscardPendingPdus_…Parallel_Bam_And_TwoCm_…Six runs each under 2× load on a four-core box.
The problem is the discovery method. Converting members as CI trips over them makes any pull request unbounded, and it works through the population in the order the runner happens to lose its bets rather than in any order that reflects risk. #113 spent three of its five CI rounds on tests it did not touch.
What to produce
There are 117
Task.Delay/Thread.Sleepcall sites undertests/CanKit.Pro.Tests/TestCases, across twenty files:Classify each into:
await Task.Delay(50); /* it has surely arrived */. These are the defects. All four found so far are here.WithTimeout(5s)around work whose duration a loaded runner controls.Parallel_Bam_And_TwoCm_Sessions_Do_Not_Interfereis this shape, and it is not a sleep, so grepping for sleeps alone will miss it.Output is a list, not a fix: which tests are in category 2 or 3, and for each, whether the interval it depends on runs on an actor that could be put on a
VirtualClock.What #113 already provides
VirtualClockwithAdvanceAsync,AdvanceToAsync,RunUntilAsyncandWaitUntilTimerArmedAsync;ProtocolActor.NextTimerDelayAsyncas a sound arm-before-you-advance barrier; and injectable actors onIsoTpChannelandJ1939NodeImpl.J1939TpChannelandCanOpenNodestill build their own actors, so anything in category 2 or 3 that lives in those layers needs the same seam first. Worth knowing before scoping, not after.Two lessons from #113 worth carrying into any conversion
ArmNCr; a sender arms STmin from a thread-pool post. Move the clock in that window and the interval is armed from the new reading and never elapses.Both were caught in review rather than by the tests failing, which is the argument for auditing rather than converting from memory.