Skip to content

Resolve the open CTT findings: aggregate fixes, de-DE ApplicationName and CTT configuration - #4545

Merged
marcschier merged 33 commits into
masterfrom
romanett/ctt-open-findings
Sep 25, 2026
Merged

marcschier merged 33 commits into
masterfrom
romanett/ctt-open-findings

Conversation

@romanett

Copy link
Copy Markdown
Contributor

Description

Resolves the open items of plans/ctt-issues.md (Needs clarification, Open server findings, CTT project configuration notes). Each item was checked against the OPC 10000 spec text. A server change was made only where the server was wrong, and every server change has a regression test. CTT script and oracle defects are filed in Mantis instead.

Server changes (Opc.Ua.Server)

  • NumberOfTransitions honours TreatUncertainAsBad.
    • Part 13 §5.4.3.24 does not count Bad values. §4.2.1.2 makes Uncertain values "equivalent to Bad" when TreatUncertainAsBad is True, and the aggregate definition doesn't override that.
    • Uncertain values are therefore no longer counted, and no longer used as the previous value, when the setting is True. The now unused TimeSlice.NonBadEarlyBound is removed.
  • DurationInStateZero/NonZero status follows the variable's interpolation.
    • §5.4.3.2.2: "A region is Uncertain if a region ends in a Bad or Uncertain value and SlopedInterpolation is used."
    • The calculator always built stepped status regions. So on a sloped variable, the last region stayed Good while TimeAverage2 and Total2 over the same interval were already Uncertain.
    • The duration value doesn't change.
  • AnnotationCount returns Bad_NoData and sets Partial.
    • Table 68 requires Bad_NoData for intervals before the start or after the end of data. §5.3.3.2 requires the Partial bit for incomplete intervals.
    • HistorianDispatcher now derives the start and end of data from the raw history (read with bounds) plus the annotation timestamps. It passes them to a new CalculateAnnotationCounts overload; the old overload is unchanged.
    • Three existing dispatcher tests change their expectations. For example, a node without any data now returns Bad_NoData instead of Good zeros.

Reference server sample and CTT configuration

  • de-DE ApplicationName. The server advertises de-DE in LocaleIdArray, but FindServers and GetEndpoints always returned the en-US name. The sample now keys the ApplicationName and registers en-US and de-DE translations (Part 4 §5.5.2.2 / §5.5.4.2 localeIds).
  • Subscription limit. Max Supported Subscriptions in samples/UAReferenceServer.ctt.xml and MaxSubscriptionCount in Ctt.ReferenceServer.Config.xml go from 100 to 200. Subscription Publish Min 05 003.js needs 190 subscriptions.
  • Alarm Cycle Time. The CTT template's Alarm Cycle Time is 30 s. At 60 s it equalled RequestedSessionTimeout, so a Limit/Level CU that starts a run finds its session expired (C40).

Tests only

Not changed

Mantis issues filed

Id Issue
C51 11458 GDS scripts expect NA records in query results
C52 11459 A & C Refresh scripts leak subscriptions on the shared alarm session
C53 11460 StartThreadPublish.js defaults misspelled properties
U2 11461 Minimum2 oracle on reverse reads
U4 11462 Part 13 clarification: TreatUncertainAsBad and simple bounds (DeltaBounds)

The A & C BadSubscriptionIdInvalid failures (Refresh, Refresh2, Shelving) come from CPU load: with other processes at 100 % CPU, all subscriptions of the CTT alarm session expire within one second. This happens with and without this PR (3 of 3 loaded runs, 0 of 2 idle runs).

CTT verification

CTT 1.05.06, scripts 1.05.513, compared at test-case level against a build of the starting commit:

Group Before After
Aggregates 4,562 error messages 4,186. NumberOfTransitions: 12 test cases go from error to pass. AnnotationCount 005-01.js/005-02.js pass. NumberOfTransitions 003-02.js/004-02.js now fail because of C1 (Mantis 11420): they request TreatUncertainAsBad = false, but the harness makes the server use its default of true.
Discovery 10 warnings 0. Find Servers Filter 003.js/006.js and Get Endpoints 002.js pass.
Subscription – Publish Min 05 003.js goes from warning to pass.
Historical Access, GDS, Node Management – no new failures

