4 Commits

Author SHA1 Message Date
romanett 9b034f8291 Enable nullable reference types centrally; migrate all unset and annotations-only projects (#4700)
# Description

Nullable reference types are now enabled once, in `common.props`, and
every project in the repository builds warning-clean with them.

**Configuration**
- `<Nullable>enable</Nullable>` lives in `common.props`. All per-project
`<Nullable>` settings are removed: 178 project files (enable,
annotations, and the `disable` in the three Aot test projects).
- 419 per-file `#nullable enable`/`restore` directives and the 4
`#nullable disable` directives are removed.
- Kept on purpose:
- The 5 `<auto-generated>` schema files (`UANodeSet.cs`, `UA Type
Dictionary.cs`, `OPCBinarySchema.cs`, `SecuredApplication.cs`,
`PInvokeHelper.cs`) keep `#nullable enable`. The compiler ignores the
project setting in generated code, so the directive is required there.
- `tests/Opc.Ua.Interop.LegacyPeer` keeps `disable`. It builds against
the 1.5.378 packages and does not import the repository props.
- `docs/DeveloperGuide.md` and `.github/copilot-instructions.md` now
describe the central setting.

**Migration** (the 43 unset and 13 annotations-only projects, roughly
10k diagnostics)
- **Production code: annotated at the declarations.** Covers
`tools/Opc.Ua.SourceGeneration.Core`, `src/Opc.Ua.Lds.Server` and the
LDS sample.
- Members, parameters and returns are nullable where null actually
flows.
- Null-check helpers carry `[NotNullWhen]` (`IsNull`, `IsValidXmlName`,
`IsMethodTypeDesign`).
- `CoreUtils.Format`, `Utils.Format`, `ITemplateWriter.WriteLine` take
`params object?[]`. `Template.AddReplacement` takes `string?` (rendering
already skips null replacements).
- `IRegisteredServerStore.RegisterAsync` returns
`Task<RegistrationEntry?>`: it returns null when a registration is
removed.
- Dereferences backed by an invariant (for example, the validated model)
use `!`, so runtime behaviour is unchanged.
- **Behavioural changes in production code** (small, deliberate):
- `NodeSetToModelDesign` throws `InvalidOperationException` when the
file is not a NodeSet. Before, it threw a NullReferenceException on the
next line.
- `ModelDesignValidator` starts its lookup tables empty instead of null.
A redundant `if (m_nodes == null) return;` guard is gone.
- **Test code: minimal annotations.**
  - `?` on values that can be null, `!` where the test asserts non-null.
  - `= null!` for fixture members assigned in SetUp.
- `TryGetStructure<T>(out T? x)` where the inferred `T?` broke the
`IEncodeable` constraint.
- A few redundant `== null` checks on structs (`NodeId`, `ArrayOf<T>`)
folded into the adjacent `IsNull`/`Count` checks.

**Notes for reviewers**
- The diff is large (~1,500 files) but almost entirely mechanical: `?`,
`!` and directive removal. The non-annotation changes are listed above.
- Known follow-up, not done here:
`IModelDesign.Namespaces/TargetNamespace/NamespaceUris` stay
non-nullable even though the validator returns null before it loads a
dictionary. Making them nullable would ripple through every generator
call site.

## Related Issues

- Fixes #4439

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] 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.

The "no new warnings" box covers net10.0 and net48: full non-incremental
builds of `UA.slnx`, `tools/SourceGeneration.slnx`,
`tools/MigrationAnalyzer.slnx` and the Aot test projects finished with 0
warnings and 0 errors under the repository's warnings-as-errors
settings. net9.0 and net8.0 also had no nullable diagnostics, but they
were not rebuilt after the final fixes. Tests have not been run locally
yet.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:48:13 +02:00
Marc Schier 23be306c65 Update NuGet dependencies (#4536)
## Summary

- update NuGet dependencies to their latest compatible stable releases
while retaining documented compatibility, native, security, and mocking
pins
- align the nbgv CLI with the central `Nerdbank.GitVersioning` version
(3.10.94) and fail explicitly on tool installation/update errors
- update the synchronized OpenUSD family to `0.14.0-alpha`; adapt the
Vision blank-frame fixture to the new `RenderDiagnosticsState`
constructor argument
- retain `Microsoft.CodeAnalysis` API packages at the existing 5.0/4.14
host bands: Roslyn 5.9 requires Immutable/Metadata 10.0.1+, while the
retained older compiler-host closure is intentionally pinned to 9.0.0

## Dependency updates

- `Dekaf` 1.17.0 → 1.21.1
- legacy ASP.NET Core 2.3 dependencies → 2.3.13; net8/net9
authentication and WebApi test packages → 8.0.31/9.0.20
- `Microsoft.CodeAnalysis.Analyzers` 4.14.0 → 5.9.0
- `TUnit` 1.68.0 → 1.69.0
- `Avalonia` / `Avalonia.Desktop` 12.1.2 → 12.1.3
- schema-test `System.Collections.Immutable` overrides 10.0.11 → 10.0.12
- `AzureSignTool` 3.0.0 → 7.0.1 and `NuGetKeyVaultSignTool` 3.0.45 →
3.2.3
- eight direct OpenUSD packages 0.12.0-alpha → 0.14.0-alpha

## Validation

- `dotnet restore UA.slnx --disable-parallel`
- clean `UA.slnx` Release build with `CustomTestTarget=net10.0`
- `UA.slnx` Release builds with `net48` and `netstandard2.1`
- OpenUSD and Vision test suites on net8/net9/net10: 882/882/921 and 484
passed + 1 environment skip on each target
- full `Opc.Ua.Tools.Tests` net10.0: 963 passed, 1 skipped
- source-generator package validation against a locally packed release
package set
- updated signing tools installed in an isolated directory; existing
`sign` CLI options verified without credentials
- nbgv 3.10.94 bootstrap exercised in simulated GitHub CI; disposable
committed fixture verified canonical and case-invalid release refs with
a four-component version

## Limits

No protected signing/publication, Linux/macOS native rendering, or full
stress/nightly suites were run locally.
2026-09-24 14:19:09 +00:00
Marc Schier b17007bd55 Stop the encoder tests flooding the VSTest channel with per-test output (#4219)
# Description

The `Core.Encoders` test job of run
[`31249610490`](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/31249610490)
printed nothing for 57 minutes and was killed by the 60-minute job
timeout. Issue #4213 recorded that as "hung at or before the first test"
— that reading was wrong, and the evidence to disprove it was already in
the job's own artifact.

The blame collector *had* written a hang dump and it *was* uploaded,
inside the ordinary `dotnet-results-Core.Encoders-*` artifact (11 MB of
it was the dump), where nobody looked for it. Opening it names the cause
precisely.

## What the dump shows

522 threads (408 background) in a process that finishes in 25 seconds
when healthy. Every test had already run; NUnit was in `OneTimeTearDown`
waiting on its event pump:

- the **event pump thread** is blocked in
`NUnitEventListener.TestFinished` →
`TestRunEventsHandler.HandleLogMessage` →
`LengthPrefixCommunicationChannel.Send` → `Socket.Send`, on a socket
that never drained. That send holds the channel's lock;
- `TestRunCache.SendResults` is blocked on that channel lock, and it
holds the cache lock;
- `TestRunCache`'s **one-second timer** then fired repeatedly into that
held cache lock, and the thread pool injected a fresh worker for each —
**399 threads** parked in `Monitor.Enter_Slowpath` behind a single lock;
- the NUnit run thread sits in `EventPump.Stop()` → `Thread.Join()`, so
the run can never complete.

## What put the socket under that much pressure

Volume of test output. Every encoder case dumps its input, its encoded
payload and its decoded result, the NUnit adapter forwards each captured
line to the runner as an individual message over that socket, and the
suite has 2929 cases. This is not marginal:

| | Before | After |
| --- | --- | --- |
| `Core.Encoders` TRX, green local run | **496 MB** | **4 MB** |
| `Core.Encoders` published CI artifact | **156 MB** | ~3 MB |
| Next-largest test job's artifact, for scale | 15 MB | 15 MB |
| `Client.ComplexTypes` TRX | 74 MB | 40 MB |

So the suite was shipping half a gigabyte of text through a socket, one
message per line, on every run and on every OS — and paying for it in
artifact storage even when nothing went wrong.

## The change

`EncoderCommon` gains a buffered `TestOutput` writer. The dumps are
collected per test and handed to `TestContext.Out` in `TearDown` **only
when the test did not pass** — which is the only time anyone reads them.
Nothing is lost: a failing test still reports its full
input/encoded/decoded dump under *Standard Output Messages*, verified by
injecting a deliberate failure. Explicit opt-ins
(`PrettifyAndValidateJson(..., outputFormatted: true)`) and the static
helpers keep writing immediately.

The same base class is shared by the `Client.ComplexTypes` fixtures, so
those are moved over too rather than left with half-buffered,
half-immediate output.

Two supporting changes:

- **`.github/workflows/buildandtest.yml`** — `timeout-minutes: 30` on
the `Test` step. The blame collector did its job here (dump at +50 min,
once the run went quiet), but the run still had not unwound 7 minutes
later when the *job* timeout cancelled everything, so the step needs a
bound below the job's. `Upload test results` already runs `if:
always()`, so the dump is still published.
- **`docs/DeveloperGuide.md`** — a "Test output" convention under
*Running tests*, so the next data-driven fixture does not re-introduce
this.

## Notes for reviewers

- `[Parallelizable]` on these fixtures is `ParallelScope.Self`, i.e.
fixture-level; tests inside a fixture stay sequential, so the
per-fixture buffer is as safe as the existing per-fixture
`RandomSource`/`DataGenerator` state that is already reset in `SetUp`.
- I separately confirmed that `--blame-hang-timeout` *does* fire on a
hang before the first test (minimal NUnit repro, dump at exactly the
configured timeout), which is what rules out the original "hung during
startup/discovery" theory independently of the dump.
- Local verification was scoped to the two affected suites rather than
the whole solution: `Opc.Ua.Core.Encoders.Tests` 2929/2929 on
**net10.0** *and* **net48**, `Opc.Ua.Client.ComplexTypes.Tests`
2773/2773 on net10.0, all building with 0 warnings. The full `UA.slnx`
run is left to CI.

## Related Issues

- Fixes #4213

## Checklist

- [x] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] 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.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da92151a-3da2-4caf-a39c-5556e56f0506
2026-08-09 21:49:05 +02:00
Marc Schier 7cec2d614d Move repository files to the consolidated layout 2026-07-16 06:25:54 +02:00