mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-20 16:52:07 +08:00
62658d1a44
# Description Fix runtime schema resolution and provide a supported DI-free construction path for generated data types. - Resolve registered data types by local/index-form and namespace-URI identifiers without using BrowseName as a namespace mapping. - Preserve identifier kind in URI registry keys, so numeric zero and empty GUID/string/ByteString identifiers remain distinct in nonzero namespaces, including during replacement. - Reconstruct typed identifiers during factory URI-to-index fallback while retaining exact lookup precedence and unknown-namespace/remote-server guards. - Make the built-in JSON, XSD, and BSD generators public, stabilize `EncodeableFactoryDefinitionSource`, and document generated activator/factory usage. - Keep the stable-API assertion effective across the netstandard2.1/net8.0 polyfill boundary by inspecting attribute metadata without referencing the ambiguous CLR type. - Merge the latest `master` without dropping upstream changes. ## Related Issues - Fixes #4423 - Fixes #4424 ## Validation - `Opc.Ua.Core.Schema.Tests`: 157/157 on `net10.0` - `Opc.Ua.Core.Schema.Tests`: 157/157 on `net48` - `Opc.Ua.Core.Schema.Tests`: 157/157 with `CustomTestTarget=netstandard2.1` and its `net8.0` consumer - Managed `SchemaAotTests`: 4/4 - NativeAOT Release publish and filtered `SchemaAotTests`: 4/4 - All three review reproductions pass against rebuilt assemblies. ## 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. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: b17fd166-fdef-4660-a9d5-97c0e69f1bd5
457 lines
21 KiB
C#
457 lines
21 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
|
|
*
|
|
* OPC Foundation MIT License 1.00
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The complete license agreement can be found here:
|
|
* http://opcfoundation.org/License/MIT/1.00/
|
|
* ======================================================================*/
|
|
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml;
|
|
using Moq;
|
|
using NUnit.Framework;
|
|
|
|
// The encodeable type registry API remains experimental.
|
|
#pragma warning disable UA_NETStandard_1
|
|
|
|
namespace Opc.Ua.Schema.Tests
|
|
{
|
|
/// <summary>
|
|
/// Tests for resolving data type definitions from the encodeable factory.
|
|
/// </summary>
|
|
[TestFixture]
|
|
[Category("Schema")]
|
|
public class EncodeableFactoryDefinitionSourceTests
|
|
{
|
|
[Test]
|
|
public void DefinitionSourceIsStableApi()
|
|
{
|
|
Assert.That(
|
|
typeof(EncodeableFactoryDefinitionSource).GetCustomAttributesData()
|
|
.Select(attribute => attribute.AttributeType.FullName),
|
|
Does.Not.Contain("System.Diagnostics.CodeAnalysis.ExperimentalAttribute"));
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveReturnsDefinitionFromFactoryType()
|
|
{
|
|
StructureDefinition definition = CreateDefinition();
|
|
var typeId = new ExpandedNodeId(new NodeId(4001, 1));
|
|
IEncodeableFactory factory = CreateFactory(typeId, "FactoryType", definition);
|
|
var source = new EncodeableFactoryDefinitionSource(factory, new NamespaceTable());
|
|
|
|
bool resolved = source.TryResolve(typeId, out UaTypeDescription? description);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.True);
|
|
Assert.That(description!.Definition, Is.SameAs(definition));
|
|
Assert.That(description.Name, Is.EqualTo("FactoryType"));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveReturnsFalseForUnknownType()
|
|
{
|
|
StructureDefinition definition = CreateDefinition();
|
|
var knownId = new ExpandedNodeId(new NodeId(4001, 1));
|
|
IEncodeableFactory factory = CreateFactory(knownId, "FactoryType", definition);
|
|
var source = new EncodeableFactoryDefinitionSource(factory, new NamespaceTable());
|
|
|
|
bool resolved = source.TryResolve(new ExpandedNodeId(new NodeId(9999, 1)), out UaTypeDescription? description);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.False);
|
|
Assert.That(description, Is.Null);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveNodeIdFindsUriFormFactoryType()
|
|
{
|
|
const string namespaceUri = "http://test.org/standin";
|
|
var namespaceUris = new NamespaceTable();
|
|
namespaceUris.Append(namespaceUri);
|
|
StructureDefinition definition = CreateDefinition();
|
|
var typeId = new ExpandedNodeId(4003, namespaceUri);
|
|
var standIn = new Encoders.Structure(
|
|
new XmlQualifiedName("UriFactoryType", namespaceUri),
|
|
typeId,
|
|
ExpandedNodeId.Null,
|
|
ExpandedNodeId.Null,
|
|
definition,
|
|
new Dictionary<string, BuiltInType> { ["Value"] = BuiltInType.Int32 });
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEncodeableType(typeId, standIn).Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, namespaceUris);
|
|
var sourceWithoutMapping = new EncodeableFactoryDefinitionSource(
|
|
factory,
|
|
new NamespaceTable());
|
|
|
|
bool nodeIdResolved = source.TryResolve(
|
|
new NodeId(4003, 1),
|
|
out UaTypeDescription? nodeIdDescription);
|
|
bool expandedNodeIdResolved = source.TryResolve(
|
|
new ExpandedNodeId(new NodeId(4003, 1)),
|
|
out UaTypeDescription? expandedNodeIdDescription);
|
|
var provider = new DefaultSchemaProvider(source, [new Json.JsonSchemaGenerator()]);
|
|
bool schemaResolved = provider.TryGetSchema(
|
|
new ExpandedNodeId(new NodeId(4003, 1)),
|
|
UaSchemaFormat.JsonCompact,
|
|
UaSchemaScope.Type,
|
|
out IUaSchema? schema);
|
|
bool unresolved = sourceWithoutMapping.TryResolve(
|
|
new NodeId(4003, 1),
|
|
out UaTypeDescription? missing);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(nodeIdResolved, Is.True);
|
|
Assert.That(nodeIdDescription!.Definition, Is.SameAs(definition));
|
|
Assert.That(nodeIdDescription.NamespaceUri, Is.EqualTo(namespaceUri));
|
|
Assert.That(expandedNodeIdResolved, Is.True);
|
|
Assert.That(expandedNodeIdDescription!.Definition, Is.SameAs(definition));
|
|
Assert.That(schemaResolved, Is.True);
|
|
Assert.That(schema, Is.Not.Null);
|
|
Assert.That(unresolved, Is.False);
|
|
Assert.That(missing, Is.Null);
|
|
});
|
|
}
|
|
|
|
[TestCaseSource(typeof(SchemaTestData), nameof(SchemaTestData.TypeIdentifierCases))]
|
|
public void TryResolveUriFindsIndexedFactoryType(NodeId localId)
|
|
{
|
|
var namespaceUris = new NamespaceTable();
|
|
namespaceUris.Append(SchemaTestData.TestNamespace);
|
|
var definition = new EnumDefinition
|
|
{
|
|
Fields = [new EnumField { Name = "Enabled", Value = 1 }]
|
|
};
|
|
var standIn = new Encoders.Enumeration(
|
|
new XmlQualifiedName("IndexedType", SchemaTestData.TestNamespace),
|
|
definition);
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEnumeratedType(new ExpandedNodeId(localId), standIn).Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, namespaceUris);
|
|
ExpandedNodeId uriId = NodeId.ToExpandedNodeId(localId, namespaceUris);
|
|
|
|
Assert.That(source.TryResolve(localId, out UaTypeDescription? indexed), Is.True);
|
|
Assert.That(indexed!.Definition, Is.SameAs(definition));
|
|
Assert.That(source.TryResolve(uriId, out UaTypeDescription? expanded), Is.True);
|
|
Assert.That(expanded!.Definition, Is.SameAs(definition));
|
|
var provider = new DefaultSchemaProvider(source, [new Json.JsonSchemaGenerator()]);
|
|
Assert.That(
|
|
provider.TryGetSchema(uriId, UaSchemaFormat.JsonCompact, UaSchemaScope.Type, out IUaSchema? schema),
|
|
Is.True);
|
|
Assert.That(schema!.ToSchemaString(), Does.Contain("IndexedType").And.Contain("Enabled"));
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveDoesNotNormalizeUnknownNamespacesOrRemoteIds()
|
|
{
|
|
var namespaceUris = new NamespaceTable();
|
|
namespaceUris.Append(SchemaTestData.TestNamespace);
|
|
var localId = new NodeId(7701, SchemaTestData.TestNamespaceIndex);
|
|
var standIn = new Encoders.Enumeration(
|
|
new XmlQualifiedName("LocalType", SchemaTestData.TestNamespace),
|
|
new EnumDefinition());
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEnumeratedType(new ExpandedNodeId(localId), standIn).Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, namespaceUris);
|
|
ArrayOf<ExpandedNodeId> unresolvedIds =
|
|
[
|
|
new ExpandedNodeId(localId, SchemaTestData.OtherNamespace),
|
|
new ExpandedNodeId(new NodeId(7701, SchemaTestData.OtherNamespaceIndex)),
|
|
new ExpandedNodeId(localId, SchemaTestData.TestNamespace, serverIndex: 1),
|
|
new ExpandedNodeId(localId, serverIndex: 1)
|
|
];
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
foreach (ExpandedNodeId typeId in unresolvedIds)
|
|
{
|
|
Assert.That(source.TryResolve(typeId, out UaTypeDescription? description), Is.False);
|
|
Assert.That(description, Is.Null);
|
|
}
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolvePrefersExactRegistration()
|
|
{
|
|
var namespaceUris = new NamespaceTable();
|
|
namespaceUris.Append(SchemaTestData.TestNamespace);
|
|
var indexedId = new ExpandedNodeId(new NodeId(7701, SchemaTestData.TestNamespaceIndex));
|
|
var uriId = new ExpandedNodeId(7701, SchemaTestData.TestNamespace);
|
|
var indexedDefinition = new EnumDefinition();
|
|
var uriDefinition = new EnumDefinition();
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder
|
|
.AddEnumeratedType(indexedId, new Encoders.Enumeration(
|
|
new XmlQualifiedName("IndexedType", SchemaTestData.TestNamespace), indexedDefinition))
|
|
.AddEnumeratedType(uriId, new Encoders.Enumeration(
|
|
new XmlQualifiedName("UriType", SchemaTestData.TestNamespace), uriDefinition))
|
|
.Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, namespaceUris);
|
|
|
|
Assert.That(source.TryResolve(indexedId, out UaTypeDescription? indexed), Is.True);
|
|
Assert.That(indexed!.Definition, Is.SameAs(indexedDefinition));
|
|
Assert.That(source.TryResolve(uriId, out UaTypeDescription? expanded), Is.True);
|
|
Assert.That(expanded!.Definition, Is.SameAs(uriDefinition));
|
|
}
|
|
|
|
[Test]
|
|
public void ConstructorRejectsNullArguments()
|
|
{
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(
|
|
() => new EncodeableFactoryDefinitionSource(null!, new NamespaceTable()),
|
|
Throws.ArgumentNullException.With.Property("ParamName").EqualTo("factory"));
|
|
Assert.That(
|
|
() => new EncodeableFactoryDefinitionSource(factory, null!),
|
|
Throws.ArgumentNullException.With.Property("ParamName").EqualTo("namespaceUris"));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void GetNamespaceTypesReturnsUniqueTypesFromKnownFactoryIds()
|
|
{
|
|
StructureDefinition definition = CreateDefinition();
|
|
var typeId = new ExpandedNodeId(new NodeId(5101, 1));
|
|
var encodingId = new ExpandedNodeId(new NodeId(5102, 1));
|
|
var standIn = new Opc.Ua.Encoders.Structure(
|
|
new XmlQualifiedName("NamespaceStructure", "http://test.org/standin"),
|
|
typeId,
|
|
encodingId,
|
|
ExpandedNodeId.Null,
|
|
definition,
|
|
new Dictionary<string, BuiltInType> { ["Value"] = BuiltInType.Int32 });
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder
|
|
.AddEncodeableType(typeId, standIn)
|
|
.AddEncodeableType(encodingId, standIn)
|
|
.Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, new NamespaceTable());
|
|
|
|
IReadOnlyCollection<UaTypeDescription> emptyNamespace = source.GetNamespaceTypes(string.Empty);
|
|
IReadOnlyCollection<UaTypeDescription> matchingNamespace = source.GetNamespaceTypes("http://test.org/standin");
|
|
IReadOnlyCollection<UaTypeDescription> missingNamespace = source.GetNamespaceTypes("http://missing");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(emptyNamespace, Is.Empty);
|
|
Assert.That(matchingNamespace, Has.Count.EqualTo(1));
|
|
Assert.That(matchingNamespace.Single().Name, Is.EqualTo("NamespaceStructure"));
|
|
Assert.That(missingNamespace, Is.Empty);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void CompositeResolverFallsThroughToFactorySource()
|
|
{
|
|
StructureDefinition definition = CreateDefinition();
|
|
var typeId = new ExpandedNodeId(new NodeId(4002, 1));
|
|
IEncodeableFactory factory = CreateFactory(typeId, "CompositeType", definition);
|
|
var registry = new DataTypeDefinitionRegistry();
|
|
var composite = new CompositeDataTypeDefinitionResolver(
|
|
[registry, new EncodeableFactoryDefinitionSource(factory, new NamespaceTable())]);
|
|
|
|
bool resolved = composite.TryResolve(typeId, out UaTypeDescription? description);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.True);
|
|
Assert.That(description!.Name, Is.EqualTo("CompositeType"));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void CompositeResolverCoversNullFalseNodeIdAndNamespaceMergeCases()
|
|
{
|
|
Assert.That(
|
|
() => new CompositeDataTypeDefinitionResolver(null!),
|
|
Throws.ArgumentNullException.With.Property("ParamName").EqualTo("resolvers"));
|
|
|
|
UaTypeDescription first = SchemaTestData.Enumeration(5201, "First", ("A", 0));
|
|
UaTypeDescription duplicate = SchemaTestData.Enumeration(5201, "Duplicate", ("B", 0));
|
|
UaTypeDescription second = SchemaTestData.Enumeration(5202, "Second", ("C", 0));
|
|
var firstRegistry = new DataTypeDefinitionRegistry();
|
|
firstRegistry.Add(first);
|
|
firstRegistry.Add(duplicate);
|
|
var secondRegistry = new DataTypeDefinitionRegistry();
|
|
secondRegistry.Add(second);
|
|
var composite = new CompositeDataTypeDefinitionResolver([firstRegistry, secondRegistry]);
|
|
|
|
bool resolved = composite.TryResolve(first.TypeId.InnerNodeId, out UaTypeDescription? description);
|
|
bool missing = composite.TryResolve(new NodeId(9999, SchemaTestData.TestNamespaceIndex), out _);
|
|
IReadOnlyCollection<UaTypeDescription> namespaceTypes = composite.GetNamespaceTypes(
|
|
SchemaTestData.TestNamespace);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.True);
|
|
Assert.That(description!.Name, Is.EqualTo("Duplicate"));
|
|
Assert.That(missing, Is.False);
|
|
Assert.That(namespaceTypes.Select(t => t.Name), Is.EqualTo(CompositeNamespaceTypeNames));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveReturnsDefinitionFromStandInStructure()
|
|
{
|
|
const string namespaceUri = "http://test.org/standin";
|
|
StructureDefinition definition = CreateDefinition();
|
|
var typeId = new ExpandedNodeId(new NodeId(5001, 1));
|
|
var standIn = new Encoders.Structure(
|
|
new XmlQualifiedName("StandInStructure", namespaceUri),
|
|
typeId,
|
|
ExpandedNodeId.Null,
|
|
ExpandedNodeId.Null,
|
|
definition,
|
|
new Dictionary<string, BuiltInType> { ["Value"] = BuiltInType.Int32 });
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEncodeableType(typeId, standIn).Commit();
|
|
var namespaceUris = new NamespaceTable();
|
|
namespaceUris.Append(namespaceUri);
|
|
var source = new EncodeableFactoryDefinitionSource(factory, namespaceUris);
|
|
|
|
bool indexResolved = source.TryResolve(typeId, out UaTypeDescription? indexDescription);
|
|
bool uriResolved = source.TryResolve(
|
|
new ExpandedNodeId(typeId.InnerNodeId, namespaceUri),
|
|
out UaTypeDescription? uriDescription);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(indexResolved, Is.True);
|
|
Assert.That(indexDescription!.Definition, Is.SameAs(definition));
|
|
Assert.That(indexDescription.Name, Is.EqualTo("StandInStructure"));
|
|
Assert.That(uriResolved, Is.True);
|
|
Assert.That(uriDescription!.Definition, Is.SameAs(definition));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveReturnsDefinitionFromStandInEnumeration()
|
|
{
|
|
var definition = new EnumDefinition
|
|
{
|
|
Fields =
|
|
[
|
|
new EnumField { Name = "A", Value = 0 },
|
|
new EnumField { Name = "B", Value = 1 }
|
|
]
|
|
};
|
|
var typeId = new ExpandedNodeId(new NodeId(5002, 1));
|
|
var standIn = new Encoders.Enumeration(
|
|
new XmlQualifiedName("StandInEnum", "http://test.org/standin"),
|
|
definition);
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEnumeratedType(typeId, standIn).Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, new NamespaceTable());
|
|
|
|
bool resolved = source.TryResolve(typeId, out UaTypeDescription? description);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.True);
|
|
Assert.That(description!.Definition, Is.SameAs(definition));
|
|
Assert.That(description.Name, Is.EqualTo("StandInEnum"));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TryResolveReturnsDefinitionFromStandInOptionSet()
|
|
{
|
|
var definition = new EnumDefinition
|
|
{
|
|
Fields =
|
|
[
|
|
new EnumField { Name = "Bit0", Value = 0 },
|
|
new EnumField { Name = "Bit1", Value = 1 }
|
|
]
|
|
};
|
|
var typeId = new ExpandedNodeId(new NodeId(5003, 1));
|
|
var standIn = new Encoders.OptionSet(
|
|
new XmlQualifiedName("StandInOptionSet", "http://test.org/standin"),
|
|
typeId,
|
|
ExpandedNodeId.Null,
|
|
ExpandedNodeId.Null,
|
|
definition);
|
|
IEncodeableFactory factory = EncodeableFactory.Create();
|
|
factory.Builder.AddEncodeableType(typeId, standIn).Commit();
|
|
var source = new EncodeableFactoryDefinitionSource(factory, new NamespaceTable());
|
|
|
|
bool resolved = source.TryResolve(typeId, out UaTypeDescription? description);
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(resolved, Is.True);
|
|
Assert.That(description!.Definition, Is.SameAs(definition));
|
|
Assert.That(description.Name, Is.EqualTo("StandInOptionSet"));
|
|
});
|
|
}
|
|
|
|
private static StructureDefinition CreateDefinition()
|
|
{
|
|
return new StructureDefinition
|
|
{
|
|
BaseDataType = DataTypeIds.Structure,
|
|
StructureType = StructureType.Structure,
|
|
Fields = new[]
|
|
{
|
|
SchemaTestData.Field("Value", SchemaTestData.BuiltIn(BuiltInType.Int32))
|
|
}
|
|
};
|
|
}
|
|
|
|
private static IEncodeableFactory CreateFactory(
|
|
ExpandedNodeId typeId,
|
|
string name,
|
|
DataTypeDefinition definition)
|
|
{
|
|
var typeMock = new Mock<IEncodeableType>();
|
|
typeMock.SetupGet(t => t.XmlName)
|
|
.Returns(new XmlQualifiedName(name, "http://test.org/factory"));
|
|
typeMock.As<IDataTypeDefinitionSource>()
|
|
.Setup(s => s.GetDataTypeDefinition(It.IsAny<NamespaceTable>()))
|
|
.Returns(definition);
|
|
|
|
IEncodeableType? encodeableType = typeMock.Object;
|
|
IEnumeratedType? enumeratedType = null;
|
|
var factoryMock = new Mock<IEncodeableFactory>();
|
|
factoryMock.Setup(f => f.TryGetEncodeableType(typeId, out encodeableType)).Returns(true);
|
|
factoryMock.Setup(f => f.TryGetEnumeratedType(
|
|
It.IsAny<ExpandedNodeId>(), out enumeratedType)).Returns(false);
|
|
return factoryMock.Object;
|
|
}
|
|
|
|
private static readonly string[] CompositeNamespaceTypeNames = ["Duplicate", "Second"];
|
|
}
|
|
}
|