Files
UA-.NETStandard/tests/Opc.Ua.SourceGeneration.Core.Tests/Resources/HierarchicalReferences.NodeSet2.xml
Marc Schier 9f14ddf5de Preserve non-child hierarchical references during NodeSet import (#4491)
# Description

Preserve forward and inverse same-namespace hierarchical references that
are not already represented by the NodeSet importer's parent/child
relationship. A reference is implicit only when both its endpoints and
its exact reference type match the selected ownership relationship;
additional links do not reparent their targets.

Also fix the generated-reference duplication that blocked the previous
attempt in #4473. Inherited and declared `NodeDesign` objects can differ
structurally while resolving to the same runtime reference.
Deduplicating by the emitted reference-type ID, direction, and target ID
prevents identical `AddReference` calls from throwing during
Robotics/OpenUSD startup.

- Add 21 importer/generator regression cases, including a minimized
recursive-placeholder reproduction of the startup failure.
- Strengthen the reference-server test to check the original missing
forward links, inverse counterparts, subtype filtering, and unchanged
ownership.
- Document ownership versus graph references in `docs/NodeManagers.md`.

## Validation

Confirmed the missing-reference cases fail before the importer fix, and
the minimized duplicate-reference case fails before the generator fix.
The original Robotics/OpenUSD fixture passed before the changes, failed
with the importer-only candidate, and passes with both fixes.

| Suite | net10.0 | net48 |
| --- | --- | --- |
| SourceGeneration.Core.Tests | 3,961 passed; 8 existing skips | 3,961
passed; 8 existing skips |
| SourceGeneration.Stack.Tests | 94 passed | 91 passed |
| SourceGeneration.Tests | 185 passed | 185 passed |
| Di.Tests, including all 17 RobotOpenUsdE2eTests | 425 passed | 425
passed |
| ReferenceServerTests | 45 passed | 45 passed |

Executed using `CustomTestTarget` with `dotnet test
tools/SourceGeneration.slnx`, the Di test project, and the
`ReferenceServerTests` filter in the Server test project. Scoped
whitespace/style checks and `git diff --check` also passed for the
changed code. Pre-existing analyzer diagnostics outside the changes were
left untouched.

The full `UA.slnx` test suite has not been run locally. CI and CodeQL
results remain pending.

## Related Issues

- Fixes #4484.
- Addresses the hierarchical-reference limitation deliberately deferred
in #4473.

## 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.
- [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.
2026-09-15 13:06:47 +00:00

120 lines
5.4 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<NamespaceUris>
<Uri>http://test.org/UA/HierarchicalReferences/</Uri>
</NamespaceUris>
<Models>
<Model ModelUri="http://test.org/UA/HierarchicalReferences/" Version="1.0.0"
PublicationDate="2024-01-01T00:00:00Z">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" Version="1.05.03"
PublicationDate="2024-01-01T00:00:00Z" />
</Model>
</Models>
<Aliases>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="HasOrderedComponent">i=49</Alias>
<Alias Alias="Organizes">i=35</Alias>
</Aliases>
<UAObjectType NodeId="ns=1;i=1000" BrowseName="1:WiringType">
<DisplayName>WiringType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1001</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=1002</Reference>
</References>
</UAObjectType>
<UAObject NodeId="ns=1;i=1001" BrowseName="1:Source" ParentNodeId="ns=1;i=1000">
<DisplayName>Source</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1000</Reference>
<Reference ReferenceType="HasOrderedComponent">ns=1;i=1002</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=85</Reference>
<Reference ReferenceType="Organizes">ns=1;i=85</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=85</Reference>
<Reference ReferenceType="Organizes">i=85</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=1002" BrowseName="1:Target" ParentNodeId="ns=1;i=1000">
<DisplayName>Target</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1000</Reference>
<Reference ReferenceType="HasOrderedComponent" IsForward="false">ns=1;i=1001</Reference>
</References>
</UAObject>
<UAObjectType NodeId="ns=1;i=1100" BrowseName="1:DerivedWiringType">
<DisplayName>DerivedWiringType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">ns=1;i=1000</Reference>
</References>
</UAObjectType>
<UAObject NodeId="ns=1;i=85" BrowseName="1:External">
<DisplayName>External</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=3000" BrowseName="1:Device">
<DisplayName>Device</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=1100</Reference>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=3001</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=3002</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=3001" BrowseName="1:Source" ParentNodeId="ns=1;i=3000">
<DisplayName>Source</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=3000</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=3002" BrowseName="1:Target" ParentNodeId="ns=1;i=3000">
<DisplayName>Target</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=3000</Reference>
</References>
</UAObject>
<UAReferenceType NodeId="ns=1;i=9000" BrowseName="1:HasPeer">
<DisplayName>HasPeer</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
</References>
<InverseName>PeerOf</InverseName>
</UAReferenceType>
<UAObjectType NodeId="ns=1;i=4000" BrowseName="1:RecursiveType">
<DisplayName>RecursiveType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=4001</Reference>
<Reference ReferenceType="ns=1;i=9000">ns=1;i=4002</Reference>
</References>
</UAObjectType>
<UAObject NodeId="ns=1;i=4001" BrowseName="1:&lt;Target&gt;"
SymbolicName="Target" ParentNodeId="ns=1;i=4000">
<DisplayName>&lt;Target&gt;</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasModellingRule">i=11510</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=4002" BrowseName="1:&lt;Peer&gt;"
SymbolicName="Peer" ParentNodeId="ns=1;i=4000">
<DisplayName>&lt;Peer&gt;</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=4000</Reference>
<Reference ReferenceType="HasModellingRule">i=11508</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=4001</Reference>
</References>
</UAObject>
</UANodeSet>