diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorConcurrentWriteTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorConcurrentWriteTests.cs index 484b2b10..588bb431 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorConcurrentWriteTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverInstanceActorConcurrentWriteTests.cs @@ -22,6 +22,18 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa { private static readonly TimeSpan WriteLatency = TimeSpan.FromMilliseconds(1200); + /// + /// Polls for writes inside the driver with an EXPLICIT short interval. Akka's + /// AwaitCondition without one polls every max / 10 of the DILATED budget — 1.5 s locally, 4.5 s + /// under CI's AKKA_TEST_TIMEFACTOR=3. The write under test is already held inside the driver by then, + /// and DriverInstanceActor.RunWriteAsync bounds it with a fixed (undilated) 5 s token, so one missed + /// first poll plus the test's own settle window cancelled it ("write timeout") before + /// ReleaseWrites(): A_queued_write_older_than_the_budget… failed every CI run that hit it + /// (hosted runs 2215 and 2260), and fails locally at timefactor 3. + /// + private void AwaitWritesEntered(SlowWritableDriver driver, int count) => + AwaitCondition(() => driver.WritesEntered == count, PresenceBudget, TimeSpan.FromMilliseconds(20)); + private (IActorRef Actor, Akka.TestKit.TestProbe Parent) Connect( IDriver driver, int? maxWritesInFlight = null, TimeSpan? queuedWriteBudget = null, TimeSpan? writeTimeout = null) { @@ -67,7 +79,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa var pending = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-a", 7), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 1, PresenceBudget); + AwaitWritesEntered(driver, 1); driver.FireDataChange("tag-a", 3.5); var published = parent.ExpectMsg(PresenceBudget); @@ -90,7 +102,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa new DriverInstanceActor.WriteAttribute($"tag-{i}", i), PresenceBudget)) .ToArray(); - AwaitCondition(() => driver.WritesEntered == 2, PresenceBudget); + AwaitWritesEntered(driver, 2); // Settle: a third write entering the driver would show up here. await Task.Delay(300); driver.WritesEntered.ShouldBe(2, "the cap let a third write into the driver"); @@ -119,13 +131,13 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa var first = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-a", 1), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 1, PresenceBudget); + AwaitWritesEntered(driver, 1); var second = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-a", 2), PresenceBudget); // Positive control: a DIFFERENT tag is not held behind tag-a. var other = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-b", 9), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 2, PresenceBudget); + AwaitWritesEntered(driver, 2); driver.EnteredRefs.ShouldBe(new[] { "tag-a", "tag-b" }); await Task.Delay(300); @@ -149,7 +161,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa var inFlight = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-0", 0), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 1, PresenceBudget); + AwaitWritesEntered(driver, 1); var stale = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-1", 1), PresenceBudget); @@ -181,7 +193,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa var hung = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-a", 1), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 1, PresenceBudget); + AwaitWritesEntered(driver, 1); var hungResult = await hung; hungResult.Success.ShouldBeFalse(); @@ -193,7 +205,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa // abandoned one is still inside it. var later = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-a", 2), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 2, PresenceBudget); + AwaitWritesEntered(driver, 2); driver.EnteredRefs.ShouldBe(new[] { "tag-a", "tag-a" }); var laterResult = await later; laterResult.Success.ShouldBeTrue(laterResult.Reason); @@ -209,7 +221,7 @@ public sealed class DriverInstanceActorConcurrentWriteTests : RuntimeActorTestBa var inFlight = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-0", 0), PresenceBudget); - AwaitCondition(() => driver.WritesEntered == 1, PresenceBudget); + AwaitWritesEntered(driver, 1); var queued = actor.Ask( new DriverInstanceActor.WriteAttribute("tag-1", 1), PresenceBudget);