plans/ctt-issues.md is updated: resolved items leave the open sections, and the new Mantis issues are recorded.

Related Issues

Checklist

  • I have signed the CLA and read the CONTRIBUTING doc.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added all necessary documentation.
  • I have verified that my changes do not introduce (new) build or analyzer warnings.
  • I ran all tests locally using the UA.slnx solution against at least .net framework and .net 10, and all passed.
  • I fixed all failing and flaky tests in the CI pipelines and all CodeQL warnings.
  • I have addressed all PR feedback received.

Tests run locally on net10.0 after merging master:

Project Filter Passed
Opc.Ua.Server.Tests Aggregate, Historian, NodeManagement, Publish, Subscription 1,419
Opc.Ua.Core.Tests TcpServerChannelBuffer 65
Opc.Ua.History.Tests Aggregate, Annotation 67
Opc.Ua.Sessions.Tests discovery locale tests 8

Before the merge, the whole Opc.Ua.Sessions.Tests ClientTest fixture passed (372 passed, 0 failed).

🤖 Generated with Claude Code

romanett and others added 29 commits September 22, 2026 09:39
Full CTT run (23 parts, 3,752 test cases) against origin/master + #4477,
- C48/C49: aggregate oracle lacks the Part 13 Uncertain status and
  Calculated bit rules that #4477 implements for Min/Max(ActualTime)
