mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
master
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9bfc780e14 |
Update NuGet dependencies and migrate tests to NUnit 5 (#4840)
# Description Update 55 NuGet dependencies to the latest stable releases available on 2026-10-19, preserving documented compatibility pins and framework-specific version bands. Existing OpenUSD prereleases remain unchanged because no newer releases are available. Migrate existing tests to NUnit 5: await asynchronous assertions, propagate async through helpers, handle nullable predicates, and replace obsolete `Order` attributes with sequencing dependencies. Sequencing dependencies use `AllowFailure = true`; filtering a dependent test also runs prerequisites. Adjust fixture-level parallelism where NUnit dependency scheduling requires it. Keep Roslyn API/runtime pins intact; select newer Immutable/Metadata runtime packages only for test and profiling hosts requiring them. Update the approved SDK pin to 10.0.402 across local, CI, and container builds. Document NUnit 5 conventions. |
||
|
|
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> |
||
|
|
70161bc145 |
Fix Part 18 RoleSet: AddRole overwriting nodes, and Roles configured at startup never getting one (#4367)
# Description Two defects in the Part 18 Role management implementation added by #3735. They are separate root causes but both concern Roles that are not one of the nine well known ones. ## 1. `AddRole` could allocate a NodeId that is already in use `RoleManager.AllocateDynamicNodeId` counted up from `i=1` in whatever namespace it was given, with no check that the identifier was free. The namespace came from the `NamespaceUri` argument of the standard `RoleSet.AddRole` Method, so a client holding `SecurityAdmin` could pass the namespace URI of a node manager's own model and receive the first identifier in it — which every model compiler and the source generator have already used. `MaterializeDynamicRoleAsync` then created the Role at that NodeId, replacing the model's node in the address space of a running server. The root cause is that `NamespaceUri` was treated as selecting the namespace the Role's NodeId is allocated in. Per Part 18 §4.2.2 it qualifies the **BrowseName**; the NodeId is server-assigned. The fix works on three levels: - `RoleManager.AddRole` resolves `NamespaceUri` into the BrowseName namespace only — recorded as both an index and a URI on the new `RoleEntry.NamespaceUri`, so the binding can re-resolve it against the server namespace table — and always allocates the NodeId in `defaultNamespaceIndex`, which `RoleStateBinding` picks from a namespace the RoleSet's NodeManager owns. A client can no longer steer allocation into another node manager's model at all. This also fixes a second-order problem: a Role node materialized into a foreign namespace was indexed in the diagnostics manager but routed elsewhere by the `MasterNodeManager`, so it could never be resolved. - `AllocateDynamicNodeId` skips identifiers that are already taken — by another Role, or by a node in the address space, via a probe `RoleStateBinding` installs. This is the fix suggested in the issue. - Materialization refuses rather than replaces. An occupied NodeId answers `Bad_NodeIdExists`, one outside the manager's namespaces answers `Bad_NodeIdInvalid`, both logged; `AddRole` rolls the manager back so it is all-or-nothing and never leaves a Role that grants access but has no node. ## 2. A Role configured at startup was enforced but got no node in the `RoleSet` `RoleStateBinding.Initialize` discovered Roles by walking the address space only, and `MaterializeDynamicRoleAsync` ran from `OnAddRoleAsync` alone. `RoleConfigurationChangeKind.RoleAdded` for an unbound Role did nothing. Every documented way of configuring a custom Role before the server is up — overriding `StandardServer.CreateRoleManager`, `IOpcUaServerBuilder.ConfigureRoles(...)`, `AddRoleManager<TManager>()` with a pre-populated manager — therefore produced a Role that `MasterNodeManager.ValidateRolePermissions` honours but that a client cannot browse, read `Identities` from, or call `AddIdentity` on. Part 18 §4.2 expects the `RoleSet` to describe the Roles the server supports, so this is a conformance gap as well as an ergonomic one. - `InitializeAsync` now materializes every `IRoleManager.RoleIds` entry the address-space walk did not return. - `OnRoleConfigurationChanged` handles `RoleAdded` for Roles that are not bound yet, so a Role added by a custom `IRoleManager` at runtime also appears. The manager raises the event synchronously from inside itself, so that work is queued rather than awaited in the handler. - Both go through one idempotent, serialized `EnsureRoleMaterializedAsync`, which is also where the NodeId checks from defect 1 live. ## Third-order fix: `ConfigureRoles` allocated NodeIds against a throwaway namespace table `ApplyRoleConfiguration` ran inside the DI factory for `IRoleManager`, where the server's namespace table does not exist yet. It built a local `NamespaceTable` and defaulted to index 1, so a configured custom Role was allocated in a namespace the RoleSet's NodeManager does not own — which the refusal added above would then correctly reject, leaving the Role invisible again and defect 2 unfixed for the `ConfigureRoles` path. The options are now staged on the manager (`RoleManager.PendingConfiguration`) and applied by the binding once the address space is up, against the real namespace table. Settings on Roles that already exist — the nine well-known ones — are still applied eagerly in the DI factory, so resolving `IRoleManager` before start-up behaves as it did. ## Notes for reviewers - **`RoleStateBinding.Bind` becomes `BindAsync`.** Materializing needs `AddPredefinedNodeAsync`. The only caller was `ServerInternalData`, in an already-async method. This is a public API change on a type introduced in `2.0.0-preview.2`. - **`AddRole`'s returned NodeId namespace changes** for a call that passes a non-empty `NamespaceUri`: it is now the server-chosen namespace rather than the caller's. This is the fix, and the existing XML doc on `IRoleManager.AddRole` already described this behaviour ("a fresh NodeId is allocated in `defaultNamespaceIndex`") — the implementation was what deviated. The BrowseName namespace is unchanged. - **`AddRole` is now all-or-nothing.** Materialization failure used to be logged and swallowed, returning `Good`. It now rolls the manager back and returns the reason, per the issue's "refusing would at least turn silent corruption into a diagnosable error". - **`RoleEntry` gains `NamespaceUri`** and its internal constructor gains a parameter. - The unit fixture in `RoleStateBindingTests` now logs "not in a namespace owned by the RoleSet's NodeManager" for eight well-known Roles. That is a fixture artifact — its test node manager does not own `ns=0`, unlike a real `DiagnosticsNodeManager` — not a real-server condition. - Unrelated but adjacent: `AddRoleHandler_TypedRoleStateUpgradedByBinding_CanCallAddIdentity` added a Role named `Engineer`, which `AddRole` rejects as a duplicate browse name. Its `Assume` never passed and its body never ran, on master too. Renamed to a non-well-known name so it exercises what it claims. ## Testing - `RoleManagerTests`: NodeId allocated in the default namespace while the BrowseName keeps the requested URI, unregistered URI rejected, identifiers reported in use by the address space skipped, an existing Role's NodeId never reused. - `RoleStateBindingTests`: `AddRole` with a foreign model namespace leaves the model's own node at `i=1` untouched; a stub `IRoleManager` that hands back an occupied NodeId gets `Bad_NodeIdExists` and a rollback; a Role configured before the address space existed is materialized and referenced by the `RoleSet`; `ConfigureRoles` options are applied at bind time; a Role added on the manager after bind is materialized. - `ServerFluentApiHostingTests`: end-to-end on a hosted server — a custom Role from `ConfigureRoles` has a node under the `RoleSet` carrying its configured identity mapping. - `RoleManagementIntegrationTests`: end-to-end against the reference server over an encrypted channel — `AddRole` with the model namespace URI qualifies the BrowseName only and the Role is browsable. Locally on .NET 10: `Opc.Ua.Server.Tests` 4801 passed, `Opc.Ua.Features.Tests` 136 passed, `Opc.Ua.Core.Security.Tests` 556 passed, `UA.slnx` builds with no new warnings. ## Related Issues - Fixes #4361 Found while building an OPC UA Part 18 sample for OPCFoundation/UA-.NETStandard-Samples#818. ## 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. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
7cec2d614d | Move repository files to the consolidated layout |