refactor(drivers): delete GetMemoryFootprint / FlushOptionalCachesAsync (#525)
Both `IDriver` members had no production call site. Their only consumer was `MemoryTracking`, deleted with the driver-tier recycle machinery in #522, and `GetMemoryFootprint`'s doc-comment claimed "polled every 30s by Core" — which had not been true since v3, even before #522 removed the poller. Owner decision on #525 was (a) delete rather than (b) build the process-wide watchdog that would consume them. The driver-side implementations are in git history and were mostly reusable; what was always missing is the trigger, and a member nobody calls is how the misleading doc-comment survived this long. Removed from `IDriver`, all 12 driver implementations, and every test stub — plus the support code that existed only to feed them, which is the part a member-only deletion would have left behind as dead weight: - MTConnect's three per-entry byte-weight constants, its cached `ProbeModelDataItemCount`, and `CountDataItems` — a full device-tree walk run on every cache fill to populate a number with no remaining reader. - OpcUaClient's `_discoveredNodeCount` field and the discovery-pass write to it. - MQTT's `ApproxBytesPerAuthoredTag`. Two behavioural notes, neither a change in what the software does: - `AbCipTemplateCache` now has no production reset path. Its only caller was AB CIP's flush, which itself had no caller — so nothing changes except how visible the fact is. Recorded on the type, along with the correction that its comment's claim about a device `ReinitializeAsync` also flushing it was never true. A UDT redefined on a live controller keeps its stale shape until the driver instance is rebuilt. - MTConnect's `/probe` cache is still dropped, by `InvalidateProbeModel()` on an Agent restart. That is now `internal` rather than `private` so the tests which used the flush purely to empty the cache drive the real path instead — they now cover a case that actually occurs in production. Several unrelated doc comments cited `FlushOptionalCachesAsync` as the canonical CAS example and are repointed to it. Tests: the ones that existed only to assert on the removed members are deleted (3 OpcUaClient, 3 Galaxy, 2 MQTT, 1 MTConnect, 1 AbCip). Worth noting one was vacuous — `FlushOptionalCachesAsync_resets_footprint_counter` asserted a footprint of 0 before the flush and 0 after with no discovery in between, so it would have passed against a flush that did nothing at all. Full suite green except 7 failures verified identical on master before and after (4 AbLegacy integration with the compose profile down, 3 Host.IntegrationTests), checked by name rather than assumed. Docs corrected where they described this as live: `DriverLifecycle.md` (which carried the #525 placeholder), `drivers/AbCip.md`, `drivers/AbLegacy.md`, and `v2/driver-stability.md` — whose "polls this every 30 s" + per-driver `DriverConfig` memory budget were never built as written and are now banner-ed as design intent rather than description. Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
This commit is contained in:
@@ -573,18 +573,6 @@ public sealed class AbCipDriverDiscoveryTests
|
||||
cache.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that FlushOptionalCachesAsync clears the template cache.</summary>
|
||||
[Fact]
|
||||
public async Task FlushOptionalCachesAsync_clears_template_cache()
|
||||
{
|
||||
var drv = new AbCipDriver(new AbCipDriverOptions(), "drv-1");
|
||||
drv.TemplateCache.Put("dev", 1, new AbCipUdtShape("T", 4, []));
|
||||
drv.TemplateCache.Count.ShouldBe(1);
|
||||
|
||||
await drv.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
drv.TemplateCache.Count.ShouldBe(0);
|
||||
}
|
||||
|
||||
// ---- helpers ----
|
||||
|
||||
/// <summary>Test implementation of IAddressSpaceBuilder that records calls.</summary>
|
||||
|
||||
@@ -210,9 +210,9 @@ public sealed class AbCipFetchUdtShapeTests
|
||||
shape.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that FlushOptionalCachesAsync empties the template cache.</summary>
|
||||
/// <summary>Verifies that a cleared template cache is re-populated from the network.</summary>
|
||||
[Fact]
|
||||
public async Task FlushOptionalCachesAsync_empties_template_cache()
|
||||
public async Task A_cleared_template_cache_refetches_from_the_controller()
|
||||
{
|
||||
var factory = new FakeTemplateReaderFactory
|
||||
{
|
||||
@@ -230,7 +230,10 @@ public sealed class AbCipFetchUdtShapeTests
|
||||
_ = await InvokeFetch(drv, "ab://10.0.0.5/1,0", 99);
|
||||
drv.TemplateCache.Count.ShouldBe(1);
|
||||
|
||||
await drv.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
// Gitea #525 deleted AbCipDriver.FlushOptionalCachesAsync, which is what used to clear this;
|
||||
// the cache's own Clear() is now the only reset, and nothing in production calls it (see the
|
||||
// AbCipTemplateCache remarks). What this test still pins is the cache-miss path below.
|
||||
drv.TemplateCache.Clear();
|
||||
drv.TemplateCache.Count.ShouldBe(0);
|
||||
|
||||
// Next fetch hits the network again.
|
||||
|
||||
@@ -174,9 +174,6 @@ public sealed class GalaxyDriverFactoryTests
|
||||
|
||||
await driver.ShutdownAsync(CancellationToken.None);
|
||||
driver.GetHealth().State.ShouldBe(DriverState.Unknown);
|
||||
|
||||
driver.GetMemoryFootprint().ShouldBe(0);
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None); // no-op shouldn't throw
|
||||
}
|
||||
|
||||
/// <summary>Verifies that reinitializing with equivalent config refreshes health.</summary>
|
||||
|
||||
+8
-43
@@ -10,8 +10,9 @@ using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Driver-level infrastructure tests for findings Driver.Galaxy-007 (dispose-CTS gate)
|
||||
/// and Driver.Galaxy-011 (GetMemoryFootprint non-zero estimate).
|
||||
/// Driver-level infrastructure tests for finding Driver.Galaxy-007 (dispose-CTS gate).
|
||||
/// Driver.Galaxy-011 (a non-zero GetMemoryFootprint estimate) was also covered here until
|
||||
/// Gitea #525 deleted that member from IDriver as unconsumed surface.
|
||||
/// </summary>
|
||||
public sealed class GalaxyDriverInfrastructureTests
|
||||
{
|
||||
@@ -22,47 +23,11 @@ public sealed class GalaxyDriverInfrastructureTests
|
||||
new GalaxyReconnectOptions());
|
||||
|
||||
// ===== Driver.Galaxy-011 regression: GetMemoryFootprint reflects registry size =====
|
||||
|
||||
/// <summary>Verifies that memory footprint is zero when there are no subscriptions.</summary>
|
||||
[Fact]
|
||||
public void GetMemoryFootprint_IsZeroWhenNoSubscriptions()
|
||||
{
|
||||
var sub = new NoOpSubscriber();
|
||||
using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub);
|
||||
|
||||
driver.GetMemoryFootprint().ShouldBe(0L);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that memory footprint is nonzero after subscriptions are active.</summary>
|
||||
[Fact]
|
||||
public async Task GetMemoryFootprint_IsNonZeroAfterSubscribe()
|
||||
{
|
||||
// When subscriptions are active the footprint estimate must be > 0 so the
|
||||
// server's memory-pressure mechanism sees the Galaxy driver as a participant.
|
||||
var sub = new NoOpSubscriber();
|
||||
using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub);
|
||||
|
||||
await driver.SubscribeAsync(["Tag.A", "Tag.B"], TimeSpan.Zero, CancellationToken.None);
|
||||
|
||||
driver.GetMemoryFootprint().ShouldBeGreaterThan(0L);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that memory footprint decreases after unsubscribing.</summary>
|
||||
[Fact]
|
||||
public async Task GetMemoryFootprint_DecreasesAfterUnsubscribe()
|
||||
{
|
||||
var sub = new NoOpSubscriber();
|
||||
using var driver = new GalaxyDriver("drv-1", Opts(), null, null, null, sub);
|
||||
|
||||
var handle = await driver.SubscribeAsync(["Tag.A", "Tag.B"], TimeSpan.Zero, CancellationToken.None);
|
||||
var afterSubscribe = driver.GetMemoryFootprint();
|
||||
afterSubscribe.ShouldBeGreaterThan(0L);
|
||||
|
||||
await driver.UnsubscribeAsync(handle, CancellationToken.None);
|
||||
var afterUnsubscribe = driver.GetMemoryFootprint();
|
||||
afterUnsubscribe.ShouldBeLessThan(afterSubscribe,
|
||||
"footprint must decrease when subscriptions are removed");
|
||||
}
|
||||
//
|
||||
// Three tests lived here asserting the footprint tracked the subscription registry. The member
|
||||
// was deleted from IDriver in Gitea #525 as unconsumed surface — nothing ever read the number —
|
||||
// and they went with it. The registry sizes they leaned on are still covered directly by the
|
||||
// subscribe/unsubscribe tests below.
|
||||
|
||||
// ===== Driver.Galaxy-007 regression: Dispose cancels the dispose CTS =====
|
||||
|
||||
|
||||
@@ -585,17 +585,21 @@ public sealed class MTConnectDiscoverTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// …and after <see cref="MTConnectDriver.FlushOptionalCachesAsync"/> has dropped that cache,
|
||||
/// discovery re-fetches and still streams the full tree. This is the whole reason discovery
|
||||
/// must go through <c>GetOrFetchProbeModelAsync</c> rather than reading the cached field: a
|
||||
/// memory-budget flush must never leave browse permanently answering "this agent has no data
|
||||
/// items".
|
||||
/// …and after the cache has been dropped, discovery re-fetches and still streams the full
|
||||
/// tree. This is the whole reason discovery must go through <c>GetOrFetchProbeModelAsync</c>
|
||||
/// rather than reading the cached field: a drop must never leave browse permanently
|
||||
/// answering "this agent has no data items".
|
||||
/// <para>Drives <see cref="MTConnectDriver.InvalidateProbeModel"/> — the Agent-restart drop,
|
||||
/// which is the only thing that empties this cache in production. It used to drive
|
||||
/// <c>FlushOptionalCachesAsync</c>, deleted with the rest of the unconsumed memory-pressure
|
||||
/// surface in Gitea #525; that path had no production caller, so this now tests a case that
|
||||
/// actually occurs.</para>
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Discover_refetches_the_probe_model_after_a_cache_flush()
|
||||
public async Task Discover_refetches_the_probe_model_after_the_cache_is_dropped()
|
||||
{
|
||||
var (driver, client) = await InitializedDriverAsync();
|
||||
await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken);
|
||||
driver.InvalidateProbeModel();
|
||||
driver.CachedProbeModel.ShouldBeNull();
|
||||
|
||||
var cap = await DiscoverAsync(driver);
|
||||
@@ -689,7 +693,7 @@ public sealed class MTConnectDiscoverTests
|
||||
public async Task Discover_propagates_an_agent_probe_failure_rather_than_streaming_an_empty_tree()
|
||||
{
|
||||
var (driver, client) = await InitializedDriverAsync();
|
||||
await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken);
|
||||
driver.InvalidateProbeModel();
|
||||
client.ProbeFailure = new HttpRequestException("agent down");
|
||||
|
||||
var builder = new CapturingBuilder();
|
||||
@@ -705,7 +709,7 @@ public sealed class MTConnectDiscoverTests
|
||||
public async Task Discover_propagates_caller_cancellation()
|
||||
{
|
||||
var (driver, _) = await InitializedDriverAsync();
|
||||
await driver.FlushOptionalCachesAsync(TestContext.Current.CancellationToken);
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
using var cts = new CancellationTokenSource();
|
||||
await cts.CancelAsync();
|
||||
|
||||
+24
-34
@@ -590,31 +590,21 @@ public sealed class MTConnectDriverLifecycleTests
|
||||
driver.GetHealth().State.ShouldBe(DriverState.Unknown);
|
||||
}
|
||||
|
||||
// ---- footprint + flush ----
|
||||
// ---- dropping the probe cache ----
|
||||
//
|
||||
// These used to drive FlushOptionalCachesAsync. Gitea #525 deleted it as an unconsumed IDriver
|
||||
// member, along with GetMemoryFootprint and the footprint test that only existed to assert on
|
||||
// it. What survives is InvalidateProbeModel — the Agent-restart drop, which is the only thing
|
||||
// that empties this cache in production, and which has the same retired-session guard and the
|
||||
// same "browse must not be left permanently empty" obligation.
|
||||
|
||||
[Fact]
|
||||
public async Task Memory_footprint_grows_when_initialize_populates_the_caches()
|
||||
public void Dropping_the_probe_cache_keeps_the_observation_index()
|
||||
{
|
||||
var (driver, _) = NewDriver();
|
||||
var (driver, _) = InitializedDriverAsync().GetAwaiter().GetResult();
|
||||
|
||||
var before = driver.GetMemoryFootprint();
|
||||
await driver.InitializeAsync("{}", CancellationToken.None);
|
||||
var after = driver.GetMemoryFootprint();
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
// Before init the only config-attributable state is the two authored tags.
|
||||
before.ShouldBeLessThan(after);
|
||||
after.ShouldBeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Flush_drops_the_probe_cache_and_keeps_the_observation_index()
|
||||
{
|
||||
var (driver, _) = await InitializedDriverAsync();
|
||||
var before = driver.GetMemoryFootprint();
|
||||
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
|
||||
driver.GetMemoryFootprint().ShouldBeLessThan(before);
|
||||
driver.CachedProbeModel.ShouldBeNull();
|
||||
|
||||
// The index is required for correctness (it IS the read surface) — never flushable.
|
||||
@@ -624,12 +614,12 @@ public sealed class MTConnectDriverLifecycleTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Flush_does_not_wedge_the_driver_the_probe_model_is_refetched_on_demand()
|
||||
public async Task Dropping_the_cache_does_not_wedge_the_driver_the_probe_model_is_refetched_on_demand()
|
||||
{
|
||||
// Task 12's DiscoverAsync reads the probe model. If flushing left no way back to it, a
|
||||
// cache-budget breach would permanently disable browse on that driver instance.
|
||||
// Task 12's DiscoverAsync reads the probe model. If dropping it left no way back, an Agent
|
||||
// restart would permanently disable browse on that driver instance.
|
||||
var (driver, client) = await InitializedDriverAsync();
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
client.ProbeCallCount.ShouldBe(1);
|
||||
|
||||
@@ -679,7 +669,7 @@ public sealed class MTConnectDriverLifecycleTests
|
||||
// it captured. What must not happen is a raw ObjectDisposedException escaping into browse —
|
||||
// the caller already handles "not connected" and should have exactly one failure mode.
|
||||
var (driver, client) = await InitializedDriverAsync();
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
// Held locally: the gate is one-shot, so the client has already cleared its own reference by
|
||||
// the time the request is parked on it.
|
||||
@@ -712,7 +702,7 @@ public sealed class MTConnectDriverLifecycleTests
|
||||
});
|
||||
|
||||
await driver.InitializeAsync("{}", CancellationToken.None);
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
var client = built[0];
|
||||
var staleModel = client.Probe;
|
||||
@@ -742,22 +732,22 @@ public sealed class MTConnectDriverLifecycleTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Flush_after_shutdown_is_a_no_op_and_does_not_resurrect_the_session()
|
||||
public async Task Dropping_the_cache_after_shutdown_is_a_no_op_and_does_not_resurrect_the_session()
|
||||
{
|
||||
// Core polls the footprint and asks for a flush on its own schedule, so a flush arriving
|
||||
// after the driver was torn down is ordinary, not exotic. It must observe the retired
|
||||
// session and do nothing — publishing a "flushed" copy of it would reinstate a session
|
||||
// holding an already-disposed client for every later caller.
|
||||
// The /sample pump can see an Agent restart at any moment, so a drop arriving after the
|
||||
// driver was torn down is ordinary, not exotic. It must observe the retired session and do
|
||||
// nothing — publishing a "dropped" copy of it would reinstate a session holding an
|
||||
// already-disposed client for every later caller.
|
||||
//
|
||||
// NOTE: this pins the retired-session guard, NOT the CompareExchange beneath it. Flush has
|
||||
// no await point, so nothing can be interleaved between its read and its publish from a
|
||||
// NOTE: this pins the retired-session guard, NOT the CompareExchange beneath it. The drop
|
||||
// has no await point, so nothing can be interleaved between its read and its publish from a
|
||||
// single-threaded test; the CAS is defence-in-depth against a genuinely concurrent
|
||||
// lifecycle call and is deliberately claimed as such rather than as tested behaviour. The
|
||||
// fetch-side CAS, which does have an await point, is covered by the test above.
|
||||
var (driver, client) = await InitializedDriverAsync();
|
||||
|
||||
await driver.ShutdownAsync(CancellationToken.None);
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
driver.InvalidateProbeModel();
|
||||
|
||||
driver.CachedProbeModel.ShouldBeNull();
|
||||
driver.GetHealth().State.ShouldBe(DriverState.Unknown);
|
||||
|
||||
+4
-5
@@ -440,8 +440,8 @@ public sealed class MTConnectHostAndRediscoverTests
|
||||
/// <summary>
|
||||
/// <b>The probe is a liveness check, never a cache writer.</b> Publishing its answer into
|
||||
/// <c>AgentSession.ProbeModel</c> would put a second writer on the field the lifecycle owns
|
||||
/// under a compare-and-swap, and would silently re-warm a cache that
|
||||
/// <c>FlushOptionalCachesAsync</c> (or an Agent restart) deliberately dropped.
|
||||
/// under a compare-and-swap, and would silently re-warm a cache that an Agent restart
|
||||
/// deliberately dropped.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Probe_ticks_never_publish_into_the_session_probe_cache()
|
||||
@@ -457,10 +457,9 @@ public sealed class MTConnectHostAndRediscoverTests
|
||||
await TickAsync(ticker);
|
||||
|
||||
driver.CachedProbeModel.ShouldBeSameAs(original);
|
||||
driver.GetMemoryFootprint().ShouldBeGreaterThan(0);
|
||||
|
||||
// …and a flushed cache stays flushed until a real consumer asks for it.
|
||||
await driver.FlushOptionalCachesAsync(Ct);
|
||||
// …and a dropped cache stays dropped until a real consumer asks for it.
|
||||
driver.InvalidateProbeModel();
|
||||
await TickAsync(ticker);
|
||||
driver.CachedProbeModel.ShouldBeNull();
|
||||
}
|
||||
|
||||
@@ -159,24 +159,6 @@ public sealed class MqttDriverDiscoveryTests
|
||||
results.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The falsifiability pin for the cache-flush contract: the last-value cache backs
|
||||
/// <c>IReadable</c>, so flushing it would silently turn every subscribed node Bad under
|
||||
/// memory pressure. <c>FlushOptionalCachesAsync</c> must leave it untouched.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task FlushOptionalCachesAsync_LeavesLastValueCacheIntact()
|
||||
{
|
||||
var driver = PlainDriver(Tag("Plant/Mqtt/dev1/Temp", "f/t"));
|
||||
driver.Subscriptions.HandleMessage("f/t", Encoding.UTF8.GetBytes("hot"), retained: false);
|
||||
|
||||
await driver.FlushOptionalCachesAsync(CancellationToken.None);
|
||||
|
||||
var results = await driver.ReadAsync(["Plant/Mqtt/dev1/Temp"], CancellationToken.None);
|
||||
results[0].StatusCode.ShouldBe(0u);
|
||||
results[0].Value.ShouldBe("hot");
|
||||
}
|
||||
|
||||
/// <summary>A malformed reinitialize delta must never fault the driver, and must not lose the running config.</summary>
|
||||
[Fact]
|
||||
public async Task ReinitializeAsync_MalformedDelta_KeepsRunningConfig_AndDoesNotFault()
|
||||
@@ -334,14 +316,6 @@ public sealed class MqttDriverDiscoveryTests
|
||||
fired.ShouldBe(0);
|
||||
}
|
||||
|
||||
/// <summary>The footprint tracks the authored table; an empty driver reports no driver-attributable bytes.</summary>
|
||||
[Fact]
|
||||
public void GetMemoryFootprint_TracksAuthoredTable()
|
||||
{
|
||||
PlainDriver().GetMemoryFootprint().ShouldBe(0);
|
||||
PlainDriver(Tag("Plant/Mqtt/dev1/Temp", "f/t")).GetMemoryFootprint().ShouldBeGreaterThan(0);
|
||||
}
|
||||
|
||||
/// <summary>Shutdown on a never-initialized driver is a no-op, not a null-reference.</summary>
|
||||
[Fact]
|
||||
public async Task ShutdownAsync_BeforeInitialize_DoesNotThrow()
|
||||
|
||||
+5
-34
@@ -13,7 +13,6 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
|
||||
/// <item>Driver.OpcUaClient-009 — WriteAsync timeout maps to BadTimeout, not BadCommunicationError</item>
|
||||
/// <item>Driver.OpcUaClient-010 — OPC UA Byte type maps to UInt16, not Int16</item>
|
||||
/// <item>Driver.OpcUaClient-012 — AutoAcceptCertificates emits a startup warning</item>
|
||||
/// <item>Driver.OpcUaClient-013 — GetMemoryFootprint and FlushOptionalCachesAsync contract</item>
|
||||
/// <item>Driver.OpcUaClient-015 — key pure-logic paths exposed for future test expansion</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
@@ -97,39 +96,11 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
|
||||
}
|
||||
|
||||
// ---- Driver.OpcUaClient-013 ----
|
||||
|
||||
/// <summary>Verifies that GetMemoryFootprint returns zero before discovery.</summary>
|
||||
[Fact]
|
||||
public void GetMemoryFootprint_returns_zero_before_first_discovery()
|
||||
{
|
||||
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-mem-013");
|
||||
// Pre-init: no nodes discovered yet, so footprint is 0.
|
||||
drv.GetMemoryFootprint().ShouldBe(0L);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that FlushOptionalCachesAsync completes without throwing.</summary>
|
||||
[Fact]
|
||||
public async Task FlushOptionalCachesAsync_completes_without_throwing()
|
||||
{
|
||||
// FlushOptionalCachesAsync is a cheap no-op before any discovery. Must not throw.
|
||||
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-flush-013");
|
||||
await Should.NotThrowAsync(async () =>
|
||||
await drv.FlushOptionalCachesAsync(TestContext.Current.CancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that FlushOptionalCachesAsync resets the footprint counter.</summary>
|
||||
[Fact]
|
||||
public async Task FlushOptionalCachesAsync_resets_footprint_counter()
|
||||
{
|
||||
// GetMemoryFootprint uses _discoveredNodeCount * 512. FlushOptionalCachesAsync resets
|
||||
// _discoveredNodeCount to 0, so a subsequent call to GetMemoryFootprint returns 0.
|
||||
// We drive this through a complete Discovery pass against a stub — instead, test the
|
||||
// contract via FlushOptionalCachesAsync clearing a counter we can observe.
|
||||
using var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions(), "opcua-flush2-013");
|
||||
drv.GetMemoryFootprint().ShouldBe(0L, "pre-flush should be 0 (no discovery)");
|
||||
await drv.FlushOptionalCachesAsync(TestContext.Current.CancellationToken);
|
||||
drv.GetMemoryFootprint().ShouldBe(0L, "post-flush should still be 0");
|
||||
}
|
||||
//
|
||||
// Three tests lived here covering GetMemoryFootprint + FlushOptionalCachesAsync. Both members
|
||||
// were deleted from IDriver in Gitea #525 as unconsumed surface, and these went with them. Worth
|
||||
// noting one was vacuous anyway: it asserted a footprint of 0 before the flush and 0 after, with
|
||||
// no discovery in between, so it would have passed against a flush that did nothing at all.
|
||||
|
||||
// ---- Driver.OpcUaClient-015: pure-logic paths ----
|
||||
|
||||
|
||||
Reference in New Issue
Block a user