diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs
index 19ef82d0..6b2f619e 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.IntegrationTests/Series/WireBackendTests.cs
@@ -71,7 +71,7 @@ public sealed class WireBackendTests
await driver.InitializeAsync("{}", ct);
await WaitFor(() =>
- driver.GetDeviceState(DeviceHost) is { FixedTreeCache: not null }, TimeSpan.FromSeconds(5));
+ driver.GetDeviceState(DeviceHost) is { FixedTreeCache: not null }, WaitTimeout);
var state = driver.GetDeviceState(DeviceHost);
state.ShouldNotBeNull();
@@ -82,7 +82,7 @@ public sealed class WireBackendTests
await WaitFor(() =>
state.LastFixedSnapshots.ContainsKey($"{DeviceHost}/Axes/X/AbsolutePosition"),
- TimeSpan.FromSeconds(3));
+ WaitTimeout);
state.LastFixedSnapshots[$"{DeviceHost}/Axes/X/AbsolutePosition"].ShouldBe(123456);
}
}
@@ -136,7 +136,7 @@ public sealed class WireBackendTests
await WaitFor(() =>
driver.GetDeviceState(DeviceHost) is { LastProgramInfo: not null },
- TimeSpan.FromSeconds(5));
+ WaitTimeout);
var snapshots = await driver.ReadAsync(
[$"{DeviceHost}/Program/Name",
@@ -194,7 +194,7 @@ public sealed class WireBackendTests
{
var state = driver.GetDeviceState(DeviceHost);
return state is not null && state.LastTimers.Count == 4;
- }, TimeSpan.FromSeconds(5));
+ }, WaitTimeout);
var snapshots = await driver.ReadAsync(
[$"{DeviceHost}/Timers/PowerOnSeconds",
@@ -265,8 +265,11 @@ public sealed class WireBackendTests
{
var state = driver.GetDeviceState(DeviceHost);
return state?.FixedTreeCache is { Capabilities.SpindleLoad: true, Capabilities.SpindleMotorSpeed: true }
- && state.LastSpindleLoads.Count >= 2;
- }, TimeSpan.FromSeconds(5));
+ && state.LastSpindleLoads.Count >= 2
+ // Motor speeds are cached after a SECOND wire round-trip (FocasDriver spindle poll), so the loads
+ // arriving first does not mean the speeds have — waiting on loads alone raced under load (#678.3).
+ && state.LastSpindleMotorSpeeds.Count >= 2;
+ }, WaitTimeout);
var snapshots = await driver.ReadAsync(
[$"{DeviceHost}/Spindle/S1/Load",
@@ -282,6 +285,10 @@ public sealed class WireBackendTests
}
}
+ /// Generous: the sim fixture answers fast, but a loaded CI host (e.g. a concurrent docker build) does not.
+ private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(15);
+
+ /// Polls ; FAILS the test with a clear message on timeout rather than returning silently.
private static async Task WaitFor(Func pred, TimeSpan timeout)
{
var deadline = DateTime.UtcNow + timeout;
@@ -290,5 +297,7 @@ public sealed class WireBackendTests
if (pred()) return;
await Task.Delay(50);
}
+ if (pred()) return;
+ Assert.Fail($"Condition not met within {timeout.TotalSeconds:0.#}s waiting on the FOCAS wire backend.");
}
}