- C14 withdrawn: Auditing Connections pass since the event queue size
  fix (#4480)
- AddNodes BadNodeIdExists after a child rename (#4477 x #4486)
- Security None 007.js / Basic256Sha256 005.js fail on master too
  (CloseSession latency outlasts the idle channel lifetime)
- C10 also covers A & C Confirm Test_001.js depending on alarm phase
- coverage notes for UAFX, PubSub Publisher UADP and UserDefinedCU
- ctt-testing.md section 10: how to run every group in parts

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured with a request-arrival probe build of the reference server: the
server receives CloseSession ~520 ms after the CTT timestamps it and
completes it in 2-5 ms; the CTT stops the Session's SessionThread (~550 ms)
between stamping and sending (C50). Security None 007.js / Basic256Sha256
005.js fail because TcpTransportListener.DetectInactiveChannels closes an
idle SecureChannel after ChannelLifetime (30 s) while its SecurityToken is
valid for 60 s (Part 4 5.6.2.1); with ChannelLifetime 120000 the test passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The server closes SecureChannels without traffic after ChannelLifetime.
CTT Security None 007.js and Security Basic256Sha256 005.js keep channels
without a Session idle for about 50 s and expect the newest one to still
be open; with 120000 both pass (were BadInvalidState on close).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TcpTransportListener.DetectInactiveChannels closed every channel that had
no traffic for ChannelLifetime (30 s by default), also open channels whose
security token was still valid. OPC 10000-4 5.6.2.1 keeps a SecureChannel
until it is closed or its last token expired; unused Session-less channels
are only to be evicted before reaching the channel limit. CTT Security
None 007.js and Security Basic256Sha256 005.js failed on the close of such
a channel (60 s token, about 50 s idle).

The inactivity cleanup now skips open channels whose current or renewed
token has not expired. Channels that never opened, faulted channels and
channels without a token keep the ChannelLifetime timeout, and the
oldest-unused eviction at MaxChannelCount is unchanged. An idle open
channel now stays until its token expires.

Reverts the ChannelLifetime workaround in Ctt.ReferenceServer.Config.xml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reference server sends identical UserTokenPolicies (PolicyId 2, empty
SecurityPolicyUri) on all Basic256Sha256 endpoints; only the CTT's
UaEndpointDescription.FindTokenType rewrites the cached SignAndEncrypt
copies before Security User Name Password 2 015.js compares them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Opc.Ua.Sessions.Tests ClientTest.ConnectCloseSessionCloseChannelAsync
failed on CI (ubuntu, macOS): a client that detaches from a live Session
and goes silent expects the server to close the channel after the
ChannelLifetime inactivity timeout, although its token is still valid.

Only open channels without a Session are now kept while their token is
valid (OPC 10000-4 5.6.2.1 restricts closing unused Session-less
SecureChannels); a silent channel carrying a Session ends after the
inactivity timeout as before. CTT Security None 007.js and Security
Basic256Sha256 005.js still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ReadSymmetricMessage accepts messages secured with PreviousToken until it
expires, which can be later than the current token when the lifetimes
differ. Keep a Session-less open channel while any of its current,
renewed or previous tokens is valid, and cover the lifetime mismatch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rerun of all 23 parts against origin/master plus the inactivity-cleanup
fix. Everything matches the run of 2026-09-15 except the Aggregates group,
which grew from 4,498 to 4,562 error messages: #4503 computes DeltaBounds
for Uncertain bounds, and a logged run shows server and oracle disagreeing
in both directions about which bounds are Bad and which Uncertain (U4).
Security None 007.js and Basic256Sha256 005.js pass with the default 30 s
ChannelLifetime, and Monitor Value Change V2 020.js passes again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Link the 32 issues filed on 2026-09-24 (11420-11454), the existing
reports that already cover 12 of the drafts, and the drafts held back
for further investigation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeping idle Session-less SecureChannels open until their security token
expires lets an unauthenticated client hold a channel for up to the
SecurityTokenLifetime (one hour by default) instead of ChannelLifetime.
The CTT reference server configuration raises ChannelLifetime instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CTT Security None 007.js and Security Basic256Sha256 005.js keep
SecureChannels without a Session idle for about 51 s and expect the
newest one to still be open. With ChannelLifetime 120000 both pass
again; the default of 30000 and the server code are unchanged.

Record the validation run of 2026-09-24, the date dependency of C39 and
the %TEMP% cleanup that removed the isolated CTT PKI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
List every failing CTT test case with a short abstract and the Mantis
link; the details live in Mantis. Keep the defects that are not filed
yet, the open questions, the open server findings and the project
configuration notes, and drop the findings the server has fixed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe setup, running, selections, result analysis, run comparison,
triage and the per-group recipes as current guidance, without references
to past runs, fixed issues or individual ctt-issues.md entries. Point to
ctt-issues.md for the known CTT defects to expect. The selection
generator now reads every profile set in a fixed order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nclusive

AddNodeAsync_DuplicateBrowseNameUnderParent_ReturnsBadBrowseNameDuplicatedAsync
added Object nodes without a TypeDefinition, so the first AddNodes returned
BadTypeDefinitionInvalid and the Assume made the test inconclusive; the
BadBrowseNameDuplicated check was never exercised. Pass BaseObjectType and
assert the first add instead of assuming it.

The AddNodes-after-rename finding itself (server-assigned NodeId colliding with
a renamed child) is already fixed on this branch by #4477: ReserveAddNodesNodeId
falls back to a counter NodeId for server-assigned ids and returns
BadNodeIdExists only for a client RequestedNewNodeId (OPC 10000-4 5.8.2.4:
"The requested node id is already used by another node."), covered by
IndexedBrowseNameReusePreservesExistingNodeIdentityAsync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Subscription Publish Min 05 003.js creates 5 subscriptions in each of
half the Max Supported Sessions (38 sessions, 190 subscriptions). With a
limit of 100 it reduces the session count and warns. Raise both the CTT
setting Max Supported Subscriptions and the CTT server configuration's
MaxSubscriptionCount to 200 so they stay equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The A & C collector scripts capture events for one Alarm Cycle Time on
an idle CU session before they create their subscriptions. With 60 s
the capture equals RequestedSessionTimeout (60000 ms), so a Limit/Level
CU that starts a run finds its session timed out (C40, Mantis 11448).
The reference server's alarm sources report every alarm type at most
11 s apart and the longest event chain takes about 45 s, so 30 s still
captures every type and stays below the 90 s maximum test time. This is
the value the runbook already uses for the A & C group.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reference server advertises de-DE in LocaleIdArray, but FindServers and
GetEndpoints always returned the en-US ApplicationName: the server
description's ApplicationName carries no translation key, so
ResourceManager.Translate has nothing to look up. CTT Discovery Find
Servers Filter 003.js/006.js and Get Endpoints 002.js request de-DE and
warn about the en-US result.

Key the ApplicationName and register an en-US and a de-DE translation, so
the server returns the requested locale (OPC 10000-4 5.5.2.2/5.5.4.2
localeIds).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pins the shape of CTT GDS Application Directory 005.js: a Call request
with a String input argument of MaxStringLength + 10 % in two chunks.
OPC 10000-4 §5.3 requires a Bad_EncodingLimitsExceeded ServiceFault and
§7.34 says "The requestHandle in the ResponseHeader should be set to
what was provided in the RequestHeader even if these values were not
valid." The server does this since #4488; the test fails with
RequestHandle 0 when the handle is not read from the first chunk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…AsBad is set

Part 13 §5.4.3.24 does not count Bad raw values and compares the earliest
counted value with the previous non-Bad value. §4.2.1.2 makes an Uncertain
value equivalent to Bad when TreatUncertainAsBad is True, and the
NumberOfTransitions definition does not say otherwise. The calculator
ignored the setting: it counted Uncertain values and used a preceding
Uncertain value as the previous value.

The count now skips values that are not Good under the configuration
(IsGood) and starts from the latest such value before the interval
(EarlyBound). With TreatUncertainAsBad=False the result is unchanged. The
now unused TimeSlice.NonBadEarlyBound is removed.

For the reference server's seeded pattern (index mod 10 = 7 Bad, 9
Uncertain) 24 monotonic samples give 20 transitions instead of 22, which
matches the CTT oracle (plans/ctt-issues.md U1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part 13 §5.4.3.22/.23 compute the DurationInStateZero/NonZero status with
the time-based StatusCode calculation over Simple Bounding Values.
§5.4.3.2.2 defines the regions for that calculation: "A region is
Uncertain if a region ends in a Bad or Uncertain value and
SlopedInterpolation is used. The end point has no effect on the region if
SteppedInterpolation is used."

The calculator built the status regions with stepped calculations for
every variable, so on a non-stepped variable the region ending at an
Uncertain simple end bound (for example when the raw value after the end
time is Bad) stayed Good, while TimeAverage2 and Total2 over the same
interval reported Uncertain. The status now uses GetTimeBasedStatusCode,
which follows the variable's Stepped setting as TimeAverage2, Total2 and
Minimum2 do. The duration is still summed over stepped regions that start
at a Good raw value, so the value does not change.

With TreatUncertainAsBad=True such an interval is now
Uncertain_DataSubNormal, as the CTT oracle expects (plans/ctt-issues.md
U3); with TreatUncertainAsBad=False the Uncertain region counts as Good.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part 13 §5.4.3.20 (Table 68) defines the AnnotationCount status as "Good
unless the interval is before the start of data or after the end of
data", with Bad_NoData for both cases, and sets the Partial bit "If an
interval is not a complete interval" (§5.3.3.2). The dispatcher computed
every interval as Good, Calculated, including intervals of a node without
any history and the interval that overlaps the end of data.

HistorianDispatcher now determines the start and end of data from the raw
history (read with bounds over the requested window, as the other
aggregates do) together with the annotation timestamps, and passes them
to a new CalculateAnnotationCounts overload. That overload returns
Bad_NoData for intervals entirely before or after the data and sets the
Partial bit with the same edge rules as AggregateCalculator. The existing
overload keeps its behavior (unbounded data range).

Test expectations that changed:
- CountsAnnotationsPerInterval: raw data ends at 29 s, so [20 s, 30 s) is
  Partial.
- ZeroIntervalReturnsSingleBucket: the only data (annotations at 1-3 s)
  lies inside [0 s, 30 s), so the interval is Partial.
- WithNoAnnotationsReturnsZeros is now WithoutDataReturnsBadNoData: a node
  with neither raw data nor annotations has no data, so both intervals
  are Bad_NoData instead of Good zeros.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetValueBasedStatusCode ignored TreatUncertainAsBad=False and counted
Uncertain values as neither Good nor Bad until #4503 changed it. Part 13
§5.4.3.2.1: "If TreatUncertainAsBad = False then Uncertain regions are
included with the Good regions when calculating the above ratios, if the
TreatUncertainAsBad = True then the Uncertain regions are included as Bad
regions." No test covered the value-based path, so add a Count regression
test for both settings, direct and through the historian dispatcher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OPC 10000-4 v1.05 5.14.5.1 (Publish, Description): "When a Server
receives a new Publish request that exceeds its limit it shall de-queue
the oldest Publish request and return a response with the result set to
Bad_TooManyPublishRequests."

SessionPublishQueue.PublishAsync rejected the new request instead and
kept the older ones queued. When the admission limit is reached it now
removes the head of the request queue (new requests are added last and
requeued ones first, so the head is the oldest), fails it with
BadTooManyPublishRequests through the same retire-once completion used
by the other queue paths, and queues the new request. A requeued request
was admitted before every request still queued, so it is itself the
oldest and is still the one rejected.

The admission tests that expected the new request to be rejected now
expect the oldest live request to be returned instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
OPC 10000-4 v1.05 5.14.5.1 (Publish, Description): "But a Server shall
accept more queued Publish requests than created Subscriptions. It is
expected that a Server supports several Publish requests per
Subscription."

The admission limit of SessionPublishQueue was the fixed
ServerConfiguration.MaxPublishRequestCount (20 by default), so a session
with 20 or more subscriptions got Bad_TooManyPublishRequests before it
could queue one request more than it has subscriptions. The effective
limit is now at least the number of the session's subscriptions plus
one. The queue stays bounded: the subscriptions are already limited by
ServerConfiguration.MaxSubscriptionCount.

Tests that configured a limit of one with one subscription now use two,
the lowest limit a session with one subscription can have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
U1, U3 and the aggregate calculator findings are fixed in the server,
AddNodes after a rename was fixed by #4477, the RequestHandle echo by
#4488.
The idle SecureChannel finding is covered by the CTT configuration's
ChannelLifetime and moves to the configuration notes.

New CTT defects with drafted Mantis reports: C51 (GDS scripts expect
NA records), C52 (A & C Refresh scripts leak subscriptions on the alarm
session), C53 (StartThreadPublish.js default typos) and U2 (Minimum2
oracle on reverse reads). U4 stays open pending a Part 13 clarification.
Update the configuration notes for the new template values (Alarm Cycle
Time 30 s, 200 subscriptions) and the de-DE ApplicationName, and explain
what the remaining notes would need.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
C51 11458, C52 11459, C53 11460 (CTT UA Scripts), U2 11461 (CTT UA
Binary) and the Part 13 clarification request for U4, 11462.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…indings

# Conflicts:
#	plans/ctt-issues.md
#	plans/ctt-testing.md
#	src/Opc.Ua.Server/Subscription/SessionPublishQueue.cs
#	tests/Opc.Ua.Server.Tests/SessionPublishQueueTests.cs
#	tests/Opc.Ua.Server.Tests/Subscription/SessionPublishQueueAdmissionTests.cs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 25, 2026 06:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved AnnotationCount provider behavior, raw-history handling, test isolation, and stale configuration documentation remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 1 Medium severity

Open (2)
What changed in this PR

This PR resolves CTT findings across aggregate processing, discovery localization, reference-server configuration, and regression coverage.

Changes:

  • Corrects aggregate status, transition, duration, and annotation handling.
  • Adds discovery, transport, node-management, and aggregate regression tests.
  • Updates reference-server CTT settings and tracking documentation.
File Summary
tests/​Opc.Ua.Sessions.Tests/​ClientTest.cs Adds localized discovery coverage; the test should be isolated from arbitrary external servers.
tests/​Opc.Ua.Server.Tests/​NodeManager/​AsyncCustomNodeManagerNodeManagementTests.cs Enables duplicate BrowseName regression coverage.
tests/​Opc.Ua.Server.Tests/​Historian/​HistorianDispatcherBranchTests.cs Tests AnnotationCount edge statuses.
tests/​Opc.Ua.Server.Tests/​CountAggregateCalculatorTests.cs Tests annotation and transition behavior.
tests/​Opc.Ua.Server.Tests/​AggregateCttRegressionTests.cs Adds aggregate status regression coverage.
tests/​Opc.Ua.Core.Tests/​Stack/​Transport/​TcpServerChannelBufferTests.cs Tests RequestHandle echoing in faults.
src/​Opc.Ua.Server/​Historian/​HistorianDispatcher.cs Derives AnnotationCount data boundaries; raw-history lookup requires bounded and capability-aware handling.
src/​Opc.Ua.Server/​Aggregates/​CountAggregateCalculator.cs Adds boundary-aware annotation statuses; native provider paths still require the corrected semantics.
src/​Opc.Ua.Server/​Aggregates/​AggregateCalculator.cs Removes obsolete transition-bound tracking.
samples/​UAReferenceServer.ctt.xml Updates CTT subscription capacity and alarm timing.
samples/​Reference/​ConsoleReferenceServer/​Ctt.ReferenceServer.Config.xml Raises subscription capacity; the documented runbook remains stale.
samples/​Quickstarts.Servers/​ReferenceServer/​ReferenceServer.cs Adds localized application names.
plans/​ctt-testing.md Documents CTT configuration.
plans/​ctt-issues.md Records resolved findings and Mantis issues.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Opc.Ua.Server/Aggregates/CountAggregateCalculator.cs
Comment thread src/Opc.Ua.Server/Historian/HistorianDispatcher.cs Outdated
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Code coverage

✅ Coverage gate passed.

Check Result Threshold
✅ Project line rate 87.51% (271322/310061 lines) >= 70.00%
✅ Project branch rate 77.83% >= 60.00%
✅ Patch coverage 90.32% (112/124 changed lines) >= 75.00% (> 100 changed lines)
ℹ️ Baseline delta (advisory) +13.91 pp 73.60% recorded
Uncovered changed lines
  • src/Opc.Ua.Server/Historian/HistorianDispatcher.cs: 3337, 3356, 3382, 3397, 3398, 3400, 3401, 3402, 3404, 3406, 3408, 3410

Coverage is above the recorded baseline - consider ratcheting coverage-thresholds.json.

Thresholds live in coverage-thresholds.json. Whole report before exclusions: line 86.61%, branch 76.98%.

@codecov

codecov Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.09677% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.64%. Comparing base (b4136d8) to head (d93bbe9).

Files with missing lines Patch % Lines
src/Opc.Ua.Server/Historian/HistorianDispatcher.cs 81.01% 12 Missing and 3 partials ⚠️
...c.Ua.Server/Aggregates/CountAggregateCalculator.cs 96.96% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4545      +/-   ##
==========================================
+ Coverage   81.62%   81.64%   +0.02%     
==========================================
  Files        2073     2073              
  Lines      309946   310061     +115     
  Branches    53815    53840      +25     
==========================================
+ Hits       252984   253164     +180     
+ Misses      38638    38584      -54     
+ Partials    18324    18313      -11     
Flag Coverage Δ
actions 81.64% <87.09%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...erver/Historian/SharedKeyValueHistorianProvider.cs 63.93% <100.00%> (+0.62%) ⬆️
...rc/Opc.Ua.Server/Aggregates/AggregateCalculator.cs 83.18% <ø> (+0.05%) ⬆️
...c.Ua.Server/Aggregates/CountAggregateCalculator.cs 80.00% <96.96%> (+2.22%) ⬆️
src/Opc.Ua.Server/Historian/HistorianDispatcher.cs 82.33% <81.01%> (-0.01%) ⬇️

... and 31 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

romanett and others added 3 commits September 25, 2026 09:06
Array range slicing needs RuntimeHelpers.GetSubArray, which net472, net48 and netstandard2.0 lack (CS0656).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-value historian

HistorianDispatcher read the whole raw history of the window to find the start and end of data. Read with bounds only the first value in each direction instead. SharedKeyValueHistorianProvider computes AnnotationCount natively and still used the overload without data extents, so empty nodes returned Good zeros; pass the first and last raw and annotation timestamps (Part 13 5.4.3.20, 5.3.3.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Unresolved findings remain in historian boundary handling, annotation-range calculation, and external-server test setup.

Review effort: Lite
Findings: None

Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Use global annotation edges for boundary classification

src/​Opc.Ua.Server/​Historian/​HistorianDispatcher.cs:1131

The annotation timestamps collected here are limited to the requested window, so they cannot establish the global annotation-data edges when no raw sample exists in the window (or when annotations extend beyond the raw history). For example, an annotation at t=0 and another at t=100 queried over [40,60) produces an empty annotation list and raw edges at only t=0, causing this code to report Bad_NoData even though the interval is inside the data range and should produce a calculated zero. Read the first/last annotation timestamps outside the request (or obtain equivalent provider metadata) before applying the boundary classification.

The data extent used only the annotations inside the requested window, so a window between two annotations without raw values was reported as Bad_NoData. Also read the nearest annotation before and after the window (one each), as for the raw edges (Part 13 5.4.3.20).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@marcschier
marcschier merged commit d635676 into master Sep 25, 2026
168 checks passed
@marcschier
marcschier deleted the romanett/ctt-open-findings branch September 25, 2026 10:30
marcschier pushed a commit that referenced this pull request Sep 29, 2026
…CTT regression from #4573) (#4593)

## Description

Fixes a CTT regression introduced by #4573, found in a full CTT run of
master after the merge.

**Attribute Write Index `Err-001.js` went Pass → Error.** The test
writes a range that starts beyond the end of each array
(`1073741824:1073741824+len`), with data whose size differs from the
range by one element. It expects `Bad_IndexRangeNoData` or
`Bad_WriteNotSupported`. #4573 checked the size of the written data
before it checked whether the range lies within the target, so the
server returned `Bad_IndexRangeDataMismatch`.

Part 4 §7.27 says no data exists within such a range, so
`Bad_IndexRangeNoData` is the correct result. `NumericRange.UpdateRange`
now checks the lower bounds against the target first, for arrays,
Strings, ByteStrings and matrices. A size mismatch within the target
still returns `Bad_IndexRangeDataMismatch` (Part 4 §5.11.4.4), and the
existing mismatch tests are unchanged.

Also in this PR: `plans/ctt-issues.md` C1 now lists Aggregate –
NumberOfTransitions `003-02.js` and `004-02.js`. Since #4545,
NumberOfTransitions honours TreatUncertainAsBad, and the harness always
sends `UseServerCapabilitiesDefaults = TRUE` (Mantis 11420).

## CTT run (scripts 1.05.513, all 23 parts)

The run compared master with #4573 merged (`20c2d77d5`) against the last
recorded full run (2026-09-24, `22d07a31e`) at test-case level. It also
compared against master just before the merge (`87c35b84b`).

| Change vs recorded run | Cause |
| --- | --- |
| Attribute Write Index `Err-001.js` Pass → Error | This PR fixes it;
with the fix: Pass, 0 errors in the group |
| NumberOfTransitions: 11 cases Error → Pass; `003-02`/`004-02` Pass →
Error | #4545 (intentional), CTT defect C1 |
| Subscription Basic `Err-002.js` | Local project still claimed 100
subscriptions while #4545 raised the server to 200; passes with the
checked-in template value |
| A & C Refresh2/Shelving/Confirm: 9 cases improved | — |

Every other part is identical at test-case level, apart from the usual
"Timestamp shows a delay" warnings. All remaining errors are documented
CTT defects.

## Tests

- 3 new `NumericRangeTests` (array, String, matrix: range beyond the
target with a wrong-size slice → `Bad_IndexRangeNoData`)
- `Opc.Ua.Types.Tests` (net10.0): 11006 passed, 0 failed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
antonellaBerchesCj added a commit to antonellaBerchesCj/UA-.NETStandard that referenced this pull request Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants