Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
5 changes: 3 additions & 2 deletions src/Opc.Ua.Types/Encoders/BinaryEncoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -806,12 +806,13 @@ public void WriteLocalizedText(string? fieldName, LocalizedText value)
// calculate the encoding.
byte encoding = 0;

if (value.Locale != null)
Comment thread
marcschier marked this conversation as resolved.
Resolved
// Empty fields carry no information; omit them as required by OPC 10000-6, Table 24.
if (!string.IsNullOrEmpty(value.Locale))
{
encoding |= (byte)LocalizedTextEncodingBits.Locale;
}

if (value.Text != null)
if (!string.IsNullOrEmpty(value.Text))
Comment thread
cristipogacean marked this conversation as resolved.
Resolved
{
encoding |= (byte)LocalizedTextEncodingBits.Text;
}
Expand Down
19 changes: 16 additions & 3 deletions tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,11 @@
int expectedOrder = value.Equals(Variant.Null) ? 0 : int.MinValue;
await Assert.That(value.CompareTo(value)).IsEqualTo(expectedOrder);
await Assert.That(value.GetHashCode()).IsEqualTo(input.GetHashCode());
await CheckAsync(input, value, default(VariantBuilder)).ConfigureAwait(false);
var binaryExpected = new LocalizedText(
string.IsNullOrEmpty(input.Locale) ? null : input.Locale,
string.IsNullOrEmpty(input.Text) ? null : input.Text);
await CheckAsync(input, value, default(VariantBuilder), binaryExpected)
.ConfigureAwait(false);
}
}

Expand Down Expand Up @@ -189,7 +193,16 @@
await Assert.That((Variant.From(0L) | Variant.From(7L)).GetInt64()).IsEqualTo(7L);
}

private async Task CheckAsync<T>(T input, Variant value, IVariantBuilder<T> builder)
private Task CheckAsync<T>(T input, Variant value, IVariantBuilder<T> builder)
{
return CheckAsync(input, value, builder, input);
}

private async Task CheckAsync<T>(
T input,
Variant value,
IVariantBuilder<T> builder,
T binaryExpected)
{
await Assert.That(EqualityComparer<T>.Default.Equals(builder.GetValue(value), input)).IsTrue();
await Assert.That(builder.WithValue(input).Equals(value)).IsTrue();
Expand All @@ -204,7 +217,7 @@
encoder.CloseAndReturnBuffer() ?? throw new InvalidOperationException("No encoded value."), context);
Variant decoded = decoder.ReadVariant(null);
await Assert.That(decoded.TypeInfo).IsEqualTo(value.TypeInfo);
await Assert.That(decoded.Equals(value)).IsTrue();
await Assert.That(decoded.Equals(Variant.From(binaryExpected))).IsTrue();

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / aot-ubuntu-latest

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / aot-macos-latest

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Debug (windows)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Debug (windows)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Release (linux)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Release (linux)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / aot-macos-15-intel

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Release (windows)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'

Check failure on line 220 in tests/Opc.Ua.Aot.Tests/VariantStorageAotTests.cs

View workflow job for this annotation

GitHub Actions / build-UA net10.0 Release (windows)

The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Variant.From<T>(T)'
}

private sealed class NativeMemoryManager : MemoryManager<byte>
Expand Down
50 changes: 50 additions & 0 deletions tests/Opc.Ua.Types.Tests/BuiltIn/LocalizedTextTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using NUnit.Framework;
using Opc.Ua.Tests;

namespace Opc.Ua.Types.Tests.BuiltIn
{
Expand Down Expand Up @@ -249,5 +250,54 @@ public void LocalizedText_MulLocale_DeepCopy()
Is.EqualTo(localizedText.Translations["fr-FR"]),
"French translation should be the same");
}

[TestCase(null, null, 0x00)]
[TestCase("", "", 0x00)]
[TestCase("en-US", null, 0x01)]
[TestCase("en-US", "", 0x01)]
[TestCase(null, "Text", 0x02)]
[TestCase("", "Text", 0x02)]
[TestCase("en-US", "Text", 0x03)]
public void BinaryEncodingOmitsNullOrEmptyFields(
string? locale,
string? text,
byte expectedEncodingMask)
{
ITelemetryContext telemetryContext = NUnitTelemetryContext.Create();
var messageContext = ServiceMessageContext.CreateEmpty(telemetryContext);
byte[] buffer;
using (var encoder = new BinaryEncoder(messageContext))
{
encoder.WriteLocalizedText(null, new LocalizedText(locale, text));
buffer = encoder.CloseAndReturnBuffer() ??
throw new AssertionException("The encoder returned no buffer.");
}

Assert.That(buffer[0], Is.EqualTo(expectedEncodingMask));
using var decoder = new BinaryDecoder(buffer, messageContext);
LocalizedText decoded = decoder.ReadLocalizedText(null);
Assert.That(decoded.Locale, Is.EqualTo(string.IsNullOrEmpty(locale) ? null : locale));
Assert.That(decoded.Text, Is.EqualTo(string.IsNullOrEmpty(text) ? null : text));
}

[Test]
public void TranslationBackedEmptyLocaleIsNotEncodedInBinary()
{
ITelemetryContext telemetryContext = NUnitTelemetryContext.Create();
var messageContext = ServiceMessageContext.CreateEmpty(telemetryContext);
byte[] buffer;
using (var encoder = new BinaryEncoder(messageContext))
{
encoder.WriteLocalizedText(null, new LocalizedText("Key", string.Empty, "Text"));
buffer = encoder.CloseAndReturnBuffer() ??
throw new AssertionException("The encoder returned no buffer.");
}

Assert.That(buffer[0], Is.EqualTo(0x02));
using var decoder = new BinaryDecoder(buffer, messageContext);
LocalizedText decoded = decoder.ReadLocalizedText(null);
Assert.That(decoded.Locale, Is.Null);
Assert.That(decoded.Text, Is.EqualTo("Text"));
}
}
}
Loading