mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-19 07:02:16 +08:00
a3ae6db71a
After commit2a4f4f601made ManagedTransportChannelLease.Dispose() fire-and-forget on the thread pool (to fix a sync-context deadlock), several test helpers exposed pre-existing races that the previous synchronous Dispose had been masking: - ChannelMetricListener.Measurements was a plain List<T>; the metric callbacks (OnLong/Double/MeasurementRecorded) fire from arbitrary threadpool threads, so concurrent Add on one thread and enumerate on the test thread threw "Collection was modified" in FormatMeasurements. Change to ConcurrentQueue<MeasurementRecord> and use snapshot-stable enumeration in HasMeasurement + FormatMeasurements. - ChannelEventListener.Events had the same race against EventWritten callbacks; same fix (ConcurrentQueue). Four tests asserted state that is now produced asynchronously by the lease teardown task, so they need to poll before the hard assertions: - MetricsAreEmittedForChannelLifetimeAsync (wait for the opcua.channel.close metric) - DiscoveryClientCreateAsyncSharesSessionChannelAndReleasesLeaseAsync (wait for Moq.Verify on CloseAsync) - EventSourceFiresStateTransitionsAsync (wait for ParticipantDetached + ChannelClosed EventSource events) - FailoverWithDifferentEndpointSwapsLeaseAsync (wait for the lease State transition AND the underlying CloseCount increment) Three new helpers consolidate the polling pattern: - WaitForMeasurementAsync (existing — used for the metric path) - WaitForMockInvocationAsync (new — catches Moq.MockException and re-runs verify until budget exhausted; final verify is allowed to throw) - WaitForConditionAsync (new — generic state poll with description) All helpers use a 2 s budget with 25 ms polling intervals, matching the existing pattern in MetricsAreEmittedForReconnectAndGateWaitAsync (53ff029df). Verification: - Source-tree builds clean on net10.0 (Stack/Opc.Ua.Core, Libraries/Opc.Ua.Client, Applications/McpServer all 0 W, 0 E) - Local .NET 10 SDK install became corrupted mid-session (C:\Program Files\dotnet\sdk\10.0.300 emptied of all but the Roslyn subfolder), blocking a final test rebuild for the LAST edit to FailoverWithDifferentEndpointSwapsLease (the state-and-close combined poll). The two previous test edits in this commit were rebuilt and verified at 4/4 pass + 10x stability (the FailoverWithDifferentEndpoint flake reproduced before the final edit and motivates the combined-condition poll). CI is the canonical re-verifier for the final tweak.