diff --git a/src/Adapter/MSTest.CoreAdapter/Helpers/DictionaryHelper.cs b/src/Adapter/MSTest.CoreAdapter/Helpers/DictionaryHelper.cs new file mode 100644 index 0000000000..be32b0c2d0 --- /dev/null +++ b/src/Adapter/MSTest.CoreAdapter/Helpers/DictionaryHelper.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers +{ + using System; + using System.Collections.Generic; + using System.Linq; + + internal static class DictionaryHelper + { + public static IDictionary ConcatWithOverwrites( + this IDictionary source, + IDictionary overwrite, + string sourceFriendlyName = "source", + string overwriteFriendlyName = "overwrite") + where TKey : IEquatable + { + if ((source == null || source?.Count == 0) && (overwrite == null || overwrite?.Count == 0)) + { + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Both {0} and {1} dictionaries are null or empty, returning empty dictionary.", sourceFriendlyName, overwriteFriendlyName); + return new Dictionary(); + } + + if (overwrite == null || overwrite?.Count == 0) + { + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", overwriteFriendlyName, sourceFriendlyName); + return source.ToDictionary(p => p.Key, p => p.Value); + } + + if (source == null || source?.Count == 0) + { + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} is null or empty, returning the {1} dictionary.", sourceFriendlyName, overwriteFriendlyName); + return overwrite.ToDictionary(p => p.Key, p => p.Value); + } + + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} has {1} keys. And {2} has {3} keys. Merging them.", sourceFriendlyName, source.Count, overwriteFriendlyName, overwrite.Count); + var destination = source.ToDictionary(p => p.Key, p => p.Value); + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Taking all keys from {0}: {1}.", sourceFriendlyName, string.Join(", ", source.Keys)); + var overwrites = new List(); + foreach (var k in overwrite.Keys) + { + if (destination.ContainsKey(k)) + { + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} already contains key {1}. Overwriting it with value from {2}.", sourceFriendlyName, k, overwriteFriendlyName); + destination[k] = overwrite[k]; + overwrites.Add(k); + } + else + { + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: The {0} does not contain key {1}. Adding it from {2}.", sourceFriendlyName, k, overwriteFriendlyName); + destination.Add(k, overwrite[k]); + } + } + + PlatformServiceProvider.Instance.AdapterTraceLogger.LogInfo("DictionaryHelper.ConcatWithOverwrites: Merging done: Resulting dictionary has keys {0}, overwrites {1}.", string.Join(", ", destination.Keys), string.Join(", ", overwrites)); + + return destination; + } + } +} diff --git a/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj b/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj index 08d4ff98be..f020861623 100644 --- a/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj +++ b/src/Adapter/MSTest.CoreAdapter/MSTest.CoreAdapter.csproj @@ -38,6 +38,7 @@ + diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/DictionaryHelperTests.cs b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/DictionaryHelperTests.cs new file mode 100644 index 0000000000..f637eea49a --- /dev/null +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/Helpers/DictionaryHelperTests.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests +{ + extern alias FrameworkV1; + extern alias FrameworkV2; + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using FluentAssertions; + using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter; + using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers; + using Moq; + + using TestableImplementations; + using Assert = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.Assert; + using CollectionAssert = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert; + using TestClass = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestClassAttribute; + using TestCleanup = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute; + using TestInitialize = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute; + using TestMethod = FrameworkV1::Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute; + using UTF = FrameworkV2::Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class DictionaryHelperTests + { + [TestMethod] + public void ConcatenatingDictionariesReturnsEmptyDictionaryWhenBothSidesAreNullOrEmpty() + { + Dictionary source = null; + + var overwrite = new Dictionary(); + + var actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + var expected = new Dictionary(); + + actual.Should().BeEquivalentTo(expected); + } + + [TestMethod] + public void ConcatenatingDictionariesReturnsSourceSideWhenOverwriteIsNullOrEmpty() + { + var source = new Dictionary + { + ["aaa"] = "source", + ["bbb"] = "source", + }; + + Dictionary overwrite = null; + + var actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + + actual.Should().BeEquivalentTo(source); + } + + [TestMethod] + public void ConcatenatingDictionariesReturnsOverwriteSideWhenSourceIsNullOrEmpty() + { + Dictionary source = null; + + var overwrite = new Dictionary + { + ["bbb"] = "overwrite", + ["ccc"] = "overwrite", + }; + + var actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + + actual.Should().BeEquivalentTo(overwrite); + } + + [TestMethod] + public void ConcatenatingDictionariesShouldMergeThemAndTakeDuplicateKeysFromOverwrite() + { + var source = new Dictionary + { + ["aaa"] = "source", + ["bbb"] = "source", + }; + + var overwrite = new Dictionary + { + ["bbb"] = "overwrite", + ["ccc"] = "overwrite", + }; + + var actual = source.ConcatWithOverwrites(overwrite, nameof(source), nameof(overwrite)); + var expected = new Dictionary + { + // this is only present in source, take it + ["aaa"] = "source", + + // this is present in source and overwrite, take it from overwrite + ["bbb"] = "overwrite", + + // this is present only in overwrite, take it from overwrite + ["ccc"] = "overwrite", + }; + + actual.Should().BeEquivalentTo(expected); + } + } +} \ No newline at end of file diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/MSTest.CoreAdapter.Unit.Tests.csproj b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/MSTest.CoreAdapter.Unit.Tests.csproj index c9ed6a0685..666bbf7aea 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/MSTest.CoreAdapter.Unit.Tests.csproj +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/MSTest.CoreAdapter.Unit.Tests.csproj @@ -39,6 +39,9 @@ $(TestFxRoot)packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll True + + ..\..\..\packages\FluentAssertions.5.10.3\lib\net45\FluentAssertions.dll + ..\..\..\packages\Microsoft.TestPlatform.ObjectModel.$(TestPlatformVersion)\lib\net451\Microsoft.TestPlatform.CoreUtilities.dll @@ -62,9 +65,13 @@ ..\..\..\packages\System.Collections.Immutable.1.5.0\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + ..\..\..\packages\System.Reflection.Metadata.1.6.0\lib\portable-net45+win8\System.Reflection.Metadata.dll + + ..\..\..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll + @@ -99,6 +106,7 @@ + diff --git a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/packages.config b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/packages.config index 38ddde6bc6..3ac937af7a 100644 --- a/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/packages.config +++ b/test/UnitTests/MSTest.CoreAdapter.Unit.Tests/packages.config @@ -1,11 +1,13 @@  - + + + \ No newline at end of file