test(calc): make the not-evaluated-inline assertion exact, not thread-id based
The first form compared the publish's managed thread id with the caller's, which flaked (1 in 6): the evaluation pump may reuse the pooled thread the test released while awaiting the drain, so an id match proves nothing. It now sets a ThreadLocal flag for exactly the OnDependencyValue window, so 'published inline' is answered on the stack rather than by thread identity. Re-falsified by making SchedulePump run the pump synchronously (red immediately, reverted); 5 consecutive clean suite runs after.
This commit is contained in:
@@ -118,28 +118,33 @@ public sealed class CalculationDriverTests
|
||||
/// <c>DependencyConsumerMuxAdapter.OnDependencyValueChanged</c> — i.e. on an Akka actor's own
|
||||
/// dispatcher thread. Evaluating there means a script run (bounded only by <c>RunTimeout</c>,
|
||||
/// default 2 s) occupies that thread, so the callback must queue the work and return instead.
|
||||
/// <para>The publish is the observable: it can only happen after the script ran, so a publish on
|
||||
/// the caller's own thread proves the script ran inline. The value assertion is the positive
|
||||
/// control — without it this would pass just as happily against a driver that evaluates nothing.</para>
|
||||
/// <para>The publish is the observable: it can only happen after the script ran, so a publish that
|
||||
/// lands on the caller's stack <em>between</em> entering and leaving the callback proves the script
|
||||
/// ran inline. A thread-local flag set for exactly that window is the exact test — comparing thread
|
||||
/// <i>ids</i> is not, because the pump is free to reuse the pooled thread the test released while
|
||||
/// waiting, which makes an id match mean nothing. The value assertion is the positive control:
|
||||
/// without it this would pass just as happily against a driver that evaluates nothing at all.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task A_dependency_arrival_does_not_run_the_script_on_the_callers_thread()
|
||||
{
|
||||
var (driver, published, _) = Build(
|
||||
CalcTag("calc/e/dbl", "return (double)ctx.GetTag(\"src/a\").Value * 2.0;"));
|
||||
var publishThreadId = 0;
|
||||
driver.OnDataChange += (_, _) => publishThreadId = Environment.CurrentManagedThreadId;
|
||||
using var insideCallback = new ThreadLocal<bool>();
|
||||
var publishedInline = false;
|
||||
driver.OnDataChange += (_, _) => { if (insideCallback.Value) publishedInline = true; };
|
||||
await driver.InitializeAsync("{}", TestContext.Current.CancellationToken);
|
||||
|
||||
var callerThreadId = Environment.CurrentManagedThreadId;
|
||||
insideCallback.Value = true;
|
||||
driver.OnDependencyValue("src/a", 5.0, StatusGood, DateTime.UtcNow);
|
||||
insideCallback.Value = false;
|
||||
|
||||
(await WaitUntil(() => !published.IsEmpty, TimeSpan.FromSeconds(5)))
|
||||
(await WaitUntil(() => !published.IsEmpty, TimeSpan.FromSeconds(10)))
|
||||
.ShouldBeTrue("the queued evaluation must still compute and publish the value");
|
||||
published.TryDequeue(out var e).ShouldBeTrue();
|
||||
e!.Snapshot.Value.ShouldBe(10.0);
|
||||
|
||||
publishThreadId.ShouldNotBe(callerThreadId,
|
||||
publishedInline.ShouldBeFalse(
|
||||
"the calc script must run on the driver's own evaluation pump, never on the mux callback thread");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user