Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
ed092f4
Add OPC UA Conformance Test (CTT) NUnit parity suite + supporting work
marcschier May 10, 2026
d82e15a
update
marcschier May 10, 2026
089a099
Fix AOT trim warnings IL2075 in RoleManagementHandler
marcschier May 10, 2026
4d06e07
Bump AOT test transport quotas for grown address space
marcschier May 10, 2026
b71acaa
AOT tests: tune transport quotas to 16 MB MaxMessageSize
marcschier May 10, 2026
bbb16bb
AOT tests: skip 4 BrowseFullAddressSpace-based tests on cttunit
marcschier May 10, 2026
7d52da2
Bump test fixtures' MaxMessageSize to 16 MB for grown address space
marcschier May 10, 2026
12ff7ac
ComplexTypes tests: Ignore 2 BrowseFullAddressSpace-dependent tests
marcschier May 10, 2026
d789af7
Gate optional conformance node managers behind EnableConformanceNodeM…
marcschier May 11, 2026
1cd99ac
ComplexTypes: tolerate small drift between Browse and Fetch counts
marcschier May 11, 2026
a2b5c30
Client.Tests: tolerate Browse/Fetch off-by-one (same fix as ComplexTy…
marcschier May 11, 2026
548200f
Conformance tests: tolerate slow CI runners + empty FileSystem volumes
marcschier May 11, 2026
95964a7
Revert OperationTimeout bump; cap conformance jobs at 2.5 h
marcschier May 11, 2026
36334cd
MonitorTriggering: resilient publish + session-recovery SetUp
marcschier May 11, 2026
533654e
Conformance fixture: bump SessionTimeout / OperationTimeout to 5 min
marcschier May 11, 2026
a08a976
Conformance UserManagement: bypass connect retry on expected-fail
marcschier May 11, 2026
8691c70
CI: scope AOT TestReport artifact name by OS to avoid 409 conflict
marcschier May 11, 2026
c094217
Conformance Security: bypass connect retry on expected-fail negative …
marcschier May 11, 2026
c5ec01e
ClientFixture: stop retrying connect on permanent auth/cert failures
marcschier May 11, 2026
18e8477
CI: enable --blame-hang-timeout 5m on dotnet test step
marcschier May 11, 2026
6159781
Conformance fixture: OperationTimeout back to 60 s (keep SessionTimeo…
marcschier May 12, 2026
b0f6909
Conformance fixture: shrink OperationTimeout to 30 s
marcschier May 12, 2026
6c8d0c8
Conformance fixture: active session health-check in [SetUp]
marcschier May 12, 2026
fa90489
Conformance tests: PublishWithTimeoutAsync helper + bulk migration
marcschier May 12, 2026
c83d437
Conformance FileSystem: Assert.Ignore when discovered file is empty
marcschier May 12, 2026
df16cb4
Merge master into cttunit
marcschier May 12, 2026
78dce59
Conformance: fix Certificate ownership after master merge
marcschier May 12, 2026
d2cfc55
Conformance SecurityX509User: ignore tests requiring v1.6 ICertificat…
marcschier May 12, 2026
54ee32f
Core CertificateManager test: mark refcount-counter test [NonParallel…
marcschier May 12, 2026
53e1f6b
Address PR #3750 review feedback + skip X509 v1.6 test
Copilot May 12, 2026
463053d
Merge remote-tracking branch 'origin/master' into cttunit
Copilot May 12, 2026
83c0bb3
Conformance tests: ignore BadRequestTimeout/Interrupted/ConnectionClosed
Copilot May 12, 2026
026654d
Conformance tests: more CI-flakiness tolerance + FS access tolerance
Copilot May 12, 2026
a286eb8
SubscriptionUnitTests: use TrySetResult for FastDataChangeCallback
Copilot May 12, 2026
8943d2f
ReferenceNodeManager: dispose simulation timer before semaphore
Copilot May 12, 2026
bbbe685
Conformance tests: tolerance for 2 more flaky-on-Windows-CI tests
Copilot May 12, 2026
d62b338
Conformance: SetUp tolerance helper + 1 more flaky test fix
Copilot May 12, 2026
90750f7
Conformance: route PublishWithAck through PublishWithTimeoutAsync
Copilot May 12, 2026
dd47d5f
Conformance: tolerance for 3 more keep-alive-publish flaky tests
Copilot May 12, 2026
d392bf5
Conformance: tolerance for 2 more flaky tests (Win Conformance polish)
Copilot May 12, 2026
46bb54e
Conformance: tolerance for 3 more flaky trigger-related tests
Copilot May 12, 2026
5220da3
CI: bump --blame-hang-timeout from 5m to 10m
Copilot May 12, 2026
e380a5f
Conformance: centralise transient-CI-timeout tolerance + cover 2 more…
Copilot May 12, 2026
fd4ae57
Conformance: extend tolerance helper + cover 3 more flaky tests
Copilot May 13, 2026
def5d67
Revert iteration 5 dispose pattern -- it introduced an unhandled crash
Copilot May 13, 2026
c236071
Conformance: wrap CreateMonitoredItemInitialValueReturnedAsync
Copilot May 13, 2026
8e3c85e
Conformance: wrap DefaultDiscardOldestBehavesAsTrueAsync
Copilot May 13, 2026
6f42592
Conformance: bulk tolerance for 11 more flaky tests + bump OperationT…
Copilot May 13, 2026
e327331
Conformance: wrap 2 more flaky tests from Linux run
Copilot May 13, 2026
721ff93
Rename Opc.Ua.Client.Conformance.Tests -> Opc.Ua.Conformance.Tests an…
Copilot May 13, 2026
1d73f94
Remove GlobalUsings.cs; use explicit ISession aliases per file
Copilot May 13, 2026
d849a28
Conformance: wrap PublishMin05AsyncPublishFiveConcurrentAsync
Copilot May 13, 2026
71a6306
Address review feedback (@romanett)
Copilot May 13, 2026
074bdff
Merge remote-tracking branch 'origin/master' into cttunit
Copilot May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 98 additions & 2 deletions .github/workflows/buildandtest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ jobs:
build-and-test:
name: test-${{matrix.os}}-${{matrix.csproj}}
runs-on: ${{ matrix.os }}
# Per-job time cap. The non-Conformance test projects all finish well
# inside an hour; the Conformance suite is split into shards in the
# `build-and-test-conformance` job below and has its own cap.
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -55,8 +59,11 @@ jobs:
run: dotnet build ${{ env.CSPROJECT }} --framework ${{ matrix.framework }} --configuration ${{ matrix.configuration }} /p:CustomTestTarget=${{ matrix.customtesttarget }}

- name: Test
# note: /p:CollectCoverage=true is only used to disable deterministic builds
run: dotnet test ${{ env.CSPROJECT }} --no-build --framework ${{ matrix.framework }} --logger trx --configuration ${{ matrix.configuration }} /p:CollectCoverage=true /p:CustomTestTarget=${{ matrix.customtesttarget }} --collect:"XPlat Code Coverage" --settings ./Tests/coverlet.runsettings.xml --results-directory ${{ env.TESTRESULTS }}
# note: /p:CollectCoverage=true is only used to disable deterministic builds.
# --blame-hang-timeout terminates the test host if a single test hangs > 10 min
# and dumps a process dump, instead of letting the runner stall until GHA
# evicts it. --blame writes a sequence.xml so we know which test was running.
run: dotnet test ${{ env.CSPROJECT }} --no-build --framework ${{ matrix.framework }} --logger trx --configuration ${{ matrix.configuration }} /p:CollectCoverage=true /p:CustomTestTarget=${{ matrix.customtesttarget }} --collect:"XPlat Code Coverage" --settings ./Tests/coverlet.runsettings.xml --results-directory ${{ env.TESTRESULTS }} --blame --blame-hang-timeout 10m --blame-hang-dump-type mini

- name: Upload test results
uses: actions/upload-artifact@v7
Expand All @@ -79,6 +86,95 @@ jobs:
# Use always() to always run this step to publish test results when there are test failures
if: ${{ always() }}

build-and-test-conformance:
name: test-${{matrix.os}}-Conformance-${{matrix.shard}}
runs-on: ${{ matrix.os }}
# The conformance test suite has ~3,200 NUnit fixtures. We shard it into
# 6 parallel jobs to keep wall-time per shard under ~25 min on the slow
# hosted runners (was 50–100 min as a single job). Each shard uses a
# --filter that pins to its folders AND excludes the LongRunning
# category, which gets its own shard with a more generous budget.
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
# os: [ubuntu-latest, windows-latest, macOS-latest] - disable mac os due to cost
os: [ubuntu-latest, windows-latest]
shard:
- Security
- Subscription
- InformationModel
- AlarmsHistory
- Discovery
- LongRunning
include:
- framework: 'net10.0'
dotnet-version: '10.0.x'
configuration: 'Release'
customtesttarget: net10.0
- shard: Security
filter: '(FullyQualifiedName~Conformance.Tests.Security.|FullyQualifiedName~Conformance.Tests.Auditing.)&TestCategory!=LongRunning'
- shard: Subscription
filter: '(FullyQualifiedName~Conformance.Tests.SubscriptionServices.|FullyQualifiedName~Conformance.Tests.MonitoredItemServices.)&TestCategory!=LongRunning'
- shard: InformationModel
filter: '(FullyQualifiedName~Conformance.Tests.InformationModel.|FullyQualifiedName~Conformance.Tests.AddressSpaceModel.|FullyQualifiedName~Conformance.Tests.AttributeServices.|FullyQualifiedName~Conformance.Tests.ViewServices.|FullyQualifiedName~Conformance.Tests.MethodServices.|FullyQualifiedName~Conformance.Tests.NodeManagement.|FullyQualifiedName~Conformance.Tests.AliasName.)&TestCategory!=LongRunning'
- shard: AlarmsHistory
filter: '(FullyQualifiedName~Conformance.Tests.AlarmsAndConditions.|FullyQualifiedName~Conformance.Tests.HistoricalAccess.|FullyQualifiedName~Conformance.Tests.DataAccess.|FullyQualifiedName~Conformance.Tests.FileSystem.)&TestCategory!=LongRunning'
- shard: Discovery
filter: '(FullyQualifiedName~Conformance.Tests.Discovery.|FullyQualifiedName~Conformance.Tests.DiscoveryServices.|FullyQualifiedName~Conformance.Tests.GDS.|FullyQualifiedName~Conformance.Tests.SessionServices.|FullyQualifiedName~Conformance.Tests.Miscellaneous.|FullyQualifiedName~Conformance.Tests.Mock.)&TestCategory!=LongRunning'
- shard: LongRunning
filter: 'TestCategory=LongRunning'

env:
OS: ${{ matrix.os }}
DOTNET_VERSION: ${{ matrix.dotnet-version }}
CONFIGURATION: ${{ matrix.configuration }}
CSPROJ: Conformance
CSPROJECT: "./Tests/Opc.Ua.Conformance.Tests/Opc.Ua.Conformance.Tests.csproj"
TESTRESULTS: "TestResults-Conformance-${{matrix.shard}}-${{matrix.os}}-${{matrix.framework}}-${{matrix.configuration}}"

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Setup .NET ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Set Cloud Version
shell: pwsh
run: ./.azurepipelines/set-version.ps1

- name: Build
run: dotnet build ${{ env.CSPROJECT }} --framework ${{ matrix.framework }} --configuration ${{ matrix.configuration }} /p:CustomTestTarget=${{ matrix.customtesttarget }}

- name: Test
# The Conformance suite has individual tests that legitimately take 1-3 min
# under load (keep-alive cycles, slow-sampling waits); the LongRunning
# shard concentrates those into a separate budget. Keep --blame-hang-timeout
# generous enough to absorb a normal test on top.
run: dotnet test ${{ env.CSPROJECT }} --no-build --framework ${{ matrix.framework }} --logger trx --configuration ${{ matrix.configuration }} /p:CollectCoverage=true /p:CustomTestTarget=${{ matrix.customtesttarget }} --collect:"XPlat Code Coverage" --settings ./Tests/coverlet.runsettings.xml --results-directory ${{ env.TESTRESULTS }} --filter "${{ matrix.filter }}" --blame --blame-hang-timeout 10m --blame-hang-dump-type mini

- name: Upload test results
uses: actions/upload-artifact@v7
with:
name: dotnet-results-Conformance-${{matrix.shard}}-${{matrix.os}}-${{matrix.framework}}-${{matrix.configuration}}
path: ${{ env.TESTRESULTS }}
if: ${{ always() }}

- name: Upload to Codecov
uses: codecov/codecov-action@v6
with:
name: codecov-umbrella
token: ${{ secrets.CODECOV_TOKEN }}
directory: ${{ env.TESTRESULTS }}
env_vars: CSPROJ,OS,DOTNET_VERSION,CONFIGURATION
fail_ci_if_error: false
verbose: true
if: ${{ always() }}

aot-test:
name: aot-${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down
30 changes: 30 additions & 0 deletions Applications/ConsoleLdsServer/ConsoleLdsServer.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(AppTargetFrameWorks)</TargetFrameworks>
<AssemblyName>ConsoleLdsServer</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>ConsoleLdsServer</PackageId>
<Company>OPC Foundation</Company>
<Description>.NET Console Local Discovery Server (LDS / LDS-ME)</Description>
<Copyright>Copyright © 2004-2025 OPC Foundation, Inc</Copyright>
<RootNamespace>Opc.Ua.Lds.Server.Console</RootNamespace>
<PublishAot Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net10.0'))">true</PublishAot>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Stack\Opc.Ua.Core\Opc.Ua.Core.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Configuration\Opc.Ua.Configuration.csproj" />
<ProjectReference Include="..\..\Libraries\Opc.Ua.Lds.Server\Opc.Ua.Lds.Server.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Extensions.Logging" />
</ItemGroup>
<ItemGroup>
<None Update="Lds.Server.Config.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
106 changes: 106 additions & 0 deletions Applications/ConsoleLdsServer/Lds.Server.Config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationConfiguration
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd"
xmlns="http://opcfoundation.org/UA/SDK/Configuration.xsd"
>
<ApplicationName>OPC UA Local Discovery Server</ApplicationName>
<ApplicationUri>urn:localhost:UA:LocalDiscoveryServer</ApplicationUri>
<ProductUri>uri:opcfoundation.org:LocalDiscoveryServer</ProductUri>
<ApplicationType>DiscoveryServer_3</ApplicationType>
<SecurityConfiguration>
<ApplicationCertificates>
<CertificateIdentifier>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/own</StorePath>
<SubjectName>CN=OPC UA Local Discovery Server, C=US, S=Arizona, O=OPC Foundation, DC=localhost</SubjectName>
<CertificateTypeString>RsaSha256</CertificateTypeString>
</CertificateIdentifier>
</ApplicationCertificates>
<TrustedIssuerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/issuer</StorePath>
</TrustedIssuerCertificates>
<TrustedPeerCertificates>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/trusted</StorePath>
</TrustedPeerCertificates>
<RejectedCertificateStore>
<StoreType>Directory</StoreType>
<StorePath>%LocalApplicationData%/OPC Foundation/pki/rejected</StorePath>
</RejectedCertificateStore>
<MaxRejectedCertificates>5</MaxRejectedCertificates>
<AutoAcceptUntrustedCertificates>false</AutoAcceptUntrustedCertificates>
<RejectSHA1SignedCertificates>true</RejectSHA1SignedCertificates>
<RejectUnknownRevocationStatus>true</RejectUnknownRevocationStatus>
<MinimumCertificateKeySize>2048</MinimumCertificateKeySize>
<AddAppCertToTrustedStore>false</AddAppCertToTrustedStore>
<SendCertificateChain>true</SendCertificateChain>
</SecurityConfiguration>
<TransportConfigurations></TransportConfigurations>
<TransportQuotas>
<OperationTimeout>120000</OperationTimeout>
<MaxStringLength>1048576</MaxStringLength>
<MaxByteStringLength>1048576</MaxByteStringLength>
<MaxArrayLength>65535</MaxArrayLength>
<MaxMessageSize>4194304</MaxMessageSize>
<MaxBufferSize>65535</MaxBufferSize>
<ChannelLifetime>30000</ChannelLifetime>
<SecurityTokenLifetime>3600000</SecurityTokenLifetime>
</TransportQuotas>
<!--
Note: the LDS subclasses ServerBase via DiscoveryServerBase. Per Part 12 the
standard endpoint is opc.tcp://{host}:4840. We populate <ServerConfiguration>
(not <DiscoveryServerConfiguration>) because ServerBase startup paths read
SecurityPolicies, UserTokenPolicies, and ServerCapabilities from the former.
-->
<ServerConfiguration>
<BaseAddresses>
<ua:String>opc.tcp://localhost:4840</ua:String>
</BaseAddresses>
<SecurityPolicies>
<ServerSecurityPolicy>
<SecurityMode>None_1</SecurityMode>
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#None</SecurityPolicyUri>
</ServerSecurityPolicy>
<ServerSecurityPolicy>
<SecurityMode>Sign_2</SecurityMode>
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</SecurityPolicyUri>
</ServerSecurityPolicy>
<ServerSecurityPolicy>
<SecurityMode>SignAndEncrypt_3</SecurityMode>
<SecurityPolicyUri>http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256</SecurityPolicyUri>
</ServerSecurityPolicy>
</SecurityPolicies>
<MinRequestThreadCount>5</MinRequestThreadCount>
<MaxRequestThreadCount>100</MaxRequestThreadCount>
<MaxQueuedRequestCount>2000</MaxQueuedRequestCount>
<UserTokenPolicies>
<ua:UserTokenPolicy>
<ua:TokenType>Anonymous_0</ua:TokenType>
</ua:UserTokenPolicy>
</UserTokenPolicies>
<DiagnosticsEnabled>false</DiagnosticsEnabled>
<MaxSessionCount>0</MaxSessionCount>
<MaxChannelCount>1000</MaxChannelCount>
<MaxRequestAge>600000</MaxRequestAge>
<ServerProfileArray>
<ua:String>http://opcfoundation.org/UA-Profile/Server/LocalDiscovery2017</ua:String>
</ServerProfileArray>
<ShutdownDelay>5</ShutdownDelay>
<ServerCapabilities>
<ua:String>LDS</ua:String>
</ServerCapabilities>
<SupportedPrivateKeyFormats>
<ua:String>PFX</ua:String>
<ua:String>PEM</ua:String>
</SupportedPrivateKeyFormats>
<MaxTrustListSize>0</MaxTrustListSize>
<MultiCastDnsEnabled>false</MultiCastDnsEnabled>
</ServerConfiguration>
<TraceConfiguration>
<OutputFilePath>%LocalApplicationData%/OPC Foundation/Logs/Opc.Ua.LocalDiscoveryServer.log.txt</OutputFilePath>
<DeleteOnLoad>true</DeleteOnLoad>
<TraceMasks>515</TraceMasks>
</TraceConfiguration>
</ApplicationConfiguration>
Loading
Loading