diff --git a/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj b/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj index c442d0e38..01a220e07 100644 --- a/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj +++ b/Fuzzing/Encoders/Fuzz.Tests/Opc.Ua.Encoders.Fuzz.Tests.csproj @@ -31,7 +31,8 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/Stack/Opc.Ua.Core/Stack/Bindings/BufferManager.cs b/Stack/Opc.Ua.Core/Stack/Bindings/BufferManager.cs index 730b2b708..2f79d6849 100644 --- a/Stack/Opc.Ua.Core/Stack/Bindings/BufferManager.cs +++ b/Stack/Opc.Ua.Core/Stack/Bindings/BufferManager.cs @@ -405,7 +405,7 @@ class Allocation private readonly object m_lock = new object(); private int m_allocated; private int m_id; - private SortedDictionary m_allocations = new SortedDictionary(); + private Dictionary m_allocations = new Dictionary(); #else private const byte kCookieLength = 1; #endif diff --git a/Tests/Common/Main.cs b/Tests/Common/Main.cs index 533666dba..da1588d60 100644 --- a/Tests/Common/Main.cs +++ b/Tests/Common/Main.cs @@ -28,19 +28,66 @@ * ======================================================================*/ using System; +using System.Linq; +using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; +using Opc.Ua; -static class Program +[assembly: Config(typeof(BenchmarksDefaultConfig))] + +class BenchmarksDefaultConfig : ManualConfig { - // Main Method - public static void Main(String[] args) + public BenchmarksDefaultConfig() { - IConfig config = ManualConfig.Create(DefaultConfig.Instance) - // need this option because of reference to nunit.framework - .WithOptions(ConfigOptions.DisableOptimizationsValidator) - ; - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); + if (Program.CmdLineUsed) + { + var defaults = DefaultConfig.Instance; + foreach (var exporter in defaults.GetExporters()) + { + AddExporter(exporter); + } + foreach (var logger in defaults.GetLoggers()) + { + AddLogger(logger); + } + foreach (var analyser in defaults.GetAnalysers()) + { + AddAnalyser(analyser); + } + foreach (var validator in defaults.GetValidators()) + { + AddValidator(validator); + } + WithOptions(ConfigOptions.DisableOptimizationsValidator); + } + else + { + AddJob(Job.Dry); + AddLogger(ConsoleLogger.Default); + AddValidator(JitOptimizationsValidator.DontFailOnError); + } } -} + static class Program + { + /// + /// Whether command line was used to start. + /// + public static bool CmdLineUsed = false; + + // Main Method + public static void Main(string[] args) + { + IConfig config = ManualConfig.Create(DefaultConfig.Instance) + // need this option because of reference to nunit.framework + .WithOptions(ConfigOptions.DisableOptimizationsValidator) + ; + CmdLineUsed = true; + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); + } + } +} diff --git a/Tests/Opc.Ua.Client.ComplexTypes.Tests/Opc.Ua.Client.ComplexTypes.Tests.csproj b/Tests/Opc.Ua.Client.ComplexTypes.Tests/Opc.Ua.Client.ComplexTypes.Tests.csproj index 1672657e9..7f8a1356a 100644 --- a/Tests/Opc.Ua.Client.ComplexTypes.Tests/Opc.Ua.Client.ComplexTypes.Tests.csproj +++ b/Tests/Opc.Ua.Client.ComplexTypes.Tests/Opc.Ua.Client.ComplexTypes.Tests.csproj @@ -11,6 +11,8 @@ + + all runtime; build; native; contentfiles; analyzers diff --git a/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs b/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs index 1a485928b..aae4cc354 100644 --- a/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs +++ b/Tests/Opc.Ua.Client.Tests/ContinuationPointInBatchTest.cs @@ -824,7 +824,7 @@ public void ParallelManagedBrowseWithManyContinuationPoints(ManagedBrowseTestDat } } -#endregion Tests + #endregion Tests #region async tests diff --git a/Tests/Opc.Ua.Client.Tests/Opc.Ua.Client.Tests.csproj b/Tests/Opc.Ua.Client.Tests/Opc.Ua.Client.Tests.csproj index f43c0d631..3f76f3f12 100644 --- a/Tests/Opc.Ua.Client.Tests/Opc.Ua.Client.Tests.csproj +++ b/Tests/Opc.Ua.Client.Tests/Opc.Ua.Client.Tests.csproj @@ -25,6 +25,8 @@ + + all runtime; build; native; contentfiles; analyzers diff --git a/Tests/Opc.Ua.Configuration.Tests/Opc.Ua.Configuration.Tests.csproj b/Tests/Opc.Ua.Configuration.Tests/Opc.Ua.Configuration.Tests.csproj index 0c90649cc..396b672ee 100644 --- a/Tests/Opc.Ua.Configuration.Tests/Opc.Ua.Configuration.Tests.csproj +++ b/Tests/Opc.Ua.Configuration.Tests/Opc.Ua.Configuration.Tests.csproj @@ -33,7 +33,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj b/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj index ea041e192..225147ab2 100644 --- a/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj +++ b/Tests/Opc.Ua.Core.Tests/Opc.Ua.Core.Tests.csproj @@ -13,6 +13,8 @@ + + diff --git a/Tests/Opc.Ua.Core.Tests/Types/Utils/DictionaryKeyBenchmark.cs b/Tests/Opc.Ua.Core.Tests/Types/Utils/DictionaryKeyBenchmark.cs new file mode 100644 index 000000000..30bdf9e5f --- /dev/null +++ b/Tests/Opc.Ua.Core.Tests/Types/Utils/DictionaryKeyBenchmark.cs @@ -0,0 +1,601 @@ +/* ======================================================================== + * Copyright (c) 2005-2024 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; +using System.Diagnostics; +using System.Runtime.InteropServices; +using BenchmarkDotNet.Attributes; +using NUnit.Framework; +using Assert = NUnit.Framework.Legacy.ClassicAssert; + + +using BenchmarkDotNet.Order; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using Opc.Ua; +using Opc.Ua.Test; +using System.Collections.Concurrent; +using BenchmarkDotNet.Configs; +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif + +namespace Opc.Ua.Core.Tests.Types.UtilsTests +{ + /// + /// Performance tests for uint key dictionaries. + /// + [TestFixture, Category("Dictionary")] + [SetCulture("en-us"), SetUICulture("en-us")] + [Parallelizable] + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.FastestToSlowest)] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [BenchmarkCategory("uint")] + [CategoriesColumn] + public class DictionaryUIntKeyBenchmark : DictionaryKeyBenchmark + { + } + + /// + /// Performance tests for NodeId key dictionaries. + /// + [TestFixture, Category("Dictionary")] + [SetCulture("en-us"), SetUICulture("en-us")] + [Parallelizable] + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.FastestToSlowest)] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + [BenchmarkCategory(nameof(NodeId))] + [CategoriesColumn] + public class DictionaryNodeIdKeyBenchmark : DictionaryKeyBenchmark + { + [Params(IdType.Numeric, IdType.String, IdType.Guid, IdType.Opaque, IdType.Opaque + 1)] + public IdType NodeIdType { get; set; } = (uint)IdType.Numeric; + + private NodeIdDictionary m_nodeIdDictionary = null; + + /// + /// Tests performance and memory usage of ImmutableDictionary. + /// + [Test] + [BenchmarkCategory("TryGet")] + [Benchmark()] + public void TestNodeIdDictionary() + { + foreach (NodeId key in m_lookup) + { + _ = m_nodeIdDictionary.TryGetValue(key, out _); + } + } + + protected override NodeId GetRandomKey(Random random, DataGenerator generator) + { + IdType nodeIdType = NodeIdType; + if (NodeIdType > IdType.Opaque) + { + nodeIdType = (IdType)random.Next((int)IdType.Numeric, (int)IdType.Opaque); + } + + switch (nodeIdType) + { + case IdType.String: + return new NodeId(generator.GetRandomString(), (ushort)random.Next(0, 10)); + case IdType.Guid: + return new NodeId(Guid.NewGuid(), (ushort)random.Next(0, 10)); + case IdType.Opaque: + return new NodeId(generator.GetRandomByteString(), (ushort)random.Next(0, 10)); + default: + case IdType.Numeric: + return new NodeId((uint)random.Next(0, 10 * NumElements), (ushort)random.Next(0, 10)); + } + } + + /// + /// Overridden to initialize the NodeIdDictionary. + /// + protected override void Initialize() + { + base.Initialize(); + m_nodeIdDictionary = new NodeIdDictionary(); + foreach (var entry in m_regularDictionary) + { + m_nodeIdDictionary.Add(entry.Key, entry.Value); + } + } + } + + /// + /// Performance tests for T key dictionaries. Abstract to exclude from tests. + /// + public abstract class DictionaryKeyBenchmark + { + // [Params(16, 128, 1024)] + public int NumElements { get; set; } = 1024; + + // Create a Dictionary and a SortedDictionary + protected T[] m_lookup = null; + protected Dictionary m_regularDictionary = null; + protected NamespaceTable m_namespaceTable = new NamespaceTable(new List { Namespaces.OpcUa, "urn:myserver", "http://opcfoundation.org/bar", "http://opcfoundation.org/foo" }); + + // test dictionaries + private ImmutableDictionary m_immutableDictionary = null; + private ConcurrentDictionary m_concurrentDictionary = null; + private SortedDictionary m_sortedDictionary = null; + private ReadOnlyDictionary m_readonlyDictionary = null; +#if NET8_0_OR_GREATER + private FrozenDictionary m_frozenDictionary = null; +#endif + + // worker dictionaries for add/remove + private Dictionary m_regularWorker = null; + private ConcurrentDictionary m_concurrentWorker = null; + private SortedDictionary m_sortedWorker = null; + + // synchronization + private object m_lock; + + #region Test Setup + [OneTimeSetUp] + public void OneTimeSetUp() + { + Initialize(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + } + #endregion + + #region Benchmark Setup + /// + /// Set up some variables for benchmarks. + /// + [GlobalSetup] + public void GlobalSetup() + { + Initialize(); + } + + /// + /// Tear down benchmark variables. + /// + [GlobalCleanup] + public void GlobalCleanup() + { + } + #endregion + + #region TryGetValue + /// + /// Tests performance and memory usage of ImmutableDictionary. + /// + [Test] + [Benchmark(Description = "Immutable")] + [BenchmarkCategory("TryGet")] + public void TestImmutableDictionary() + { + foreach (T key in m_lookup) + { + _ = m_immutableDictionary.TryGetValue(key, out _); + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark(Description = "Concurrent")] + [BenchmarkCategory("TryGet")] + public void TestConcurrentDictionary() + { + foreach (T key in m_lookup) + { + _ = m_concurrentDictionary.TryGetValue(key, out _); + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark] + [BenchmarkCategory("TryGet")] + public void TestSortedDictionary() + { + foreach (T key in m_lookup) + { + _ = m_sortedDictionary.TryGetValue(key, out _); + } + } + + /// + /// Tests performance and memory usage of ReadonlyDictionary. + /// + [Test] + [Benchmark(Description = "Readonly")] + [BenchmarkCategory("TryGet")] + public void TestReadonlyDictionary() + { + foreach (T key in m_lookup) + { + _ = m_readonlyDictionary.TryGetValue(key, out _); + } + } + + /// + /// Tests performance and memory usage of Dictionary. + /// + [Test] + [Benchmark(Description = "Regular", Baseline = true)] + [BenchmarkCategory("TryGet")] + public void TestRegularDictionary() + { + foreach (T key in m_lookup) + { + _ = m_regularDictionary.TryGetValue(key, out _); + } + } + + /// + /// Tests performance and memory usage of FrozenDictionary. + /// + [Test] + [Benchmark(Description = "Frozen")] + [BenchmarkCategory("TryGet")] + public void TestFrozenDictionary() + { +#if NET8_0_OR_GREATER + foreach (T key in m_lookup) + { + _ = m_frozenDictionary.TryGetValue(key, out _); + } +#else + TestRegularDictionary(); +#endif + } + #endregion + + #region RemoveValue + [IterationSetup(Target = nameof(TryRemoveConcurrentDictionary))] + public void PrepareTryRemoveConcurrentDictionary() + { + m_concurrentWorker = new ConcurrentDictionary(m_regularDictionary); + } + + [Test] + [Benchmark(Description = "Concurrent")] + [BenchmarkCategory("Remove")] + public void TryRemoveConcurrentDictionary() + { + foreach (T key in m_lookup) + { + _ = m_concurrentWorker.TryRemove(key, out _); + } + } + + [IterationSetup(Target = nameof(TryRemoveSortedDictionary))] + public void PrepareTryRemoveSortedDictionary() + { + m_sortedWorker = new SortedDictionary(m_regularDictionary); + } + + [Test] + [Benchmark] + [BenchmarkCategory("Remove")] + public void TryRemoveSortedDictionary() + { + foreach (T key in m_lookup) + { + lock (m_lock) + { + _ = m_sortedWorker.Remove(key); + } + } + } + + [IterationSetup(Target = nameof(TryRemoveRegularDictionary))] + public void PrepareTryRemoveDictionary() + { + m_regularWorker = new Dictionary(m_regularDictionary); + } + + /// + /// Tests performance and memory usage of Dictionary. + /// + [Test] + [Benchmark(Description = "Regular", Baseline = true)] + [BenchmarkCategory("Remove")] + public void TryRemoveRegularDictionary() + { + foreach (T key in m_lookup) + { + lock (m_lock) + { + _ = m_regularWorker.Remove(key); + } + } + } + #endregion + + #region Iterate + /// + /// Tests performance and memory usage of ImmutableDictionary. + /// + [Test] + [Benchmark(Description = "Immutable")] + [BenchmarkCategory("Iterate")] + public void IterateImmutableDictionary() + { + foreach (var keyPair in m_immutableDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark(Description = "Concurrent")] + [BenchmarkCategory("Iterate")] + public void IterateConcurrentDictionary() + { + foreach (var keyPair in m_concurrentDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark(Description = "ConcurrentToArray")] + [BenchmarkCategory("Iterate")] + public void IterateToArrayConcurrentDictionary() + { + foreach (var keyPair in m_concurrentDictionary.ToArray()) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark] + [BenchmarkCategory("Iterate")] + public void IterateSortedDictionary() + { + foreach (var keyPair in m_sortedDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of ReadonlyDictionary. + /// + [Test] + [Benchmark(Description = "Readonly")] + [BenchmarkCategory("Iterate")] + public void IterateReadonlyDictionary() + { + foreach (var keyPair in m_readonlyDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of Dictionary. + /// + [Test] + [Benchmark(Description = "Regular", Baseline = true)] + [BenchmarkCategory("Iterate")] + public void IterateRegularDictionary() + { + foreach (var keyPair in m_regularDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } + } + + /// + /// Tests performance and memory usage of FrozenDictionary. + /// + [Test] + [Benchmark(Description = "Frozen")] + [BenchmarkCategory("Iterate")] + public void IterateFrozenDictionary() + { +#if NET8_0_OR_GREATER + foreach (var keyPair in m_frozenDictionary) + { + _ = keyPair.Key; + _ = keyPair.Value; + } +#else + IterateRegularDictionary(); +#endif + } + #endregion + + #region Count + public const int CountRepeats = 1000; + /// + /// Tests performance and memory usage of ImmutableDictionary. + /// + [Test] + [Benchmark(Description = "Immutable")] + [BenchmarkCategory("Count")] + public void CountImmutableDictionary() + { + for (int i = 0; i < CountRepeats; i++) + { + _ = m_immutableDictionary.Count; + } + } + + /// + /// Tests performance and memory usage of ConcurrentDictionary. + /// + [Test] + [Benchmark(Description = "Concurrent")] + [BenchmarkCategory("Count")] + public void CountConcurrentDictionary() + { + for (int i = 0; i < CountRepeats; i++) + { + _ = m_concurrentDictionary.Count; + } + } + + /// + /// Tests performance and memory usage of SortedDictionary. + /// + [Test] + [Benchmark(Description = "Sorted")] + [BenchmarkCategory("Count")] + public void CountSortedDictionary() + { + for (int i = 0; i < CountRepeats; i++) + { + _ = m_sortedDictionary.Count; + } + } + + /// + /// Tests performance and memory usage of ReadonlyDictionary. + /// + [Test] + [Benchmark(Description = "Readonly")] + [BenchmarkCategory("Count")] + public void CountReadonlyDictionary() + { + for (int i = 0; i < CountRepeats; i++) + { + _ = m_readonlyDictionary.Count; + } + } + + /// + /// Tests performance and memory usage of Dictionary. + /// + [Test] + [Benchmark(Description = "Regular", Baseline = true)] + [BenchmarkCategory("Count")] + public void CountRegularDictionary() + { + for (int i = 0; i < CountRepeats; i++) + { + _ = m_regularDictionary.Count; + } + } + + /// + /// Tests performance and memory usage of FrozenDictionary. + /// + [Test] + [Benchmark(Description = "Frozen")] + [BenchmarkCategory("Count")] + public void CountFrozenDictionary() + { +#if NET8_0_OR_GREATER + for (int i = 0; i < CountRepeats; i++) + { + _ = m_frozenDictionary.Count; + } +#endif + } + #endregion + + #region Helper Methods + protected virtual T GetRandomKey(Random random, DataGenerator generator) + { + if (typeof(T) == typeof(uint)) + { + return (T)(object)Convert.ToUInt32(random.Next(0, 10 * NumElements)); // Random uint key + } + else if (typeof(T) == typeof(NodeId)) + { + return (T)(object)new NodeId((uint)random.Next(0, 10 * NumElements), (ushort)random.Next(0, 10)); + } + else if (typeof(T) == typeof(ExpandedNodeId)) + { + return (T)(object)new ExpandedNodeId((uint)random.Next(0, 10 * NumElements), m_namespaceTable.GetString((uint)random.Next(0, m_namespaceTable.Count - 1))); + } + else + { + throw new NotSupportedException("Unsupported key type"); + } + } + + /// + /// Initializes a new instance. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5394:Do not use insecure randomness", Justification = "Not used for crypto.")] + protected virtual void Initialize() + { + var random = new Random(0x62541); + var randomSource = new RandomSource(0x62541); + var generator = new DataGenerator(randomSource); + + // Populate dictionaries with random data + m_regularDictionary = new Dictionary(NumElements); + m_lookup = new T[NumElements]; + for (int i = 0; i < NumElements; i++) + { + m_lookup[i] = GetRandomKey(random, generator); + m_regularDictionary[m_lookup[i]] = generator.GetRandomDataValue(); + } + + // initialize other dicts + m_sortedDictionary = new SortedDictionary(m_regularDictionary); + m_readonlyDictionary = new ReadOnlyDictionary(m_regularDictionary); + m_concurrentDictionary = new ConcurrentDictionary(m_regularDictionary); + m_immutableDictionary = m_regularDictionary.ToImmutableDictionary(); +#if NET8_0_OR_GREATER + m_frozenDictionary = m_regularDictionary.ToFrozenDictionary(); +#endif + m_lock = new object(); + } + #endregion + } +} diff --git a/Tests/Opc.Ua.Core.Tests/Types/Utils/HiResClock.cs b/Tests/Opc.Ua.Core.Tests/Types/Utils/HiResClock.cs index 342be1fc1..3aa8c8da7 100644 --- a/Tests/Opc.Ua.Core.Tests/Types/Utils/HiResClock.cs +++ b/Tests/Opc.Ua.Core.Tests/Types/Utils/HiResClock.cs @@ -153,7 +153,8 @@ public void HiResUtcNowTickCount(bool disabled) long lastTickCount = HiResClock.UtcNow.Ticks; long firstTickCount = lastTickCount; int counts = 0; - while (stopWatch.ElapsedMilliseconds <= HiResClockTestDuration) + long elapsedMilliseconds = stopWatch.ElapsedMilliseconds; + while (elapsedMilliseconds <= HiResClockTestDuration) { long tickCount; do @@ -164,6 +165,7 @@ public void HiResUtcNowTickCount(bool disabled) Assert.LessOrEqual(lastTickCount, tickCount); lastTickCount = tickCount; counts++; + elapsedMilliseconds = stopWatch.ElapsedMilliseconds; } if (!disabled) { @@ -171,7 +173,7 @@ public void HiResUtcNowTickCount(bool disabled) } stopWatch.Stop(); long elapsed = (lastTickCount - firstTickCount) / TimeSpan.TicksPerMillisecond; - TestContext.Out.WriteLine("HiResClock counts: {0} resolution: {1}µs", counts, stopWatch.ElapsedMilliseconds * 1000 / counts); + TestContext.Out.WriteLine("HiResClock counts: {0} resolution: {1}µs", counts, (elapsedMilliseconds * 1000.0 / counts)); // test accuracy of counter vs. stop watch try { diff --git a/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj b/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj index e3fb13096..42851197b 100644 --- a/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj +++ b/Tests/Opc.Ua.Gds.Tests/Opc.Ua.Gds.Tests.csproj @@ -26,6 +26,8 @@ + + diff --git a/Tests/Opc.Ua.PubSub.Tests/Opc.Ua.PubSub.Tests.csproj b/Tests/Opc.Ua.PubSub.Tests/Opc.Ua.PubSub.Tests.csproj index e94dd8810..671cdf912 100644 --- a/Tests/Opc.Ua.PubSub.Tests/Opc.Ua.PubSub.Tests.csproj +++ b/Tests/Opc.Ua.PubSub.Tests/Opc.Ua.PubSub.Tests.csproj @@ -33,7 +33,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/Tests/Opc.Ua.Security.Certificates.Tests/Opc.Ua.Security.Certificates.Tests.csproj b/Tests/Opc.Ua.Security.Certificates.Tests/Opc.Ua.Security.Certificates.Tests.csproj index 74deacf33..081cbca26 100644 --- a/Tests/Opc.Ua.Security.Certificates.Tests/Opc.Ua.Security.Certificates.Tests.csproj +++ b/Tests/Opc.Ua.Security.Certificates.Tests/Opc.Ua.Security.Certificates.Tests.csproj @@ -35,7 +35,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/Tests/Opc.Ua.Server.Tests/Opc.Ua.Server.Tests.csproj b/Tests/Opc.Ua.Server.Tests/Opc.Ua.Server.Tests.csproj index 6d535bd75..7a7d89c09 100644 --- a/Tests/Opc.Ua.Server.Tests/Opc.Ua.Server.Tests.csproj +++ b/Tests/Opc.Ua.Server.Tests/Opc.Ua.Server.Tests.csproj @@ -33,7 +33,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + +