build(analyzers): wire OTOPCUA0001 tree-wide + triage the surfaced hits (arch-review 07/C-1)
Inject the custom UnwrappedCapabilityCallAnalyzer as an OutputItemType=Analyzer ProjectReference from Directory.Build.props (excluding the analyzer + its test project) so OTOPCUA0001 runs on every src/ and tests/ compilation — it previously enforced its CapabilityInvoker-wrapping rule against nothing but its own 31 unit tests (the 'built-but-never-wired' failure mode). Triage of the ~280 surfaced hits, three categories: 1. RESILIENCE-DISPATCH-GAP (7 sites, DriverInstanceActor x6 + GenericDriverNodeManager x1): a REAL, previously-untracked gap the analyzer caught on first wiring — the Phase 6.1 CapabilityInvoker resilience pipeline (retry/breaker/bulkhead/telemetry) is constructed ONLY in tests and was never wired into the production dispatch layer. Scoped per-site #pragma with a greppable RESILIENCE-DISPATCH-GAP marker explicitly noting these are tracked-but-not-intentional, pending the dispatch-wiring remediation (filed as a follow-up). Keeps the analyzer live everywhere else in those projects so a NEW unwrapped call still fails the build. 2. Driver-INTERNAL self-calls (3 sites, AbCipAlarmProjection x2 + S7Driver x1): a driver's own poll/ack path calling its own capability method. The invoker wraps the driver from the dispatch layer OUTWARD; a driver re-wrapping its own internal calls would double-wrap. Genuinely intentional — scoped #pragma with that rationale. 3. Wire-level test suites + manual-testing CLIs (12 projects): invoke drivers directly by design — the analyzer's own documented intentional case. Project-level NoWarn with a comment. Verified: full solution build green, 0 OTOPCUA0001 hits; analyzer's 31 tests pass; negative control — dropping one dispatch-gap pragma re-fires OTOPCUA0001 and fails the Runtime build, proving the analyzer is genuinely live tree-wide, not disabled.
This commit is contained in:
@@ -30,4 +30,23 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<NuGetAuditSuppress Include="https://github.com/advisories/GHSA-2m69-gcr7-jv3q" />
|
<NuGetAuditSuppress Include="https://github.com/advisories/GHSA-2m69-gcr7-jv3q" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Wire the custom OTOPCUA0001 analyzer (UnwrappedCapabilityCallAnalyzer) into every project so
|
||||||
|
the CapabilityInvoker-wrapping rule is enforced tree-wide, not just against the analyzer's own
|
||||||
|
unit tests (arch-review 07/C-1 — the "built-but-never-wired" failure mode). Analyzer projects
|
||||||
|
target netstandard2.0 and are loaded into the compiler host, so OutputItemType="Analyzer" +
|
||||||
|
ReferenceOutputAssembly="false" is the correct incantation (the DLL is not linked). Excluded
|
||||||
|
from the analyzer project itself (self-reference cycle) and its test project (which already
|
||||||
|
references the analyzer directly). OTOPCUA0001 defaults to Warning; projects with genuine
|
||||||
|
intentional unwrapped calls (unit tests exercising drivers at the wire level, the invoker's own
|
||||||
|
internals) carry a scoped NoWarn / pragma — see the per-project suppressions.
|
||||||
|
-->
|
||||||
|
<ItemGroup Condition="'$(MSBuildProjectName)' != 'ZB.MOM.WW.OtOpcUa.Analyzers'
|
||||||
|
and '$(MSBuildProjectName)' != 'ZB.MOM.WW.OtOpcUa.Analyzers.Tests'">
|
||||||
|
<ProjectReference Include="$(MSBuildThisFileDirectory)src/Tooling/ZB.MOM.WW.OtOpcUa.Analyzers/ZB.MOM.WW.OtOpcUa.Analyzers.csproj"
|
||||||
|
OutputItemType="Analyzer"
|
||||||
|
ReferenceOutputAssembly="false"
|
||||||
|
PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -68,7 +68,9 @@ public class GenericDriverNodeManager(IDriver driver) : IDisposable
|
|||||||
throw new NotSupportedException($"Driver '{Driver.DriverInstanceId}' does not implement ITagDiscovery.");
|
throw new NotSupportedException($"Driver '{Driver.DriverInstanceId}' does not implement ITagDiscovery.");
|
||||||
|
|
||||||
var capturing = new CapturingBuilder(builder, _alarmSinks);
|
var capturing = new CapturingBuilder(builder, _alarmSinks);
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into GenericDriverNodeManager; tracked, NOT intentional.
|
||||||
await discovery.DiscoverAsync(capturing, ct);
|
await discovery.DiscoverAsync(capturing, ct);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
|
|
||||||
if (Driver is IAlarmSource alarmSource)
|
if (Driver is IAlarmSource alarmSource)
|
||||||
{
|
{
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this manual-testing CLI invokes driver capability methods
|
||||||
|
DIRECTLY for one-shot probe/read/write/subscribe diagnostics — not the resilient runtime dispatch
|
||||||
|
path, so CapabilityInvoker wrapping does not apply. Suppressed project-wide. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
|||||||
@@ -146,7 +146,9 @@ internal sealed class AbCipAlarmProjection : IAsyncDisposable
|
|||||||
// Best-effort — the driver's WriteAsync returns per-item status; individual ack
|
// Best-effort — the driver's WriteAsync returns per-item status; individual ack
|
||||||
// failures don't poison the batch. Swallow the return so a single faulted ack
|
// failures don't poison the batch. Swallow the return so a single faulted ack
|
||||||
// doesn't bubble out of the caller's batch expectation.
|
// doesn't bubble out of the caller's batch expectation.
|
||||||
|
#pragma warning disable OTOPCUA0001 // Driver-INTERNAL self-call: the projection is owned by AbCipDriver; CapabilityInvoker wraps the driver from the dispatch layer OUTWARD, so a driver's own internal ack path must NOT re-wrap. Intentional, not a dispatch gap.
|
||||||
_ = await _driver.WriteAsync(requests, cancellationToken).ConfigureAwait(false);
|
_ = await _driver.WriteAsync(requests, cancellationToken).ConfigureAwait(false);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Releases all resources associated with this alarm projection.</summary>
|
/// <summary>Releases all resources associated with this alarm projection.</summary>
|
||||||
@@ -225,7 +227,9 @@ internal sealed class AbCipAlarmProjection : IAsyncDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#pragma warning disable OTOPCUA0001 // Driver-INTERNAL self-call: the projection is owned by AbCipDriver; CapabilityInvoker wraps the driver from the dispatch layer OUTWARD, so a driver's own internal poll path must NOT re-wrap. Intentional, not a dispatch gap.
|
||||||
var results = await _driver.ReadAsync(refs, ct).ConfigureAwait(false);
|
var results = await _driver.ReadAsync(refs, ct).ConfigureAwait(false);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
Tick(sub, results);
|
Tick(sub, results);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) when (ct.IsCancellationRequested) { break; }
|
catch (OperationCanceledException) when (ct.IsCancellationRequested) { break; }
|
||||||
|
|||||||
@@ -1294,7 +1294,9 @@ public sealed class S7Driver
|
|||||||
|
|
||||||
private async Task PollOnceAsync(SubscriptionState state, bool forceRaise, CancellationToken ct)
|
private async Task PollOnceAsync(SubscriptionState state, bool forceRaise, CancellationToken ct)
|
||||||
{
|
{
|
||||||
|
#pragma warning disable OTOPCUA0001 // Driver-INTERNAL self-call: S7Driver's own poll loop reading via its own ReadAsync; CapabilityInvoker wraps the driver from the dispatch layer OUTWARD, so the driver's internal poll must NOT re-wrap. Intentional, not a dispatch gap.
|
||||||
var snapshots = await ReadAsync(state.TagReferences, ct).ConfigureAwait(false);
|
var snapshots = await ReadAsync(state.TagReferences, ct).ConfigureAwait(false);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
for (var i = 0; i < state.TagReferences.Count; i++)
|
for (var i = 0; i < state.TagReferences.Count; i++)
|
||||||
{
|
{
|
||||||
var tagRef = state.TagReferences[i];
|
var tagRef = state.TagReferences[i];
|
||||||
|
|||||||
@@ -572,7 +572,9 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
var results = await writable.WriteAsync(request, cts.Token);
|
var results = await writable.WriteAsync(request, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
if (results is { Count: 1 } && IsGoodStatus(results[0].StatusCode))
|
if (results is { Count: 1 } && IsGoodStatus(results[0].StatusCode))
|
||||||
{
|
{
|
||||||
replyTo.Tell(new WriteAttributeResult(true, null));
|
replyTo.Tell(new WriteAttributeResult(true, null));
|
||||||
@@ -620,7 +622,9 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
await src.AcknowledgeAsync(request, cts.Token);
|
await src.AcknowledgeAsync(request, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
_log.Info("DriverInstance {Id}: acknowledged native condition {Cond} by {User}",
|
_log.Info("DriverInstance {Id}: acknowledged native condition {Cond} by {User}",
|
||||||
_driverInstanceId, msg.ConditionId, msg.OperatorUser);
|
_driverInstanceId, msg.ConditionId, msg.OperatorUser);
|
||||||
}
|
}
|
||||||
@@ -657,8 +661,10 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
subscribable.OnDataChange += _dataChangeHandler;
|
subscribable.OnDataChange += _dataChangeHandler;
|
||||||
|
|
||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
_subscriptionHandle = await subscribable
|
_subscriptionHandle = await subscribable
|
||||||
.SubscribeAsync(msg.FullReferences, msg.PublishingInterval, cts.Token);
|
.SubscribeAsync(msg.FullReferences, msg.PublishingInterval, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
|
|
||||||
replyTo.Tell(new SubscriptionEstablished(_subscriptionHandle.DiagnosticId, msg.FullReferences.Count));
|
replyTo.Tell(new SubscriptionEstablished(_subscriptionHandle.DiagnosticId, msg.FullReferences.Count));
|
||||||
_log.Info("DriverInstance {Id}: subscribed to {Count} refs ({Diag})",
|
_log.Info("DriverInstance {Id}: subscribed to {Count} refs ({Diag})",
|
||||||
@@ -682,7 +688,9 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
await subscribable.UnsubscribeAsync(_subscriptionHandle, cts.Token);
|
await subscribable.UnsubscribeAsync(_subscriptionHandle, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -759,7 +767,9 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
_alarmSubscriptionHandle = await src.SubscribeAlarmsAsync(refs, cts.Token);
|
_alarmSubscriptionHandle = await src.SubscribeAlarmsAsync(refs, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
_log.Info("DriverInstance {Id}: native-alarm subscription established for {Count} alarm ref(s) ({Diag})",
|
_log.Info("DriverInstance {Id}: native-alarm subscription established for {Count} alarm ref(s) ({Diag})",
|
||||||
_driverInstanceId, refs.Count, _alarmSubscriptionHandle.DiagnosticId);
|
_driverInstanceId, refs.Count, _alarmSubscriptionHandle.DiagnosticId);
|
||||||
}
|
}
|
||||||
@@ -818,7 +828,9 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
|
|||||||
// resume on the actor task scheduler so the Context.Parent.Tell + Timers calls below run with a
|
// resume on the actor task scheduler so the Context.Parent.Tell + Timers calls below run with a
|
||||||
// live ActorContext. ConfigureAwait(false) would resume off-context and throw
|
// live ActorContext. ConfigureAwait(false) would resume off-context and throw
|
||||||
// NotSupportedException("no active ActorContext") — see the same warning on HandleSubscribeAsync.
|
// NotSupportedException("no active ActorContext") — see the same warning on HandleSubscribeAsync.
|
||||||
|
#pragma warning disable OTOPCUA0001 // RESILIENCE-DISPATCH-GAP (07/C-1): dispatch bypasses CapabilityInvoker — Phase 6.1 pipeline built but never wired into DriverInstanceActor; tracked, NOT intentional.
|
||||||
await discovery.DiscoverAsync(builder, cts.Token);
|
await discovery.DiscoverAsync(builder, cts.Token);
|
||||||
|
#pragma warning restore OTOPCUA0001
|
||||||
nodes = builder.Nodes.ToArray(); // immutable snapshot — never hand the builder's live list across actors
|
nodes = builder.Nodes.ToArray(); // immutable snapshot — never hand the builder's live list across actors
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
+5
@@ -7,6 +7,11 @@
|
|||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<RootNamespace>ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests</RootNamespace>
|
<RootNamespace>ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms.Tests</RootNamespace>
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): these tests invoke ScriptedAlarmSource's IAlarmSource
|
||||||
|
methods DIRECTLY to exercise its wire-level behavior, which is the analyzer's own documented
|
||||||
|
intentional case ("a unit test invoking the driver directly ... move the suppression into a
|
||||||
|
NoWarn for the test project"). Genuinely intentional — not a resilience-dispatch gap. -->
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
+5
@@ -7,6 +7,11 @@
|
|||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
<RootNamespace>ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests</RootNamespace>
|
<RootNamespace>ZB.MOM.WW.OtOpcUa.Core.VirtualTags.Tests</RootNamespace>
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): these tests invoke VirtualTagSource's IReadable/ISubscribable
|
||||||
|
methods DIRECTLY to exercise its wire-level behavior, which is the analyzer's own documented
|
||||||
|
intentional case ("a unit test invoking the driver directly ... move the suppression into a
|
||||||
|
NoWarn for the test project"). Genuinely intentional — not a resilience-dispatch gap. -->
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this suite invokes driver capability methods DIRECTLY to
|
||||||
|
exercise wire-level behavior — the analyzer's documented intentional case ("move the suppression
|
||||||
|
into a NoWarn for the test project"). Not a resilience-dispatch gap. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this suite invokes driver capability methods DIRECTLY to
|
||||||
|
exercise wire-level behavior — the analyzer's documented intentional case ("move the suppression
|
||||||
|
into a NoWarn for the test project"). Not a resilience-dispatch gap. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this suite invokes driver capability methods DIRECTLY to
|
||||||
|
exercise wire-level behavior — the analyzer's documented intentional case ("move the suppression
|
||||||
|
into a NoWarn for the test project"). Not a resilience-dispatch gap. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
+7
@@ -1,5 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<!-- OTOPCUA0001 (arch-review 07/C-1): this suite invokes driver capability methods DIRECTLY to
|
||||||
|
exercise wire-level behavior — the analyzer's documented intentional case ("move the suppression
|
||||||
|
into a NoWarn for the test project"). Not a resilience-dispatch gap. -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>$(NoWarn);OTOPCUA0001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|||||||
Reference in New Issue
Block a user