diff --git a/src/Adapter/MSTest.TestAdapter/AdapterTestProperties.cs b/src/Adapter/MSTest.TestAdapter/AdapterTestProperties.cs
new file mode 100644
index 0000000000..eaa2668c8d
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/AdapterTestProperties.cs
@@ -0,0 +1,116 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+
+///
+/// The VSTest object-model registrations used by the adapter to carry MSTest
+/// discovery/execution metadata (and host test-case-management values) on VSTest test cases and results.
+/// These live in the adapter layer because they depend on the VSTest object model; the platform services
+/// engine describes the same information with its own neutral types.
+///
+internal static class AdapterTestProperties
+{
+ #region Test Property registration
+ internal static readonly TestProperty WorkItemIdsProperty = TestProperty.Register("WorkItemIds", WorkItemIdsLabel, typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("MSTestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+#pragma warning disable CS0618 // Type or member is obsolete
+ internal static readonly TestProperty TestCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", TestCategoryLabel, typeof(string[]), TestPropertyAttributes.Hidden | TestPropertyAttributes.Trait, typeof(TestCase));
+#pragma warning restore CS0618 // Type or member is obsolete
+
+ internal static readonly TestProperty PriorityProperty = TestProperty.Register("MSTestDiscoverer.Priority", PriorityLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+#if !WINDOWS_UWP && !WIN_UI
+ internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair[]), TestPropertyAttributes.Hidden, typeof(TestCase));
+#endif
+
+ internal static readonly TestProperty DoNotParallelizeProperty = TestProperty.Register("MSTestDiscoverer.DoNotParallelize", DoNotParallelizeLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
+
+ internal static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
+
+ internal static readonly TestProperty TestRunIdProperty = TestProperty.Register(TestRunId, TestRunId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestPlanIdProperty = TestProperty.Register(TestPlanId, TestPlanId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestCaseIdProperty = TestProperty.Register(TestCaseId, TestCaseId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestPointIdProperty = TestProperty.Register(TestPointId, TestPointId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestConfigurationIdProperty = TestProperty.Register(TestConfigurationId, TestConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestConfigurationNameProperty = TestProperty.Register(TestConfigurationName, TestConfigurationName, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty IsInLabEnvironmentProperty = TestProperty.Register(IsInLabEnvironment, IsInLabEnvironment, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildConfigurationIdProperty = TestProperty.Register(BuildConfigurationId, BuildConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildDirectoryProperty = TestProperty.Register(BuildDirectory, BuildDirectory, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildFlavorProperty = TestProperty.Register(BuildFlavor, BuildFlavor, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildNumberProperty = TestProperty.Register(BuildNumber, BuildNumber, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildPlatformProperty = TestProperty.Register(BuildPlatform, BuildPlatform, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty BuildUriProperty = TestProperty.Register(BuildUri, BuildUri, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TfsServerCollectionUrlProperty = TestProperty.Register(TfsServerCollectionUrl, TfsServerCollectionUrl, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TfsTeamProjectProperty = TestProperty.Register(TfsTeamProject, TfsTeamProject, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty ParameterTypesProperty = TestProperty.Register("MSTest.ParameterTypes", "ParameterTypes", typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestDynamicDataTypeProperty = TestProperty.Register("MSTest.DynamicDataType", "DynamicDataType", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestCaseIndexProperty = TestProperty.Register("MSTest.TestCaseIndex", "TestCaseIndex", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestDynamicDataProperty = TestProperty.Register("MSTest.DynamicData", "DynamicData", typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty TestDataSourceIgnoreMessageProperty = TestProperty.Register("MSTest.TestDataSourceIgnoreMessageProperty", "TestDataSourceIgnoreMessageProperty", typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ internal static readonly TestProperty UnfoldingStrategy = TestProperty.Register("MSTestDiscoverer.UnfoldingStrategy", "UnfoldingStrategy", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
+
+ #endregion
+
+ #region Private Constants
+
+ ///
+ /// These are the Test properties used by the adapter, which essentially correspond
+ /// to attributes on tests, and may be available in command line/TeamBuild to filter tests.
+ /// These Property names should not be localized.
+ ///
+ private const string TestClassNameLabel = "ClassName";
+ private const string TestCategoryLabel = "TestCategory";
+ private const string PriorityLabel = "Priority";
+#if !WINDOWS_UWP && !WIN_UI
+ private const string DeploymentItemsLabel = "DeploymentItems";
+#endif
+ private const string DoNotParallelizeLabel = "DoNotParallelize";
+ private const string ExecutionIdLabel = "ExecutionId";
+ private const string ParentExecIdLabel = "ParentExecId";
+ private const string WorkItemIdsLabel = "WorkItemIds";
+
+ private const string TestRunId = "__Tfs_TestRunId__";
+ private const string TestPlanId = "__Tfs_TestPlanId__";
+ private const string TestCaseId = "__Tfs_TestCaseId__";
+ private const string TestPointId = "__Tfs_TestPointId__";
+ private const string TestConfigurationId = "__Tfs_TestConfigurationId__";
+ private const string TestConfigurationName = "__Tfs_TestConfigurationName__";
+ private const string IsInLabEnvironment = "__Tfs_IsInLabEnvironment__";
+ private const string BuildConfigurationId = "__Tfs_BuildConfigurationId__";
+ private const string BuildDirectory = "__Tfs_BuildDirectory__";
+ private const string BuildFlavor = "__Tfs_BuildFlavor__";
+ private const string BuildNumber = "__Tfs_BuildNumber__";
+ private const string BuildPlatform = "__Tfs_BuildPlatform__";
+ private const string BuildUri = "__Tfs_BuildUri__";
+ private const string TfsServerCollectionUrl = "__Tfs_TfsServerCollectionUrl__";
+ private const string TfsTeamProject = "__Tfs_TeamProject__";
+
+ #endregion
+}
diff --git a/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs b/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs
new file mode 100644
index 0000000000..93e2892098
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/Execution/TcmTestPropertiesProvider.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using TestPlatformObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
+
+///
+/// Reads and parses the test-case-management (TCM) properties supplied by a host on a test case, in order to
+/// surface them to the running test through TestContext.
+///
+///
+/// This is a translation point between the VSTest test-case object model and the neutral execution context bag
+/// consumed by the platform services engine. It runs at the adapter boundary (when a host hands the adapter
+/// concrete test cases to run) and produces a string-keyed dictionary so the engine never depends on the VSTest
+/// TestProperty object model. It is expected to move entirely into the adapter layer once the platform
+/// services no longer reference the VSTest object model.
+///
+internal static class TcmTestPropertiesProvider
+{
+ ///
+ /// Gets the host execution context properties from a test case, keyed by the host property identifier.
+ ///
+ /// Test case.
+ /// The properties, or when the test case is null or carries no test case id.
+ public static IReadOnlyDictionary? GetTcmProperties(TestPlatformObjectModel.TestCase? testCase)
+ {
+ // Return empty properties when testCase is null or when test case id is zero.
+ if (testCase == null ||
+ testCase.GetPropertyValue(AdapterTestProperties.TestCaseIdProperty, default) == 0)
+ {
+ return null;
+ }
+
+ var tcmProperties = new Dictionary(capacity: 15)
+ {
+ // Step 1: Add common properties.
+ [AdapterTestProperties.TestRunIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestRunIdProperty, default),
+ [AdapterTestProperties.TestPlanIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestPlanIdProperty, default),
+ [AdapterTestProperties.BuildConfigurationIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildConfigurationIdProperty, default),
+ [AdapterTestProperties.BuildDirectoryProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildDirectoryProperty, default),
+ [AdapterTestProperties.BuildFlavorProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildFlavorProperty, default),
+ [AdapterTestProperties.BuildNumberProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildNumberProperty, default),
+ [AdapterTestProperties.BuildPlatformProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildPlatformProperty, default),
+ [AdapterTestProperties.BuildUriProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.BuildUriProperty, default),
+ [AdapterTestProperties.TfsServerCollectionUrlProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TfsServerCollectionUrlProperty, default),
+ [AdapterTestProperties.TfsTeamProjectProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TfsTeamProjectProperty, default),
+ [AdapterTestProperties.IsInLabEnvironmentProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.IsInLabEnvironmentProperty, default),
+
+ // Step 2: Add test case specific properties.
+ [AdapterTestProperties.TestCaseIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestCaseIdProperty, default),
+ [AdapterTestProperties.TestConfigurationIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestConfigurationIdProperty, default),
+ [AdapterTestProperties.TestConfigurationNameProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestConfigurationNameProperty, default),
+ [AdapterTestProperties.TestPointIdProperty.Id] = testCase.GetPropertyValue(AdapterTestProperties.TestPointIdProperty, default),
+ };
+
+ return tcmProperties;
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestCaseExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs
similarity index 86%
rename from src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestCaseExtensions.cs
rename to src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs
index 4c69d171ee..b3c757cfad 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestCaseExtensions.cs
+++ b/src/Adapter/MSTest.TestAdapter/Extensions/TestCaseExtensions.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -89,53 +88,53 @@ internal static UnitTestElement ToUnitTestElementWithUpdatedSource(this TestCase
}
string? managedTypeName = testCase.GetManagedType();
- string? legacyTestClassName = testCase.GetPropertyValue(EngineConstants.TestClassNameProperty) as string;
+ string? legacyTestClassName = testCase.GetPropertyValue(AdapterTestProperties.TestClassNameProperty) as string;
string? testClassName = testCase.GetClassNameWhenFullyQualifiedNameStartsWith(managedTypeName)
?? testCase.GetClassNameWhenFullyQualifiedNameStartsWith(legacyTestClassName)
?? managedTypeName
?? legacyTestClassName;
string name = testCase.GetTestName(testClassName);
- var testMethod = new TestMethod(testCase.GetManagedMethod(), testCase.GetHierarchy(), name, testClassName!, source, testCase.DisplayName, testCase.GetPropertyValue(EngineConstants.ParameterTypesProperty, null));
+ var testMethod = new TestMethod(testCase.GetManagedMethod(), testCase.GetHierarchy(), name, testClassName!, source, testCase.DisplayName, testCase.GetPropertyValue(AdapterTestProperties.ParameterTypesProperty, null));
- var dataType = (DynamicDataType)testCase.GetPropertyValue(EngineConstants.TestDynamicDataTypeProperty, (int)DynamicDataType.None);
+ var dataType = (DynamicDataType)testCase.GetPropertyValue(AdapterTestProperties.TestDynamicDataTypeProperty, (int)DynamicDataType.None);
if (dataType != DynamicDataType.None)
{
- string[]? data = testCase.GetPropertyValue(EngineConstants.TestDynamicDataProperty, null);
+ string[]? data = testCase.GetPropertyValue(AdapterTestProperties.TestDynamicDataProperty, null);
testMethod.DataType = dataType;
testMethod.SerializedData = data;
- testMethod.TestCaseIndex = testCase.GetPropertyValue(EngineConstants.TestCaseIndexProperty, 0);
- testMethod.TestDataSourceIgnoreMessage = testCase.GetPropertyValue(EngineConstants.TestDataSourceIgnoreMessageProperty) as string;
+ testMethod.TestCaseIndex = testCase.GetPropertyValue(AdapterTestProperties.TestCaseIndexProperty, 0);
+ testMethod.TestDataSourceIgnoreMessage = testCase.GetPropertyValue(AdapterTestProperties.TestDataSourceIgnoreMessageProperty) as string;
}
var testElement = new UnitTestElement(testMethod)
{
- TestCategory = testCase.GetPropertyValue(EngineConstants.TestCategoryProperty) as string[],
- Priority = testCase.GetPropertyValue(EngineConstants.PriorityProperty) as int?,
- UnfoldingStrategy = (TestDataSourceUnfoldingStrategy)testCase.GetPropertyValue(EngineConstants.UnfoldingStrategy, (int)TestDataSourceUnfoldingStrategy.Auto),
+ TestCategory = testCase.GetPropertyValue(AdapterTestProperties.TestCategoryProperty) as string[],
+ Priority = testCase.GetPropertyValue(AdapterTestProperties.PriorityProperty) as int?,
+ UnfoldingStrategy = (TestDataSourceUnfoldingStrategy)testCase.GetPropertyValue(AdapterTestProperties.UnfoldingStrategy, (int)TestDataSourceUnfoldingStrategy.Auto),
};
if (testCase.Traits.Any())
{
- testElement.Traits = [.. testCase.Traits];
+ testElement.Traits = [.. testCase.Traits.Select(t => new TestTrait(t.Name, t.Value))];
}
- string[]? workItemIds = testCase.GetPropertyValue(EngineConstants.WorkItemIdsProperty, null);
+ string[]? workItemIds = testCase.GetPropertyValue(AdapterTestProperties.WorkItemIdsProperty, null);
if (workItemIds is { Length: > 0 })
{
testElement.WorkItemIds = workItemIds;
}
#if !WINDOWS_UWP && !WIN_UI
- KeyValuePair[]? deploymentItems = testCase.GetPropertyValue[]>(EngineConstants.DeploymentItemsProperty, null);
+ KeyValuePair[]? deploymentItems = testCase.GetPropertyValue[]>(AdapterTestProperties.DeploymentItemsProperty, null);
if (deploymentItems is { Length: > 0 })
{
testElement.DeploymentItems = deploymentItems;
}
#endif
- testElement.DoNotParallelize = testCase.GetPropertyValue(EngineConstants.DoNotParallelizeProperty, false);
+ testElement.DoNotParallelize = testCase.GetPropertyValue(AdapterTestProperties.DoNotParallelizeProperty, false);
return testElement;
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs
similarity index 93%
rename from src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs
rename to src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs
index 0eac8b99a2..834b522ee8 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Extensions/TestResultExtensions.cs
+++ b/src/Adapter/MSTest.TestAdapter/Extensions/TestResultExtensions.cs
@@ -4,6 +4,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VSTestAttachmentSet = Microsoft.VisualStudio.TestPlatform.ObjectModel.AttachmentSet;
@@ -48,8 +49,8 @@ internal static VSTestTestResult ToTestResult(this TestResult frameworkTestResul
ComputerName = computerName,
};
- testResult.SetPropertyValue(EngineConstants.ExecutionIdProperty, frameworkTestResult.ExecutionId);
- testResult.SetPropertyValue(EngineConstants.ParentExecIdProperty, frameworkTestResult.ParentExecId);
+ testResult.SetPropertyValue(AdapterTestProperties.ExecutionIdProperty, frameworkTestResult.ExecutionId);
+ testResult.SetPropertyValue(AdapterTestProperties.ParentExecIdProperty, frameworkTestResult.ParentExecId);
if (!StringEx.IsNullOrEmpty(frameworkTestResult.LogOutput))
{
diff --git a/src/Adapter/MSTest.TestAdapter/Extensions/UnitTestElementExtensions.cs b/src/Adapter/MSTest.TestAdapter/Extensions/UnitTestElementExtensions.cs
new file mode 100644
index 0000000000..c6a82a8870
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/Extensions/UnitTestElementExtensions.cs
@@ -0,0 +1,231 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+
+///
+/// Materializes a VSTest from a platform-agnostic . This is the
+/// adapter-boundary translation from the neutral execution model to the VSTest object model; the platform services
+/// engine never performs it and only threads the resulting test case (as an opaque handle) to the recorder and the
+/// deployment boundary.
+///
+internal static class UnitTestElementExtensions
+{
+ private static readonly byte[] OpenParen = [40, 0]; // Encoding.Unicode.GetBytes("(");
+ private static readonly byte[] CloseParen = [41, 0]; // Encoding.Unicode.GetBytes(")");
+ private static readonly byte[] OpenBracket = [91, 0]; // Encoding.Unicode.GetBytes("[");
+ private static readonly byte[] CloseBracket = [93, 0]; // Encoding.Unicode.GetBytes("]");
+
+ ///
+ /// Returns the host test case for this test, reusing the host-provided one when present and otherwise
+ /// materializing a single VSTest test case on demand and caching it (so deployment, test-start and every
+ /// reported result share one instance, matching the historical "one test case per discovered test").
+ ///
+ internal static TestCase GetOrCreateHostTestCase(this UnitTestElement element)
+ {
+ if (element.HostRecordingHandle is not TestCase testCase)
+ {
+ testCase = element.ToTestCase();
+ element.HostRecordingHandle = testCase;
+ }
+
+ return testCase;
+ }
+
+ ///
+ /// Convert the UnitTestElement instance to an Object Model testCase instance.
+ ///
+ /// The unit test element to convert.
+ /// An instance of .
+ internal static TestCase ToTestCase(this UnitTestElement element)
+ {
+ TestMethod testMethod = element.TestMethod;
+
+ // This causes compatibility problems with older runners.
+ // string testFullName = this.TestMethod.HasManagedMethodAndTypeProperties
+ // ? $"{TestMethod.ManagedTypeName}.{TestMethod.ManagedMethodName}"
+ // : $"{TestMethod.FullClassName}.{TestMethod.Name}";
+ string testFullName = $"{testMethod.FullClassName}.{testMethod.Name}";
+
+ TestCase testCase = new(testFullName, EngineConstants.ExecutorUri, testMethod.AssemblyName)
+ {
+ DisplayName = testMethod.DisplayName,
+ LocalExtensionData = element,
+ };
+
+ if (testMethod.HasManagedMethodAndTypeProperties)
+ {
+ testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, testMethod.ManagedTypeName);
+ testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, testMethod.ManagedMethodName);
+ }
+
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, testMethod.FullClassName);
+
+ if (testMethod.ParameterTypes is not null)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.ParameterTypesProperty, testMethod.ParameterTypes);
+ }
+
+ IReadOnlyCollection? hierarchy = testMethod.Hierarchy;
+ if (hierarchy is { Count: > 0 })
+ {
+ testCase.SetHierarchy([.. hierarchy]);
+ }
+
+ // Set only if some test category is present
+ if (element.TestCategory is { Length: > 0 })
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.TestCategoryProperty, element.TestCategory);
+ }
+
+ // Set priority if present
+ if (element.Priority != null)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.PriorityProperty, element.Priority.Value);
+ }
+
+ if (element.Traits is { Length: > 0 })
+ {
+ foreach (TestTrait trait in element.Traits)
+ {
+ testCase.Traits.Add(new Trait(trait.Name, trait.Value));
+ }
+ }
+
+ if (element.WorkItemIds != null)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.WorkItemIdsProperty, element.WorkItemIds);
+ }
+
+#if !WINDOWS_UWP && !WIN_UI
+ // The list of items to deploy before running this test.
+ if (element.DeploymentItems is { Length: > 0 })
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.DeploymentItemsProperty, element.DeploymentItems);
+ }
+#endif
+
+ // Set the Do not parallelize state if present
+ if (element.DoNotParallelize)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.DoNotParallelizeProperty, element.DoNotParallelize);
+ }
+
+ if (element.UnfoldingStrategy != TestDataSourceUnfoldingStrategy.Auto)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.UnfoldingStrategy, (int)element.UnfoldingStrategy);
+ }
+
+ // Store resolved data if any
+ if (testMethod.DataType != DynamicDataType.None)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.TestDynamicDataTypeProperty, (int)testMethod.DataType);
+ testCase.SetPropertyValue(AdapterTestProperties.TestDynamicDataProperty, testMethod.SerializedData);
+ testCase.SetPropertyValue(AdapterTestProperties.TestCaseIndexProperty, testMethod.TestCaseIndex);
+ // VSTest serialization doesn't handle null so instead don't set the property so that it's deserialized as null
+ if (testMethod.TestDataSourceIgnoreMessage is not null)
+ {
+ testCase.SetPropertyValue(AdapterTestProperties.TestDataSourceIgnoreMessageProperty, testMethod.TestDataSourceIgnoreMessage);
+ }
+ }
+
+ testCase.LineNumber = element.DeclaringLineNumber ?? -1;
+ testCase.CodeFilePath = element.DeclaringFilePath;
+
+ testCase.Id = GenerateSerializedDataStrategyTestId(element, testFullName);
+
+ return testCase;
+ }
+
+ private static Guid GenerateSerializedDataStrategyTestId(UnitTestElement element, string testFullName)
+ {
+ TestMethod testMethod = element.TestMethod;
+
+ // Below comment is copied over from Test Platform.
+ // If source is a file name then just use the filename for the identifier since the file might have moved between
+ // discovery and execution (in appx mode for example). This is not elegant because the Source contents should be
+ // a black box to the framework.
+ // For example in the database adapter case this is not a file path.
+ // As discussed with team, we found no scenario for netcore, & fullclr where the Source is not present where ID
+ // is generated, which means we would always use FileName to generate ID. In cases where somehow Source Path
+ // contained garbage character the API Path.GetFileName() we are simply returning original input.
+ // For UWP where source during discovery, & during execution can be on different machine, in such case we should
+ // always use Path.GetFileName().
+ string fileNameOrFilePath = testMethod.AssemblyName;
+ try
+ {
+ fileNameOrFilePath = Path.GetFileName(fileNameOrFilePath);
+ }
+ catch (ArgumentException)
+ {
+ // In case path contains invalid characters.
+ }
+
+ byte hashVersion = 1; // Increment when changing the hashing algorithm.
+ var hash = new TestFx.Hashing.XxHash128();
+ hash.Append(Encoding.Unicode.GetBytes(fileNameOrFilePath));
+ hash.Append(Encoding.Unicode.GetBytes(testFullName));
+ if (testMethod.ParameterTypes is not null)
+ {
+ hash.Append(OpenParen);
+ hash.Append(Encoding.Unicode.GetBytes(testMethod.ParameterTypes));
+ hash.Append(CloseParen);
+ }
+
+ if (testMethod.DataType != DynamicDataType.None)
+ {
+ hash.Append(OpenBracket);
+ hash.Append(Encoding.Unicode.GetBytes(testMethod.TestCaseIndex.ToString(CultureInfo.InvariantCulture)));
+ hash.Append(CloseBracket);
+ }
+
+ byte[] hashBytes = hash.GetCurrentHash();
+
+ return VersionedGuidFromHash(hashBytes, hashVersion);
+ }
+
+ internal /* for testing */ static Guid VersionedGuidFromHash(byte[] hashBytes, byte hashVersion)
+ {
+ int firstByte = 0;
+ int versionByte = 6;
+
+ // We set first 4 bits to 0001, which is our version. Increase this when we change the hashing algorithm.
+ //
+ // Note: The logic below is operating on int32, because bitwise operators are not defined for byte in C#. But casting
+ // does not affect endianness, so we can safely assume the byte data are at the end of the int.
+ // We also don't care to specify the whole expansion of the int in the bit masks, which effectively ignores all the data in the int
+ // before the last byte data, but that is okay, they will always be zero.
+ hashBytes[firstByte] = (byte)((hashBytes[firstByte] & 0b0000_1111) | (hashVersion << 4));
+ // We set first 4bits 7th byte to 8 to indicate that this is a version 8 UUID. https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-8
+ hashBytes[versionByte] = (byte)((hashBytes[versionByte] & 0b0000_1111) | 0b1000_0000);
+
+ // We set the first 2 bits of nineth byte to 0b10. In the guid this is stored as byte, so we don't care about endiannes.
+ int variantByte = 8;
+ hashBytes[variantByte] = (byte)((hashBytes[variantByte] & 0b0011_1111) | 0b1000_0000);
+
+ Guid guid;
+
+ // On .NET Framework we cannot specify the endianness of the Guid because the new Guid(hashBytes, bigEndian: true); api is not available.
+ // Instead we construct the int and short values manually, doing what the constructor would do, to put the bytes in big-endian order.
+ guid = new Guid(
+ (hashBytes[0] << 24) | (hashBytes[1] << 16) | (hashBytes[2] << 8) | hashBytes[3],
+ (short)((hashBytes[4] << 8) | hashBytes[5]),
+ (short)((hashBytes[6] << 8) | hashBytes[7]),
+ hashBytes[8], hashBytes[9], hashBytes[10], hashBytes[11], hashBytes[12], hashBytes[13], hashBytes[14], hashBytes[15]);
+
+#if DEBUG && NET9_0_OR_GREATER
+ // Version property is only available on .NET 9 and later.
+ Debug.Assert(guid.Version == 8, $"Expected Guid version to be 8, but it was {guid.Version}");
+ // The field represents the 4 bit value, but according to the specification only the first 2 bits are used for UUID v8.
+ // So we shift 2 bits to the right to get the actual variant value.
+ int variant = guid.Variant >> 2;
+ Debug.Assert(variant == 2, $"Expected Guid variant to be 2, but it was {variant}");
+#endif
+ return guid;
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/UnitTestOutcomeHelper.cs b/src/Adapter/MSTest.TestAdapter/Helpers/UnitTestOutcomeHelper.cs
similarity index 100%
rename from src/Adapter/MSTestAdapter.PlatformServices/Helpers/UnitTestOutcomeHelper.cs
rename to src/Adapter/MSTest.TestAdapter/Helpers/UnitTestOutcomeHelper.cs
diff --git a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj
index 20616c2118..b4792d9cad 100644
--- a/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj
+++ b/src/Adapter/MSTest.TestAdapter/MSTest.TestAdapter.csproj
@@ -193,6 +193,7 @@
+
diff --git a/src/Adapter/MSTest.TestAdapter/Services/AdapterMessageLoggerExtensions.cs b/src/Adapter/MSTest.TestAdapter/Services/AdapterMessageLoggerExtensions.cs
new file mode 100644
index 0000000000..b8d4ca1b42
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/Services/AdapterMessageLoggerExtensions.cs
@@ -0,0 +1,39 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+
+///
+/// Bridges a VSTest to the platform-agnostic .
+///
+///
+/// This is the single translation point between the VSTest object model and the platform services
+/// message-logging abstraction. It is expected to move entirely into the adapter layer once the
+/// execution pipeline no longer flows VSTest handles through the platform services (see the tracking
+/// issue linked in the pull request that removes the VSTest object model from platform services).
+///
+internal static class AdapterMessageLoggerExtensions
+{
+ ///
+ /// Wraps a VSTest as an .
+ ///
+ /// The host message logger to wrap.
+ /// A platform-agnostic logger that forwards to .
+ internal static IAdapterMessageLogger ToAdapterMessageLogger(this IMessageLogger messageLogger)
+ => new HostMessageLogger(messageLogger ?? throw new ArgumentNullException(nameof(messageLogger)));
+
+ private sealed class HostMessageLogger : IAdapterMessageLogger
+ {
+ private readonly IMessageLogger _messageLogger;
+
+ public HostMessageLogger(IMessageLogger messageLogger)
+ => _messageLogger = messageLogger;
+
+ public void SendMessage(MessageLevel level, string message)
+ => _messageLogger.SendMessage(level.ToTestMessageLevel(), message);
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MessageLevel.cs b/src/Adapter/MSTest.TestAdapter/Services/MessageLevel.cs
similarity index 100%
rename from src/Adapter/MSTestAdapter.PlatformServices/Services/MessageLevel.cs
rename to src/Adapter/MSTest.TestAdapter/Services/MessageLevel.cs
diff --git a/src/Adapter/MSTest.TestAdapter/Services/TestResultRecorderExtensions.cs b/src/Adapter/MSTest.TestAdapter/Services/TestResultRecorderExtensions.cs
new file mode 100644
index 0000000000..317159ed4a
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/Services/TestResultRecorderExtensions.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+
+using FrameworkTestResult = Microsoft.VisualStudio.TestTools.UnitTesting.TestResult;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+
+///
+/// Bridges a VSTest to the platform-agnostic .
+///
+///
+/// This is the single translation point between the neutral execution model ( and
+/// the framework ) and the VSTest result object model (TestResult,
+/// TestOutcome, attachments, ...). It lives in the adapter layer; the platform services engine reports
+/// through the neutral only. The element's host test case is resolved (and
+/// materialized/cached on demand for internally discovered tests) via UnitTestElementExtensions.GetOrCreateHostTestCase
+/// so recorded results preserve host-injected data (test-case-management / data-collector properties) with full fidelity.
+///
+internal static class TestResultRecorderExtensions
+{
+ ///
+ /// Wraps a VSTest as an .
+ ///
+ /// The host recorder to wrap.
+ /// The computer name stamped on reported results.
+ /// The current MSTest settings used to map framework outcomes to host outcomes.
+ /// A platform-agnostic recorder that forwards to .
+ public static ITestResultRecorder ToTestResultRecorder(this ITestExecutionRecorder testExecutionRecorder, string computerName, MSTestSettings settings)
+ => new HostTestResultRecorder(testExecutionRecorder, computerName, settings);
+
+ private sealed class HostTestResultRecorder : ITestResultRecorder
+ {
+ private readonly ITestExecutionRecorder _testExecutionRecorder;
+ private readonly string _computerName;
+ private readonly MSTestSettings _settings;
+
+ public HostTestResultRecorder(ITestExecutionRecorder testExecutionRecorder, string computerName, MSTestSettings settings)
+ {
+ _testExecutionRecorder = testExecutionRecorder;
+ _computerName = computerName;
+ _settings = settings;
+ }
+
+ public void RecordStart(UnitTestElement testElement)
+ => _testExecutionRecorder.RecordStart(testElement.GetOrCreateHostTestCase());
+
+ public void RecordEmptyResult(UnitTestElement testElement)
+ => _testExecutionRecorder.RecordEnd(testElement.GetOrCreateHostTestCase(), TestOutcome.None);
+
+ public bool RecordResult(UnitTestElement testElement, FrameworkTestResult unitTestResult, DateTimeOffset startTime, DateTimeOffset endTime)
+ {
+ TestCase testCase = testElement.GetOrCreateHostTestCase();
+ var testResult = unitTestResult.ToTestResult(testCase, startTime, endTime, _computerName, _settings);
+
+ _testExecutionRecorder.RecordEnd(testCase, testResult.Outcome);
+
+ bool isFailed = testResult.Outcome == TestOutcome.Failed;
+ if (isFailed && PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
+ {
+ PlatformServiceProvider.Instance.AdapterTraceLogger.Info("MSTestExecutor:Test {0} failed. ErrorMessage:{1}, ErrorStackTrace:{2}.", testResult.TestCase.FullyQualifiedName, testResult.ErrorMessage, testResult.ErrorStackTrace);
+ }
+
+ try
+ {
+ if (testResult.Outcome != TestOutcome.NotFound
+ || !RuntimeContext.IsHotReloadEnabled)
+ {
+ _testExecutionRecorder.RecordResult(testResult);
+ }
+ }
+ catch (TestCanceledException)
+ {
+ // Ignore this exception
+ }
+
+ // A failure is reported only once RecordResult has completed (a swallowed TestCanceledException
+ // still counts as completed). This mirrors the original inline flow where the failure was
+ // observed as part of reporting the result.
+ return isFailed;
+ }
+ }
+}
diff --git a/src/Adapter/MSTest.TestAdapter/Services/UnitTestElementSinkExtensions.cs b/src/Adapter/MSTest.TestAdapter/Services/UnitTestElementSinkExtensions.cs
new file mode 100644
index 0000000000..79ac2f6772
--- /dev/null
+++ b/src/Adapter/MSTest.TestAdapter/Services/UnitTestElementSinkExtensions.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+
+///
+/// Bridges a VSTest to the platform-agnostic
+/// .
+///
+///
+/// This is the single translation point between the neutral model and the
+/// VSTest discovery object model (TestCase, ITestCaseDiscoverySink). It materializes a
+/// VSTest TestCase for each discovered element via UnitTestElementExtensions.ToTestCase. It is
+/// expected to move entirely into the adapter layer once discovery no longer flows VSTest discovery sinks
+/// through the platform services (see the tracking issue linked in the pull request that removes the VSTest
+/// object model from platform services).
+///
+internal static class UnitTestElementSinkExtensions
+{
+ ///
+ /// Wraps a VSTest as an .
+ ///
+ /// The host discovery sink to wrap.
+ /// A platform-agnostic sink that forwards to .
+ internal static IUnitTestElementSink ToUnitTestElementSink(this ITestCaseDiscoverySink discoverySink)
+ => new HostDiscoverySink(discoverySink ?? throw new ArgumentNullException(nameof(discoverySink)));
+
+ private sealed class HostDiscoverySink : IUnitTestElementSink
+ {
+ private readonly ITestCaseDiscoverySink _discoverySink;
+
+ public HostDiscoverySink(ITestCaseDiscoverySink discoverySink)
+ => _discoverySink = discoverySink;
+
+ public void SendTestElement(UnitTestElement testElement)
+ => _discoverySink.SendTestCase(testElement.ToTestCase());
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs
similarity index 61%
rename from src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs
rename to src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs
index 481220ad0c..4a6d42ea3c 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs
+++ b/src/Adapter/MSTest.TestAdapter/TestMethodFilter.cs
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -18,12 +19,12 @@ internal sealed class TestMethodFilter
internal TestMethodFilter() => _supportedProperties = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
- [EngineConstants.TestCategoryProperty.Label] = EngineConstants.TestCategoryProperty,
- [EngineConstants.PriorityProperty.Label] = EngineConstants.PriorityProperty,
+ [AdapterTestProperties.TestCategoryProperty.Label] = AdapterTestProperties.TestCategoryProperty,
+ [AdapterTestProperties.PriorityProperty.Label] = AdapterTestProperties.PriorityProperty,
[TestCaseProperties.FullyQualifiedName.Label] = TestCaseProperties.FullyQualifiedName,
[TestCaseProperties.DisplayName.Label] = TestCaseProperties.DisplayName,
[TestCaseProperties.Id.Label] = TestCaseProperties.Id,
- [EngineConstants.TestClassNameProperty.Label] = EngineConstants.TestClassNameProperty,
+ [AdapterTestProperties.TestClassNameProperty.Label] = AdapterTestProperties.TestClassNameProperty,
};
///
@@ -33,7 +34,7 @@ internal sealed class TestMethodFilter
/// Handler to report test messages/start/end and results.
/// Indicates that the filter is unsupported/has an error.
/// A filter expression.
- internal ITestCaseFilterExpression? GetFilterExpression(IDiscoveryContext? context, IMessageLogger logger, out bool filterHasError)
+ internal ITestCaseFilterExpression? GetFilterExpression(IDiscoveryContext? context, IAdapterMessageLogger logger, out bool filterHasError)
{
filterHasError = false;
if (context == null)
@@ -51,12 +52,30 @@ internal sealed class TestMethodFilter
catch (TestPlatformFormatException ex)
{
filterHasError = true;
- logger.SendMessage(TestMessageLevel.Error, ex.Message);
+ logger.SendMessage(MessageLevel.Error, ex.Message);
}
return filter;
}
+ ///
+ /// Builds a platform-agnostic for the properties supported by the adapter.
+ ///
+ /// The current context of the run.
+ /// Handler to report test messages/start/end and results.
+ /// Indicates that the filter is unsupported/has an error.
+ ///
+ /// A filter that evaluates instances, or when no
+ /// filter applies (in which case every element is included).
+ ///
+ internal ITestElementFilter? GetTestElementFilter(IDiscoveryContext? context, IAdapterMessageLogger logger, out bool filterHasError)
+ {
+ ITestCaseFilterExpression? filterExpression = GetFilterExpression(context, logger, out filterHasError);
+ return filterExpression is null
+ ? null
+ : new TestElementFilter(this, filterExpression);
+ }
+
///
/// Provides TestProperty for property name 'propertyName' as used in filter.
///
@@ -118,7 +137,7 @@ internal TestProperty PropertyProvider(string propertyName)
/// The logger to log exception messages too.
/// Filter expression.
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2072:'target parameter' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method.", Justification = "GetTestCaseFilter is part of the VSTest discovery contract on the concrete DiscoveryContext type; the runtime guarantees the method exists on supported hosts.")]
- private ITestCaseFilterExpression? GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context, IMessageLogger logger)
+ private ITestCaseFilterExpression? GetTestCaseFilterFromDiscoveryContext(IDiscoveryContext context, IAdapterMessageLogger logger)
{
try
{
@@ -134,9 +153,49 @@ internal TestProperty PropertyProvider(string propertyName)
}
catch (Exception ex)
{
- logger.SendMessage(TestMessageLevel.Warning, ex.Message);
+ logger.SendMessage(MessageLevel.Warning, ex.Message);
}
return null;
}
+
+ ///
+ /// Platform-agnostic filter that evaluates a against a VSTest
+ /// . This is the single point where the neutral element model is
+ /// translated to the VSTest test case in order to reuse the host's filter matching.
+ ///
+ private sealed class TestElementFilter : ITestElementFilter
+ {
+ private readonly TestMethodFilter _testMethodFilter;
+ private readonly ITestCaseFilterExpression _filterExpression;
+
+ public TestElementFilter(TestMethodFilter testMethodFilter, ITestCaseFilterExpression filterExpression)
+ {
+ _testMethodFilter = testMethodFilter;
+ _filterExpression = filterExpression;
+ }
+
+ public bool Matches(UnitTestElement testElement)
+ {
+ var testCase = testElement.ToTestCase();
+ return _filterExpression.MatchTestCase(testCase, propertyName => _testMethodFilter.PropertyValueProvider(testCase, propertyName));
+ }
+ }
+}
+
+///
+/// Adapter-boundary that closes over the VSTest discovery/run context
+/// and produces the neutral via . Created at the
+/// boundary and passed into the platform services engine/discoverer so the filter (and any parse-error report)
+/// is built at the exact point the engine needs it, preserving the original per-source timing.
+///
+internal sealed class TestElementFilterProvider : ITestElementFilterProvider
+{
+ private readonly TestMethodFilter _testMethodFilter = new();
+ private readonly IDiscoveryContext? _context;
+
+ public TestElementFilterProvider(IDiscoveryContext? context) => _context = context;
+
+ public ITestElementFilter? GetTestElementFilter(IAdapterMessageLogger logger, out bool filterHasError)
+ => _testMethodFilter.GetTestElementFilter(_context, logger, out filterHasError);
}
diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs
index a98e00c83a..ebe26f397c 100644
--- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs
+++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestDiscoverer.cs
@@ -84,9 +84,10 @@ internal async Task DiscoverTestsAsync(IEnumerable sources, IDiscoveryCo
try
{
- if (MSTestDiscovererHelpers.InitializeDiscovery(sources, discoveryContext, logger, configuration, _testSourceHandler))
+ IAdapterMessageLogger adapterLogger = logger.ToAdapterMessageLogger();
+ if (MSTestDiscovererHelpers.InitializeDiscovery(sources, discoveryContext?.RunSettings?.SettingsXml, adapterLogger, configuration, _testSourceHandler))
{
- new UnitTestDiscoverer(_testSourceHandler).DiscoverTests(sources, logger, discoverySink, discoveryContext, isMTP);
+ new UnitTestDiscoverer(_testSourceHandler).DiscoverTests(sources, adapterLogger, discoverySink.ToUnitTestElementSink(), discoveryContext?.RunSettings?.SettingsXml, new TestElementFilterProvider(discoveryContext), isMTP);
}
}
finally
diff --git a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs
index 7518f455a7..f1fd235958 100644
--- a/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs
+++ b/src/Adapter/MSTest.TestAdapter/VSTestAdapter/MSTestExecutor.cs
@@ -2,8 +2,11 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.VSTestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
@@ -139,12 +142,26 @@ internal async Task RunTestsAsync(IEnumerable? tests, IRunContext? run
try
{
- if (!MSTestDiscovererHelpers.InitializeDiscovery(from test in tests select test.Source, runContext, frameworkHandle, configuration, new TestSourceHandler()))
+ if (!MSTestDiscovererHelpers.InitializeDiscovery(from test in tests select test.Source, runContext?.RunSettings?.SettingsXml, frameworkHandle.ToAdapterMessageLogger(), configuration, new TestSourceHandler()))
{
return;
}
- await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(tests, runContext, frameworkHandle, testRunToken).ConfigureAwait(false)).ConfigureAwait(false);
+ // Convert the host test cases into the neutral model at this boundary so the execution engine runs
+ // on UnitTestElement end-to-end. Each element keeps its originating test case as an opaque handle
+ // (for full-fidelity result recording and deployment) plus the host execution-context (TCM)
+ // properties surfaced through TestContext.
+ UnitTestElement[] testElements = [.. tests.Select(ToUnitTestElement)];
+
+ // Translate the VSTest recorder into the platform-agnostic result recorder at this boundary; the
+ // execution engine reports results through this neutral recorder and never constructs VSTest results.
+ ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings);
+
+ // Extract the neutral run inputs (test-run directory + run settings XML) from the host run context at
+ // this boundary so the execution engine no longer depends on the VSTest run context.
+ var deploymentContext = new DeploymentContext(runContext?.TestRunDirectory, runContext?.RunSettings?.SettingsXml);
+
+ await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(testElements, deploymentContext, frameworkHandle.ToAdapterMessageLogger(), testResultRecorder, new TestElementFilterProvider(runContext), testRunToken).ConfigureAwait(false)).ConfigureAwait(false);
}
finally
{
@@ -152,6 +169,14 @@ internal async Task RunTestsAsync(IEnumerable? tests, IRunContext? run
}
}
+ private static UnitTestElement ToUnitTestElement(TestCase testCase)
+ {
+ UnitTestElement testElement = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
+ testElement.ExecutionContextProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
+ testElement.HostRecordingHandle = testCase;
+ return testElement;
+ }
+
internal async Task RunTestsAsync(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle, IConfiguration? configuration, bool isMTP)
{
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
@@ -183,13 +208,22 @@ internal async Task RunTestsAsync(IEnumerable? sources, IRunContext? run
try
{
TestSourceHandler testSourceHandler = new();
- if (!MSTestDiscovererHelpers.InitializeDiscovery(sources, runContext, frameworkHandle, configuration, testSourceHandler))
+ if (!MSTestDiscovererHelpers.InitializeDiscovery(sources, runContext?.RunSettings?.SettingsXml, frameworkHandle.ToAdapterMessageLogger(), configuration, testSourceHandler))
{
return;
}
sources = testSourceHandler.GetTestSources(sources);
- await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, runContext, frameworkHandle, testSourceHandler, isMTP, testRunToken).ConfigureAwait(false)).ConfigureAwait(false);
+
+ // Translate the VSTest recorder into the platform-agnostic result recorder at this boundary; the
+ // execution engine reports results through this neutral recorder and never constructs VSTest results.
+ ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings);
+
+ // Extract the neutral run inputs (test-run directory + run settings XML) from the host run context at
+ // this boundary so the execution engine no longer depends on the VSTest run context.
+ var deploymentContext = new DeploymentContext(runContext?.TestRunDirectory, runContext?.RunSettings?.SettingsXml);
+
+ await RunTestsFromRightContextAsync(frameworkHandle, async testRunToken => await TestExecutionManager.RunTestsAsync(sources, deploymentContext, frameworkHandle.ToAdapterMessageLogger(), testResultRecorder, new TestElementFilterProvider(runContext), testSourceHandler, isMTP, testRunToken).ConfigureAwait(false)).ConfigureAwait(false);
}
finally
{
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ApartmentStateSetting.cs b/src/Adapter/MSTestAdapter.PlatformServices/ApartmentStateSetting.cs
new file mode 100644
index 0000000000..4bb4f09f3a
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/ApartmentStateSetting.cs
@@ -0,0 +1,18 @@
+// 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;
+
+///
+/// Apartment state parsed from the ExecutionThreadApartmentState run settings and the
+/// mstest:execution:executionApartmentState configuration value. This is the adapter's own neutral
+/// replacement for the former VSTest PlatformAbstractions.PlatformApartmentState enum.
+///
+internal enum ApartmentStateSetting
+{
+ // The member order is load-bearing: Enum.TryParse maps numeric-string run settings values ("0"/"1")
+ // by number, so MTA must stay 0 and STA must stay 1 to preserve parse compatibility with the former
+ // VSTest PlatformAbstractions.PlatformApartmentState enum. Do not reorder.
+ MTA,
+ STA,
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Deployment/DeploymentContext.cs b/src/Adapter/MSTestAdapter.PlatformServices/Deployment/DeploymentContext.cs
new file mode 100644
index 0000000000..e6c35de10c
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Deployment/DeploymentContext.cs
@@ -0,0 +1,30 @@
+// 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.PlatformServices.Deployment;
+
+///
+/// Platform-agnostic inputs the execution pipeline needs from the running test host: the test-run/results
+/// directory and the run settings XML. This lets the platform services execution and deployment layers run
+/// without taking a dependency on a specific test platform's run-context object model (for example the VSTest
+/// IRunContext / IRunSettings types). It is populated at the adapter boundary.
+///
+internal sealed class DeploymentContext
+{
+ public DeploymentContext(string? testRunDirectory, string? runSettingsXml)
+ {
+ TestRunDirectory = testRunDirectory;
+ RunSettingsXml = runSettingsXml;
+ }
+
+ ///
+ /// Gets the host-provided test-run directory (the root under which results/deployment directories are
+ /// created), or to fall back to a temp directory.
+ ///
+ public string? TestRunDirectory { get; }
+
+ ///
+ /// Gets the run settings XML supplied by the host, or when none was provided.
+ ///
+ public string? RunSettingsXml { get; }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/AssemblyEnumeratorWrapper.cs b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/AssemblyEnumeratorWrapper.cs
index 16e83134c8..454373159d 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/AssemblyEnumeratorWrapper.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/AssemblyEnumeratorWrapper.cs
@@ -4,7 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
@@ -23,7 +22,7 @@ internal sealed class AssemblyEnumeratorWrapper
/// Gets test elements from an assembly.
///
/// A collection of test elements.
- internal static ICollection? GetTests(string? assemblyFileName, IRunSettings? runSettings, ITestSourceHandler testSourceHandler, bool isMTP, out List warnings)
+ internal static ICollection? GetTests(string? assemblyFileName, string? settingsXml, ITestSourceHandler testSourceHandler, bool isMTP, out List warnings)
{
warnings = [];
@@ -48,7 +47,7 @@ internal sealed class AssemblyEnumeratorWrapper
try
{
// Load the assembly in isolation if required.
- AssemblyEnumerationResult result = GetTestsInIsolation(fullFilePath, runSettings, isMTP);
+ AssemblyEnumerationResult result = GetTestsInIsolation(fullFilePath, settingsXml, isMTP);
warnings.AddRange(result.Warnings);
return result.TestElements;
}
@@ -86,9 +85,9 @@ internal sealed class AssemblyEnumeratorWrapper
}
}
- private static AssemblyEnumerationResult GetTestsInIsolation(string fullFilePath, IRunSettings? runSettings, bool isMTP)
+ private static AssemblyEnumerationResult GetTestsInIsolation(string fullFilePath, string? settingsXml, bool isMTP)
{
- using ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, runSettings);
+ using ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(fullFilePath, settingsXml);
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var assemblyEnumerator = (AssemblyEnumerator)isolationHost.CreateInstanceForType(typeof(AssemblyEnumerator), [MSTestSettings.CurrentSettings])!;
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs
index fad95daf23..1ae8056ed7 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Discovery/UnitTestDiscoverer.cs
@@ -4,8 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -13,13 +11,7 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
[SuppressMessage("Performance", "CA1852: Seal internal types", Justification = "Overrides required for testability")]
internal class UnitTestDiscoverer
{
- private readonly TestMethodFilter _testMethodFilter;
-
- internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler)
- {
- _testMethodFilter = new TestMethodFilter();
- _testSource = testSourceHandler;
- }
+ internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler) => _testSource = testSourceHandler;
///
/// Discovers the tests available from the provided sources.
@@ -27,18 +19,20 @@ internal UnitTestDiscoverer(ITestSourceHandler testSourceHandler)
/// The sources.
/// The logger.
/// The discovery Sink.
- /// The discovery context.
+ /// The run settings XML, or when none was provided.
+ /// Provider for the test filter, or for no filter.
/// Flag set to true when the platform running discovery is MTP.
internal void DiscoverTests(
IEnumerable sources,
- IMessageLogger logger,
- ITestCaseDiscoverySink discoverySink,
- IDiscoveryContext discoveryContext,
+ IAdapterMessageLogger logger,
+ IUnitTestElementSink discoverySink,
+ string? settingsXml,
+ ITestElementFilterProvider? filterProvider,
bool isMTP)
{
foreach (string source in sources)
{
- DiscoverTestsInSource(source, logger, discoverySink, discoveryContext, isMTP);
+ DiscoverTestsInSource(source, logger, discoverySink, settingsXml, filterProvider, isMTP);
}
}
@@ -48,16 +42,18 @@ internal void DiscoverTests(
/// The source.
/// The logger.
/// The discovery Sink.
- /// The discovery context.
+ /// The run settings XML, or when none was provided.
+ /// Provider for the test filter, or for no filter.
/// Flag set to true when the platform running discovery is MTP.
internal virtual void DiscoverTestsInSource(
string source,
- IMessageLogger logger,
- ITestCaseDiscoverySink discoverySink,
- IDiscoveryContext? discoveryContext,
+ IAdapterMessageLogger logger,
+ IUnitTestElementSink discoverySink,
+ string? settingsXml,
+ ITestElementFilterProvider? filterProvider,
bool isMTP)
{
- ICollection? testElements = AssemblyEnumeratorWrapper.GetTests(source, discoveryContext?.RunSettings, _testSource, isMTP, out List warnings);
+ ICollection? testElements = AssemblyEnumeratorWrapper.GetTests(source, settingsXml, _testSource, isMTP, out List warnings);
if (MSTestSettings.CurrentSettings.TreatDiscoveryWarningsAsErrors)
{
@@ -86,7 +82,7 @@ internal virtual void DiscoverTestsInSource(
}
string message = string.Format(CultureInfo.CurrentCulture, Resource.DiscoveryWarning, source, warning);
- logger.SendMessage(TestMessageLevel.Warning, message);
+ logger.SendMessage(MessageLevel.Warning, message);
}
}
@@ -104,15 +100,16 @@ internal virtual void DiscoverTestsInSource(
source);
}
- SendTestCases(testElements, discoverySink, discoveryContext, logger);
+ SendTestCases(testElements, discoverySink, filterProvider, logger);
}
private readonly ITestSourceHandler _testSource;
- internal void SendTestCases(IEnumerable testElements, ITestCaseDiscoverySink discoverySink, IDiscoveryContext? discoveryContext, IMessageLogger logger)
+ internal static void SendTestCases(IEnumerable testElements, IUnitTestElementSink discoverySink, ITestElementFilterProvider? filterProvider, IAdapterMessageLogger logger)
{
- // Get filter expression and skip discovery in case filter expression has parsing error.
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, logger, out bool filterHasError);
+ // Get filter and skip discovery in case filter expression has parsing error.
+ bool filterHasError = false;
+ ITestElementFilter? filter = filterProvider?.GetTestElementFilter(logger, out filterHasError);
if (filterHasError)
{
return;
@@ -120,15 +117,13 @@ internal void SendTestCases(IEnumerable testElements, ITestCase
foreach (UnitTestElement testElement in testElements)
{
- var testCase = testElement.ToTestCase();
-
- // Filter tests based on test case filters
- if (filterExpression != null && !filterExpression.MatchTestCase(testCase, p => _testMethodFilter.PropertyValueProvider(testCase, p)))
+ // Filter tests based on test case filters.
+ if (filter is not null && !filter.Matches(testElement))
{
continue;
}
- discoverySink.SendTestCase(testCase);
+ discoverySink.SendTestElement(testElement);
}
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs b/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs
index 1fefb90f68..3b3a2f90ed 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/EngineConstants.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
internal static class EngineConstants
@@ -55,107 +53,6 @@ internal static class EngineConstants
///
internal const string ClassCleanupFixtureTrait = "ClassCleanup";
- #region Test Property registration
- internal static readonly TestProperty WorkItemIdsProperty = TestProperty.Register("WorkItemIds", WorkItemIdsLabel, typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestClassNameProperty = TestProperty.Register("MSTestDiscoverer.TestClassName", TestClassNameLabel, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
-#pragma warning disable CS0618 // Type or member is obsolete
- internal static readonly TestProperty TestCategoryProperty = TestProperty.Register("MSTestDiscoverer.TestCategory", TestCategoryLabel, typeof(string[]), TestPropertyAttributes.Hidden | TestPropertyAttributes.Trait, typeof(TestCase));
-#pragma warning restore CS0618 // Type or member is obsolete
-
- internal static readonly TestProperty PriorityProperty = TestProperty.Register("MSTestDiscoverer.Priority", PriorityLabel, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
-#if !WINDOWS_UWP && !WIN_UI
- internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register("MSTestDiscoverer.DeploymentItems", DeploymentItemsLabel, typeof(KeyValuePair[]), TestPropertyAttributes.Hidden, typeof(TestCase));
-#endif
-
- internal static readonly TestProperty DoNotParallelizeProperty = TestProperty.Register("MSTestDiscoverer.DoNotParallelize", DoNotParallelizeLabel, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty ExecutionIdProperty = TestProperty.Register("ExecutionId", ExecutionIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
-
- internal static readonly TestProperty ParentExecIdProperty = TestProperty.Register("ParentExecId", ParentExecIdLabel, typeof(Guid), TestPropertyAttributes.Hidden, typeof(TestResult));
-
- internal static readonly TestProperty TestRunIdProperty = TestProperty.Register(TestRunId, TestRunId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestPlanIdProperty = TestProperty.Register(TestPlanId, TestPlanId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestCaseIdProperty = TestProperty.Register(TestCaseId, TestCaseId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestPointIdProperty = TestProperty.Register(TestPointId, TestPointId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestConfigurationIdProperty = TestProperty.Register(TestConfigurationId, TestConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestConfigurationNameProperty = TestProperty.Register(TestConfigurationName, TestConfigurationName, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty IsInLabEnvironmentProperty = TestProperty.Register(IsInLabEnvironment, IsInLabEnvironment, typeof(bool), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildConfigurationIdProperty = TestProperty.Register(BuildConfigurationId, BuildConfigurationId, typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildDirectoryProperty = TestProperty.Register(BuildDirectory, BuildDirectory, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildFlavorProperty = TestProperty.Register(BuildFlavor, BuildFlavor, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildNumberProperty = TestProperty.Register(BuildNumber, BuildNumber, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildPlatformProperty = TestProperty.Register(BuildPlatform, BuildPlatform, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty BuildUriProperty = TestProperty.Register(BuildUri, BuildUri, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TfsServerCollectionUrlProperty = TestProperty.Register(TfsServerCollectionUrl, TfsServerCollectionUrl, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TfsTeamProjectProperty = TestProperty.Register(TfsTeamProject, TfsTeamProject, typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty ParameterTypesProperty = TestProperty.Register("MSTest.ParameterTypes", "ParameterTypes", typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestDynamicDataTypeProperty = TestProperty.Register("MSTest.DynamicDataType", "DynamicDataType", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestCaseIndexProperty = TestProperty.Register("MSTest.TestCaseIndex", "TestCaseIndex", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestDynamicDataProperty = TestProperty.Register("MSTest.DynamicData", "DynamicData", typeof(string[]), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty TestDataSourceIgnoreMessageProperty = TestProperty.Register("MSTest.TestDataSourceIgnoreMessageProperty", "TestDataSourceIgnoreMessageProperty", typeof(string), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- internal static readonly TestProperty UnfoldingStrategy = TestProperty.Register("MSTestDiscoverer.UnfoldingStrategy", "UnfoldingStrategy", typeof(int), TestPropertyAttributes.Hidden, typeof(TestCase));
-
- #endregion
-
- #region Private Constants
-
- ///
- /// These are the Test properties used by the adapter, which essentially correspond
- /// to attributes on tests, and may be available in command line/TeamBuild to filter tests.
- /// These Property names should not be localized.
- ///
- private const string TestClassNameLabel = "ClassName";
- private const string TestCategoryLabel = "TestCategory";
- private const string PriorityLabel = "Priority";
-#if !WINDOWS_UWP && !WIN_UI
- private const string DeploymentItemsLabel = "DeploymentItems";
-#endif
- private const string DoNotParallelizeLabel = "DoNotParallelize";
- private const string ExecutionIdLabel = "ExecutionId";
- private const string ParentExecIdLabel = "ParentExecId";
- private const string WorkItemIdsLabel = "WorkItemIds";
-
- private const string TestRunId = "__Tfs_TestRunId__";
- private const string TestPlanId = "__Tfs_TestPlanId__";
- private const string TestCaseId = "__Tfs_TestCaseId__";
- private const string TestPointId = "__Tfs_TestPointId__";
- private const string TestConfigurationId = "__Tfs_TestConfigurationId__";
- private const string TestConfigurationName = "__Tfs_TestConfigurationName__";
- private const string IsInLabEnvironment = "__Tfs_IsInLabEnvironment__";
- private const string BuildConfigurationId = "__Tfs_BuildConfigurationId__";
- private const string BuildDirectory = "__Tfs_BuildDirectory__";
- private const string BuildFlavor = "__Tfs_BuildFlavor__";
- private const string BuildNumber = "__Tfs_BuildNumber__";
- private const string BuildPlatform = "__Tfs_BuildPlatform__";
- private const string BuildUri = "__Tfs_BuildUri__";
- private const string TfsServerCollectionUrl = "__Tfs_TfsServerCollectionUrl__";
- private const string TfsTeamProject = "__Tfs_TeamProject__";
-
- #endregion
-
///
/// Uri of the MSTest executor.
///
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/ClassCleanupManager.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/ClassCleanupManager.cs
index a41268c299..16081a0f4b 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/ClassCleanupManager.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/ClassCleanupManager.cs
@@ -3,7 +3,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using AdapterApplicationStateGuard = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.ApplicationStateGuard;
@@ -53,7 +53,7 @@ public void MarkClassComplete(string fullClassName)
}
}
- internal static void ForceCleanup(TypeCache typeCache, IDictionary sourceLevelParameters, IMessageLogger logger)
+ internal static void ForceCleanup(TypeCache typeCache, IDictionary sourceLevelParameters, IAdapterMessageLogger logger)
{
IEnumerable classInfoCache = typeCache.ClassInfoListWithExecutableCleanupMethods;
foreach (TestClassInfo classInfo in classInfoCache)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TcmTestPropertiesProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TcmTestPropertiesProvider.cs
deleted file mode 100644
index 92d8534c6b..0000000000
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TcmTestPropertiesProvider.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-
-using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
-
-using TestPlatformObjectModel = Microsoft.VisualStudio.TestPlatform.ObjectModel;
-
-namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
-
-///
-/// Reads and parses the TcmTestProperties in order to populate them in TestRunParameters.
-///
-internal static class TcmTestPropertiesProvider
-{
- ///
- /// Gets tcm properties from test case.
- ///
- /// Test case.
- /// Tcm properties.
- public static IDictionary? GetTcmProperties(TestPlatformObjectModel.TestCase? testCase)
- {
- // Return empty properties when testCase is null or when test case id is zero.
- if (testCase == null ||
- testCase.GetPropertyValue(EngineConstants.TestCaseIdProperty, default) == 0)
- {
- return null;
- }
-
- var tcmProperties = new Dictionary(capacity: 15)
- {
- // Step 1: Add common properties.
- [EngineConstants.TestRunIdProperty] = testCase.GetPropertyValue(EngineConstants.TestRunIdProperty, default),
- [EngineConstants.TestPlanIdProperty] = testCase.GetPropertyValue(EngineConstants.TestPlanIdProperty, default),
- [EngineConstants.BuildConfigurationIdProperty] = testCase.GetPropertyValue(EngineConstants.BuildConfigurationIdProperty, default),
- [EngineConstants.BuildDirectoryProperty] = testCase.GetPropertyValue(EngineConstants.BuildDirectoryProperty, default),
- [EngineConstants.BuildFlavorProperty] = testCase.GetPropertyValue(EngineConstants.BuildFlavorProperty, default),
- [EngineConstants.BuildNumberProperty] = testCase.GetPropertyValue(EngineConstants.BuildNumberProperty, default),
- [EngineConstants.BuildPlatformProperty] = testCase.GetPropertyValue(EngineConstants.BuildPlatformProperty, default),
- [EngineConstants.BuildUriProperty] = testCase.GetPropertyValue(EngineConstants.BuildUriProperty, default),
- [EngineConstants.TfsServerCollectionUrlProperty] = testCase.GetPropertyValue(EngineConstants.TfsServerCollectionUrlProperty, default),
- [EngineConstants.TfsTeamProjectProperty] = testCase.GetPropertyValue(EngineConstants.TfsTeamProjectProperty, default),
- [EngineConstants.IsInLabEnvironmentProperty] = testCase.GetPropertyValue(EngineConstants.IsInLabEnvironmentProperty, default),
-
- // Step 2: Add test case specific properties.
- [EngineConstants.TestCaseIdProperty] = testCase.GetPropertyValue(EngineConstants.TestCaseIdProperty, default),
- [EngineConstants.TestConfigurationIdProperty] = testCase.GetPropertyValue(EngineConstants.TestConfigurationIdProperty, default),
- [EngineConstants.TestConfigurationNameProperty] = testCase.GetPropertyValue(EngineConstants.TestConfigurationNameProperty, default),
- [EngineConstants.TestPointIdProperty] = testCase.GetPropertyValue(EngineConstants.TestPointIdProperty, default),
- };
-
- return tcmProperties;
- }
-}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs
index 8ae3f43e28..d76e64a597 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs
@@ -1,30 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
///
-/// The test case discovery sink.
+/// The discovery sink used internally by execution to collect the tests discovered from a source.
///
-internal sealed class TestCaseDiscoverySink : ITestCaseDiscoverySink
+///
+/// It collects the neutral model directly, so the execution pipeline can flow
+/// discovered tests without round-tripping them through a VSTest TestCase.
+///
+internal sealed class TestCaseDiscoverySink : IUnitTestElementSink
{
///
- /// Gets the tests.
+ /// Gets the discovered tests.
///
- public ICollection Tests { get; } = [];
+ public ICollection TestElements { get; } = [];
///
- /// Sends the test case.
+ /// Collects the discovered test element.
///
- /// The discovered test.
- public void SendTestCase(TestCase? discoveredTest)
- {
- if (discoveredTest != null)
- {
- Tests.Add(discoveredTest);
- }
- }
+ /// The discovered test element.
+ public void SendTestElement(UnitTestElement testElement)
+ => TestElements.Add(testElement);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs
index 90d4be897c..9f7b4ffdd3 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Parallelization.cs
@@ -1,14 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ExecutionScope = Microsoft.VisualStudio.TestTools.UnitTesting.ExecutionScope;
@@ -21,16 +18,21 @@ internal partial class TestExecutionManager
/// Execute the parameter tests.
///
/// Tests to execute.
- /// The run context.
- /// Handle to record test start/end/results.
+ /// Host-provided test-run directory and run settings XML.
+ /// Logger used to report test messages back to the host.
+ /// Recorder used to report test results back to the host.
+ /// Provider for the test filter, or for no filter.
/// Indicates if deployment is done.
- internal virtual async Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, bool isDeploymentDone)
+ internal virtual async Task ExecuteTestsAsync(IEnumerable tests, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, bool isDeploymentDone)
{
- InitializeRandomTestOrder(frameworkHandle);
+ _testResultRecorder = testResultRecorder;
+ _testElementFilterProvider = filterProvider;
+
+ InitializeRandomTestOrder(messageLogger);
var testsBySource = (from test in tests
- group test by test.Source into testGroup
- select new { Source = testGroup.Key, Tests = (IEnumerable)testGroup }).ToArray();
+ group test by test.TestMethod.AssemblyName into testGroup
+ select new { Source = testGroup.Key, Tests = (IEnumerable)testGroup }).ToArray();
if (_testOrderRandom is { } random)
{
@@ -40,7 +42,7 @@ group test by test.Source into testGroup
foreach (var group in testsBySource)
{
_testRunCancellationToken?.ThrowIfCancellationRequested();
- await ExecuteTestsInSourceAsync(group.Tests, runContext, frameworkHandle, group.Source, isDeploymentDone).ConfigureAwait(false);
+ await ExecuteTestsInSourceAsync(group.Tests, deploymentContext, messageLogger, group.Source, isDeploymentDone).ConfigureAwait(false);
}
}
@@ -48,11 +50,11 @@ group test by test.Source into testGroup
/// Execute the parameter tests present in parameter source.
///
/// Tests to execute.
- /// The run context.
- /// Handle to record test start/end/results.
+ /// Host-provided test-run directory and run settings XML.
+ /// Logger used to report test messages back to the host.
/// The test container for the tests.
/// Indicates if deployment is done.
- private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, string source, bool isDeploymentDone)
+ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, string source, bool isDeploymentDone)
{
DebugEx.Assert(!StringEx.IsNullOrEmpty(source), "Source cannot be empty");
@@ -63,7 +65,9 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
}
#endif
- using ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(source, runContext?.RunSettings);
+ IAdapterMessageLogger adapterMessageLogger = messageLogger;
+
+ using ITestSourceHost isolationHost = PlatformServiceProvider.Instance.CreateTestSourceHost(source, deploymentContext.RunSettingsXml);
bool usesAppDomains = isolationHost is TestSourceHost { UsesAppDomain: true };
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
@@ -72,7 +76,8 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
}
// Default test set is filtered tests based on user provided filter criteria
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(runContext, frameworkHandle, out bool filterHasError);
+ bool filterHasError = false;
+ ITestElementFilter? filter = _testElementFilterProvider?.GetTestElementFilter(adapterMessageLogger, out filterHasError);
if (filterHasError)
{
// Bail out without processing everything else below.
@@ -112,13 +117,13 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
int parallelWorkers = sourceSettings.Workers;
ExecutionScope parallelScope = sourceSettings.Scope;
- TestCase[] testsToRun = [.. tests.Where(t => MatchTestFilter(filterExpression, t, _testMethodFilter))];
+ UnitTestElement[] testsToRun = [.. tests.Where(t => MatchTestFilter(filter, t, source))];
if (_testOrderRandom is { } sourceRandom)
{
Shuffle(sourceRandom, testsToRun);
}
- UnitTestElement[] unitTestElements = [.. testsToRun.Select(e => e.ToUnitTestElementWithUpdatedSource(source))];
+ UnitTestElement[] unitTestElements = [.. testsToRun.Select(e => e.WithUpdatedSource(source))];
// Create an instance of a type defined in adapter so that adapter gets loaded in the child app domain
var testRunner = (UnitTestRunner)isolationHost.CreateInstanceForType(
typeof(UnitTestRunner),
@@ -142,17 +147,17 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
if (!MSTestSettings.CurrentSettings.DisableParallelization && sourceSettings.CanParallelizeAssembly && parallelWorkers > 0)
{
// Parallelization is enabled. Let's do further classification for sets.
- frameworkHandle.SendMessage(
- TestMessageLevel.Informational,
+ adapterMessageLogger.SendMessage(
+ MessageLevel.Informational,
string.Format(CultureInfo.CurrentCulture, Resource.TestParallelizationBanner, source, parallelWorkers, parallelScope));
// Create test sets for execution, we can execute them in parallel based on parallel settings
// Parallel and not parallel sets.
// Single-pass partition: enumerate testsToRun once via GroupBy (lazy GroupBy evaluated twice
// — once per FirstOrDefault — caused 2 full passes over testsToRun).
- IEnumerable? parallelizableTestSet = null;
- IEnumerable? nonParallelizableTestSet = null;
- foreach (IGrouping group in testsToRun.GroupBy(t => t.GetPropertyValue(EngineConstants.DoNotParallelizeProperty, false)))
+ IEnumerable? parallelizableTestSet = null;
+ IEnumerable? nonParallelizableTestSet = null;
+ foreach (IGrouping group in testsToRun.GroupBy(t => t.DoNotParallelize))
{
if (group.Key)
{
@@ -166,7 +171,7 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
if (parallelizableTestSet != null)
{
- ConcurrentQueue>? queue = null;
+ ConcurrentQueue>? queue = null;
// Chunk the sets into further groups based on parallel level
switch (parallelScope)
@@ -174,13 +179,13 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
case ExecutionScope.MethodLevel:
if (_testOrderRandom is { } methodRandom)
{
- IEnumerable[] methodChunks = [.. parallelizableTestSet.Select(t => (IEnumerable)[t])];
+ IEnumerable[] methodChunks = [.. parallelizableTestSet.Select(t => (IEnumerable)[t])];
Shuffle(methodRandom, methodChunks);
- queue = new ConcurrentQueue>(methodChunks);
+ queue = new ConcurrentQueue>(methodChunks);
}
else
{
- queue = new ConcurrentQueue>(parallelizableTestSet.Select(t => new[] { t }));
+ queue = new ConcurrentQueue>(parallelizableTestSet.Select(t => new[] { t }));
}
break;
@@ -188,18 +193,18 @@ private async Task ExecuteTestsInSourceAsync(IEnumerable tests, IRunCo
case ExecutionScope.ClassLevel:
if (_testOrderRandom is { } classRandom)
{
- IEnumerable[] classChunks =
+ IEnumerable[] classChunks =
[
.. parallelizableTestSet
- .GroupBy(t => t.GetPropertyValue(EngineConstants.TestClassNameProperty) as string)
- .Select(g => (IEnumerable)g.ToArray()),
+ .GroupBy(t => t.TestMethod.FullClassName)
+ .Select(g => (IEnumerable)g.ToArray()),
];
Shuffle(classRandom, classChunks);
- queue = new ConcurrentQueue>(classChunks);
+ queue = new ConcurrentQueue>(classChunks);
}
else
{
- queue = new ConcurrentQueue>(parallelizableTestSet.GroupBy(t => t.GetPropertyValue(EngineConstants.TestClassNameProperty) as string));
+ queue = new ConcurrentQueue>(parallelizableTestSet.GroupBy(t => t.TestMethod.FullClassName));
}
break;
@@ -219,9 +224,9 @@ .. parallelizableTestSet
{
_testRunCancellationToken?.ThrowIfCancellationRequested();
- if (queue.TryDequeue(out IEnumerable? testSet))
+ if (queue.TryDequeue(out IEnumerable? testSet))
{
- await ExecuteTestsWithTestRunnerAsync(testSet, frameworkHandle, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
+ await ExecuteTestsWithTestRunnerAsync(testSet, adapterMessageLogger, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
}
}
}
@@ -248,7 +253,7 @@ .. parallelizableTestSet
PlatformServiceProvider.Instance.AdapterTraceLogger.Error("Error occurred while executing tests in parallel{0}{1}", Environment.NewLine, exceptionToString);
}
- frameworkHandle.SendMessage(TestMessageLevel.Error, exceptionToString);
+ adapterMessageLogger.SendMessage(MessageLevel.Error, exceptionToString);
throw;
}
}
@@ -256,17 +261,17 @@ .. parallelizableTestSet
// Queue the non parallel set
if (nonParallelizableTestSet != null)
{
- await ExecuteTestsWithTestRunnerAsync(nonParallelizableTestSet, frameworkHandle, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
+ await ExecuteTestsWithTestRunnerAsync(nonParallelizableTestSet, adapterMessageLogger, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
}
}
else
{
- await ExecuteTestsWithTestRunnerAsync(testsToRun, frameworkHandle, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
+ await ExecuteTestsWithTestRunnerAsync(testsToRun, adapterMessageLogger, source, sourceLevelParameters, testRunner, usesAppDomains).ConfigureAwait(false);
}
if (PlatformServiceProvider.Instance.IsGracefulStopRequested)
{
- testRunner.ForceCleanup(sourceLevelParameters!, new RemotingMessageLogger(frameworkHandle));
+ testRunner.ForceCleanup(sourceLevelParameters!, new RemotingMessageLogger(adapterMessageLogger));
}
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Runner.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Runner.cs
index dd43d20b47..67de43cfe2 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Runner.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Runner.cs
@@ -1,27 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
-using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
internal partial class TestExecutionManager
{
internal void SendTestResults(
- TestCase test,
+ UnitTestElement test,
TestTools.UnitTesting.TestResult[] unitTestResults,
DateTimeOffset startTime,
DateTimeOffset endTime,
- ITestExecutionRecorder testExecutionRecorder)
+ ITestResultRecorder testResultRecorder)
{
if (unitTestResults.Length == 0)
{
- testExecutionRecorder.RecordEnd(test, TestOutcome.None);
+ testResultRecorder.RecordEmptyResult(test);
return;
}
@@ -29,46 +25,21 @@ internal void SendTestResults(
{
_testRunCancellationToken?.ThrowIfCancellationRequested();
- var testResult = unitTestResult.ToTestResult(
- test,
- startTime,
- endTime,
- _environment.MachineName,
- MSTestSettings.CurrentSettings);
-
- testExecutionRecorder.RecordEnd(test, testResult.Outcome);
-
- if (testResult.Outcome == TestOutcome.Failed)
- {
- if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
- {
- PlatformServiceProvider.Instance.AdapterTraceLogger.Info("MSTestExecutor:Test {0} failed. ErrorMessage:{1}, ErrorStackTrace:{2}.", testResult.TestCase.FullyQualifiedName, testResult.ErrorMessage, testResult.ErrorStackTrace);
- }
-
#if !WINDOWS_UWP && !WIN_UI
- _hasAnyTestFailed = true;
-#endif
- }
-
- try
- {
- if (testResult.Outcome != TestOutcome.NotFound
- || !RuntimeContext.IsHotReloadEnabled)
- {
- testExecutionRecorder.RecordResult(testResult);
- }
- }
- catch (TestCanceledException)
+ if (testResultRecorder.RecordResult(test, unitTestResult, startTime, endTime))
{
- // Ignore this exception
+ _hasAnyTestFailed = true;
}
+#else
+ testResultRecorder.RecordResult(test, unitTestResult, startTime, endTime);
+#endif
}
}
- private static bool MatchTestFilter(ITestCaseFilterExpression? filterExpression, TestCase test, TestMethodFilter testMethodFilter)
+ private static bool MatchTestFilter(ITestElementFilter? filter, UnitTestElement test, string source)
{
- if (filterExpression != null
- && !filterExpression.MatchTestCase(test, p => testMethodFilter.PropertyValueProvider(test, p)))
+ if (filter is not null
+ && !filter.Matches(test.WithUpdatedSource(source)))
{
// Skip test if not fitting filter criteria.
return false;
@@ -78,24 +49,27 @@ private static bool MatchTestFilter(ITestCaseFilterExpression? filterExpression,
}
private async Task ExecuteTestsWithTestRunnerAsync(
- IEnumerable tests,
- ITestExecutionRecorder testExecutionRecorder,
+ IEnumerable tests,
+ IAdapterMessageLogger adapterMessageLogger,
string source,
IDictionary sourceLevelParameters,
UnitTestRunner testRunner,
bool usesAppDomains)
{
- IEnumerable orderedTests = MSTestSettings.CurrentSettings.OrderTestsByNameInClass && !MSTestSettings.CurrentSettings.RandomizeTestOrder
- ? tests.OrderBy(t => t.GetManagedType()).ThenBy(t => t.GetManagedMethod())
+ // Ordering keys mirror the historical VSTest ManagedType/ManagedMethod test-case properties, which are
+ // only populated when the test method carries managed method metadata (see UnitTestElement.ToTestCase).
+ IEnumerable orderedTests = MSTestSettings.CurrentSettings.OrderTestsByNameInClass && !MSTestSettings.CurrentSettings.RandomizeTestOrder
+ ? tests.OrderBy(t => t.TestMethod.HasManagedMethodAndTypeProperties ? t.TestMethod.ManagedTypeName : null)
+ .ThenBy(t => t.TestMethod.HasManagedMethodAndTypeProperties ? t.TestMethod.ManagedMethodName : null)
: tests;
- // If testRunner is in a different AppDomain, we cannot pass the testExecutionRecorder directly.
+ // If testRunner is in a different AppDomain, we cannot pass the message logger directly.
// Instead, we pass a proxy (remoting object) that is marshallable by ref.
- IMessageLogger remotingMessageLogger = usesAppDomains
- ? new RemotingMessageLogger(testExecutionRecorder)
- : testExecutionRecorder;
+ IAdapterMessageLogger remotingMessageLogger = usesAppDomains
+ ? new RemotingMessageLogger(adapterMessageLogger)
+ : adapterMessageLogger;
- foreach (TestCase currentTest in orderedTests)
+ foreach (UnitTestElement currentTest in orderedTests)
{
_testRunCancellationToken?.ThrowIfCancellationRequested();
if (PlatformServiceProvider.Instance.IsGracefulStopRequested)
@@ -103,9 +77,11 @@ private async Task ExecuteTestsWithTestRunnerAsync(
break;
}
- UnitTestElement unitTestElement = currentTest.ToUnitTestElementWithUpdatedSource(source);
+ UnitTestElement unitTestElement = currentTest.WithUpdatedSource(source);
- testExecutionRecorder.RecordStart(currentTest);
+ // Report through the neutral recorder using the element itself; the adapter-side recorder resolves
+ // the host test case (preserving host-injected TCM / data-collector properties) with full fidelity.
+ _testResultRecorder.RecordStart(currentTest);
DateTimeOffset startTime = DateTimeOffset.Now;
@@ -115,7 +91,7 @@ private async Task ExecuteTestsWithTestRunnerAsync(
}
// Run single test passing test context properties to it.
- IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(currentTest);
+ IReadOnlyDictionary? tcmProperties = currentTest.ExecutionContextProperties;
Dictionary testContextProperties = GetTestContextProperties(tcmProperties, sourceLevelParameters, unitTestElement);
TestTools.UnitTesting.TestResult[] unitTestResult;
@@ -143,7 +119,7 @@ private async Task ExecuteTestsWithTestRunnerAsync(
DateTimeOffset endTime = DateTimeOffset.Now;
- SendTestResults(currentTest, unitTestResult, startTime, endTime, testExecutionRecorder);
+ SendTestResults(currentTest, unitTestResult, startTime, endTime, _testResultRecorder);
}
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.TestContext.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.TestContext.cs
index 7db20bc62a..e465af8e72 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.TestContext.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.TestContext.cs
@@ -3,9 +3,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
@@ -20,7 +18,7 @@ internal partial class TestExecutionManager
/// The unit test element to get properties from.
/// Test context properties.
private static Dictionary GetTestContextProperties(
- IDictionary? tcmProperties,
+ IReadOnlyDictionary? tcmProperties,
IDictionary sourceLevelParameters,
UnitTestElement unitTestElement)
{
@@ -38,9 +36,9 @@ internal partial class TestExecutionManager
// Add tcm properties.
if (tcmProperties is not null)
{
- foreach (KeyValuePair kvp in tcmProperties)
+ foreach (KeyValuePair kvp in tcmProperties)
{
- testContextProperties[kvp.Key.Id] = kvp.Value;
+ testContextProperties[kvp.Key] = kvp.Value;
}
}
@@ -52,7 +50,7 @@ internal partial class TestExecutionManager
if (unitTestElement.Traits is { Length: > 0 })
{
- foreach (Trait trait in unitTestElement.Traits)
+ foreach (TestTrait trait in unitTestElement.Traits)
{
ValidateAndAssignTestProperty(testContextProperties, trait.Name, trait.Value);
}
@@ -99,16 +97,16 @@ private static void ValidateAndAssignTestProperty(
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Requirement is to handle errors in user specified run parameters")]
- private void CacheSessionParameters(IRunContext? runContext, ITestExecutionRecorder testExecutionRecorder)
+ private void CacheSessionParameters(string? settingsXml, IAdapterMessageLogger messageLogger)
{
- if (StringEx.IsNullOrEmpty(runContext?.RunSettings?.SettingsXml))
+ if (StringEx.IsNullOrEmpty(settingsXml))
{
return;
}
try
{
- Dictionary? testRunParameters = RunSettingsUtilities.GetTestRunParameters(runContext.RunSettings.SettingsXml);
+ Dictionary? testRunParameters = RunSettingsUtilities.GetTestRunParameters(settingsXml);
if (testRunParameters != null)
{
// Clear sessionParameters to prevent key collisions of test run parameters in case
@@ -122,7 +120,7 @@ private void CacheSessionParameters(IRunContext? runContext, ITestExecutionRecor
}
catch (Exception ex)
{
- testExecutionRecorder.SendMessage(TestMessageLevel.Error, ex.Message);
+ messageLogger.SendMessage(MessageLevel.Error, ex.Message);
}
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Utilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Utilities.cs
index c21f1add17..d23abdff7b 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Utilities.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.Utilities.cs
@@ -1,28 +1,35 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
internal partial class TestExecutionManager
{
+ ///
+ /// A message logger that can be marshaled into the isolation (child app-domain) host so log messages
+ /// raised while running a test cross back to the parent-domain .
+ /// Marshaling is by reference (the wrapped logger stays in the parent domain), so no VSTest object-model
+ /// type is required in the child domain.
+ ///
private sealed class RemotingMessageLogger :
#if NETFRAMEWORK
MarshalByRefObject,
#endif
- IMessageLogger
+ IAdapterMessageLogger
{
- private readonly IMessageLogger _realMessageLogger;
+ private readonly IAdapterMessageLogger _realMessageLogger;
- public RemotingMessageLogger(IMessageLogger messageLogger)
+ public RemotingMessageLogger(IAdapterMessageLogger messageLogger)
=> _realMessageLogger = messageLogger;
- public void SendMessage(TestMessageLevel testMessageLevel, string message)
- => _realMessageLogger.SendMessage(testMessageLevel, message);
+ public void SendMessage(MessageLevel level, string message)
+ => _realMessageLogger.SendMessage(level, message);
}
- private void InitializeRandomTestOrder(IMessageLogger frameworkHandle)
+ private void InitializeRandomTestOrder(IAdapterMessageLogger messageLogger)
{
if (!MSTestSettings.CurrentSettings.RandomizeTestOrder)
{
@@ -36,8 +43,8 @@ private void InitializeRandomTestOrder(IMessageLogger frameworkHandle)
int seed = MSTestSettings.CurrentSettings.RandomTestOrderSeed ?? Guid.NewGuid().GetHashCode();
_testOrderRandom = new Random(seed);
- frameworkHandle.SendMessage(
- TestMessageLevel.Informational,
+ messageLogger.SendMessage(
+ MessageLevel.Informational,
string.Format(CultureInfo.CurrentCulture, Resource.RandomTestOrderBanner, seed));
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs
index 83e42fe0c8..d8858cdaea 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestExecutionManager.cs
@@ -1,11 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
@@ -21,7 +20,6 @@ internal partial class TestExecutionManager
/// Dictionary for test run parameters.
///
private readonly IDictionary _sessionParameters;
- private readonly IEnvironment _environment;
private readonly Func, Task> _taskFactory;
///
@@ -35,19 +33,23 @@ internal partial class TestExecutionManager
///
private Random? _testOrderRandom;
+ ///
+ /// Recorder used to report test start/end/results back to the host. Supplied at the adapter boundary and set
+ /// once per run in before any source is processed.
+ ///
+ private ITestResultRecorder _testResultRecorder = null!;
+
///
/// Initializes a new instance of the class.
///
public TestExecutionManager()
- : this(EnvironmentWrapper.Instance)
+ : this(null)
{
}
- internal TestExecutionManager(IEnvironment environment, Func, Task>? taskFactory = null)
+ internal TestExecutionManager(Func, Task>? taskFactory = null)
{
- _testMethodFilter = new TestMethodFilter();
_sessionParameters = new Dictionary();
- _environment = environment;
_taskFactory = taskFactory ?? DefaultFactoryAsync;
}
@@ -67,9 +69,11 @@ private static Task DefaultFactoryAsync(Func taskGetter)
}
///
- /// Gets or sets method filter for filtering tests.
+ /// Provider for the test filter, supplied at the adapter boundary and set once per run in
+ /// so the filter is built at the same point (and with the same per-source
+ /// timing) as before.
///
- private readonly TestMethodFilter _testMethodFilter;
+ private ITestElementFilterProvider? _testElementFilterProvider;
#if !WINDOWS_UWP && !WIN_UI
///
@@ -82,30 +86,32 @@ private static Task DefaultFactoryAsync(Func taskGetter)
/// Runs the tests.
///
/// Tests to be run.
- /// Context to use when executing the tests.
- /// Handle to the framework to record results and to do framework operations.
+ /// Host-provided test-run directory and run settings XML.
+ /// Logger used to report test messages back to the host.
+ /// Recorder used to report test results back to the host.
+ /// Provider for the test filter, or for no filter.
/// Test run cancellation token.
- internal async Task RunTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, TestRunCancellationToken runCancellationToken)
+ internal async Task RunTestsAsync(IEnumerable tests, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, TestRunCancellationToken runCancellationToken)
{
DebugEx.Assert(tests != null, "tests");
- DebugEx.Assert(runContext != null, "runContext");
- DebugEx.Assert(frameworkHandle != null, "frameworkHandle");
+ DebugEx.Assert(deploymentContext != null, "deploymentContext");
+ DebugEx.Assert(messageLogger != null, "messageLogger");
DebugEx.Assert(runCancellationToken != null, "runCancellationToken");
_testRunCancellationToken = runCancellationToken;
PlatformServiceProvider.Instance.TestRunCancellationToken = _testRunCancellationToken;
#if !WINDOWS_UWP && !WIN_UI
- bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
+ bool isDeploymentDone = Deploy(tests, deploymentContext, messageLogger);
#else
const bool isDeploymentDone = false;
#endif
// Placing this after deployment since we need information post deployment that we pass in as properties.
- CacheSessionParameters(runContext, frameworkHandle);
+ CacheSessionParameters(deploymentContext.RunSettingsXml, messageLogger);
// Execute the tests
- await ExecuteTestsAsync(tests, runContext, frameworkHandle, isDeploymentDone).ConfigureAwait(false);
+ await ExecuteTestsAsync(tests, deploymentContext, messageLogger, testResultRecorder, filterProvider, isDeploymentDone).ConfigureAwait(false);
#if !WINDOWS_UWP && !WIN_UI
if (!_hasAnyTestFailed)
@@ -115,41 +121,41 @@ internal async Task RunTestsAsync(IEnumerable tests, IRunContext? runC
#endif
}
- internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runContext, IFrameworkHandle frameworkHandle, ITestSourceHandler testSourceHandler, bool isMTP, TestRunCancellationToken cancellationToken)
+ internal async Task RunTestsAsync(IEnumerable sources, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, ITestSourceHandler testSourceHandler, bool isMTP, TestRunCancellationToken cancellationToken)
{
_testRunCancellationToken = cancellationToken;
PlatformServiceProvider.Instance.TestRunCancellationToken = _testRunCancellationToken;
var discoverySink = new TestCaseDiscoverySink();
- var tests = new List();
+ var tests = new List();
+
+ IAdapterMessageLogger logger = messageLogger;
// deploy everything first.
foreach (string source in sources)
{
_testRunCancellationToken?.ThrowIfCancellationRequested();
- var logger = (IMessageLogger)frameworkHandle;
-
// discover the tests
- GetUnitTestDiscoverer(testSourceHandler).DiscoverTestsInSource(source, logger, discoverySink, runContext, isMTP);
- tests.AddRange(discoverySink.Tests);
+ GetUnitTestDiscoverer(testSourceHandler).DiscoverTestsInSource(source, logger, discoverySink, deploymentContext.RunSettingsXml, filterProvider, isMTP);
+ tests.AddRange(discoverySink.TestElements);
// Clear discoverSinksTests so that it just stores test for one source at one point of time
- discoverySink.Tests.Clear();
+ discoverySink.TestElements.Clear();
}
#if !WINDOWS_UWP && !WIN_UI
- bool isDeploymentDone = PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, runContext, frameworkHandle);
+ bool isDeploymentDone = Deploy(tests, deploymentContext, messageLogger);
#else
const bool isDeploymentDone = false;
#endif
// Placing this after deployment since we need information post deployment that we pass in as properties.
- CacheSessionParameters(runContext, frameworkHandle);
+ CacheSessionParameters(deploymentContext.RunSettingsXml, messageLogger);
// Run tests.
- await ExecuteTestsAsync(tests, runContext, frameworkHandle, isDeploymentDone).ConfigureAwait(false);
+ await ExecuteTestsAsync(tests, deploymentContext, messageLogger, testResultRecorder, filterProvider, isDeploymentDone).ConfigureAwait(false);
#if !WINDOWS_UWP && !WIN_UI
if (!_hasAnyTestFailed)
@@ -159,5 +165,14 @@ internal async Task RunTestsAsync(IEnumerable sources, IRunContext? runC
#endif
}
+#if !WINDOWS_UWP && !WIN_UI
+ ///
+ /// Runs deployment for the given tests via the platform-agnostic deployment service using the neutral
+ /// supplied by the adapter boundary.
+ ///
+ private static bool Deploy(IEnumerable tests, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger)
+ => PlatformServiceProvider.Instance.TestDeployment.Deploy(tests, deploymentContext, messageLogger);
+#endif
+
internal virtual UnitTestDiscoverer GetUnitTestDiscoverer(ITestSourceHandler testSourceHandler) => new(testSourceHandler);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestRunInfo.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestRunInfo.cs
index 5999529378..90e7c4698b 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestRunInfo.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestRunInfo.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
@@ -39,7 +38,7 @@ public static TestRunInfo CreateFrom(IReadOnlyList testsToRun)
private static PlannedTest ToPlannedTest(UnitTestElement element)
{
- Trait[]? traits = element.Traits;
+ TestTrait[]? traits = element.Traits;
KeyValuePair[] testProperties;
if (traits is { Length: > 0 })
{
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.RunSingleTest.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.RunSingleTest.cs
index 8537205e91..396541bad2 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.RunSingleTest.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.RunSingleTest.cs
@@ -5,7 +5,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
@@ -14,7 +13,7 @@ internal sealed partial class UnitTestRunner
{
// Task cannot cross app domains.
// For now, TestExecutionManager will call this sync method which is hacky.
- internal TestResult[] RunSingleTest(UnitTestElement unitTestElement, IDictionary testContextProperties, IMessageLogger messageLogger)
+ internal TestResult[] RunSingleTest(UnitTestElement unitTestElement, IDictionary testContextProperties, IAdapterMessageLogger messageLogger)
=> RunSingleTestAsync(unitTestElement, testContextProperties, messageLogger).GetAwaiter().GetResult();
///
@@ -24,7 +23,7 @@ internal TestResult[] RunSingleTest(UnitTestElement unitTestElement, IDictionary
/// The test context properties.
/// The message logger.
/// The .
- internal async Task RunSingleTestAsync(UnitTestElement unitTestElement, IDictionary testContextProperties, IMessageLogger messageLogger)
+ internal async Task RunSingleTestAsync(UnitTestElement unitTestElement, IDictionary testContextProperties, IAdapterMessageLogger messageLogger)
{
if (unitTestElement is null)
{
@@ -288,5 +287,5 @@ private static bool IsTestMethodRunnable(
return true;
}
- internal void ForceCleanup(IDictionary sourceLevelParameters, IMessageLogger logger) => ClassCleanupManager.ForceCleanup(_typeCache, sourceLevelParameters, logger);
+ internal void ForceCleanup(IDictionary sourceLevelParameters, IAdapterMessageLogger logger) => ClassCleanupManager.ForceCleanup(_typeCache, sourceLevelParameters, logger);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.TestFilter.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.TestFilter.cs
index 8b0ba506c4..89f2a214eb 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.TestFilter.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/UnitTestRunner.TestFilter.cs
@@ -6,7 +6,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
// The programmatic test-filter types (ITestFilter, TestFilterContext, TestFilterResult,
@@ -150,7 +149,7 @@ private static TestFilterContext CreateFilterContext(UnitTestElement element)
private async Task FinishFilteredOutTestAsync(
TestMethod testMethod,
IDictionary testContextProperties,
- IMessageLogger messageLogger,
+ IAdapterMessageLogger messageLogger,
TestResult[] filterResult,
ITestContext testContextForTestExecution)
{
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/MSTestDiscovererHelpers.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/MSTestDiscovererHelpers.cs
index 593c6b73f5..20b5ef982d 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/MSTestDiscovererHelpers.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/MSTestDiscovererHelpers.cs
@@ -3,8 +3,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -20,7 +19,7 @@ internal static class MSTestDiscovererHelpers
internal static bool AreValidSources(IEnumerable sources, ITestSourceHandler testSourceHandler)
=> sources.Any(source => testSourceHandler.ValidSourceExtensions.Any(extension => string.Equals(Path.GetExtension(source), extension, StringComparison.OrdinalIgnoreCase)));
- internal static bool InitializeDiscovery(IEnumerable sources, IDiscoveryContext? discoveryContext, IMessageLogger messageLogger, IConfiguration? configuration, ITestSourceHandler testSourceHandler)
+ internal static bool InitializeDiscovery(IEnumerable sources, string? settingsXml, IAdapterMessageLogger messageLogger, IConfiguration? configuration, ITestSourceHandler testSourceHandler)
{
if (!AreValidSources(sources, testSourceHandler))
{
@@ -30,12 +29,12 @@ internal static bool InitializeDiscovery(IEnumerable sources, IDiscovery
// Populate the runsettings.
try
{
- MSTestSettings.PopulateSettings(discoveryContext, messageLogger, configuration);
+ MSTestSettings.PopulateSettings(settingsXml, messageLogger, configuration);
return true;
}
catch (AdapterSettingsException ex)
{
- messageLogger.SendMessage(TestMessageLevel.Error, ex.Message);
+ messageLogger.SendMessage(MessageLevel.Error, ex.Message);
return false;
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectHelper.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectHelper.cs
index cd6c40f80c..518667da1d 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectHelper.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectHelper.cs
@@ -5,9 +5,9 @@
using System.Security;
#endif
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
@@ -198,7 +198,7 @@ internal static bool IsDoNotParallelizeSet(Assembly assembly)
///
/// The member to inspect.
/// List of traits.
- internal static Trait[] GetTestPropertiesAsTraits(MethodInfo testPropertyProvider)
+ internal static TestTrait[] GetTestPropertiesAsTraits(MethodInfo testPropertyProvider)
=> PlatformServiceProvider.Instance.ReflectionOperations.GetTestPropertiesAsTraits(testPropertyProvider);
///
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectionHelper.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectionHelper.cs
index 721f5afd7a..b85063c118 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectionHelper.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/ReflectionHelper.cs
@@ -1,8 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
@@ -62,7 +62,7 @@ public static string[] GetTestCategories(this IReflectionOperations reflectionOp
/// The reflection operations to use.
/// The member to inspect.
/// List of traits.
- public static Trait[] GetTestPropertiesAsTraits(this IReflectionOperations reflectionOperations, MethodInfo testPropertyProvider)
+ public static TestTrait[] GetTestPropertiesAsTraits(this IReflectionOperations reflectionOperations, MethodInfo testPropertyProvider)
{
Attribute[] attributesFromMethod = reflectionOperations.GetCustomAttributesCached(testPropertyProvider);
Attribute[] attributesFromClass = testPropertyProvider.ReflectedType is { } testClass ? reflectionOperations.GetCustomAttributesCached(testClass) : [];
@@ -89,13 +89,13 @@ public static Trait[] GetTestPropertiesAsTraits(this IReflectionOperations refle
return [];
}
- var traits = new Trait[countTestPropertyAttribute];
+ var traits = new TestTrait[countTestPropertyAttribute];
int index = 0;
foreach (Attribute attribute in attributesFromMethod)
{
if (attribute is TestPropertyAttribute testProperty)
{
- traits[index++] = new Trait(testProperty.Name, testProperty.Value);
+ traits[index++] = new TestTrait(testProperty.Name, testProperty.Value);
}
}
@@ -103,7 +103,7 @@ public static Trait[] GetTestPropertiesAsTraits(this IReflectionOperations refle
{
if (attribute is TestPropertyAttribute testProperty)
{
- traits[index++] = new Trait(testProperty.Name, testProperty.Value);
+ traits[index++] = new TestTrait(testProperty.Name, testProperty.Value);
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/RunSettingsUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/RunSettingsUtilities.cs
index a8d30dc0de..9d52227cd5 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/RunSettingsUtilities.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/RunSettingsUtilities.cs
@@ -1,8 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
@@ -18,6 +17,11 @@ internal static class RunSettingsUtilities
IgnoreWhitespace = true,
};
+ // The runsettings node names, historically taken from the VSTest object model's Constants type.
+ internal const string RunConfigurationSettingsName = "RunConfiguration";
+
+ internal const string TestRunParametersName = "TestRunParameters";
+
///
/// Gets the set of user defined test run parameters from settings xml as key value pairs.
///
@@ -25,23 +29,23 @@ internal static class RunSettingsUtilities
/// The test run parameters.
/// If there is no test run parameters section defined in the settingsxml a blank dictionary is returned.
internal static Dictionary? GetTestRunParameters(string? settingsXml)
- => GetNodeValue(settingsXml, Constants.TestRunParametersName, TestRunParameters.FromXml);
+ => GetNodeValue(settingsXml, TestRunParametersName, TestRunParameters.FromXml);
///
/// Throws if the node has an attribute.
///
/// The reader.
- /// Thrown if the node has an attribute.
+ /// Thrown if the node has an attribute.
internal static void ThrowOnHasAttributes(XmlReader reader)
{
if (reader.HasAttributes)
{
reader.MoveToNextAttribute();
- throw new SettingsException(
+ throw new InvalidRunSettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlAttribute,
- TestPlatform.ObjectModel.Constants.RunConfigurationSettingsName,
+ RunConfigurationSettingsName,
reader.Name));
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/TestRunParameters.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/TestRunParameters.cs
index 9a324be172..c891b114d4 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/TestRunParameters.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/TestRunParameters.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
@@ -47,11 +47,11 @@ internal static Dictionary FromXml(XmlReader reader)
break;
default:
- throw new SettingsException(
+ throw new InvalidRunSettingsException(
string.Format(
CultureInfo.CurrentCulture,
Resource.InvalidSettingsXmlElement,
- Constants.TestRunParametersName,
+ RunSettingsUtilities.TestRunParametersName,
reader.Name));
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Helpers/XmlReaderUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/XmlReaderUtilities.cs
new file mode 100644
index 0000000000..7daf921451
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Helpers/XmlReaderUtilities.cs
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+
+///
+/// Minimal, platform-agnostic navigation helpers for reading runsettings XML.
+/// These replace the equivalent helpers from the VSTest object model so the platform services layer does not
+/// depend on it; the navigation semantics are unchanged.
+///
+internal static class XmlReaderUtilities
+{
+ private const string RunSettingsRootNodeName = "RunSettings";
+
+ ///
+ /// Advances the reader to the root element and verifies it is the <RunSettings> node.
+ ///
+ /// The reader positioned before the root element.
+ /// Thrown when the root element is not <RunSettings>.
+ internal static void ReadToRootNode(XmlReader reader)
+ {
+ reader.ReadToNextElement();
+
+ // Verify that it is a "RunSettings" node.
+ if (reader.Name != RunSettingsRootNodeName)
+ {
+ throw new InvalidRunSettingsException($"Could not find '{RunSettingsRootNodeName}' node in the runsettings XML. Found '<{reader.Name}>' instead.");
+ }
+ }
+
+ ///
+ /// Reads until the next element node (or end of document).
+ ///
+ /// The reader.
+ internal static void ReadToNextElement(this XmlReader reader)
+ {
+ while (!reader.EOF && reader.Read() && reader.NodeType != XmlNodeType.Element)
+ {
+ }
+ }
+
+ ///
+ /// Skips the current subtree and positions the reader on the next element node.
+ ///
+ /// The reader.
+ internal static void SkipToNextElement(this XmlReader reader)
+ {
+ reader.Skip();
+
+ if (reader.NodeType != XmlNodeType.Element)
+ {
+ reader.ReadToNextElement();
+ }
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/IPlatformServiceProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/IPlatformServiceProvider.cs
index 25761fb0a8..8537dd59e7 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/IPlatformServiceProvider.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/IPlatformServiceProvider.cs
@@ -3,7 +3,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -67,7 +66,7 @@ internal interface IPlatformServiceProvider
///
/// The source.
///
- ///
+ ///
/// The run Settings for the session.
///
///
@@ -75,7 +74,7 @@ internal interface IPlatformServiceProvider
///
ITestSourceHost CreateTestSourceHost(
string source,
- TestPlatform.ObjectModel.Adapter.IRunSettings? runSettings);
+ string? settingsXml);
///
/// Gets the TestContext object for a platform.
@@ -97,5 +96,5 @@ ITestSourceHost CreateTestSourceHost(
///
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
///
- ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome);
+ ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IAdapterMessageLogger messageLogger, UTF.UnitTestOutcome outcome);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IAdapterMessageLogger.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IAdapterMessageLogger.cs
new file mode 100644
index 0000000000..fd894a8ad9
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IAdapterMessageLogger.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+
+///
+/// Platform-agnostic sink for diagnostic messages (informational / warning / error) that the adapter
+/// surfaces to whichever test host is running the tests.
+///
+///
+/// This abstraction lets the platform services layer report messages without taking a dependency on a
+/// specific test platform's object model. The mapping to a concrete host logger (for example the VSTest
+/// IMessageLogger) is provided by the adapter layer.
+///
+internal interface IAdapterMessageLogger
+{
+ ///
+ /// Reports a diagnostic message to the running test host.
+ ///
+ /// The severity of the message.
+ /// The message text.
+ void SendMessage(MessageLevel level, string message);
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestDeployment.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestDeployment.cs
index 1aabea8f1c..a3215764fb 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestDeployment.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestDeployment.cs
@@ -3,8 +3,8 @@
#if !WINDOWS_UWP && !WIN_UI
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
@@ -14,13 +14,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Int
internal interface ITestDeployment
{
///
- /// Deploy deployment items for the specified test cases.
+ /// Deploy deployment items for the specified tests.
///
- /// The test cases.
- /// The run context.
- /// The framework handle.
+ /// The tests.
+ /// The host deployment inputs (test-run directory, run settings XML).
+ /// The logger used to surface deployment warnings.
/// True if deployment is done.
- bool Deploy(IEnumerable testCases, IRunContext? runContext, IFrameworkHandle frameworkHandle);
+ bool Deploy(IEnumerable testElements, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger);
///
/// Gets the set of deployment items on a method and its corresponding class.
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilter.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilter.cs
new file mode 100644
index 0000000000..357f8762e4
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilter.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+
+///
+/// Platform-agnostic predicate that decides whether a discovered or executed
+/// should be included in the current test run, based on the user-provided test-case filter.
+///
+///
+/// This abstraction lets the platform services discovery and execution pipelines apply filtering over the
+/// neutral model without taking a dependency on a specific test platform's
+/// filter object model (for example the VSTest ITestCaseFilterExpression, TestProperty and
+/// IRunContext.GetTestCaseFilter types). The concrete filter is produced at the platform boundary by a
+/// wrapper that parses the host's filter (currently TestMethodFilter, which wraps the VSTest filter
+/// expression), and is expected to move fully out of the platform services layer in a later phase.
+/// A filter means "no filter" and every element is included.
+///
+internal interface ITestElementFilter
+{
+ ///
+ /// Determines whether the given matches the filter and should be included.
+ ///
+ /// The test element to evaluate.
+ /// if the element matches the filter; otherwise, .
+ bool Matches(UnitTestElement testElement);
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs
new file mode 100644
index 0000000000..202b395383
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestElementFilterProvider.cs
@@ -0,0 +1,31 @@
+// 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.PlatformServices.Interface;
+
+///
+/// Platform-agnostic factory that produces the in effect for the current
+/// run or discovery.
+///
+///
+/// This abstraction lets the platform services discovery and execution pipelines obtain the active filter
+/// without taking a dependency on a specific test platform's filter object model (for example the VSTest
+/// ITestCaseFilterExpression parsed from an IRunContext / IDiscoveryContext). The concrete
+/// provider is created at the adapter boundary, closing over the host filter context; the engine invokes it at
+/// the exact point it needs the filter so parse-error reporting keeps the same timing and per-source semantics.
+///
+internal interface ITestElementFilterProvider
+{
+ ///
+ /// Builds the for the current run/discovery.
+ ///
+ /// Logger used to report a filter parsing error.
+ ///
+ /// Set to when the filter is unsupported or failed to parse; in that case the caller
+ /// should report no tests for the affected source.
+ ///
+ ///
+ /// The filter to apply, or when no filter applies (every element is included).
+ ///
+ ITestElementFilter? GetTestElementFilter(IAdapterMessageLogger logger, out bool filterHasError);
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestResultRecorder.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestResultRecorder.cs
new file mode 100644
index 0000000000..7887c7df51
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/ITestResultRecorder.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+
+using FrameworkTestResult = Microsoft.VisualStudio.TestTools.UnitTesting.TestResult;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+
+///
+/// Platform-agnostic sink for reporting the lifecycle and results of test execution to whichever
+/// test host is running the tests.
+///
+///
+/// This abstraction lets the platform services execution engine report test start/end and results using only
+/// the neutral and framework models, without
+/// taking a dependency on a specific test platform's result object model (for example the VSTest
+/// TestResult, TestOutcome and attachment types). The concrete recorder — which builds the host
+/// result representation — is provided at the adapter boundary (see the VSTest implementation in
+/// TestResultRecorderExtensions in the adapter layer).
+///
+internal interface ITestResultRecorder
+{
+ ///
+ /// Signals that execution of the given test has started.
+ ///
+ /// The test whose execution is starting.
+ void RecordStart(UnitTestElement testElement);
+
+ ///
+ /// Signals that execution of the given test ended without producing any result.
+ ///
+ /// The test whose execution ended.
+ void RecordEmptyResult(UnitTestElement testElement);
+
+ ///
+ /// Reports a single framework for the given test to the test host.
+ ///
+ /// The test the result belongs to.
+ /// The framework result produced by executing the test.
+ /// The time at which the test started executing.
+ /// The time at which the test finished executing.
+ /// if the result was reported as a failure; otherwise, .
+ bool RecordResult(UnitTestElement testElement, FrameworkTestResult unitTestResult, DateTimeOffset startTime, DateTimeOffset endTime);
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IUnitTestElementSink.cs b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IUnitTestElementSink.cs
new file mode 100644
index 0000000000..7f52cf48f8
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Interfaces/IUnitTestElementSink.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
+
+///
+/// Platform-agnostic sink that receives the tests discovered by the adapter and hands them to the
+/// discovery consumer (a test host during discovery, or the execution pipeline during a run).
+///
+///
+/// This abstraction lets the platform services discovery pipeline emit the neutral
+/// model without taking a dependency on a specific test platform's
+/// discovery object model (for example the VSTest TestCase and ITestCaseDiscoverySink
+/// types). The concrete sink is produced at the platform boundary by a wrapper over the host's
+/// discovery sink (currently UnitTestElementSinkExtensions, which wraps the VSTest
+/// ITestCaseDiscoverySink and materializes a TestCase for each element), and is expected
+/// to move fully out of the platform services layer in a later phase.
+///
+internal interface IUnitTestElementSink
+{
+ ///
+ /// Reports a discovered to the running test host.
+ ///
+ /// The discovered test element.
+ void SendTestElement(UnitTestElement testElement);
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj b/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj
index 6a9e8820b1..97813d16d1 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj
+++ b/src/Adapter/MSTestAdapter.PlatformServices/MSTestAdapter.PlatformServices.csproj
@@ -36,7 +36,6 @@
-
+
+
+
+
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.Configuration.cs b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.Configuration.cs
index 6768b0b290..ee399d9ab5 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.Configuration.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.Configuration.cs
@@ -10,9 +10,8 @@
#endif
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using MessageLevel = Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel;
using StringEx = Microsoft.VisualStudio.TestTools.UnitTesting.StringEx;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -20,34 +19,34 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
internal sealed partial class MSTestSettings
{
///
- /// Populate adapter settings from the context.
+ /// Populate adapter settings from the run settings XML.
///
- /// The discovery context.
+ /// The run settings XML, or when none was provided.
/// The logger for messages.
/// The configuration.
- internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger? logger, IConfiguration? configuration)
+ internal static void PopulateSettings(string? settingsXml, IAdapterMessageLogger? logger, IConfiguration? configuration)
{
#if !WINDOWS_UWP
if (configuration?["mstest"] is not null
- && context?.RunSettings is not null
- && RunSettingsFileHasMSTestSettings(context.RunSettings.SettingsXml))
+ && settingsXml is not null
+ && RunSettingsFileHasMSTestSettings(settingsXml))
{
throw new InvalidOperationException(Resource.DuplicateConfigurationError);
}
#endif
var settings = new MSTestSettings();
- var runConfigurationSettings = RunConfigurationSettings.GetSettings(context?.RunSettings?.SettingsXml);
+ var runConfigurationSettings = RunConfigurationSettings.GetSettings(settingsXml);
#if !WINDOWS_UWP
- if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml) && configuration?["mstest"] is null)
+ if (!StringEx.IsNullOrEmpty(settingsXml) && configuration?["mstest"] is null)
#else
- if (!StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml))
+ if (!StringEx.IsNullOrEmpty(settingsXml))
#endif
{
- MSTestSettings? aliasSettings = GetSettings(context.RunSettings.SettingsXml, SettingsNameAlias, logger);
- settings = aliasSettings ?? GetSettings(context.RunSettings.SettingsXml, SettingsName, logger) ?? new MSTestSettings();
- SetGlobalSettings(context.RunSettings.SettingsXml, settings, logger);
+ MSTestSettings? aliasSettings = GetSettings(settingsXml, SettingsNameAlias, logger);
+ settings = aliasSettings ?? GetSettings(settingsXml, SettingsName, logger) ?? new MSTestSettings();
+ SetGlobalSettings(settingsXml, settings, logger);
}
#if !WINDOWS_UWP
else if (configuration?["mstest"] is not null)
@@ -62,7 +61,7 @@ internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger
if (settings.RandomizeTestOrder && settings.OrderTestsByNameInClass)
{
- logger?.SendMessage(TestMessageLevel.Warning, Resource.RandomTestOrderAndOrderTestsByNameInClassConflict);
+ logger?.SendMessage(MessageLevel.Warning, Resource.RandomTestOrderAndOrderTestsByNameInClassConflict);
}
// Track configuration source for telemetry.
@@ -71,14 +70,14 @@ internal static void PopulateSettings(IDiscoveryContext? context, IMessageLogger
{
telemetry.ConfigurationSource = configuration?["mstest"] is not null
? "testconfig.json"
- : !StringEx.IsNullOrEmpty(context?.RunSettings?.SettingsXml)
+ : !StringEx.IsNullOrEmpty(settingsXml)
? "runsettings"
: "none";
}
#endif
}
- private static void SetGlobalSettings(string runsettingsXml, MSTestSettings settings, IMessageLogger? logger)
+ private static void SetGlobalSettings(string runsettingsXml, MSTestSettings settings, IAdapterMessageLogger? logger)
{
XElement? runConfigElement = XDocument.Parse(runsettingsXml).Element("RunSettings")?.Element("RunConfiguration");
if (runConfigElement == null)
@@ -93,12 +92,12 @@ private static void SetGlobalSettings(string runsettingsXml, MSTestSettings sett
}
else if (disableParallelizationString is not null)
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, disableParallelizationString, "DisableParallelization"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, disableParallelizationString, "DisableParallelization"));
}
}
#if !WINDOWS_UWP
- private static void ParseBooleanSetting(IConfiguration configuration, string key, IMessageLogger? logger, Action setSetting)
+ private static void ParseBooleanSetting(IConfiguration configuration, string key, IAdapterMessageLogger? logger, Action setSetting)
{
if (configuration[$"mstest:{key}"] is not string value)
{
@@ -111,11 +110,11 @@ private static void ParseBooleanSetting(IConfiguration configuration, string key
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
}
}
- private static void ParseDebuggerLaunchModeSetting(IConfiguration configuration, string key, IMessageLogger? logger, Action setSetting)
+ private static void ParseDebuggerLaunchModeSetting(IConfiguration configuration, string key, IAdapterMessageLogger? logger, Action setSetting)
{
if (configuration[$"mstest:{key}"] is not string value)
{
@@ -128,11 +127,11 @@ private static void ParseDebuggerLaunchModeSetting(IConfiguration configuration,
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
}
}
- private static void ParseTimeoutSetting(IConfiguration configuration, string key, IMessageLogger? logger, Action setSetting)
+ private static void ParseTimeoutSetting(IConfiguration configuration, string key, IAdapterMessageLogger? logger, Action setSetting)
{
if (configuration[$"mstest:{key}"] is not string value)
{
@@ -149,11 +148,11 @@ private static void ParseTimeoutSetting(IConfiguration configuration, string key
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidTimeoutValue, value, key));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidTimeoutValue, value, key));
}
}
- private static void ParseSignedIntegerSetting(IConfiguration configuration, string key, IMessageLogger? logger, Action setSetting)
+ private static void ParseSignedIntegerSetting(IConfiguration configuration, string key, IAdapterMessageLogger? logger, Action setSetting)
{
if (configuration[$"mstest:{key}"] is not string value)
{
@@ -166,7 +165,7 @@ private static void ParseSignedIntegerSetting(IConfiguration configuration, stri
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, value, key));
}
}
@@ -176,7 +175,7 @@ private static void ParseSignedIntegerSetting(IConfiguration configuration, stri
/// Configuration to load the settings from.
/// The logger for messages.
/// The MSTest settings.
- internal static void SetSettingsFromConfig(IConfiguration configuration, IMessageLogger? logger, MSTestSettings settings)
+ internal static void SetSettingsFromConfig(IConfiguration configuration, IAdapterMessageLogger? logger, MSTestSettings settings)
{
// 'orderTestsByNameInClass' has moved under 'execution:' for consistency with the other execution settings.
// Prefer the new 'mstest:execution:orderTestsByNameInClass' key. Only fall back to the deprecated flat
@@ -188,7 +187,7 @@ internal static void SetSettingsFromConfig(IConfiguration configuration, IMessag
}
else if (configuration["mstest:orderTestsByNameInClass"] is not null)
{
- logger?.SendMessage(TestMessageLevel.Warning, Resource.DeprecatedFlatOrderTestsByNameInClassKey);
+ logger?.SendMessage(MessageLevel.Warning, Resource.DeprecatedFlatOrderTestsByNameInClassKey);
ParseBooleanSetting(configuration, "orderTestsByNameInClass", logger, value => settings.OrderTestsByNameInClass = value);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.RunSettingsXml.cs b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.RunSettingsXml.cs
index 73dd94d50b..84d021f440 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.RunSettingsXml.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/MSTestSettings.RunSettingsXml.cs
@@ -1,10 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using DebuggerLaunchMode = Microsoft.VisualStudio.TestTools.UnitTesting.DebuggerLaunchMode;
+using MessageLevel = Microsoft.VisualStudio.TestTools.UnitTesting.MessageLevel;
using StringEx = Microsoft.VisualStudio.TestTools.UnitTesting.StringEx;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -44,7 +45,7 @@ private static bool RunSettingsFileHasMSTestSettings(string? runSettingsXml)
}
using var stringReader = new StringReader(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
@@ -60,7 +61,7 @@ private static bool RunSettingsFileHasMSTestSettings(string? runSettingsXml)
}
#endif
- internal static MSTestSettings? GetSettings(string? runSettingsXml, string settingName, IMessageLogger? logger)
+ internal static MSTestSettings? GetSettings(string? runSettingsXml, string settingName, IAdapterMessageLogger? logger)
{
if (StringEx.IsNullOrWhiteSpace(runSettingsXml))
{
@@ -68,7 +69,7 @@ private static bool RunSettingsFileHasMSTestSettings(string? runSettingsXml)
}
using var stringReader = new StringReader(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
XmlReaderUtilities.ReadToRootNode(reader);
reader.ReadToNextElement();
@@ -81,7 +82,7 @@ private static bool RunSettingsFileHasMSTestSettings(string? runSettingsXml)
return !reader.EOF ? ToSettings(reader.ReadSubtree(), logger) : null;
}
- private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logger)
+ private static MSTestSettings ToSettings(XmlReader reader, IAdapterMessageLogger? logger)
{
if (reader is null)
{
@@ -108,7 +109,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, captureTraceOutput, "CaptureTraceOutput"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, captureTraceOutput, "CaptureTraceOutput"));
}
break;
@@ -120,7 +121,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, mapInconclusiveToFailed, "MapInconclusiveToFailed"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, mapInconclusiveToFailed, "MapInconclusiveToFailed"));
}
break;
@@ -132,7 +133,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, mapNotRunnableToFailed, "MapNotRunnableToFailed"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, mapNotRunnableToFailed, "MapNotRunnableToFailed"));
}
break;
@@ -144,7 +145,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, treatDiscoveryWarningsAsErrors, "TreatDiscoveryWarningsAsErrors"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, treatDiscoveryWarningsAsErrors, "TreatDiscoveryWarningsAsErrors"));
}
break;
@@ -166,7 +167,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, considerEmptyDataSourceAsInconclusive, "ConsiderEmptyDataSourceAsInconclusive"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, considerEmptyDataSourceAsInconclusive, "ConsiderEmptyDataSourceAsInconclusive"));
}
break;
@@ -193,7 +194,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, cooperativeCancellationTimeout, "CooperativeCancellationTimeout"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, cooperativeCancellationTimeout, "CooperativeCancellationTimeout"));
}
break;
@@ -205,7 +206,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, orderTestsByNameInClass, "OrderTestsByNameInClass"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, orderTestsByNameInClass, "OrderTestsByNameInClass"));
}
break;
@@ -217,7 +218,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, randomizeTestOrder, "RandomizeTestOrder"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, randomizeTestOrder, "RandomizeTestOrder"));
}
break;
@@ -229,7 +230,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, randomTestOrderSeed, "RandomTestOrderSeed"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, randomTestOrderSeed, "RandomTestOrderSeed"));
}
break;
@@ -241,7 +242,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, launchDebuggerOnAssertionFailure, "LaunchDebuggerOnAssertionFailure"));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, launchDebuggerOnAssertionFailure, "LaunchDebuggerOnAssertionFailure"));
}
break;
@@ -256,7 +257,7 @@ private static MSTestSettings ToSettings(XmlReader reader, IMessageLogger? logge
return settings;
}
- private static void ParseTimeoutSetting(string rawValue, string settingName, IMessageLogger? logger, Action setSetting)
+ private static void ParseTimeoutSetting(string rawValue, string settingName, IAdapterMessageLogger? logger, Action setSetting)
{
if (int.TryParse(rawValue, out int result) && result > 0)
{
@@ -264,7 +265,7 @@ private static void ParseTimeoutSetting(string rawValue, string settingName, IMe
}
else
{
- logger?.SendMessage(TestMessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidTimeoutValue, rawValue, settingName));
+ logger?.SendMessage(MessageLevel.Warning, string.Format(CultureInfo.CurrentCulture, Resource.InvalidTimeoutValue, rawValue, settingName));
}
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/InvalidRunSettingsException.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/InvalidRunSettingsException.cs
new file mode 100644
index 0000000000..3e9a421915
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/InvalidRunSettingsException.cs
@@ -0,0 +1,20 @@
+// 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.ObjectModel;
+
+///
+/// Thrown when the runsettings XML is structurally invalid (for example a bad attribute, an unexpected element,
+/// or a wrong root node). This is the platform-agnostic replacement for the settings exception the platform
+/// services layer historically surfaced from the VSTest object model. It is intentionally distinct from
+/// : is caught by the discovery
+/// initialization path (reported and treated as "no tests"), whereas a structural runsettings error propagates
+/// to the host, preserving the original behavior.
+///
+internal sealed class InvalidRunSettingsException : Exception
+{
+ internal InvalidRunSettingsException(string? message)
+ : base(message)
+ {
+ }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs
index f108598c65..a95cc80dcd 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestMethod.cs
@@ -135,6 +135,11 @@ private static string GetManagedTypeName(string fullClassName)
internal TestMethod Clone() => (TestMethod)MemberwiseClone();
+ // NOTE: This method has a latent bug (it assigns AssemblyName/MethodInfo on `this` instead of on the
+ // returned clone), tracked by https://github.com/microsoft/testfx/issues/9573. It is intentionally left
+ // untouched here so the existing ToTestCase / test-case filter bridge callers keep their exact current
+ // behavior; the execution engine uses the correct CloneWithSource below instead. The two are unified once
+ // #9573 is addressed.
internal TestMethod CloneWithUpdatedSource(string source)
{
var clone = (TestMethod)MemberwiseClone();
@@ -142,4 +147,15 @@ internal TestMethod CloneWithUpdatedSource(string source)
MethodInfo = null;
return clone;
}
+
+ // Correct counterpart of CloneWithUpdatedSource: assigns the updated source and clears the cached
+ // MethodInfo on the returned clone (leaving `this` untouched). Used by the execution engine's source
+ // resolution. See https://github.com/microsoft/testfx/issues/9573.
+ internal TestMethod CloneWithSource(string source)
+ {
+ var clone = (TestMethod)MemberwiseClone();
+ clone.AssemblyName = source;
+ clone.MethodInfo = null;
+ return clone;
+ }
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestTrait.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestTrait.cs
new file mode 100644
index 0000000000..44c4799091
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/TestTrait.cs
@@ -0,0 +1,33 @@
+// 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.ObjectModel;
+
+///
+/// A platform-agnostic name/value trait associated with a test (for example the values produced by
+/// [TestProperty] attributes). This mirrors the shape historically carried by the test platform's
+/// trait type but does not depend on any specific test platform's object model, so the platform services
+/// layer can describe test traits without referencing that object model.
+///
+#if NETFRAMEWORK
+// A (which carries these) is serialized across app domains on .NET Framework.
+[Serializable]
+#endif
+internal readonly struct TestTrait
+{
+ public TestTrait(string name, string value)
+ {
+ Name = name;
+ Value = value;
+ }
+
+ ///
+ /// Gets the trait name.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the trait value.
+ ///
+ public string Value { get; }
+}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs
index fb20da9858..8549fcc16e 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs
@@ -1,9 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
-using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
@@ -17,11 +14,6 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
[DebuggerDisplay("{TestMethod.DisplayName} ({TestMethod.ManagedTypeName})")]
internal sealed class UnitTestElement
{
- private static readonly byte[] OpenParen = [40, 0]; // Encoding.Unicode.GetBytes("(");
- private static readonly byte[] CloseParen = [41, 0]; // Encoding.Unicode.GetBytes(")");
- private static readonly byte[] OpenBracket = [91, 0]; // Encoding.Unicode.GetBytes("[");
- private static readonly byte[] CloseBracket = [93, 0]; // Encoding.Unicode.GetBytes("]");
-
///
/// Initializes a new instance of the class.
///
@@ -53,7 +45,7 @@ public UnitTestElement(TestMethod testMethod)
///
/// Gets or sets the traits for test method.
///
- public Trait[]? Traits { get; set; }
+ public TestTrait[]? Traits { get; set; }
///
/// Gets or sets the priority of the test method, if any.
@@ -81,6 +73,35 @@ public UnitTestElement(TestMethod testMethod)
///
internal string[]? WorkItemIds { get; set; }
+ ///
+ /// Gets or sets host-provided execution context properties (for example the values historically read
+ /// from a test-case-management host: run id, plan id, build configuration, test point id, ...) that are
+ /// surfaced to the running test through TestContext. Keyed by the host property identifier so the
+ /// platform services layer does not depend on a specific test platform's property object model. This is
+ /// only populated when a host supplies such values (for internally discovered tests it stays null).
+ ///
+#if NETFRAMEWORK
+ // Consumed on the source-processing side before execution; the isolation host copy never reads it.
+ [field: NonSerialized]
+#endif
+ internal IReadOnlyDictionary? ExecutionContextProperties { get; set; }
+
+ ///
+ /// Gets or sets the host's test case for this test, used to report its lifecycle and results back to the
+ /// host with full fidelity (preserving any host-injected data — such as test-case-management or
+ /// data-collector properties — that the neutral model does not otherwise carry) and to describe it to the
+ /// (still VSTest-based) deployment service. For tests handed to the adapter by a host it is that original
+ /// test case; for tests discovered internally by the platform services it is until
+ /// materialized on demand by the adapter's UnitTestElementExtensions.GetOrCreateHostTestCase. The execution engine treats it as an
+ /// opaque handle — it reads nothing VSTest-specific off it and only threads it into the result recorder and
+ /// the deployment boundary.
+ ///
+#if NETFRAMEWORK
+ // Result recording and deployment happen on the source-processing side; the isolation host copy never reads it.
+ [field: NonSerialized]
+#endif
+ internal object? HostRecordingHandle { get; set; }
+
internal UnitTestElement Clone()
{
var clone = (UnitTestElement)MemberwiseClone();
@@ -88,6 +109,11 @@ internal UnitTestElement Clone()
return clone;
}
+ // Legacy source-updating clone used only by the ToTestCase / test-case filter bridge
+ // (TestCaseExtensions.ToUnitTestElementWithUpdatedSource). It delegates to the buggy
+ // TestMethod.CloneWithUpdatedSource and is retained to preserve that path's exact current behavior; the
+ // execution engine uses WithUpdatedSource / CloneWithSource instead. Tracked by
+ // https://github.com/microsoft/testfx/issues/9573.
internal UnitTestElement CloneWithUpdatedSource(string source)
{
var clone = (UnitTestElement)MemberwiseClone();
@@ -95,190 +121,24 @@ internal UnitTestElement CloneWithUpdatedSource(string source)
return clone;
}
- ///
- /// Convert the UnitTestElement instance to an Object Model testCase instance.
- ///
- /// An instance of .
- internal TestCase ToTestCase()
- {
- // This causes compatibility problems with older runners.
- // string testFullName = this.TestMethod.HasManagedMethodAndTypeProperties
- // ? $"{TestMethod.ManagedTypeName}.{TestMethod.ManagedMethodName}"
- // : $"{TestMethod.FullClassName}.{TestMethod.Name}";
- string testFullName = $"{TestMethod.FullClassName}.{TestMethod.Name}";
-
- TestCase testCase = new(testFullName, EngineConstants.ExecutorUri, TestMethod.AssemblyName)
- {
- DisplayName = TestMethod.DisplayName,
- LocalExtensionData = this,
- };
-
- if (TestMethod.HasManagedMethodAndTypeProperties)
- {
- testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, TestMethod.ManagedTypeName);
- testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, TestMethod.ManagedMethodName);
- }
-
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, TestMethod.FullClassName);
-
- if (TestMethod.ParameterTypes is not null)
- {
- testCase.SetPropertyValue(EngineConstants.ParameterTypesProperty, TestMethod.ParameterTypes);
- }
-
- IReadOnlyCollection? hierarchy = TestMethod.Hierarchy;
- if (hierarchy is { Count: > 0 })
- {
- testCase.SetHierarchy([.. hierarchy]);
- }
-
- // Set only if some test category is present
- if (TestCategory is { Length: > 0 })
- {
- testCase.SetPropertyValue(EngineConstants.TestCategoryProperty, TestCategory);
- }
-
- // Set priority if present
- if (Priority != null)
- {
- testCase.SetPropertyValue(EngineConstants.PriorityProperty, Priority.Value);
- }
-
- if (Traits is { Length: > 0 })
- {
- testCase.Traits.AddRange(Traits);
- }
-
- if (WorkItemIds != null)
- {
- testCase.SetPropertyValue(EngineConstants.WorkItemIdsProperty, WorkItemIds);
- }
-
-#if !WINDOWS_UWP && !WIN_UI
- // The list of items to deploy before running this test.
- if (DeploymentItems is { Length: > 0 })
- {
- testCase.SetPropertyValue(EngineConstants.DeploymentItemsProperty, DeploymentItems);
- }
-#endif
-
- // Set the Do not parallelize state if present
- if (DoNotParallelize)
- {
- testCase.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, DoNotParallelize);
- }
-
- if (UnfoldingStrategy != TestDataSourceUnfoldingStrategy.Auto)
- {
- testCase.SetPropertyValue(EngineConstants.UnfoldingStrategy, (int)UnfoldingStrategy);
- }
-
- // Store resolved data if any
- if (TestMethod.DataType != DynamicDataType.None)
- {
- testCase.SetPropertyValue(EngineConstants.TestDynamicDataTypeProperty, (int)TestMethod.DataType);
- testCase.SetPropertyValue(EngineConstants.TestDynamicDataProperty, TestMethod.SerializedData);
- testCase.SetPropertyValue(EngineConstants.TestCaseIndexProperty, TestMethod.TestCaseIndex);
- // VSTest serialization doesn't handle null so instead don't set the property so that it's deserialized as null
- if (TestMethod.TestDataSourceIgnoreMessage is not null)
- {
- testCase.SetPropertyValue(EngineConstants.TestDataSourceIgnoreMessageProperty, TestMethod.TestDataSourceIgnoreMessage);
- }
- }
-
- testCase.LineNumber = DeclaringLineNumber ?? -1;
- testCase.CodeFilePath = DeclaringFilePath;
-
- SetTestCaseId(testCase, testFullName);
-
- return testCase;
- }
-
- private void SetTestCaseId(TestCase testCase, string testFullName)
- => testCase.Id = GenerateSerializedDataStrategyTestId(testFullName);
-
- private Guid GenerateSerializedDataStrategyTestId(string testFullName)
+ // Correct source-updating clone: the returned clone (only) targets the new source. Used by the execution
+ // engine via WithUpdatedSource. See https://github.com/microsoft/testfx/issues/9573.
+ internal UnitTestElement CloneWithSource(string source)
{
- // Below comment is copied over from Test Platform.
- // If source is a file name then just use the filename for the identifier since the file might have moved between
- // discovery and execution (in appx mode for example). This is not elegant because the Source contents should be
- // a black box to the framework.
- // For example in the database adapter case this is not a file path.
- // As discussed with team, we found no scenario for netcore, & fullclr where the Source is not present where ID
- // is generated, which means we would always use FileName to generate ID. In cases where somehow Source Path
- // contained garbage character the API Path.GetFileName() we are simply returning original input.
- // For UWP where source during discovery, & during execution can be on different machine, in such case we should
- // always use Path.GetFileName().
- string fileNameOrFilePath = TestMethod.AssemblyName;
- try
- {
- fileNameOrFilePath = Path.GetFileName(fileNameOrFilePath);
- }
- catch (ArgumentException)
- {
- // In case path contains invalid characters.
- }
-
- byte hashVersion = 1; // Increment when changing the hashing algorithm.
- var hash = new TestFx.Hashing.XxHash128();
- hash.Append(Encoding.Unicode.GetBytes(fileNameOrFilePath));
- hash.Append(Encoding.Unicode.GetBytes(testFullName));
- if (TestMethod.ParameterTypes is not null)
- {
- hash.Append(OpenParen);
- hash.Append(Encoding.Unicode.GetBytes(TestMethod.ParameterTypes));
- hash.Append(CloseParen);
- }
-
- if (TestMethod.DataType != DynamicDataType.None)
- {
- hash.Append(OpenBracket);
- hash.Append(Encoding.Unicode.GetBytes(TestMethod.TestCaseIndex.ToString(CultureInfo.InvariantCulture)));
- hash.Append(CloseBracket);
- }
-
- byte[] hashBytes = hash.GetCurrentHash();
-
- return VersionedGuidFromHash(hashBytes, hashVersion);
+ var clone = (UnitTestElement)MemberwiseClone();
+ clone.TestMethod = TestMethod.CloneWithSource(source);
+ return clone;
}
- internal /* for testing */ static Guid VersionedGuidFromHash(byte[] hashBytes, byte hashVersion)
- {
- int firstByte = 0;
- int versionByte = 6;
-
- // We set first 4 bits to 0001, which is our version. Increase this when we change the hashing algorithm.
- //
- // Note: The logic below is operating on int32, because bitwise operators are not defined for byte in C#. But casting
- // does not affect endianness, so we can safely assume the byte data are at the end of the int.
- // We also don't care to specify the whole expansion of the int in the bit masks, which effectively ignores all the data in the int
- // before the last byte data, but that is okay, they will always be zero.
- hashBytes[firstByte] = (byte)((hashBytes[firstByte] & 0b0000_1111) | (hashVersion << 4));
- // We set first 4bits 7th byte to 8 to indicate that this is a version 8 UUID. https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-8
- hashBytes[versionByte] = (byte)((hashBytes[versionByte] & 0b0000_1111) | 0b1000_0000);
-
- // We set the first 2 bits of nineth byte to 0b10. In the guid this is stored as byte, so we don't care about endiannes.
- int variantByte = 8;
- hashBytes[variantByte] = (byte)((hashBytes[variantByte] & 0b0011_1111) | 0b1000_0000);
-
- Guid guid;
-
- // On .NET Framework we cannot specify the endianness of the Guid because the new Guid(hashBytes, bigEndian: true); api is not available.
- // Instead we construct the int and short values manually, doing what the constructor would do, to put the bytes in big-endian order.
- guid = new Guid(
- (hashBytes[0] << 24) | (hashBytes[1] << 16) | (hashBytes[2] << 8) | hashBytes[3],
- (short)((hashBytes[4] << 8) | hashBytes[5]),
- (short)((hashBytes[6] << 8) | hashBytes[7]),
- hashBytes[8], hashBytes[9], hashBytes[10], hashBytes[11], hashBytes[12], hashBytes[13], hashBytes[14], hashBytes[15]);
-
-#if DEBUG && NET9_0_OR_GREATER
- // Version property is only available on .NET 9 and later.
- Debug.Assert(guid.Version == 8, $"Expected Guid version to be 8, but it was {guid.Version}");
- // The field represents the 4 bit value, but according to the specification only the first 2 bits are used for UUID v8.
- // So we shift 2 bits to the right to get the actual variant value.
- int variant = guid.Variant >> 2;
- Debug.Assert(variant == 2, $"Expected Guid variant to be 2, but it was {variant}");
-#endif
- return guid;
- }
+ ///
+ /// Returns this element when it already targets , otherwise a clone whose test
+ /// method points at . This mirrors the source resolution the adapter previously
+ /// performed while converting a host test case, so a deployed source (relocated to the deployment
+ /// directory) is honored without reloading the assembly from its original location (see
+ /// https://github.com/microsoft/testfx/issues/6713).
+ ///
+ /// The (possibly deployment-relocated) source of the test.
+ /// An element whose is .
+ internal UnitTestElement WithUpdatedSource(string source)
+ => TestMethod.AssemblyName == source ? this : CloneWithSource(source);
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs b/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs
index 81c975c823..aa460a8231 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/PlatformServiceProvider.cs
@@ -4,7 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using UTF = Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -130,7 +129,7 @@ internal static IPlatformServiceProvider Instance
///
/// The source.
///
- ///
+ ///
/// The run Settings for the session.
///
///
@@ -138,9 +137,9 @@ internal static IPlatformServiceProvider Instance
///
public ITestSourceHost CreateTestSourceHost(
string source,
- TestPlatform.ObjectModel.Adapter.IRunSettings? runSettings)
+ string? settingsXml)
{
- var testSourceHost = new TestSourceHost(source, runSettings);
+ var testSourceHost = new TestSourceHost(source, settingsXml);
testSourceHost.SetupHost();
return testSourceHost;
@@ -166,7 +165,7 @@ public ITestSourceHost CreateTestSourceHost(
///
/// This was required for compatibility reasons since the TestContext object that the V1 adapter had for desktop is not .Net Core compliant.
///
- public ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IMessageLogger messageLogger, UTF.UnitTestOutcome outcome)
+ public ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IAdapterMessageLogger messageLogger, UTF.UnitTestOutcome outcome)
{
var testContextImplementation = new TestContextImplementation(testMethod, testClassFullName, properties, messageLogger, TestRunCancellationToken);
testContextImplementation.SetOutcome(outcome);
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs
index 5723c35a44..8423ab05b6 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/RunConfigurationSettings.cs
@@ -5,7 +5,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
#endif
-using Microsoft.VisualStudio.TestPlatform.PlatformAbstractions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
@@ -96,13 +95,13 @@ private static RunConfigurationSettings ToSettings(XmlReader reader)
{
case "EXECUTIONTHREADAPARTMENTSTATE":
{
- if (Enum.TryParse(reader.ReadInnerXml(), out PlatformApartmentState platformApartmentState))
+ if (Enum.TryParse(reader.ReadInnerXml(), out ApartmentStateSetting apartmentStateSetting))
{
- settings.ExecutionApartmentState = platformApartmentState switch
+ settings.ExecutionApartmentState = apartmentStateSetting switch
{
- PlatformApartmentState.STA => ApartmentState.STA,
- PlatformApartmentState.MTA => ApartmentState.MTA,
- _ => throw new NotSupportedException($"Platform apartment state '{platformApartmentState}' is not supported."),
+ ApartmentStateSetting.STA => ApartmentState.STA,
+ ApartmentStateSetting.MTA => ApartmentState.MTA,
+ _ => throw new NotSupportedException($"Platform apartment state '{apartmentStateSetting}' is not supported."),
};
}
@@ -131,13 +130,13 @@ internal static RunConfigurationSettings SetRunConfigurationSettingsFromConfig(I
// }
// }
string? apartmentStateValue = configuration["mstest:execution:executionApartmentState"];
- if (Enum.TryParse(apartmentStateValue, out PlatformApartmentState platformApartmentState))
+ if (Enum.TryParse(apartmentStateValue, out ApartmentStateSetting apartmentStateSetting))
{
- settings.ExecutionApartmentState = platformApartmentState switch
+ settings.ExecutionApartmentState = apartmentStateSetting switch
{
- PlatformApartmentState.STA => ApartmentState.STA,
- PlatformApartmentState.MTA => ApartmentState.MTA,
- _ => throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, platformApartmentState, "execution:executionApartmentState")),
+ ApartmentStateSetting.STA => ApartmentState.STA,
+ ApartmentStateSetting.MTA => ApartmentState.MTA,
+ _ => throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resource.InvalidValue, apartmentStateSetting, "execution:executionApartmentState")),
};
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
index d844431a45..c10ea5169b 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/MSTestAdapterSettings.cs
@@ -4,9 +4,9 @@
#if !WINDOWS_UWP
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -206,7 +206,7 @@ public static bool IsAppDomainCreationDisabled(string? settingsXml)
if (!StringEx.IsNullOrEmpty(settingsXml))
{
StringReader stringReader = new(settingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
var xmlDoc = new XmlDocument() { XmlResolver = null };
xmlDoc.Load(reader);
@@ -384,7 +384,7 @@ private void ReadAssemblyResolutionPath(XmlReader reader)
else
{
string message = string.Format(CultureInfo.CurrentCulture, Resource.InvalidSettingsXmlElement, reader.Name, "AssemblyResolution");
- throw new SettingsException(message);
+ throw new InvalidRunSettingsException(message);
}
// Move to the next element under tag AssemblyResolution
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs
index 5efc98a041..9cfb5f78d4 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestContextImplementation.cs
@@ -10,7 +10,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ITestMethod = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ObjectModel.ITestMethod;
@@ -71,7 +70,7 @@ public override string ToString()
#else
private readonly object _propertiesLock = new();
#endif
- private readonly IMessageLogger? _messageLogger;
+ private readonly IAdapterMessageLogger? _messageLogger;
private readonly TestRunCancellationToken? _testRunCancellationToken;
private CancellationTokenRegistration? _cancellationTokenRegistration;
@@ -113,7 +112,7 @@ public override string ToString()
/// Properties/configuration passed in.
/// The message logger to use.
/// The global test run cancellation token.
- internal TestContextImplementation(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IMessageLogger? messageLogger, TestRunCancellationToken? testRunCancellationToken)
+ internal TestContextImplementation(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IAdapterMessageLogger? messageLogger, TestRunCancellationToken? testRunCancellationToken)
{
// testMethod can be null when running ForceCleanup (done when reaching --maximum-failed-tests.
DebugEx.Assert(properties != null, "properties is not null");
@@ -428,7 +427,7 @@ public void SetDisplayName(string? displayName)
///
public override void DisplayMessage(MessageLevel messageLevel, string message)
- => _messageLogger?.SendMessage(messageLevel.ToTestMessageLevel(), message);
+ => _messageLogger?.SendMessage(messageLevel, message);
#endregion
///
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs
index 11a4cd49cb..1591ad3481 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestDeployment.cs
@@ -4,14 +4,10 @@
#if !WINDOWS_UWP && !WIN_UI
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-#if NETFRAMEWORK
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
-#endif
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -104,13 +100,13 @@ public void Cleanup()
///
/// Deploy files related to the list of tests specified.
///
- /// The tests.
- /// The run context.
- /// The framework handle.
+ /// The tests.
+ /// The host deployment inputs.
+ /// The logger used to surface deployment warnings.
/// Return true if deployment is done.
- public bool Deploy(IEnumerable testCases, IRunContext? runContext, IFrameworkHandle frameworkHandle)
+ public bool Deploy(IEnumerable testElements, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger)
{
- DebugEx.Assert(testCases != null, "tests");
+ DebugEx.Assert(testElements != null, "tests");
// Reset runDirectories before doing deployment, so that older values of runDirectories is not picked
// even if test host is kept alive.
@@ -118,7 +114,7 @@ public bool Deploy(IEnumerable testCases, IRunContext? runContext, IFr
_adapterSettings = MSTestSettingsProvider.Settings;
bool canDeploy = CanDeploy();
- bool hasDeploymentItems = testCases.Any(DeploymentItemUtility.HasDeploymentItems);
+ bool hasDeploymentItems = testElements.Any(DeploymentItemUtility.HasDeploymentItems);
// deployment directories should not be created in this case,simply return
if (!canDeploy && hasDeploymentItems)
@@ -127,8 +123,8 @@ public bool Deploy(IEnumerable testCases, IRunContext? runContext, IFr
}
#if NETFRAMEWORK
- string? firstTestSource = testCases.FirstOrDefault()?.Source;
- RunDirectories = _deploymentUtility.CreateDeploymentDirectories(runContext, firstTestSource);
+ string? firstTestSource = testElements.FirstOrDefault()?.TestMethod.AssemblyName;
+ RunDirectories = _deploymentUtility.CreateDeploymentDirectories(deploymentContext, firstTestSource);
// Deployment directories are created but deployment will not happen.
// This is added just to keep consistency with MSTest v1 behavior.
@@ -143,8 +139,8 @@ public bool Deploy(IEnumerable testCases, IRunContext? runContext, IFr
return false;
}
- string? firstTestSource = testCases.FirstOrDefault()?.Source;
- RunDirectories = _deploymentUtility.CreateDeploymentDirectories(runContext, firstTestSource);
+ string? firstTestSource = testElements.FirstOrDefault()?.TestMethod.AssemblyName;
+ RunDirectories = _deploymentUtility.CreateDeploymentDirectories(deploymentContext, firstTestSource);
#endif
// Object model currently does not have support for SuspendCodeCoverage. We can remove this once support is added
@@ -153,15 +149,15 @@ public bool Deploy(IEnumerable testCases, IRunContext? runContext, IFr
#endif
{
// Group the tests by source
- var testsBySource = from test in testCases
- group test by test.Source into testGroup
+ var testsBySource = from test in testElements
+ group test by test.TestMethod.AssemblyName into testGroup
select new { Source = testGroup.Key, Tests = testGroup };
TestRunDirectories runDirectories = RunDirectories;
foreach (var group in testsBySource)
{
// do the deployment
- _deploymentUtility.Deploy(@group.Tests, @group.Source, runContext, frameworkHandle, RunDirectories);
+ _deploymentUtility.Deploy(@group.Tests, @group.Source, deploymentContext, messageLogger, RunDirectories);
}
// Update the runDirectories
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHandler.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHandler.cs
index 4255641119..ae97ba6ab2 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHandler.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHandler.cs
@@ -5,9 +5,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.AppContainer;
#endif
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-#if NETFRAMEWORK
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
-#endif
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -79,7 +76,7 @@ public bool IsAssemblyReferenced(AssemblyName assemblyName, string source)
{
#if NETFRAMEWORK
// This loads the dll in a different app domain. We can optimize this to load in the current domain since this code could be run in a new app domain anyway.
- bool? utfReference = AssemblyHelper.DoesReferencesAssembly(source, assemblyName);
+ bool? utfReference = DoesSourceReferenceAssembly(source, assemblyName);
// If no reference to UTF don't run discovery. Take conservative approach. If not able to find proceed with discovery.
return !utfReference.HasValue || utfReference.Value;
@@ -94,6 +91,85 @@ public bool IsAssemblyReferenced(AssemblyName assemblyName, string source)
#endif
}
+#if NETFRAMEWORK
+ ///
+ /// Checks whether the source assembly directly references the given assembly.
+ /// Only the assembly simple name and public key token are matched; version is ignored.
+ /// Returns if the reference could not be determined.
+ ///
+ /// The path to the source assembly to inspect.
+ /// The assembly to look for in the source's references.
+ /// if referenced, if not, if undeterminable.
+ private static bool? DoesSourceReferenceAssembly(string source, AssemblyName referenceAssembly)
+ {
+ if (string.IsNullOrEmpty(source) || referenceAssembly is null)
+ {
+ return null;
+ }
+
+ try
+ {
+ string? referenceAssemblyName = referenceAssembly.Name;
+ byte[]? referenceAssemblyPublicKeyToken = referenceAssembly.GetPublicKeyToken();
+
+ // ReflectionOnlyLoadFrom loads from the specified path only (no probing) and does not
+ // execute any code from the loaded assembly.
+ var assembly = Assembly.ReflectionOnlyLoadFrom(source);
+
+ foreach (AssemblyName referencedAssembly in assembly.GetReferencedAssemblies())
+ {
+ // Match without version: only the simple name and public key token.
+ if (!string.Equals(referencedAssembly.Name, referenceAssemblyName, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ if (ArePublicKeyTokensEqual(referencedAssembly.GetPublicKeyToken(), referenceAssemblyPublicKeyToken))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ catch
+ {
+ // Return null if we are not able to check.
+ return null;
+ }
+ }
+
+ private static bool ArePublicKeyTokensEqual(byte[]? left, byte[]? right)
+ {
+ // GetPublicKeyToken() returns null (or an empty array) for unsigned assemblies.
+ // Treat two unsigned tokens as equal, and a signed vs. unsigned pair as unequal.
+ if (left is null || left.Length == 0)
+ {
+ return right is null || right.Length == 0;
+ }
+
+ if (right is null || right.Length == 0)
+ {
+ return false;
+ }
+
+ if (left.Length != right.Length)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < left.Length; ++i)
+ {
+ if (left[i] != right[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+#endif
+
///
/// Gets the set of sources (dll's/exe's) that contain tests. If a source is a package (appx), return the file (dll/exe) that contains tests from it.
///
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs
index 7ff1be7380..933d8d3185 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Services/TestSourceHost.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if !WINDOWS_UWP
@@ -8,13 +8,6 @@
#if NETFRAMEWORK
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
#endif
-#if NETFRAMEWORK
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-#endif
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-#if NETFRAMEWORK || (NET && !WINDOWS_UWP)
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
-#endif
#if !WINDOWS_UWP
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif
@@ -30,6 +23,13 @@ namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
#pragma warning disable CA1852 // Seal internal types - needs to be non-sealed because it's mocked in tests.
internal class TestSourceHost : ITestSourceHost
{
+#if NETFRAMEWORK || (NET && !WINDOWS_UWP)
+ private const string ObjectModelAssemblyName = "Microsoft.VisualStudio.TestPlatform.ObjectModel";
+#endif
+#if NETFRAMEWORK
+ private const string CoreUtilitiesAssemblyName = "Microsoft.TestPlatform.CoreUtilities";
+#endif
+
#if !WINDOWS_UWP
#pragma warning disable IDE0052 // Remove unread private members
private readonly string _sourceFileName;
@@ -66,10 +66,10 @@ internal class TestSourceHost : ITestSourceHost
/// Initializes a new instance of the class.
///
/// The source file name.
- /// The run-settings provided for this session.
- public TestSourceHost(string sourceFileName, IRunSettings? runSettings)
+ /// The run-settings XML provided for this session.
+ public TestSourceHost(string sourceFileName, string? settingsXml)
#if NETFRAMEWORK
- : this(sourceFileName, runSettings, new AppDomainWrapper())
+ : this(sourceFileName, settingsXml, new AppDomainWrapper())
#endif
{
#if !WINDOWS_UWP && !NETFRAMEWORK
@@ -81,7 +81,7 @@ public TestSourceHost(string sourceFileName, IRunSettings? runSettings)
}
#if NETFRAMEWORK
- internal TestSourceHost(string sourceFileName, IRunSettings? runSettings, IAppDomain appDomain)
+ internal TestSourceHost(string sourceFileName, string? settingsXml, IAppDomain appDomain)
{
_sourceFileName = sourceFileName;
_appDomain = appDomain;
@@ -90,7 +90,7 @@ internal TestSourceHost(string sourceFileName, IRunSettings? runSettings, IAppDo
SetContext(sourceFileName);
// Set isAppDomainCreationDisabled flag
- _isAppDomainCreationDisabled = runSettings != null && MSTestAdapterSettings.IsAppDomainCreationDisabled(runSettings.SettingsXml);
+ _isAppDomainCreationDisabled = settingsXml is not null && MSTestAdapterSettings.IsAppDomainCreationDisabled(settingsXml);
}
///
@@ -179,7 +179,7 @@ public void SetupHost()
// For unknown reasons, with MSTest 3.4+ we start to see infinite cycles of assembly resolution of this dll in the new app
// domain. In older versions, this was not the case, and the callback was allowing to fully lookup and load the dll before
// triggering the next resolution.
- AppDomain.Load(typeof(EqtTrace).Assembly.GetName());
+ AppDomain.Load(GetLoadedAssembly(CoreUtilitiesAssemblyName).GetName());
// Add an assembly resolver in the child app-domain...
Type assemblyResolverType = typeof(AssemblyResolver);
@@ -352,6 +352,17 @@ internal string GetTargetFrameworkVersionString(string sourceFileName)
#endif
#if NETFRAMEWORK || (NET && !WINDOWS_UWP)
+ ///
+ /// Resolves a loaded test-platform assembly by simple name, so this file needs no compile-time reference to
+ /// the VSTest object model. These assemblies are loaded by the adapter/host before the source host runs, so
+ /// the resolved identity matches what a direct type reference would have produced.
+ ///
+ /// The simple assembly name.
+ /// The loaded assembly.
+ private static Assembly GetLoadedAssembly(string simpleName)
+ => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => string.Equals(a.GetName().Name, simpleName, StringComparison.Ordinal))
+ ?? Assembly.Load(simpleName);
+
///
/// Gets the probing paths to load the test assembly dependencies.
///
@@ -403,7 +414,7 @@ internal virtual List GetResolutionPaths(string sourceFileName, bool isP
resolutionPaths.Add(adapterDirectory);
}
- string? testPlatformDirectory = Path.GetDirectoryName(AssemblyFileLocator.TryGetLocation(typeof(AssemblyHelper).Assembly));
+ string? testPlatformDirectory = Path.GetDirectoryName(AssemblyFileLocator.TryGetLocation(GetLoadedAssembly(ObjectModelAssemblyName)));
if (!string.IsNullOrEmpty(testPlatformDirectory) && !resolutionPaths.Contains(testPlatformDirectory))
{
// Adding TestPlatform folder to resolution paths
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs
index a5751f5031..f3d0b2afbc 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/AppDomainUtilities.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if NETFRAMEWORK
@@ -6,7 +6,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
@@ -18,9 +17,22 @@ internal static class AppDomainUtilities
{
private const string ObjectModelVersionBuiltAgainst = "11.0.0.0";
+ private const string ObjectModelAssemblyName = "Microsoft.VisualStudio.TestPlatform.ObjectModel";
+
private static readonly Version DefaultVersion = new();
private static readonly Version Version45 = new("4.5");
+ ///
+ /// Resolves the loaded VSTest object-model assembly by simple name, so this AppDomain-wiring code does not
+ /// need a compile-time reference to it. By the time these methods run (test source host setup during
+ /// discovery/execution) the adapter has already loaded the object model into the current (parent) domain,
+ /// so its identity — including any binding redirect in effect — matches what a direct type reference resolved to.
+ ///
+ /// The object-model assembly.
+ private static Assembly GetObjectModelAssembly()
+ => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => string.Equals(a.GetName().Name, ObjectModelAssemblyName, StringComparison.Ordinal))
+ ?? Assembly.Load(ObjectModelAssemblyName);
+
///
/// Gets or sets the Xml Utilities instance.
///
@@ -90,7 +102,7 @@ internal static string GetTargetFrameworkVersionString(string testSourcePath)
var resolutionPaths = new List
{
- Path.GetDirectoryName(typeof(TestCase).Assembly.Location),
+ Path.GetDirectoryName(GetObjectModelAssembly().Location),
Path.GetDirectoryName(testSourcePath),
};
@@ -157,10 +169,10 @@ internal static void SetConfigurationFile(AppDomainSetup appDomainSetup, string?
try
{
// Add redirection of the built 11.0 Object Model assembly to the current version if that is not 11.0
- string currentVersionOfObjectModel = typeof(TestCase).Assembly.GetName().Version.ToString();
+ string currentVersionOfObjectModel = GetObjectModelAssembly().GetName().Version.ToString();
if (!string.Equals(currentVersionOfObjectModel, ObjectModelVersionBuiltAgainst, StringComparison.Ordinal))
{
- AssemblyName assemblyName = typeof(TestCase).Assembly.GetName();
+ AssemblyName assemblyName = GetObjectModelAssembly().GetName();
byte[] configurationBytes =
XmlUtilities.AddAssemblyRedirection(
testSourceConfigFile,
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs
index 50546a66e2..c371e13baa 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentItemUtility.cs
@@ -3,9 +3,9 @@
#if !WINDOWS_UWP && !WIN_UI
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
@@ -105,21 +105,17 @@ internal static bool IsValidDeploymentItem([NotNullWhen(true)] string? sourcePat
///
/// Returns whether there are any deployment items defined on the test.
///
- /// The test Case.
+ /// The test.
/// True if has deployment items.
- internal static bool HasDeploymentItems(TestCase testCase)
- {
- KeyValuePair[]? deploymentItems = GetDeploymentItems(testCase);
-
- return deploymentItems is { Length: > 0 };
- }
+ internal static bool HasDeploymentItems(UnitTestElement testElement)
+ => testElement.DeploymentItems is { Length: > 0 };
- internal static IList GetDeploymentItems(IEnumerable tests)
+ internal static IList GetDeploymentItems(IEnumerable tests)
{
List allDeploymentItems = [];
- foreach (TestCase test in tests)
+ foreach (UnitTestElement test in tests)
{
- KeyValuePair[]? items = GetDeploymentItems(test);
+ KeyValuePair[]? items = test.DeploymentItems;
if (items == null || items.Length == 0)
{
continue;
@@ -220,14 +216,6 @@ private static List GetDeploymentItems(IEnumerable
- /// Returns the deployment items defined on the test.
- ///
- /// The test Case.
- /// The .
- private static KeyValuePair[]? GetDeploymentItems(TestCase testCase) => testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) as
- KeyValuePair[];
-
private static KeyValuePair[]? ToKeyValuePairs(IEnumerable deploymentItemList)
{
if (!deploymentItemList.Any())
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs
index be6335ba69..47713d16af 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/DeploymentUtilityBase.cs
@@ -4,13 +4,11 @@
#if !WINDOWS_UWP && !WIN_UI
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
-
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
@@ -43,31 +41,31 @@ public DeploymentUtilityBase(DeploymentItemUtility deploymentItemUtility, Assemb
protected AssemblyUtility AssemblyUtility { get; set; }
- public bool Deploy(IEnumerable tests, string source, IRunContext? runContext, ITestExecutionRecorder testExecutionRecorder, TestRunDirectories runDirectories)
+ public bool Deploy(IEnumerable tests, string source, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, TestRunDirectories runDirectories)
{
IList deploymentItems = DeploymentItemUtility.GetDeploymentItems(tests);
// we just deploy source if there are no deployment items for current source but there are deployment items for other sources
- return Deploy(source, runContext, testExecutionRecorder, deploymentItems, runDirectories);
+ return Deploy(source, deploymentContext, messageLogger, deploymentItems, runDirectories);
}
///
/// Create deployment directories.
///
- /// The run context.
+ /// The host deployment inputs.
///
/// The path to the test assembly of the first test case. In most cases, all
/// test cases belong to the same assembly, but not guaranteed. We are using the path from
/// the first test case as a "best effort" implementation. DeploymentItem isn't correctly designed and should be deprecated in future.
///
/// TestRunDirectories instance.
- public TestRunDirectories CreateDeploymentDirectories(IRunContext? runContext, string? firstTestSource)
+ public TestRunDirectories CreateDeploymentDirectories(DeploymentContext deploymentContext, string? firstTestSource)
{
- string resultsDirectory = GetTestResultsDirectory(runContext);
+ string resultsDirectory = GetTestResultsDirectory(deploymentContext);
string rootDeploymentDirectory = GetRootDeploymentDirectory(resultsDirectory);
#if NETFRAMEWORK
- bool isAppDomainCreationDisabled = runContext?.RunSettings != null && MSTestAdapterSettings.IsAppDomainCreationDisabled(runContext.RunSettings.SettingsXml);
+ bool isAppDomainCreationDisabled = deploymentContext.RunSettingsXml is not null && MSTestAdapterSettings.IsAppDomainCreationDisabled(deploymentContext.RunSettingsXml);
#else
// AppDomains are only supported in .NET Framework.
const bool isAppDomainCreationDisabled = true;
@@ -94,10 +92,10 @@ public TestRunDirectories CreateDeploymentDirectories(IRunContext? runContext, s
///
/// Get the parent test results directory where deployment will be done.
///
- /// The run context.
+ /// The host deployment inputs.
/// The test results directory.
- public static string GetTestResultsDirectory(IRunContext? runContext) => !StringEx.IsNullOrEmpty(runContext?.TestRunDirectory)
- ? runContext.TestRunDirectory
+ public static string GetTestResultsDirectory(DeploymentContext deploymentContext) => !StringEx.IsNullOrEmpty(deploymentContext.TestRunDirectory)
+ ? deploymentContext.TestRunDirectory
: Path.GetFullPath(Path.Combine(Path.GetTempPath(), TestRunDirectories.DefaultDeploymentRootDirectory));
///
@@ -397,20 +395,20 @@ protected static bool IsOutputDirectoryValid(DeploymentItem deploymentItem, stri
///
/// Log the parameter warnings on the parameter logger.
///
- /// Execution recorder.
+ /// Message logger.
/// Warnings.
- private static void LogWarnings(ITestExecutionRecorder testExecutionRecorder, IEnumerable warnings)
+ private static void LogWarnings(IAdapterMessageLogger messageLogger, IEnumerable warnings)
{
- DebugEx.Assert(testExecutionRecorder != null, "Logger should not be null");
+ DebugEx.Assert(messageLogger != null, "Logger should not be null");
// log the warnings
foreach (string warning in warnings)
{
- testExecutionRecorder.SendMessage(TestMessageLevel.Warning, warning);
+ messageLogger.SendMessage(MessageLevel.Warning, warning);
}
}
- private bool Deploy(string source, IRunContext? runContext, ITestExecutionRecorder testExecutionRecorder, IList deploymentItems, TestRunDirectories runDirectories)
+ private bool Deploy(string source, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, IList deploymentItems, TestRunDirectories runDirectories)
{
if (PlatformServiceProvider.Instance.AdapterTraceLogger.IsInfoEnabled)
{
@@ -427,10 +425,10 @@ private bool Deploy(string source, IRunContext? runContext, ITestExecutionRecord
PlatformServiceProvider.Instance.AdapterTraceLogger.Info("MSTestExecutor: Using deployment directory {0} for source {1}.", runDirectories.OutDirectory, source);
}
- IEnumerable warnings = Deploy([.. deploymentItems], source, runDirectories.OutDirectory, GetTestResultsDirectory(runContext));
+ IEnumerable warnings = Deploy([.. deploymentItems], source, runDirectories.OutDirectory, GetTestResultsDirectory(deploymentContext));
// Log warnings
- LogWarnings(testExecutionRecorder, warnings);
+ LogWarnings(messageLogger, warnings);
return deploymentItems is { Count: > 0 };
}
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SuspendCodeCoverage.cs b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SuspendCodeCoverage.cs
new file mode 100644
index 0000000000..4a8dc95a16
--- /dev/null
+++ b/src/Adapter/MSTestAdapter.PlatformServices/Utilities/SuspendCodeCoverage.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#if NETFRAMEWORK
+namespace Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
+
+///
+/// Suspends dynamic code-coverage instrumentation of the modules that are loaded while this object is alive
+/// (between construction and disposal) by setting the well-known collector environment variable. The previous
+/// value of the environment variable is restored on dispose.
+///
+internal sealed class SuspendCodeCoverage : IDisposable
+{
+ private const string SuspendCodeCoverageEnvVarName = "__VANGUARD_SUSPEND_INSTRUMENT__";
+ private const string SuspendCodeCoverageEnvVarTrueValue = "TRUE";
+
+ private readonly string? _previousEnvironmentValue;
+
+ private bool _isDisposed;
+
+ public SuspendCodeCoverage()
+ {
+ _previousEnvironmentValue = Environment.GetEnvironmentVariable(SuspendCodeCoverageEnvVarName, EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable(SuspendCodeCoverageEnvVarName, SuspendCodeCoverageEnvVarTrueValue, EnvironmentVariableTarget.Process);
+ }
+
+ public void Dispose()
+ {
+ if (_isDisposed)
+ {
+ return;
+ }
+
+ Environment.SetEnvironmentVariable(SuspendCodeCoverageEnvVarName, _previousEnvironmentValue, EnvironmentVariableTarget.Process);
+ _isDisposed = true;
+ }
+}
+#endif
diff --git a/src/TestFramework/TestFramework/TestFramework.csproj b/src/TestFramework/TestFramework/TestFramework.csproj
index f7e4b961f1..759d85cc85 100644
--- a/src/TestFramework/TestFramework/TestFramework.csproj
+++ b/src/TestFramework/TestFramework/TestFramework.csproj
@@ -58,6 +58,7 @@
+
diff --git a/test/IntegrationTests/MSTest.IntegrationTests/TestCaseFilteringTests.cs b/test/IntegrationTests/MSTest.IntegrationTests/TestCaseFilteringTests.cs
new file mode 100644
index 0000000000..11d9b30662
--- /dev/null
+++ b/test/IntegrationTests/MSTest.IntegrationTests/TestCaseFilteringTests.cs
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Collections.Immutable;
+
+using Microsoft.MSTestV2.CLIAutomation;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+
+using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult;
+
+namespace MSTest.IntegrationTests;
+
+///
+/// Regression guard for the platform-agnostic filtering refactor (neutral ITestElementFilter).
+/// Filtering is now expressed over the neutral UnitTestElement: discovery matches
+/// element.ToTestCase(), and execution matches
+/// incomingTestCase.ToUnitTestElementWithUpdatedSource(source).ToTestCase(). These tests lock the
+/// end-to-end discover-then-run behavior for FullyQualifiedName and Id filters — the two
+/// properties most sensitive to the TestCase ↔ UnitTestElement round-trip that the refactor
+/// re-routes — so a future change cannot silently break --filter.
+///
+[TestClass]
+public class TestCaseFilteringTests : CLITestBase
+{
+ private const string TestAsset = "DiscoverInternalsProject";
+
+ // A non-parameterized test with a stable, filter-safe fully qualified name (no filter operator chars).
+ private const string TargetDisplayName = "TopLevelInternalClass_TestMethod1";
+
+ [TestMethod]
+ public void FilterByFullyQualifiedNameSelectsExactlyTheMatchingTestDuringDiscovery()
+ {
+ string assemblyPath = GetAssetFullPath(TestAsset);
+ TestCase target = GetTargetTestCase(assemblyPath);
+
+ ImmutableArray filtered = DiscoverTests(assemblyPath, $"FullyQualifiedName={target.FullyQualifiedName}");
+
+ Assert.HasCount(1, filtered);
+ Assert.AreEqual(target.FullyQualifiedName, filtered[0].FullyQualifiedName);
+ Assert.AreEqual(target.Id, filtered[0].Id);
+ }
+
+ [TestMethod]
+ public void FilterByIdSelectsExactlyTheMatchingTestDuringDiscovery()
+ {
+ string assemblyPath = GetAssetFullPath(TestAsset);
+ TestCase target = GetTargetTestCase(assemblyPath);
+
+ ImmutableArray filtered = DiscoverTests(assemblyPath, $"Id={target.Id}");
+
+ Assert.HasCount(1, filtered);
+ Assert.AreEqual(target.FullyQualifiedName, filtered[0].FullyQualifiedName);
+ Assert.AreEqual(target.Id, filtered[0].Id);
+ }
+
+ [TestMethod]
+ public async Task FilterByFullyQualifiedNameSelectsExactlyTheMatchingTestDuringExecution()
+ {
+ string assemblyPath = GetAssetFullPath(TestAsset);
+ ImmutableArray allTests = DiscoverTests(assemblyPath);
+ TestCase target = allTests.First(t => t.DisplayName == TargetDisplayName);
+
+ SimulateCrossProcessTestCases(allTests);
+
+ ImmutableArray results = await RunTestsAsync(allTests, $"FullyQualifiedName={target.FullyQualifiedName}");
+
+ // HasCount(1) is the load-bearing assertion: it fails if the filter is ignored (all tests run) or if
+ // the FQN no longer matches after reconstruction (no test runs). The outcome check proves the target
+ // was actually executed via the reconstructed element, not merely selected.
+ Assert.HasCount(1, results);
+ Assert.AreEqual(target.FullyQualifiedName, results[0].TestCase.FullyQualifiedName);
+ Assert.AreEqual(TestOutcome.Passed, results[0].Outcome);
+ }
+
+ [TestMethod]
+ public async Task FilterByIdSelectsExactlyTheMatchingTestDuringExecution()
+ {
+ string assemblyPath = GetAssetFullPath(TestAsset);
+ ImmutableArray allTests = DiscoverTests(assemblyPath);
+ Guid targetId = allTests.First(t => t.DisplayName == TargetDisplayName).Id;
+
+ SimulateCrossProcessTestCases(allTests);
+
+ ImmutableArray results = await RunTestsAsync(allTests, $"Id={targetId}");
+
+ // HasCount(1) is the load-bearing assertion: filtering by Id after the TestCase -> UnitTestElement ->
+ // TestCase reconstruction must still recompute the same Id and select exactly the target. The outcome
+ // check proves the target was actually executed via the reconstructed element.
+ Assert.HasCount(1, results);
+ Assert.AreEqual(targetId, results[0].TestCase.Id);
+ Assert.AreEqual(TestOutcome.Passed, results[0].Outcome);
+ }
+
+ private static TestCase GetTargetTestCase(string assemblyPath)
+ => DiscoverTests(assemblyPath).First(t => t.DisplayName == TargetDisplayName);
+
+ // Clears LocalExtensionData so the execution pipeline rebuilds the UnitTestElement (and its regenerated
+ // TestCase/Id) from the test-case properties, mirroring the out-of-proc VSTest path where deserialized
+ // test cases carry no LocalExtensionData. This is the round-trip the filtering refactor must preserve.
+ private static void SimulateCrossProcessTestCases(ImmutableArray testCases)
+ {
+ foreach (TestCase testCase in testCases)
+ {
+ testCase.LocalExtensionData = null;
+ }
+ }
+}
diff --git a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs
index c1d2b1fad9..40326955f6 100644
--- a/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs
+++ b/test/IntegrationTests/MSTest.IntegrationTests/Utilities/CLITestBase.discovery.cs
@@ -7,11 +7,15 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using ITestResultRecorder = Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestResultRecorder;
using TestResult = Microsoft.VisualStudio.TestPlatform.ObjectModel.TestResult;
namespace Microsoft.MSTestV2.CLIAutomation;
@@ -27,7 +31,7 @@ internal static ImmutableArray DiscoverTests(string assemblyPath, stri
string runSettingsXml = GetRunSettingsXml(string.Empty);
var context = new InternalDiscoveryContext(runSettingsXml, testCaseFilter);
- unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger, sink, context, false);
+ unitTestDiscoverer.DiscoverTestsInSource(assemblyPath, logger.ToAdapterMessageLogger(), sink.ToUnitTestElementSink(), runSettingsXml, new TestElementFilterProvider(context), false);
return sink.DiscoveredTests;
}
@@ -37,10 +41,36 @@ internal static async Task> RunTestsAsync(IEnumerable
var testExecutionManager = new TestExecutionManager();
var frameworkHandle = new InternalFrameworkHandle();
- await testExecutionManager.ExecuteTestsAsync(testCases, null, frameworkHandle, false);
+ ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings);
+ await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), new DeploymentContext(null, null), frameworkHandle.ToAdapterMessageLogger(), testResultRecorder, filterProvider: null, false);
return frameworkHandle.GetFlattenedTestResults();
}
+ internal static async Task> RunTestsAsync(IEnumerable testCases, string? testCaseFilter)
+ {
+ var testExecutionManager = new TestExecutionManager();
+ var frameworkHandle = new InternalFrameworkHandle();
+
+ string runSettingsXml = GetRunSettingsXml(string.Empty);
+ var runContext = new InternalRunContext(runSettingsXml, testCaseFilter);
+
+ ITestResultRecorder testResultRecorder = frameworkHandle.ToTestResultRecorder(Environment.MachineName, MSTestSettings.CurrentSettings);
+ await testExecutionManager.ExecuteTestsAsync(ToUnitTestElements(testCases), new DeploymentContext(runContext.TestRunDirectory, runContext.RunSettings?.SettingsXml), frameworkHandle.ToAdapterMessageLogger(), testResultRecorder, new TestElementFilterProvider(runContext), false);
+ return frameworkHandle.GetFlattenedTestResults();
+ }
+
+ // Mirrors the adapter's execution boundary (see MSTestExecutor): each host test case becomes a neutral
+ // UnitTestElement carrying its execution-context (TCM) properties and the originating test case as an
+ // opaque recording handle, so results are recorded against the exact same TestCase instances.
+ private static IEnumerable ToUnitTestElements(IEnumerable testCases)
+ => testCases.Select(static testCase =>
+ {
+ UnitTestElement element = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
+ element.ExecutionContextProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
+ element.HostRecordingHandle = testCase;
+ return element;
+ });
+
#region Helper classes
private class InternalLogger : IMessageLogger
{
@@ -73,15 +103,46 @@ public InternalDiscoveryContext(string runSettings, string? testCaseFilter)
public IRunSettings? RunSettings { get; }
public ITestCaseFilterExpression? GetTestCaseFilter(IEnumerable supportedProperties, Func propertyProvider) => _filter;
+ }
- private class InternalRunSettings : IRunSettings
- {
- public InternalRunSettings(string runSettings) => SettingsXml = runSettings;
+ private sealed class InternalRunContext : IRunContext
+ {
+ private readonly ITestCaseFilterExpression? _filter;
- public string SettingsXml { get; }
+ public InternalRunContext(string runSettings, string? testCaseFilter)
+ {
+ RunSettings = new InternalRunSettings(runSettings);
- public ISettingsProvider? GetSettings(string? settingsName) => throw new NotImplementedException();
+ if (testCaseFilter != null)
+ {
+ _filter = TestCaseFilterFactory.ParseTestFilter(testCaseFilter);
+ }
}
+
+ public IRunSettings? RunSettings { get; }
+
+ public bool KeepAlive => false;
+
+ public bool InIsolation => false;
+
+ public bool IsDataCollectionEnabled => false;
+
+ public bool IsBeingDebugged => false;
+
+ public string? TestRunDirectory => null;
+
+ public string? SolutionDirectory => null;
+
+ public ITestCaseFilterExpression? GetTestCaseFilter(IEnumerable? supportedProperties, Func propertyProvider) => _filter;
+ }
+
+ private sealed class InternalRunSettings : IRunSettings
+ {
+ public InternalRunSettings(string runSettings) => SettingsXml = runSettings;
+
+ public string SettingsXml { get; }
+
+ public ISettingsProvider? GetSettings(string? settingsName) => throw new NotImplementedException();
}
private sealed class InternalFrameworkHandle : IFrameworkHandle
diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs
index f052814199..aafd28b3a1 100644
--- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs
+++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/DesktopTestSourceHostTests.cs
@@ -5,11 +5,8 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
-using Moq;
-
using TestFramework.ForTestingMSTest;
namespace PlatformServices.Desktop.ComponentTests;
@@ -37,9 +34,10 @@ public void ParentDomainShouldHonorSearchDirectoriesSpecifiedInRunsettings()
""";
+ LoadMSTestSettings(runSettingsXml);
_testSourceHost = new TestSourceHost(
GetTestAssemblyPath("DesktopTestProjectx86Debug"),
- GetMockedIRunSettings(runSettingsXml).Object);
+ runSettingsXml);
_testSourceHost.SetupHost();
// Loading SampleProjectForAssemblyResolution.dll should not throw.
@@ -67,9 +65,10 @@ public void ChildDomainResolutionPathsShouldHaveSearchDirectoriesSpecifiedInRuns
""";
+ LoadMSTestSettings(runSettingsXml);
_testSourceHost = new TestSourceHost(
GetTestAssemblyPath("DesktopTestProjectx86Debug"),
- GetMockedIRunSettings(runSettingsXml).Object);
+ runSettingsXml);
_testSourceHost.SetupHost();
var asm = Assembly.LoadFrom(sampleProjectPath);
@@ -125,17 +124,12 @@ private static string GetTestAssemblyPath(string assetName)
return testAssetPath;
}
- private static Mock GetMockedIRunSettings(string runSettingsXml)
+ private static void LoadMSTestSettings(string runSettingsXml)
{
- var mockRunSettings = new Mock();
- mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
-
StringReader stringReader = new(runSettingsXml);
var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
MSTestSettingsProvider mstestSettingsProvider = new();
reader.ReadToFollowing("MSTestV2");
mstestSettingsProvider.Load(reader);
-
- return mockRunSettings;
}
}
diff --git a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj
index de991b9157..0f8dac1571 100644
--- a/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj
+++ b/test/IntegrationTests/PlatformServices.Desktop.IntegrationTests/PlatformServices.Desktop.IntegrationTests.csproj
@@ -15,6 +15,9 @@
+
+
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs
index 2766c1f9da..6fd99105f0 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/AssemblyEnumeratorTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.ObjectModel;
@@ -9,6 +9,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -64,7 +65,7 @@ public void ConstructorShouldPopulateSettings()
actualReader.ReadInnerXml();
});
var mockMessageLogger = new Mock();
- var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, mockMessageLogger.Object);
+ var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, mockMessageLogger.Object.ToAdapterMessageLogger());
adapterSettings.Should().NotBeNull();
// Constructor has the side effect of populating the passed settings to MSTestSettings.CurrentSettings
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs
index 73c2af6afc..a45bce3a57 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/TypeEnumeratorTests.cs
@@ -8,6 +8,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -120,7 +121,7 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByDefault()
mockRunContext.Setup(dc => dc.RunSettings).Returns(mockRunSettings.Object);
mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(mockRunContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(mockRunContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true);
TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName!);
@@ -150,7 +151,7 @@ public void GetTestsShouldReturnBaseTestMethodsFromAnotherAssemblyByConfiguratio
mockRunContext.Setup(dc => dc.RunSettings).Returns(mockRunSettings.Object);
mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(mockRunContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(mockRunContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
SetupTestClassAndTestMethods(isValidTestClass: true, isValidTestMethod: true);
TypeEnumerator typeEnumerator = GetTypeEnumeratorInstance(typeof(DummyDerivedTestClass), Assembly.GetExecutingAssembly().FullName!);
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs
index 1cb7bcd452..361eb07e69 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Discovery/UnitTestDiscovererTests.cs
@@ -80,7 +80,7 @@ public void DiscoverTestsShouldThrowOnFileNotFound()
.Returns(false);
}
- Action act = () => _unitTestDiscoverer.DiscoverTests(sources, _mockMessageLogger.Object, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, false);
+ Action act = () => _unitTestDiscoverer.DiscoverTests(sources, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object.RunSettings?.SettingsXml, new TestElementFilterProvider(_mockDiscoveryContext.Object), false);
act.Should().Throw()
.WithMessage(string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, sources[0]));
}
@@ -93,7 +93,7 @@ public void DiscoverTestsInSourceShouldThrowOnFileNotFound()
_testablePlatformServiceProvider.MockFileOperations.Setup(fo => fo.DoesFileExist(Source))
.Returns(false);
- Action act = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, false);
+ Action act = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object.RunSettings?.SettingsXml, new TestElementFilterProvider(_mockDiscoveryContext.Object), false);
act.Should().Throw()
.WithMessage(string.Format(CultureInfo.CurrentCulture, Resource.TestAssembly_FileDoesNotExist, Source));
}
@@ -122,10 +122,10 @@ public void DiscoverTestsInSourceShouldSendBackTestCasesDiscovered()
""");
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
// Act
- _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, false);
+ _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object.RunSettings?.SettingsXml, new TestElementFilterProvider(_mockDiscoveryContext.Object), false);
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.AtLeastOnce);
@@ -157,10 +157,10 @@ public void DiscoverTestsInSourceShouldThrowWhenTreatDiscoveryWarningsAsErrorsIs
""";
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(settingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
// Act
- Action action = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, false);
+ Action action = () => _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object.RunSettings?.SettingsXml, new TestElementFilterProvider(_mockDiscoveryContext.Object), false);
// Assert
action.Should().Throw()
@@ -195,11 +195,11 @@ public void DiscoverTestsInSourceShouldNotThrowWhenTreatDiscoveryWarningsAsError
""");
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
// Act & Assert
// Should not throw an exception
- _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, false);
+ _unitTestDiscoverer.DiscoverTestsInSource(Source, _mockMessageLogger.Object.ToAdapterMessageLogger(), _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), _mockDiscoveryContext.Object.RunSettings?.SettingsXml, new TestElementFilterProvider(_mockDiscoveryContext.Object), false);
// Verify warning message was sent to logger (not error)
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.AtLeastOnce);
@@ -209,7 +209,7 @@ public void DiscoverTestsInSourceShouldNotThrowWhenTreatDiscoveryWarningsAsError
public void SendTestCasesShouldNotSendAnyTestCasesIfThereAreNoTestElements()
{
// There is a null check for testElements in the code flow before this function call. So not adding a unit test for that.
- _unitTestDiscoverer.SendTestCases(new List { }, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(new List { }, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(_mockDiscoveryContext.Object), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.IsAny()), Times.Never);
@@ -221,7 +221,7 @@ public void SendTestCasesShouldSendAllTestCaseData()
var test2 = new UnitTestElement(CreateTestMethod("M2", "C", "A", displayName: null));
var testElements = new List { test1, test2 };
- _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object, _mockDiscoveryContext.Object, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(_mockDiscoveryContext.Object), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once);
@@ -240,7 +240,7 @@ public void SendTestCasesShouldSendFilteredTestCasesIfValidFilterExpression()
var testElements = new List { test1, test2 };
// Action
- _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object, discoveryContext, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once);
@@ -259,7 +259,7 @@ public void SendTestCasesShouldSendAllTestCasesIfNullFilterExpression()
var testElements = new List { test1, test2 };
// Action
- _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object, discoveryContext, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once);
@@ -278,7 +278,7 @@ public void SendTestCasesShouldSendAllTestCasesIfGetTestCaseFilterNotPresent()
var testElements = new List { test1, test2 };
// Action
- _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object, discoveryContext, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Once);
@@ -297,7 +297,7 @@ public void SendTestCasesShouldNotSendAnyTestCasesIfFilterError()
var testElements = new List { test1, test2 };
// Action
- _unitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object, discoveryContext, _mockMessageLogger.Object);
+ UnitTestDiscoverer.SendTestCases(testElements, _mockTestCaseDiscoverySink.Object.ToUnitTestElementSink(), new TestElementFilterProvider(discoveryContext), _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
_mockTestCaseDiscoverySink.Verify(ds => ds.SendTestCase(It.Is(tc => tc.FullyQualifiedName == "C.M1")), Times.Never);
@@ -325,15 +325,16 @@ public DummyNavigationData(string fileName, int minLineNumber, int maxLineNumber
{
internal override void DiscoverTestsInSource(
string source,
- IMessageLogger logger,
- ITestCaseDiscoverySink discoverySink,
- IDiscoveryContext? discoveryContext,
+ IAdapterMessageLogger logger,
+ IUnitTestElementSink discoverySink,
+ string? settingsXml,
+ ITestElementFilterProvider? filterProvider,
bool isMTP)
{
- var testCase1 = new TestCase("A", new Uri("executor://testExecutor"), source);
- var testCase2 = new TestCase("B", new Uri("executor://testExecutor"), source);
- discoverySink.SendTestCase(testCase1);
- discoverySink.SendTestCase(testCase2);
+ var testElement1 = new UnitTestElement(new TestMethod("A", "C", source, displayName: null));
+ var testElement2 = new UnitTestElement(new TestMethod("B", "C", source, displayName: null));
+ discoverySink.SendTestElement(testElement1);
+ discoverySink.SendTestElement(testElement2);
}
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs
index 64e70e9b80..f0b4f62f51 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TcmTestPropertiesProviderTests.cs
@@ -1,8 +1,9 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
@@ -15,26 +16,26 @@ public class TcmTestPropertiesProviderTests : TestContainer
{
private readonly TestProperty[] _tcmKnownProperties =
[
- EngineConstants.TestRunIdProperty,
- EngineConstants.TestPlanIdProperty,
- EngineConstants.BuildConfigurationIdProperty,
- EngineConstants.BuildDirectoryProperty,
- EngineConstants.BuildFlavorProperty,
- EngineConstants.BuildNumberProperty,
- EngineConstants.BuildPlatformProperty,
- EngineConstants.BuildUriProperty,
- EngineConstants.TfsServerCollectionUrlProperty,
- EngineConstants.TfsTeamProjectProperty,
- EngineConstants.IsInLabEnvironmentProperty,
- EngineConstants.TestCaseIdProperty,
- EngineConstants.TestConfigurationIdProperty,
- EngineConstants.TestConfigurationNameProperty,
- EngineConstants.TestPointIdProperty,
+ AdapterTestProperties.TestRunIdProperty,
+ AdapterTestProperties.TestPlanIdProperty,
+ AdapterTestProperties.BuildConfigurationIdProperty,
+ AdapterTestProperties.BuildDirectoryProperty,
+ AdapterTestProperties.BuildFlavorProperty,
+ AdapterTestProperties.BuildNumberProperty,
+ AdapterTestProperties.BuildPlatformProperty,
+ AdapterTestProperties.BuildUriProperty,
+ AdapterTestProperties.TfsServerCollectionUrlProperty,
+ AdapterTestProperties.TfsTeamProjectProperty,
+ AdapterTestProperties.IsInLabEnvironmentProperty,
+ AdapterTestProperties.TestCaseIdProperty,
+ AdapterTestProperties.TestConfigurationIdProperty,
+ AdapterTestProperties.TestConfigurationNameProperty,
+ AdapterTestProperties.TestPointIdProperty,
];
public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIsNull()
{
- IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(null);
+ IReadOnlyDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(null);
tcmProperties.Should().BeNull();
}
@@ -61,7 +62,7 @@ public void GetTcmPropertiesShouldReturnEmptyDictionaryIfTestCaseIdIsZero()
];
SetTestCaseProperties(testCase, propertiesValue);
- IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
+ IReadOnlyDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
tcmProperties.Should().BeNull();
}
@@ -88,7 +89,7 @@ public void GetTcmPropertiesShouldGetAllPropertiesFromTestCase()
];
SetTestCaseProperties(testCase, propertiesValue);
- IDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
+ IReadOnlyDictionary? tcmProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
VerifyTcmProperties(tcmProperties, testCase);
}
@@ -116,7 +117,7 @@ public void GetTcmPropertiesShouldCopyMultiplePropertiesCorrectlyFromTestCase()
345
];
SetTestCaseProperties(testCase1, propertiesValue1);
- IDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1);
+ IReadOnlyDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1);
VerifyTcmProperties(tcmProperties1, testCase1);
// Verify 2nd call.
@@ -140,7 +141,7 @@ public void GetTcmPropertiesShouldCopyMultiplePropertiesCorrectlyFromTestCase()
346
];
SetTestCaseProperties(testCase2, propertiesValue2);
- IDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2);
+ IReadOnlyDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2);
VerifyTcmProperties(tcmProperties2, testCase2);
}
@@ -167,7 +168,7 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase()
345
];
SetTestCaseProperties(testCase1, propertiesValue1);
- IDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1);
+ IReadOnlyDictionary? tcmProperties1 = TcmTestPropertiesProvider.GetTcmProperties(testCase1);
VerifyTcmProperties(tcmProperties1, testCase1);
// Verify 2nd call.
@@ -191,7 +192,7 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase()
346
];
SetTestCaseProperties(testCase2, propertiesValue2);
- IDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2);
+ IReadOnlyDictionary? tcmProperties2 = TcmTestPropertiesProvider.GetTcmProperties(testCase2);
VerifyTcmProperties(tcmProperties2, testCase2);
// Verify 3rd call.
@@ -215,7 +216,7 @@ public void GetTcmPropertiesShouldHandleDuplicateTestsProperlyFromTestCase()
347
];
SetTestCaseProperties(testCase3, propertiesValue3);
- IDictionary? tcmProperties3 = TcmTestPropertiesProvider.GetTcmProperties(testCase3);
+ IReadOnlyDictionary? tcmProperties3 = TcmTestPropertiesProvider.GetTcmProperties(testCase3);
VerifyTcmProperties(tcmProperties3, testCase3);
}
@@ -232,12 +233,12 @@ private void SetTestCaseProperties(TestCase testCase, object[] propertiesValue)
}
}
- private void VerifyTcmProperties(IDictionary? tcmProperties, TestCase testCase)
+ private void VerifyTcmProperties(IReadOnlyDictionary? tcmProperties, TestCase testCase)
{
tcmProperties.Should().NotBeNull();
foreach (TestProperty property in _tcmKnownProperties)
{
- testCase.GetPropertyValue(property)!.Equals(tcmProperties[property]).Should().BeTrue();
+ testCase.GetPropertyValue(property)!.Equals(tcmProperties[property.Id]).Should().BeTrue();
}
}
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs
index 8eb4ab7011..07f916bd40 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestCaseDiscoverySinkTests.cs
@@ -4,7 +4,7 @@
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using TestFramework.ForTestingMSTest;
@@ -18,25 +18,31 @@ public class TestCaseDiscoverySinkTests : TestContainer
public void TestCaseDiscoverySinkConstructorShouldInitializeTests()
{
- _testCaseDiscoverySink.Tests.Should().NotBeNull();
- _testCaseDiscoverySink.Tests.Count.Should().Be(0);
+ _testCaseDiscoverySink.TestElements.Should().NotBeNull();
+ _testCaseDiscoverySink.TestElements.Count.Should().Be(0);
}
- public void SendTestCaseShouldNotAddTestIfTestCaseIsNull()
+ public void SendTestElementShouldAddTheTestElement()
{
- _testCaseDiscoverySink.SendTestCase(null);
+ var testElement = new UnitTestElement(new TestMethod("M", "C", "A", displayName: null));
- _testCaseDiscoverySink.Tests.Should().NotBeNull();
- _testCaseDiscoverySink.Tests.Count.Should().Be(0);
+ _testCaseDiscoverySink.SendTestElement(testElement);
+
+ _testCaseDiscoverySink.TestElements.Should().NotBeNull();
+ _testCaseDiscoverySink.TestElements.Count.Should().Be(1);
+ _testCaseDiscoverySink.TestElements.ToArray()[0].Should().BeSameAs(testElement);
}
- public void SendTestCaseShouldAddTheTestCaseToTests()
+ public void SendTestElementShouldAddEachTestElementInOrder()
{
- TestCase tc = new("TAttribute", new Uri("executor://TestExecutorUri"), "A");
- _testCaseDiscoverySink.SendTestCase(tc);
+ var testElement1 = new UnitTestElement(new TestMethod("M1", "C", "A", displayName: null));
+ var testElement2 = new UnitTestElement(new TestMethod("M2", "C", "A", displayName: null));
+
+ _testCaseDiscoverySink.SendTestElement(testElement1);
+ _testCaseDiscoverySink.SendTestElement(testElement2);
- _testCaseDiscoverySink.Tests.Should().NotBeNull();
- _testCaseDiscoverySink.Tests.Count.Should().Be(1);
- _testCaseDiscoverySink.Tests.ToArray()[0].Should().Be(tc);
+ _testCaseDiscoverySink.TestElements.Count.Should().Be(2);
+ _testCaseDiscoverySink.TestElements.ToArray()[0].Should().BeSameAs(testElement1);
+ _testCaseDiscoverySink.TestElements.ToArray()[1].Should().BeSameAs(testElement2);
}
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs
index 18cfc4734a..464ea6f415 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestExecutionManagerTests.cs
@@ -5,8 +5,10 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.Discovery;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
@@ -33,27 +35,41 @@ public class TestExecutionManagerTests : TestContainer
private readonly TestProperty[] _tcmKnownProperties =
[
- EngineConstants.TestRunIdProperty,
- EngineConstants.TestPlanIdProperty,
- EngineConstants.BuildConfigurationIdProperty,
- EngineConstants.BuildDirectoryProperty,
- EngineConstants.BuildFlavorProperty,
- EngineConstants.BuildNumberProperty,
- EngineConstants.BuildPlatformProperty,
- EngineConstants.BuildUriProperty,
- EngineConstants.TfsServerCollectionUrlProperty,
- EngineConstants.TfsTeamProjectProperty,
- EngineConstants.IsInLabEnvironmentProperty,
- EngineConstants.TestCaseIdProperty,
- EngineConstants.TestConfigurationIdProperty,
- EngineConstants.TestConfigurationNameProperty,
- EngineConstants.TestPointIdProperty,
+ AdapterTestProperties.TestRunIdProperty,
+ AdapterTestProperties.TestPlanIdProperty,
+ AdapterTestProperties.BuildConfigurationIdProperty,
+ AdapterTestProperties.BuildDirectoryProperty,
+ AdapterTestProperties.BuildFlavorProperty,
+ AdapterTestProperties.BuildNumberProperty,
+ AdapterTestProperties.BuildPlatformProperty,
+ AdapterTestProperties.BuildUriProperty,
+ AdapterTestProperties.TfsServerCollectionUrlProperty,
+ AdapterTestProperties.TfsTeamProjectProperty,
+ AdapterTestProperties.IsInLabEnvironmentProperty,
+ AdapterTestProperties.TestCaseIdProperty,
+ AdapterTestProperties.TestConfigurationIdProperty,
+ AdapterTestProperties.TestConfigurationNameProperty,
+ AdapterTestProperties.TestPointIdProperty,
];
private TestableRunContextTestExecutionTests _runContext;
private List _callers = [];
private int _enqueuedParallelTestsCount;
+ ///
+ /// Builds the VSTest-backed result recorder the execution engine now receives at its boundary, recording into
+ /// exactly as the adapter does in production.
+ ///
+ private ITestResultRecorder TestResultRecorder
+ => _frameworkHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings);
+
+ ///
+ /// Builds the neutral run inputs (test-run directory + run settings XML) the execution engine now receives at
+ /// its boundary, extracted from exactly as the adapter does in production.
+ ///
+ private DeploymentContext CurrentDeploymentContext
+ => new(_runContext.TestRunDirectory, _runContext.RunSettings?.SettingsXml);
+
public TestExecutionManagerTests()
{
_runContext = new TestableRunContextTestExecutionTests(() => new TestableTestCaseFilterExpression(_ => true));
@@ -63,7 +79,6 @@ public TestExecutionManagerTests()
_mockTestSourceHandler = new Mock();
_testExecutionManager = new TestExecutionManager(
- EnvironmentWrapper.Instance,
task =>
{
_enqueuedParallelTestsCount++;
@@ -92,7 +107,7 @@ public async Task RunTestsForTestWithFilterErrorShouldSendZeroResults()
// Causing the FilterExpressionError
_runContext = new TestableRunContextTestExecutionTests(() => throw new TestPlatformFormatException());
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken);
// No Results
_frameworkHandle.TestCaseStartList.Count.Should().Be(0);
@@ -108,7 +123,7 @@ public async Task RunTestsForTestWithFilterShouldSendResultsForFilteredTests()
_runContext = new TestableRunContextTestExecutionTests(() => new TestableTestCaseFilterExpression(p => p.DisplayName == "PassingTest"));
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken);
// FailingTest should be skipped because it does not match the filter criteria.
List expectedTestCaseStartList = ["PassingTest"];
@@ -125,7 +140,7 @@ public void SendTestResults_WhenUnitTestResultsIsEmpty_RecordsEndWithoutResult()
TestCase testCase = GetTestCase(typeof(DummyTestClass), "PassingTest");
Microsoft.VisualStudio.TestTools.UnitTesting.TestResult[] unitTestResults = [];
- _testExecutionManager.SendTestResults(testCase, unitTestResults, DateTimeOffset.Now, DateTimeOffset.Now, _frameworkHandle);
+ _testExecutionManager.SendTestResults(ToUnitTestElement(testCase), unitTestResults, DateTimeOffset.Now, DateTimeOffset.Now, _frameworkHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings));
_frameworkHandle.TestCaseEndList.Should().Equal("PassingTest:None");
_frameworkHandle.ResultsList.Should().BeEmpty();
@@ -136,7 +151,7 @@ public async Task RunTestsForIgnoredTestShouldSendResultsMarkingIgnoredTestsAsSk
TestCase testCase = GetTestCase(typeof(DummyTestClass), "IgnoredTest");
TestCase[] tests = [testCase];
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken);
_frameworkHandle.TestCaseStartList[0].Should().Be("IgnoredTest");
_frameworkHandle.TestCaseEndList[0].Should().Be("IgnoredTest:Skipped");
@@ -149,7 +164,7 @@ public async Task RunTestsForASingleTestShouldSendSingleResult()
TestCase[] tests = [testCase];
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
List expectedTestCaseStartList = ["PassingTest"];
List expectedTestCaseEndList = ["PassingTest:Passed"];
@@ -166,7 +181,7 @@ public async Task RunTestsForMultipleTestShouldSendMultipleResults()
TestCase failingTestCase = GetTestCase(typeof(DummyTestClass), "FailingTest");
TestCase[] tests = [testCase, failingTestCase];
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken);
List expectedTestCaseStartList = ["PassingTest", "FailingTest"];
List expectedTestCaseEndList = ["PassingTest:Passed", "FailingTest:Failed"];
@@ -186,7 +201,7 @@ public async Task RunTestsForCancellationTokenCanceledSetToTrueShouldSendZeroRes
// Cancel the test run
_cancellationToken.Cancel();
- Func func = () => _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, _cancellationToken);
+ Func func = () => _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _cancellationToken);
await func.Should().ThrowAsync();
// No Results
@@ -204,13 +219,13 @@ public async Task RunTestsForTestShouldDeployBeforeExecution()
// Setup mocks.
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
testablePlatformService.MockTestDeployment.Setup(
- td => td.Deploy(tests, _runContext, _frameworkHandle)).Callback(() => SetCaller("Deploy"));
+ td => td.Deploy(It.IsAny>(), It.IsAny(), It.IsAny())).Callback(() => SetCaller("Deploy"));
await _testExecutionManager.RunTestsAsync(
- tests,
- _runContext,
- _frameworkHandle,
- new TestRunCancellationToken());
+ ToUnitTestElements(tests),
+ CurrentDeploymentContext,
+ _frameworkHandle.ToAdapterMessageLogger(),
+ TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_callers[0].Should().Be("Deploy", "Deploy should be called before execution.");
_callers[1].Should().Be("LoadAssembly", "Deploy should be called before execution.");
@@ -230,7 +245,7 @@ public async Task RunTestsForTestShouldCleanupAfterExecution()
td => td.Cleanup()).Callback(() => SetCaller("Cleanup"));
#endif
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_callers[0].Should().Be("LoadAssembly", "Cleanup should be called after execution.");
@@ -247,7 +262,7 @@ public async Task RunTestsForTestShouldNotCleanupOnTestFailure()
TestCase[] tests = [testCase, failingTestCase];
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
testablePlatformService.MockTestDeployment.Verify(td => td.Cleanup(), Times.Never);
}
@@ -262,11 +277,11 @@ public async Task RunTestsForTestShouldLoadSourceFromDeploymentDirectoryIfDeploy
// Setup mocks.
testablePlatformService.MockTestDeployment.Setup(
- td => td.Deploy(tests, _runContext, _frameworkHandle)).Returns(true);
+ td => td.Deploy(It.IsAny>(), It.IsAny(), It.IsAny())).Returns(true);
testablePlatformService.MockTestDeployment.Setup(td => td.GetDeploymentDirectory())
.Returns(@"C:\temp");
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
testablePlatformService.MockFileOperations.Verify(
fo => fo.LoadAssembly(It.Is(s => s.StartsWith("C:\\temp"))),
@@ -292,7 +307,7 @@ public async Task RunTestsForTestShouldPassInTestRunParametersInformationAsPrope
""");
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClass.TestContextProperties!.Contains(
new KeyValuePair("webAppUrl", "http://localhost")).Should().BeTrue();
@@ -314,7 +329,7 @@ public async Task RunTestsForTestShouldPassInTcmPropertiesAsPropertiesToTheTest(
""");
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
VerifyTcmProperties(DummyTestClass.TestContextProperties, testCase);
}
@@ -327,7 +342,7 @@ public async Task RunTestsForTestShouldPassInDeploymentInformationAsPropertiesTo
// Setup mocks.
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
testablePlatformService.MockSettingsProvider.Verify(sp => sp.GetProperties(It.IsAny()), Times.Once);
}
@@ -351,7 +366,7 @@ public async Task RunTestsShouldClearSessionParametersAcrossRuns()
""");
// Trigger First Run
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
// Update runsettings to have different values for similar keys
_runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns(
@@ -368,7 +383,7 @@ public async Task RunTestsShouldClearSessionParametersAcrossRuns()
""");
// Trigger another Run
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
"http://updatedLocalHost".Equals(DummyTestClass.TestContextProperties!["webAppUrl"]).Should().BeTrue();
}
@@ -382,7 +397,7 @@ private async Task RunTestsForSourceShouldRunTestsInASource()
{
var sources = new List { Assembly.GetExecutingAssembly().Location };
- await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, _mockTestSourceHandler.Object, false, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(sources, CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken);
_frameworkHandle.TestCaseStartList.Contains("PassingTest").Should().BeTrue();
_frameworkHandle.TestCaseEndList.Contains("PassingTest:Passed").Should().BeTrue();
@@ -404,7 +419,7 @@ private async Task RunTestsForSourceShouldPassInTestRunParametersInformationAsPr
""");
- await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, _mockTestSourceHandler.Object, false, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(sources, CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken);
DummyTestClass.TestContextProperties!.Contains(
new KeyValuePair("webAppUrl", "http://localhost")).Should().BeTrue();
@@ -415,7 +430,7 @@ private async Task RunTestsForSourceShouldPassInDeploymentInformationAsPropertie
{
var sources = new List { Assembly.GetExecutingAssembly().Location };
- await _testExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, _mockTestSourceHandler.Object, false, _cancellationToken);
+ await _testExecutionManager.RunTestsAsync(sources, CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken);
DummyTestClass.TestContextProperties.Should().NotBeNull();
}
@@ -426,10 +441,10 @@ public async Task RunTestsForMultipleSourcesShouldRunEachTestJustOnce()
var sources = new List { Assembly.GetExecutingAssembly().Location, Assembly.GetExecutingAssembly().Location };
TestableTestExecutionManager testableTestExecutionManager = new()
{
- ExecuteTestsWrapper = (tests, runContext, frameworkHandle, isDeploymentDone) => testsCount += tests.Count(),
+ ExecuteTestsWrapper = (tests, runContext, frameworkHandle, testResultRecorder, isDeploymentDone) => testsCount += tests.Count(),
};
- await testableTestExecutionManager.RunTestsAsync(sources, _runContext, _frameworkHandle, _mockTestSourceHandler.Object, false, _cancellationToken);
+ await testableTestExecutionManager.RunTestsAsync(sources, CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), _mockTestSourceHandler.Object, false, _cancellationToken);
testsCount.Should().Be(4);
}
@@ -461,8 +476,8 @@ public async Task RunTestsForTestShouldRunTestsInParallelWhenEnabledInRunsetting
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1);
DummyTestClassForParallelize2.ThreadIds.Count.Should().Be(1);
@@ -498,8 +513,8 @@ public async Task RunTestsForTestShouldRunTestsByMethodLevelWhenSpecified()
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_enqueuedParallelTestsCount.Should().Be(2);
@@ -535,8 +550,8 @@ public async Task RunTestsForTestShouldRunTestsWithSpecifiedNumberOfWorkers()
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1);
DummyTestClassForParallelize2.ThreadIds.Count.Should().Be(1);
@@ -570,7 +585,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromRuns
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
testablePlatformService.SetupMockReflectionOperations();
@@ -599,7 +614,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromRuns
testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny()))
.Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t));
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1);
}
@@ -629,7 +644,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromSour
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
testablePlatformService.SetupMockReflectionOperations();
@@ -658,7 +673,7 @@ public async Task RunTestsForTestShouldNotRunTestsInParallelWhenDisabledFromSour
testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny()))
.Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t));
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClassForParallelize.ThreadIds.Count.Should().Be(1);
}
@@ -675,8 +690,8 @@ public async Task RunTestsForTestShouldRunNonParallelizableTestsSeparately()
TestCase testCase3 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod3");
TestCase testCase4 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod4");
- testCase3.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true);
- testCase4.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true);
+ testCase3.SetPropertyValue(AdapterTestProperties.DoNotParallelizeProperty, true);
+ testCase4.SetPropertyValue(AdapterTestProperties.DoNotParallelizeProperty, true);
TestCase[] tests = [testCase1, testCase2, testCase3, testCase4];
_runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns(
@@ -696,8 +711,8 @@ public async Task RunTestsForTestShouldRunNonParallelizableTestsSeparately()
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_enqueuedParallelTestsCount.Should().Be(2);
DummyTestClassWithDoNotParallelizeMethods.ParallelizableTestsThreadIds.Count.Should().BeOneOf(1, 2);
@@ -730,7 +745,7 @@ public async Task RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOver
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
TestablePlatformServiceProvider testablePlatformService = SetupTestablePlatformService();
testablePlatformService.SetupMockReflectionOperations();
@@ -763,7 +778,7 @@ public async Task RunTestsForTestShouldPreferParallelSettingsFromRunSettingsOver
testablePlatformService.MockReflectionOperations.Setup(fo => fo.GetRuntimeMethods(It.IsAny()))
.Returns((Type t) => originalReflectionOperation.GetRuntimeMethods(t));
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_enqueuedParallelTestsCount.Should().Be(2);
@@ -785,7 +800,7 @@ private async Task RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState
TestCase testCase3 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod3");
TestCase testCase4 = GetTestCase(typeof(DummyTestClassWithDoNotParallelizeMethods), "TestMethod4");
- testCase4.SetPropertyValue(EngineConstants.DoNotParallelizeProperty, true);
+ testCase4.SetPropertyValue(AdapterTestProperties.DoNotParallelizeProperty, true);
TestCase[] tests = [testCase1, testCase2, testCase3, testCase4];
_runContext.MockRunSettings.Setup(rs => rs.SettingsXml).Returns(
@@ -802,8 +817,8 @@ private async Task RunTestsForTestShouldRunTestsInTheParentDomainsApartmentState
try
{
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
DummyTestClassWithDoNotParallelizeMethods.ThreadApartmentStates.Count.Should().Be(1);
DummyTestClassWithDoNotParallelizeMethods.ThreadApartmentStates.ToArray()[0].Should().Be(Thread.CurrentThread.GetApartmentState());
@@ -842,15 +857,15 @@ static TestCase[] BuildTests() =>
""");
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
var firstHandle = new TestableFrameworkHandle();
- var firstManager = new TestExecutionManager(EnvironmentWrapper.Instance, task => task());
- await firstManager.RunTestsAsync(BuildTests(), _runContext, firstHandle, new TestRunCancellationToken());
+ var firstManager = new TestExecutionManager(task => task());
+ await firstManager.RunTestsAsync(ToUnitTestElements(BuildTests()), CurrentDeploymentContext, firstHandle.ToAdapterMessageLogger(), firstHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
var secondHandle = new TestableFrameworkHandle();
- var secondManager = new TestExecutionManager(EnvironmentWrapper.Instance, task => task());
- await secondManager.RunTestsAsync(BuildTests(), _runContext, secondHandle, new TestRunCancellationToken());
+ var secondManager = new TestExecutionManager(task => task());
+ await secondManager.RunTestsAsync(ToUnitTestElements(BuildTests()), CurrentDeploymentContext, secondHandle.ToAdapterMessageLogger(), secondHandle.ToTestResultRecorder(EnvironmentWrapper.Instance.MachineName, MSTestSettings.CurrentSettings), new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
// Same seed must produce the same order across separate runs.
firstHandle.TestCaseStartList.Should().Equal(secondHandle.TestCaseStartList);
@@ -885,9 +900,9 @@ public async Task RunTestsWithoutRandomizeTestOrderShouldPreserveInputOrder()
""");
- MSTestSettings.PopulateSettings(_runContext, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_runContext.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
- await _testExecutionManager.RunTestsAsync(tests, _runContext, _frameworkHandle, new TestRunCancellationToken());
+ await _testExecutionManager.RunTestsAsync(ToUnitTestElements(tests), CurrentDeploymentContext, _frameworkHandle.ToAdapterMessageLogger(), TestResultRecorder, new TestElementFilterProvider(_runContext), new TestRunCancellationToken());
_frameworkHandle.TestCaseStartList.Should().Equal("TestA", "TestB", "TestC");
}
@@ -904,6 +919,21 @@ private static TestCase GetTestCase(Type typeOfClass, string testName)
return element.ToTestCase();
}
+ // Mirrors the conversion the adapter performs at the execution boundary (see MSTestExecutor): each host
+ // test case becomes a neutral UnitTestElement carrying the host execution-context (TCM) properties and the
+ // originating test case as an opaque recording handle, so recorded results are reported against the exact
+ // same TestCase instances the tests build (keeping the framework-handle Verify assertions meaningful).
+ private static UnitTestElement[] ToUnitTestElements(params TestCase[] tests)
+ => [.. tests.Select(ToUnitTestElement)];
+
+ private static UnitTestElement ToUnitTestElement(TestCase testCase)
+ {
+ UnitTestElement element = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
+ element.ExecutionContextProperties = TcmTestPropertiesProvider.GetTcmProperties(testCase);
+ element.HostRecordingHandle = testCase;
+ return element;
+ }
+
private TestablePlatformServiceProvider SetupTestablePlatformService()
{
var testablePlatformService = new TestablePlatformServiceProvider();
@@ -1248,11 +1278,11 @@ internal sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpressi
internal class TestableTestExecutionManager : TestExecutionManager
{
- internal Action, IRunContext?, IFrameworkHandle, bool> ExecuteTestsWrapper { get; set; } = null!;
+ internal Action, DeploymentContext, IAdapterMessageLogger, ITestResultRecorder, bool> ExecuteTestsWrapper { get; set; } = null!;
- internal override Task ExecuteTestsAsync(IEnumerable tests, IRunContext? runContext, IFrameworkHandle frameworkHandle, bool isDeploymentDone)
+ internal override Task ExecuteTestsAsync(IEnumerable tests, DeploymentContext deploymentContext, IAdapterMessageLogger messageLogger, ITestResultRecorder testResultRecorder, ITestElementFilterProvider? filterProvider, bool isDeploymentDone)
{
- ExecuteTestsWrapper?.Invoke(tests, runContext, frameworkHandle, isDeploymentDone);
+ ExecuteTestsWrapper?.Invoke(tests, deploymentContext, messageLogger, testResultRecorder, isDeploymentDone);
return Task.CompletedTask;
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs
index 990bc3ddb1..4ac15c0473 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestMethodFilterTests.cs
@@ -1,10 +1,12 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -89,7 +91,7 @@ public void PropertyProviderValueForValidTestAndSupportedPropertyNameReturnsValu
public void GetFilterExpressionForNullRunContextReturnsNull()
{
TestableTestExecutionRecorder recorder = new();
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(null, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(null, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().BeNull();
filterHasError.Should().BeFalse();
@@ -100,7 +102,7 @@ public void GetFilterExpressionForValidRunContextReturnsValidTestCaseFilterExpre
TestableTestExecutionRecorder recorder = new();
var dummyFilterExpression = new TestableTestCaseFilterExpression();
TestableRunContext runContext = new(() => dummyFilterExpression);
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(runContext, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(runContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().Be(dummyFilterExpression);
filterHasError.Should().BeFalse();
@@ -114,7 +116,7 @@ public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterReturnsVa
TestableTestExecutionRecorder recorder = new();
var dummyFilterExpression = new TestableTestCaseFilterExpression();
TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new(() => dummyFilterExpression);
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().Be(dummyFilterExpression);
filterHasError.Should().BeFalse();
@@ -127,7 +129,7 @@ public void GetFilterExpressionForDiscoveryContextWithoutGetTestCaseFilterReturn
{
TestableTestExecutionRecorder recorder = new();
TestableDiscoveryContextWithoutGetTestCaseFilter discoveryContext = new();
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().BeNull();
filterHasError.Should().BeFalse();
@@ -137,7 +139,7 @@ public void GetFilterExpressionForRunContextGetTestCaseFilterThrowingExceptionRe
{
TestableTestExecutionRecorder recorder = new();
TestableRunContext runContext = new(() => throw new TestPlatformFormatException("DummyException"));
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(runContext, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(runContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().BeNull();
filterHasError.Should().BeTrue();
@@ -152,7 +154,7 @@ public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterThrowingE
{
TestableTestExecutionRecorder recorder = new();
TestableDiscoveryContextWithGetTestCaseFilter discoveryContext = new(() => throw new TestPlatformFormatException("DummyException"));
- ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder, out bool filterHasError);
+ ITestCaseFilterExpression? filterExpression = _testMethodFilter.GetFilterExpression(discoveryContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
filterExpression.Should().BeNull();
filterHasError.Should().BeTrue();
@@ -160,6 +162,55 @@ public void GetFilterExpressionForDiscoveryContextWithGetTestCaseFilterThrowingE
recorder.TestMessageLevel.Should().Be(TestMessageLevel.Error);
}
+ public void GetTestElementFilterForNullContextReturnsNull()
+ {
+ TestableTestExecutionRecorder recorder = new();
+ ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(null, recorder.ToAdapterMessageLogger(), out bool filterHasError);
+
+ filter.Should().BeNull();
+ filterHasError.Should().BeFalse();
+ }
+
+ public void GetTestElementFilterForContextWithoutFilterReturnsNull()
+ {
+ TestableTestExecutionRecorder recorder = new();
+ TestableDiscoveryContextWithoutGetTestCaseFilter discoveryContext = new();
+ ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(discoveryContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
+
+ filter.Should().BeNull();
+ filterHasError.Should().BeFalse();
+ }
+
+ public void GetTestElementFilterMatchesElementsUsingUnderlyingFilterExpression()
+ {
+ TestableTestExecutionRecorder recorder = new();
+ MatchingTestCaseFilterExpression matchingFilterExpression = new(testCase => testCase.DisplayName == "M1");
+ TestableRunContext runContext = new(() => matchingFilterExpression);
+
+ ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(runContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
+
+ filter.Should().NotBeNull();
+ filterHasError.Should().BeFalse();
+
+ var matching = new UnitTestElement(new TestMethod("M1", "C", "A", displayName: "M1"));
+ var nonMatching = new UnitTestElement(new TestMethod("M2", "C", "A", displayName: "M2"));
+
+ filter!.Matches(matching).Should().BeTrue();
+ filter.Matches(nonMatching).Should().BeFalse();
+ }
+
+ public void GetTestElementFilterForFilterErrorReturnsNullWithFilterHasErrorTrue()
+ {
+ TestableTestExecutionRecorder recorder = new();
+ TestableRunContext runContext = new(() => throw new TestPlatformFormatException("DummyException"));
+ ITestElementFilter? filter = _testMethodFilter.GetTestElementFilter(runContext, recorder.ToAdapterMessageLogger(), out bool filterHasError);
+
+ filter.Should().BeNull();
+ filterHasError.Should().BeTrue();
+ recorder.Message.Should().Be("DummyException");
+ recorder.TestMessageLevel.Should().Be(TestMessageLevel.Error);
+ }
+
[DummyTestClass]
internal class DummyTestClassWithTestMethods
{
@@ -232,5 +283,16 @@ private sealed class TestableTestCaseFilterExpression : ITestCaseFilterExpressio
public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => throw new NotImplementedException();
}
+ private sealed class MatchingTestCaseFilterExpression : ITestCaseFilterExpression
+ {
+ private readonly Func _matchTestCase;
+
+ public MatchingTestCaseFilterExpression(Func matchTestCase) => _matchTestCase = matchTestCase;
+
+ public string TestCaseFilterValue => null!;
+
+ public bool MatchTestCase(TestCase testCase, Func propertyValueProvider) => _matchTestCase(testCase);
+ }
+
private class DummyTestClassAttribute : TestClassAttribute;
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs
index e5cb2d9162..3395bd01e3 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestPropertyAttributeTests.cs
@@ -39,7 +39,7 @@ protected override void Dispose(bool disposing)
public void GetTestMethodInfoShouldAddPropertiesFromContainingClassCorrectly()
{
- TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassBase).GetMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived))!)];
+ TestTrait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassBase).GetMethod(nameof(DummyTestClassBase.VirtualTestMethodInBaseAndDerived))!)];
traits.Length.Should().Be(3);
traits[0].Name.Should().Be("TestMethodKeyFromBase");
traits[0].Value.Should().Be("TestMethodValueFromBase");
@@ -51,7 +51,7 @@ public void GetTestMethodInfoShouldAddPropertiesFromContainingClassCorrectly()
public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClassesAndOverriddenMethodsCorrectly_OverriddenIsTestMethod()
{
- TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived))!)];
+ TestTrait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInBaseAndDerived))!)];
traits.Length.Should().Be(6);
traits[0].Name.Should().Be("DerivedMethod1Key");
traits[0].Value.Should().Be("DerivedMethod1Value");
@@ -69,7 +69,7 @@ public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClasse
public void GetTestMethodInfoShouldAddPropertiesFromContainingClassAndBaseClassesAndOverriddenMethodsCorrectly_OverriddenIsNotTestMethod()
{
- TestPlatform.ObjectModel.Trait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase))!)];
+ TestTrait[] traits = [.. ReflectHelper.GetTestPropertiesAsTraits(typeof(DummyTestClassDerived).GetMethod(nameof(DummyTestClassDerived.VirtualTestMethodInDerivedButNotTestMethodInBase))!)];
traits.Length.Should().Be(6);
traits[0].Name.Should().Be("DerivedMethod2Key");
traits[0].Value.Should().Be("DerivedMethod2Value");
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestRunInfoTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestRunInfoTests.cs
index e787279850..2dbfe026bf 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestRunInfoTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TestRunInfoTests.cs
@@ -54,7 +54,7 @@ public void CreateFromPopulatesCategoriesAndProperties()
var element = new UnitTestElement(testMethod)
{
TestCategory = ["Smoke", "Compatibility"],
- Traits = [new Trait("Owner", "alice"), new Trait("Owner", "bob"), new Trait("Priority", "1")],
+ Traits = [new TestTrait("Owner", "alice"), new TestTrait("Owner", "bob"), new TestTrait("Priority", "1")],
};
var info = TestRunInfo.CreateFrom([element]);
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs
index 97ec776f20..91615b5c93 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/TypeCacheTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
@@ -7,6 +7,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -817,7 +818,7 @@ public void GetTestMethodInfoWhenTimeoutAttributeNotSetShouldReturnTestMethodInf
""";
- var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
settings.Should().NotBeNull();
MSTestSettings.PopulateSettings(settings);
@@ -841,7 +842,7 @@ public void GetTestMethodInfoWhenTimeoutAttributeSetShouldReturnTimeoutBasedOnAt
""";
- var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
settings.Should().NotBeNull();
MSTestSettings.PopulateSettings(settings);
@@ -870,7 +871,7 @@ public void GetTestMethodInfoForInvalidGlobalTimeoutShouldReturnTestMethodInfoWi
""";
- var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
settings.Should().NotBeNull();
MSTestSettings.PopulateSettings(settings);
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs
index bfa71fc029..cb9b662fb9 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Execution/UnitTestRunnerTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
@@ -7,6 +7,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -65,7 +66,7 @@ public void ConstructorShouldPopulateSettings()
actualReader.ReadInnerXml();
});
- var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
adapterSettings.Should().NotBeNull();
var assemblyEnumerator = new UnitTestRunner(adapterSettings, []);
@@ -449,7 +450,7 @@ private MSTestSettings GetSettingsWithDebugTrace(bool captureDebugTraceValue)
actualReader.ReadInnerXml();
});
- var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
settings.Should().NotBeNull();
return settings;
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs
index 9aa147eb9e..b515c22a03 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestCaseExtensionsTests.cs
@@ -3,6 +3,7 @@
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -22,9 +23,9 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet()
};
string[] testCategories = ["DummyCategory"];
- testCase.SetPropertyValue(EngineConstants.PriorityProperty, 2);
- testCase.SetPropertyValue(EngineConstants.TestCategoryProperty, testCategories);
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName");
+ testCase.SetPropertyValue(AdapterTestProperties.PriorityProperty, 2);
+ testCase.SetPropertyValue(AdapterTestProperties.TestCategoryProperty, testCategories);
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, "DummyClassName");
UnitTestElement resultUnitTestElement = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
@@ -37,7 +38,7 @@ public void ToUnitTestElementShouldReturnUnitTestElementWithFieldsSet()
public void ToUnitTestElementForTestCaseWithNoPropertiesShouldReturnUnitTestElementWithDefaultFields()
{
TestCase testCase = new("DummyClass.DummyMethod", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!);
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName");
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, "DummyClassName");
UnitTestElement resultUnitTestElement = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
@@ -49,7 +50,7 @@ public void ToUnitTestElementForTestCaseWithNoPropertiesShouldReturnUnitTestElem
public void ToUnitTestElementShouldAddDeclaringClassNameToTestElementWhenAvailable()
{
TestCase testCase = new("DummyClass.DummyMethod", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!);
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "DummyClassName");
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, "DummyClassName");
UnitTestElement resultUnitTestElement = testCase.ToUnitTestElementWithUpdatedSource(testCase.Source);
@@ -59,7 +60,7 @@ public void ToUnitTestElementShouldAddDeclaringClassNameToTestElementWhenAvailab
public void ToUnitTestElementShouldPreferManagedTypeOverTestClassNameWhenAvailable()
{
TestCase testCase = new("SemanticClassName.DummyMethod", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!);
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, "SyntacticClassName");
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, "SyntacticClassName");
testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, "SemanticClassName");
testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, "DummyMethod");
@@ -74,7 +75,7 @@ public void ToUnitTestElementShouldParseLegacyClosedGenericFullyQualifiedNameWhe
Type closedType = typeof(DummyGenericTestClass);
string methodName = nameof(DummyGenericTestClass<>.GenericTestMethod);
TestCase testCase = new($"{closedType.FullName}.{methodName}", new("DummyUri", UriKind.Relative), Assembly.GetCallingAssembly().FullName!);
- testCase.SetPropertyValue(EngineConstants.TestClassNameProperty, closedType.FullName);
+ testCase.SetPropertyValue(AdapterTestProperties.TestClassNameProperty, closedType.FullName);
testCase.SetPropertyValue(TestCaseExtensions.ManagedTypeProperty, typeof(DummyGenericTestClass<>).FullName);
testCase.SetPropertyValue(TestCaseExtensions.ManagedMethodProperty, methodName);
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs
index 8eb46d4545..cd07a3efac 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Extensions/TestResultExtensionsTests.cs
@@ -3,6 +3,7 @@
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -153,8 +154,8 @@ public void ToUnitTestResultsForTestResultShouldSetParentInfo()
var convertedResult = result.ToTestResult(new(), default, default, string.Empty, new());
- ((Guid)convertedResult.GetPropertyValue(EngineConstants.ExecutionIdProperty)!).Should().Be(executionId);
- ((Guid)convertedResult.GetPropertyValue(EngineConstants.ParentExecIdProperty)!).Should().Be(parentExecId);
+ ((Guid)convertedResult.GetPropertyValue(AdapterTestProperties.ExecutionIdProperty)!).Should().Be(executionId);
+ ((Guid)convertedResult.GetPropertyValue(AdapterTestProperties.ParentExecIdProperty)!).Should().Be(parentExecId);
}
public void ToUnitTestResultsShouldHaveResultsFileProvidedToTestResult()
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs
index e2301915a4..b09f10bfd0 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/RunSettingsUtilitiesTests.cs
@@ -4,7 +4,7 @@
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using TestFramework.ForTestingMSTest;
@@ -131,7 +131,7 @@ public void GetTestRunParametersThrowsWhenTRPNodeHasAttributes()
""";
- new Action(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)).Should().Throw();
+ new Action(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)).Should().Throw();
}
public void GetTestRunParametersThrowsWhenTRPNodeHasNonParameterTypeChildNodes()
@@ -152,7 +152,7 @@ public void GetTestRunParametersThrowsWhenTRPNodeHasNonParameterTypeChildNodes()
""";
- new Action(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)).Should().Throw();
+ new Action(() => RunSettingsUtilities.GetTestRunParameters(settingsXml)).Should().Throw();
}
public void GetTestRunParametersIgnoresMalformedKeyValues()
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs
index e256a8db07..27ba637848 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Helpers/UnitTestOutcomeHelperTests.cs
@@ -1,10 +1,11 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -28,7 +29,7 @@ public UnitTestOutcomeHelperTests()
""";
var mockMessageLogger = new Mock();
- _adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, mockMessageLogger.Object)!;
+ _adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, mockMessageLogger.Object.ToAdapterMessageLogger())!;
}
public void UniTestHelperToTestOutcomeForUnitTestOutcomePassedShouldReturnTestOutcomePassed()
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj
index f2fa7a0cdd..b265512c2a 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestAdapter.PlatformServices.UnitTests.csproj
@@ -25,6 +25,7 @@
+
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs
index 7f3a0cae03..fb598c3b15 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/MSTestSettingsTests.cs
@@ -5,6 +5,7 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
@@ -57,7 +58,7 @@ public void MapInconclusiveToFailedIsByDefaultFalseWhenNotSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.MapInconclusiveToFailed.Should().BeFalse();
}
@@ -72,7 +73,7 @@ public void MapNotRunnableToFailedIsByDefaultTrueWhenNotSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.MapNotRunnableToFailed.Should().BeTrue();
}
@@ -88,7 +89,7 @@ public void MapInconclusiveToFailedShouldBeConsumedFromRunSettingsWhenSpecified(
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.MapInconclusiveToFailed.Should().BeTrue();
}
@@ -116,7 +117,7 @@ public void RunSettings_WithInvalidValues_GettingAWarningForEachInvalidSetting()
""";
- var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Exactly(13));
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '3' for runsettings entry 'CooperativeCancellationTimeout', setting will be ignored."), Times.Once);
@@ -146,7 +147,7 @@ public void RunSettings_WithZeroTimeout_GettingAWarningAndTimeoutIsNotSet()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(0);
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '0' for runsettings entry 'TestTimeout'. The timeout must be a strictly positive integer (in milliseconds); a value of 0 or less is not allowed. Omit the entry to use the default (no timeout). The setting will be ignored."), Times.Once);
@@ -163,7 +164,7 @@ public void MapNotRunnableToFailedShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.MapNotRunnableToFailed.Should().BeTrue();
}
@@ -178,7 +179,7 @@ public void CaptureDebugTracesShouldBeTrueByDefault()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.CaptureDebugTraces.Should().BeTrue();
}
@@ -194,7 +195,7 @@ public void CaptureDebugTracesShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.CaptureDebugTraces.Should().BeFalse();
}
@@ -210,7 +211,7 @@ public void TestTimeoutShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(4000);
}
@@ -225,7 +226,7 @@ public void TestTimeoutShouldBeSetToZeroIfNotSpecifiedInRunSettings()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(0);
}
@@ -240,7 +241,7 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeTrueByDefault()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TreatDiscoveryWarningsAsErrors.Should().BeTrue();
}
@@ -256,7 +257,7 @@ public void TreatDiscoveryWarningsAsErrorsShouldBeConsumedFromRunSettingsWhenSpe
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TreatDiscoveryWarningsAsErrors.Should().BeTrue();
}
@@ -271,7 +272,7 @@ public void ParallelizationSettingsShouldNotBeSetByDefault()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.HasValue.Should().BeFalse();
adapterSettings.ParallelizationScope.HasValue.Should().BeFalse();
@@ -287,7 +288,7 @@ public void RandomizeTestOrderShouldBeFalseByDefault()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.RandomizeTestOrder.Should().BeFalse();
adapterSettings.RandomTestOrderSeed.Should().BeNull();
@@ -305,7 +306,7 @@ public void RandomizeTestOrderShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.RandomizeTestOrder.Should().BeTrue();
adapterSettings.RandomTestOrderSeed.Should().Be(42);
@@ -322,7 +323,7 @@ public void RandomTestOrderSeedShouldAcceptNegativeValues()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.RandomTestOrderSeed.Should().Be(-7);
}
@@ -338,7 +339,7 @@ public void RandomizeTestOrderShouldWarnWhenValueIsInvalid()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.RandomizeTestOrder.Should().BeFalse();
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value 'notabool' for runsettings entry 'RandomizeTestOrder', setting will be ignored."), Times.Once);
@@ -355,7 +356,7 @@ public void RandomTestOrderSeedShouldWarnWhenValueIsInvalid()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.RandomTestOrderSeed.Should().BeNull();
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value 'notanint' for runsettings entry 'RandomTestOrderSeed', setting will be ignored."), Times.Once);
@@ -375,7 +376,7 @@ public void PopulateSettingsShouldWarnWhenRandomizeTestOrderAndOrderTestsByNameI
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings.CurrentSettings.RandomizeTestOrder.Should().BeTrue();
MSTestSettings.CurrentSettings.OrderTestsByNameInClass.Should().BeTrue();
@@ -399,7 +400,7 @@ public void PopulateSettingsShouldNotWarnWhenOnlyRandomizeTestOrderIsEnabled()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings.CurrentSettings.RandomizeTestOrder.Should().BeTrue();
MSTestSettings.CurrentSettings.OrderTestsByNameInClass.Should().BeFalse();
@@ -419,7 +420,7 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNotInt()
""";
- Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
AdapterSettingsException exception = act.Should().Throw().Which;
exception.Message.Should().Contain("Invalid value 'GoneFishing' specified for 'Workers'. The value should be a non-negative integer.");
@@ -438,7 +439,7 @@ public void GetSettingsShouldThrowIfParallelizationWorkersIsNegative()
""";
- Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
AdapterSettingsException exception = act.Should().Throw().Which;
exception.Message.Should().Contain("Invalid value '-1' specified for 'Workers'. The value should be a non-negative integer.");
}
@@ -456,7 +457,7 @@ public void ParallelizationWorkersShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.Should().Be(2);
}
@@ -474,7 +475,7 @@ public void ParallelizationWorkersShouldBeSetToProcessorCountWhenSetToZero()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.Should().Be(Environment.ProcessorCount);
}
@@ -491,7 +492,7 @@ public void ParallelizationSettingsShouldBeSetToDefaultsWhenNotSet()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.Should().Be(Environment.ProcessorCount);
adapterSettings.ParallelizationScope.Should().Be(ExecutionScope.ClassLevel);
@@ -508,7 +509,7 @@ public void ParallelizationSettingsShouldBeSetToDefaultsOnAnEmptyParalleizeSetti
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.Should().Be(Environment.ProcessorCount);
adapterSettings.ParallelizationScope.Should().Be(ExecutionScope.ClassLevel);
@@ -528,7 +529,7 @@ public void ParallelizationSettingsShouldBeConsumedFromRunSettingsWhenSpecified(
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationWorkers.Should().Be(127);
adapterSettings.ParallelizationScope.Should().Be(ExecutionScope.MethodLevel);
@@ -547,7 +548,7 @@ public void GetSettingsShouldThrowIfParallelizationScopeIsNotValid()
""";
- Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
AdapterSettingsException exception = act.Should().Throw().Which;
exception.Message.Should().Contain("Invalid value 'JustParallelizeWillYou' specified for 'Scope'. Supported scopes are ClassLevel, MethodLevel.");
}
@@ -565,7 +566,7 @@ public void ParallelizationScopeShouldBeConsumedFromRunSettingsWhenSpecified()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.ParallelizationScope.Should().Be(ExecutionScope.MethodLevel);
}
@@ -583,7 +584,7 @@ public void GetSettingsShouldThrowWhenParallelizeHasInvalidElements()
""";
- Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object);
+ Action act = () => MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger());
AdapterSettingsException exception = act.Should().Throw().Which;
exception.Message.Should().Contain("MSTestAdapter encountered an unexpected element 'Hola' in its settings 'Parallelize'. Remove this element and try again.");
}
@@ -601,7 +602,7 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettings()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(12);
}
@@ -621,7 +622,7 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsWithData()
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(12);
adapterSettings.ParallelizationWorkers.Should().Be(127);
@@ -640,7 +641,7 @@ public void GetSettingsShouldBeAbleToReadAfterParallelizationSettingsOnEmptyPara
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
adapterSettings.TestTimeout.Should().Be(12);
}
@@ -655,7 +656,7 @@ public void DisableParallelizationShouldBeFalseByDefault()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings.CurrentSettings.DisableParallelization.Should().BeFalse();
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Never);
@@ -674,7 +675,7 @@ public void DisableParallelizationShouldBeConsumedFromRunSettingsWhenSpecified()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings.CurrentSettings.DisableParallelization.Should().BeTrue();
}
@@ -692,7 +693,7 @@ public void DisableParallelization_WithInvalidValue_GettingAWarning()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '3' for runsettings entry 'DisableParallelization', setting will be ignored."), Times.Once);
}
@@ -718,7 +719,7 @@ public void GetSettingsShouldProbePlatformSpecificSettingsAlso()
actualReader.ReadInnerXml();
});
- var adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
_testablePlatformServiceProvider.MockSettingsProvider.Verify(sp => sp.Load(It.IsAny()), Times.Once);
}
@@ -744,7 +745,7 @@ public void GetSettingsShouldOnlyPassTheElementSubTreeToPlatformService()
observedXml = actualReader.ReadOuterXml();
});
- MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
(expectedRunSettingXml == observedXml).Should().BeTrue();
}
@@ -791,7 +792,7 @@ public void GetSettingsShouldBeAbleToReadSettingsAfterThePlatformServiceReadsIts
}
});
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
// Assert.
dummyPlatformSpecificSetting.Should().BeTrue();
@@ -844,7 +845,7 @@ public void GetSettingsShouldBeAbleToReadSettingsIfThePlatformServiceDoesNotUnde
}
});
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
// Assert.
dummyPlatformSpecificSetting.Should().BeTrue();
@@ -893,7 +894,7 @@ public void GetSettingsShouldOnlyReadTheAdapterSection()
}
});
- MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
// Assert.
outOfScopeCall.Should().BeFalse();
@@ -944,7 +945,7 @@ public void GetSettingsShouldWorkIfThereAreCommentsInTheXML()
}
});
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
// Assert.
dummyPlatformSpecificSetting.Should().BeTrue();
@@ -980,7 +981,7 @@ public void CurrentSettingShouldReturnCachedLoadedSettings()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
MSTestSettings adapterSettings2 = MSTestSettings.CurrentSettings;
@@ -1009,7 +1010,7 @@ public void PopulateSettingsShouldFillInSettingsFromSettingsObject()
""";
- var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object);
+ var settings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsName, _mockMessageLogger.Object.ToAdapterMessageLogger());
settings.Should().NotBeNull();
MSTestSettings.PopulateSettings(settings);
@@ -1021,7 +1022,7 @@ public void PopulateSettingsShouldFillInSettingsFromSettingsObject()
public void PopulateSettingsShouldInitializeDefaultAdapterSettingsWhenDiscoveryContextIsNull()
{
- MSTestSettings.PopulateSettings(null, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(null, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
adapterSettings.CaptureDebugTraces.Should().BeTrue();
@@ -1032,7 +1033,7 @@ public void PopulateSettingsShouldInitializeDefaultAdapterSettingsWhenDiscoveryC
public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsIsNull()
{
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
adapterSettings.CaptureDebugTraces.Should().BeTrue();
@@ -1044,7 +1045,7 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsIsNull
public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsEmpty()
{
_mockDiscoveryContext.Setup(md => md.RunSettings!.SettingsXml).Returns(string.Empty);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
adapterSettings.CaptureDebugTraces.Should().BeTrue();
@@ -1066,7 +1067,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
@@ -1091,7 +1092,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestSection()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
@@ -1117,7 +1118,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2Section()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
@@ -1146,7 +1147,7 @@ public void PopulateSettingsShouldInitializeSettingsFromMSTestV2OverMSTestV1Sect
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
MSTestSettings adapterSettings = MSTestSettings.CurrentSettings;
@@ -1185,7 +1186,7 @@ public void ConfigJson_WithInvalidValues_GettingAWarningForEachInvalidSetting()
.Returns((string key) => configDictionary.TryGetValue(key, out string? value) ? value : null);
var settings = new MSTestSettings();
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, It.IsAny()), Times.Exactly(12));
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '3' for runsettings entry 'timeout:useCooperativeCancellation', setting will be ignored."), Times.Once);
@@ -1216,7 +1217,7 @@ public void ConfigJson_WithZeroTimeout_GettingAWarningAndTimeoutIsNotSet()
.Returns((string key) => configDictionary.TryGetValue(key, out string? value) ? value : null);
var settings = new MSTestSettings();
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
settings.TestTimeout.Should().Be(0);
_mockMessageLogger.Verify(lm => lm.SendMessage(TestMessageLevel.Warning, "Invalid value '0' for runsettings entry 'timeout:test'. The timeout must be a strictly positive integer (in milliseconds); a value of 0 or less is not allowed. Omit the entry to use the default (no timeout). The setting will be ignored."), Times.Once);
@@ -1255,7 +1256,7 @@ public void ConfigJson_WithValidValues_ValuesAreSetCorrectly()
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.OrderTestsByNameInClass.Should().BeTrue();
@@ -1296,7 +1297,7 @@ public void ConfigJson_WithDeprecatedFlatOrderTestsByNameInClassKey_ValueIsSetAn
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.OrderTestsByNameInClass.Should().BeTrue();
@@ -1318,7 +1319,7 @@ public void ConfigJson_WithExecutionOrderTestsByNameInClassKey_ValueIsSetAndNoWa
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.OrderTestsByNameInClass.Should().BeTrue();
@@ -1342,7 +1343,7 @@ public void ConfigJson_WithBothOrderTestsByNameInClassKeys_NewExecutionKeyTakesP
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.OrderTestsByNameInClass.Should().BeTrue();
@@ -1370,7 +1371,7 @@ private void ConfigJson_Parllelism_Enabled_Core(bool parallelismEnabled)
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.DisableParallelization.Should().Be(!parallelismEnabled);
@@ -1390,7 +1391,7 @@ public void ConfigJson_WithValidValues_MethodScope()
var settings = new MSTestSettings();
// Act
- MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object, settings);
+ MSTestSettings.SetSettingsFromConfig(mockConfig.Object, _mockMessageLogger.Object.ToAdapterMessageLogger(), settings);
// Assert
settings.ParallelizationScope.Should().Be(ExecutionScope.MethodLevel);
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs
index f8cd836e35..8a13019086 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestElementTests.cs
@@ -3,6 +3,7 @@
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Extensions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
@@ -33,6 +34,44 @@ public void UnitTestElementConstructorShouldThrowIfTestMethodIsNull()
#endregion
+ #region Source resolution / host test case tests
+
+ public void WithUpdatedSourceShouldReturnSameInstanceWhenSourceUnchanged()
+ => _unitTestElement.WithUpdatedSource("A").Should().BeSameAs(_unitTestElement);
+
+ public void WithUpdatedSourceShouldReturnCloneWithNewSourceAndLeaveOriginalUnchanged()
+ {
+ UnitTestElement clone = _unitTestElement.WithUpdatedSource("B");
+
+ clone.Should().NotBeSameAs(_unitTestElement);
+ clone.TestMethod.AssemblyName.Should().Be("B");
+ // The original element (and its test method) must not be mutated by the clone.
+ _unitTestElement.TestMethod.AssemblyName.Should().Be("A");
+ clone.TestMethod.Should().NotBeSameAs(_testMethod);
+ }
+
+ public void GetOrCreateHostTestCaseShouldReturnHostHandleWhenPresent()
+ {
+ var hostTestCase = new TestCase("C.M", EngineConstants.ExecutorUri, "A");
+ _unitTestElement.HostRecordingHandle = hostTestCase;
+
+ _unitTestElement.GetOrCreateHostTestCase().Should().BeSameAs(hostTestCase);
+ }
+
+ public void GetOrCreateHostTestCaseShouldMaterializeAndCacheWhenNoHandle()
+ {
+ _unitTestElement.HostRecordingHandle.Should().BeNull();
+
+ TestCase materialized = _unitTestElement.GetOrCreateHostTestCase();
+
+ materialized.Should().NotBeNull();
+ // Subsequent calls (deployment, test-start, each reported result) reuse the same instance.
+ _unitTestElement.GetOrCreateHostTestCase().Should().BeSameAs(materialized);
+ _unitTestElement.HostRecordingHandle.Should().BeSameAs(materialized);
+ }
+
+ #endregion
+
#region ToTestCase tests
public void ToTestCaseShouldSetFullyQualifiedName()
@@ -75,7 +114,7 @@ public void ToTestCaseShouldSetTestClassNameProperty()
{
var testCase = _unitTestElement.ToTestCase();
- (testCase.GetPropertyValue(EngineConstants.TestClassNameProperty) as string).Should().Be("C");
+ (testCase.GetPropertyValue(AdapterTestProperties.TestClassNameProperty) as string).Should().Be("C");
}
public void ToTestCaseShouldUseFullClassNameAsManagedTypeName()
@@ -91,17 +130,17 @@ public void ToTestCaseShouldSetTestCategoryIfPresent()
_unitTestElement.TestCategory = null;
var testCase = _unitTestElement.ToTestCase();
- testCase.GetPropertyValue(EngineConstants.TestCategoryProperty).Should().BeNull();
+ testCase.GetPropertyValue(AdapterTestProperties.TestCategoryProperty).Should().BeNull();
_unitTestElement.TestCategory = [];
testCase = _unitTestElement.ToTestCase();
- testCase.GetPropertyValue(EngineConstants.TestCategoryProperty).Should().BeNull();
+ testCase.GetPropertyValue(AdapterTestProperties.TestCategoryProperty).Should().BeNull();
_unitTestElement.TestCategory = ["TC"];
testCase = _unitTestElement.ToTestCase();
- new string[] { "TC" }.SequenceEqual((string[])testCase.GetPropertyValue(EngineConstants.TestCategoryProperty)!).Should().BeTrue();
+ new string[] { "TC" }.SequenceEqual((string[])testCase.GetPropertyValue(AdapterTestProperties.TestCategoryProperty)!).Should().BeTrue();
}
public void ToTestCaseShouldSetPriorityIfPresent()
@@ -109,12 +148,12 @@ public void ToTestCaseShouldSetPriorityIfPresent()
_unitTestElement.Priority = null;
var testCase = _unitTestElement.ToTestCase();
- ((int)testCase.GetPropertyValue(EngineConstants.PriorityProperty)!).Should().Be(0);
+ ((int)testCase.GetPropertyValue(AdapterTestProperties.PriorityProperty)!).Should().Be(0);
_unitTestElement.Priority = 1;
testCase = _unitTestElement.ToTestCase();
- ((int)testCase.GetPropertyValue(EngineConstants.PriorityProperty)!).Should().Be(1);
+ ((int)testCase.GetPropertyValue(AdapterTestProperties.PriorityProperty)!).Should().Be(1);
}
public void ToTestCaseShouldSetTraitsIfPresent()
@@ -126,7 +165,7 @@ public void ToTestCaseShouldSetTraitsIfPresent()
testCase.Traits.Count().Should().Be(0);
#pragma warning restore CA1827 // Do not use Count() or LongCount() when Any() can be used
- var trait = new Trait("trait", "value");
+ var trait = new TestTrait("trait", "value");
_unitTestElement.Traits = [trait];
testCase = _unitTestElement.ToTestCase();
@@ -141,7 +180,7 @@ public void ToTestCaseShouldSetPropertiesIfPresent()
var testCase = _unitTestElement.ToTestCase();
- ((string[])testCase.GetPropertyValue(EngineConstants.WorkItemIdsProperty)!).Should().Equal(["2312", "22332"]);
+ ((string[])testCase.GetPropertyValue(AdapterTestProperties.WorkItemIdsProperty)!).Should().Equal(["2312", "22332"]);
}
#if !WINDOWS_UWP && !WIN_UI
@@ -150,17 +189,17 @@ public void ToTestCaseShouldSetDeploymentItemPropertyIfPresent()
_unitTestElement.DeploymentItems = null;
var testCase = _unitTestElement.ToTestCase();
- testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty).Should().BeNull();
+ testCase.GetPropertyValue(AdapterTestProperties.DeploymentItemsProperty).Should().BeNull();
_unitTestElement.DeploymentItems = [];
testCase = _unitTestElement.ToTestCase();
- testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty).Should().BeNull();
+ testCase.GetPropertyValue(AdapterTestProperties.DeploymentItemsProperty).Should().BeNull();
_unitTestElement.DeploymentItems = [new("s", "d")];
testCase = _unitTestElement.ToTestCase();
- _unitTestElement.DeploymentItems.SequenceEqual(testCase.GetPropertyValue(EngineConstants.DeploymentItemsProperty) as KeyValuePair[]).Should().BeTrue();
+ _unitTestElement.DeploymentItems.SequenceEqual(testCase.GetPropertyValue(AdapterTestProperties.DeploymentItemsProperty) as KeyValuePair[]).Should().BeTrue();
}
#endif
@@ -188,7 +227,7 @@ public void ToTestCase_WhenStrategyIsData_DoesNotUseDefaultTestCaseId()
static Guid GuidFromString(string data)
{
byte[] hash = TestFx.Hashing.XxHash128.Hash(Encoding.Unicode.GetBytes(data));
- return UnitTestElement.VersionedGuidFromHash(hash, hashVersion: 1);
+ return UnitTestElementExtensions.VersionedGuidFromHash(hash, hashVersion: 1);
}
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs
index d2fcb3feeb..806e904a6a 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModel/UnitTestResultTests.cs
@@ -1,10 +1,11 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
@@ -28,7 +29,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomePassedShouldReturnTestOu
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Passed, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Passed);
@@ -44,7 +45,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeFailedShouldReturnTestOu
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Failed, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
@@ -60,7 +61,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeErrorShouldReturnTestOut
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Error, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
@@ -77,7 +78,7 @@ public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturn
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.NotRunnable, adapterSettings);
resultOutcome.Should().Be(TestOutcome.None);
@@ -93,7 +94,7 @@ public void UnitTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturn
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.NotRunnable, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
@@ -109,7 +110,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeTimeoutShouldReturnTestO
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Timeout, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
@@ -125,7 +126,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeIgnoredShouldReturnTestO
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Ignored, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Skipped);
@@ -141,7 +142,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturn
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Inconclusive, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Skipped);
@@ -158,7 +159,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInconclusiveShouldReturn
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.Inconclusive, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
@@ -174,7 +175,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeNotFoundShouldReturnTest
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.NotFound, adapterSettings);
resultOutcome.Should().Be(TestOutcome.NotFound);
@@ -190,7 +191,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeInProgressShouldReturnTe
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.InProgress, adapterSettings);
resultOutcome.Should().Be(TestOutcome.None);
@@ -207,7 +208,7 @@ public void UniTestHelperToTestOutcomeForUnitTestOutcomeNotRunnableShouldReturnT
""";
- MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object)!;
+ MSTestSettings adapterSettings = MSTestSettings.GetSettings(runSettingsXml, MSTestSettings.SettingsNameAlias, _mockMessageLogger.Object.ToAdapterMessageLogger())!;
var resultOutcome = UnitTestOutcomeHelper.ToTestOutcome(UnitTestOutcome.NotRunnable, adapterSettings);
resultOutcome.Should().Be(TestOutcome.Failed);
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModelDecouplingTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModelDecouplingTests.cs
new file mode 100644
index 0000000000..16efa478fc
--- /dev/null
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/ObjectModelDecouplingTests.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using AwesomeAssertions;
+
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+
+using TestFramework.ForTestingMSTest;
+
+namespace MSTestAdapter.PlatformServices.UnitTests;
+
+public class ObjectModelDecouplingTests : TestContainer
+{
+ // Guards the platform-agnostic contract: the compiled MSTestAdapter.PlatformServices assembly must not
+ // reference the VSTest object model (Microsoft.VisualStudio.TestPlatform.ObjectModel) or any other
+ // Microsoft.*.TestPlatform.* assembly. All VSTest coupling lives in the MSTest.TestAdapter layer above it.
+ public void PlatformServicesAssemblyShouldNotReferenceAnyTestPlatformAssembly()
+ {
+ Assembly platformServices = typeof(TestSourceHandler).Assembly;
+
+ IEnumerable testPlatformReferences = platformServices
+ .GetReferencedAssemblies()
+ .Select(reference => reference.Name!)
+ .Where(name => name.IndexOf("TestPlatform", StringComparison.OrdinalIgnoreCase) >= 0);
+
+ testPlatformReferences.Should().BeEmpty(
+ "PlatformServices must stay platform-agnostic and must not reference the VSTest object model or any Microsoft.*.TestPlatform.* assembly");
+ }
+}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs
index 9e3d878be2..94e060c056 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/RunConfigurationSettingsTests.cs
@@ -4,6 +4,7 @@
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.UnitTests.TestableImplementations;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
@@ -60,7 +61,7 @@ public void ConfigurationSettingsShouldReturnDefaultSettingsIfNotSet()
public void PopulateSettingsShouldInitializeDefaultConfigurationSettingsWhenDiscoveryContextIsNull()
{
- MSTestSettings.PopulateSettings(null, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(null, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings;
settings.ExecutionApartmentState.Should().BeNull();
@@ -68,7 +69,7 @@ public void PopulateSettingsShouldInitializeDefaultConfigurationSettingsWhenDisc
public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsIsNull()
{
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings;
settings.ExecutionApartmentState.Should().BeNull();
@@ -77,7 +78,7 @@ public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsIsNull
public void PopulateSettingsShouldInitializeDefaultSettingsWhenRunSettingsXmlIsEmpty()
{
_mockDiscoveryContext.Setup(md => md.RunSettings!.SettingsXml).Returns(string.Empty);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings;
settings.ExecutionApartmentState.Should().BeNull();
@@ -96,7 +97,7 @@ public void PopulateSettingsShouldInitializeSettingsToDefaultIfNotSpecified()
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings;
settings.Should().NotBeNull();
@@ -119,7 +120,7 @@ public void PopulateSettingsShouldInitializeSettingsFromRunConfigurationSection(
_mockDiscoveryContext.Setup(dc => dc.RunSettings).Returns(_mockRunSettings.Object);
_mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
- MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object, _mockMessageLogger.Object, null);
+ MSTestSettings.PopulateSettings(_mockDiscoveryContext.Object.RunSettings?.SettingsXml, _mockMessageLogger.Object.ToAdapterMessageLogger(), null);
RunConfigurationSettings settings = MSTestSettings.RunConfigurationSettings;
settings.Should().NotBeNull();
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs
index ad2b3917b3..b154dfc5c1 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestDeploymentTests.cs
@@ -4,12 +4,11 @@
#if NETFRAMEWORK
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Moq;
@@ -45,15 +44,12 @@ public DesktopTestDeploymentTests()
public void DeployShouldDeployFilesInASourceAndReturnTrue()
{
- TestCase testCase = GetTestCase(Assembly.GetExecutingAssembly().Location);
+ UnitTestElement testCase = GetTestCase(Assembly.GetExecutingAssembly().Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
string? warning;
string sourceFile = Assembly.GetExecutingAssembly().GetName().Name + ".exe";
@@ -68,17 +64,14 @@ public void DeployShouldDeployFilesInASourceAndReturnTrue()
public void DeployShouldDeployFilesInMultipleSourcesAndReturnTrue()
{
- TestCase testCase1 = GetTestCase(Assembly.GetExecutingAssembly().Location);
+ UnitTestElement testCase1 = GetTestCase(Assembly.GetExecutingAssembly().Location);
string sourceFile2 = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "a.dll");
- TestCase testCase2 = GetTestCase(sourceFile2);
+ UnitTestElement testCase2 = GetTestCase(sourceFile2);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase1, testCase2 }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase1, testCase2 }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
string? warning;
string sourceFile1 = Assembly.GetExecutingAssembly().GetName().Name + ".exe";
@@ -100,15 +93,12 @@ public void DeployShouldDeployFilesInMultipleSourcesAndReturnTrue()
public void DeployShouldCreateDeploymentDirectories()
{
- TestCase testCase = GetTestCase(typeof(DesktopTestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(DesktopTestDeploymentTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
// matched twice because root deployment and out directory are same in net core
_mockFileUtility.Verify(fu => fu.CreateDirectoryIfNotExists(testRunDirectories.RootDeploymentDirectory), Times.Once);
@@ -118,16 +108,16 @@ public void DeployShouldCreateDeploymentDirectories()
#region private methods
- private TestCase GetTestCase(string source)
+ private static UnitTestElement GetTestCase(string source)
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), source);
+ var testCase = new UnitTestElement(new TestMethod("M", "C", source, displayName: null));
KeyValuePair[] kvpArray =
[
new KeyValuePair(
DefaultDeploymentItemPath,
DefaultDeploymentItemOutputDirectory)
];
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray);
+ testCase.DeploymentItems = kvpArray;
return testCase;
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs
index c30b8edf73..e40ca50f4d 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/DesktopTestSourceHostTests.cs
@@ -8,7 +8,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Moq;
@@ -135,10 +134,7 @@ public void SetupHostShouldHaveParentDomainsAppBaseSetToTestSourceLocation()
""";
string location = typeof(TestSourceHost).Assembly.Location;
- var mockRunSettings = new Mock();
- mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
-
- TestSourceHost sourceHost = new(location, mockRunSettings.Object);
+ TestSourceHost sourceHost = new(location, runSettingsXml);
try
{
@@ -189,10 +185,7 @@ public void NoAppDomainShouldGetCreatedWhenDisableAppDomainIsSetToTrue()
";
string location = typeof(TestSourceHost).Assembly.Location;
- var mockRunSettings = new Mock();
- mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
-
- Mock testSourceHost = new(location, mockRunSettings.Object) { CallBase = true };
+ Mock testSourceHost = new(location, runSettingsXml) { CallBase = true };
try
{
@@ -220,10 +213,7 @@ public void AppDomainShouldGetCreatedWhenDisableAppDomainIsSetToFalse()
""";
string location = typeof(TestSourceHost).Assembly.Location;
- var mockRunSettings = new Mock();
- mockRunSettings.Setup(rs => rs.SettingsXml).Returns(runSettingsXml);
-
- Mock testSourceHost = new(location, mockRunSettings.Object) { CallBase = true };
+ Mock testSourceHost = new(location, runSettingsXml) { CallBase = true };
try
{
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs
index eaa1380860..89b89ef3aa 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/MSTestAdapterSettingsTests.cs
@@ -3,10 +3,10 @@
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Helpers;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Moq;
@@ -202,7 +202,7 @@ public void ToSettingsShouldNotThrowExceptionWhenRunSettingsXmlUnderTagMSTestV2I
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
MSTestAdapterSettings.ToSettings(reader);
@@ -222,12 +222,12 @@ public void ToSettingsShouldThrowExceptionWhenRunSettingsXmlIsWrong()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
void ShouldThrowException() => MSTestAdapterSettings.ToSettings(reader);
- new Action(ShouldThrowException).Should().Throw();
+ new Action(ShouldThrowException).Should().Throw();
}
#endregion
@@ -242,7 +242,7 @@ public void DeploymentEnabledIsByDefaultTrueWhenNotSpecified()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
var adapterSettings = MSTestAdapterSettings.ToSettings(reader);
adapterSettings.DeploymentEnabled.Should().BeTrue();
@@ -257,7 +257,7 @@ public void DeploymentEnabledShouldBeConsumedFromRunSettingsWhenSpecified()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
var adapterSettings = MSTestAdapterSettings.ToSettings(reader);
adapterSettings.DeploymentEnabled.Should().BeFalse();
@@ -275,7 +275,7 @@ public void DeployTestSourceDependenciesIsEnabledByDefault()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
var adapterSettings = MSTestAdapterSettings.ToSettings(reader);
adapterSettings.DeployTestSourceDependencies.Should().BeTrue();
@@ -290,7 +290,7 @@ public void DeployTestSourceDependenciesWhenFalse()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
var adapterSettings = MSTestAdapterSettings.ToSettings(reader);
adapterSettings.DeployTestSourceDependencies.Should().BeFalse();
@@ -305,7 +305,7 @@ public void DeployTestSourceDependenciesWhenTrue()
""";
StringReader stringReader = new(runSettingsXml);
- var reader = XmlReader.Create(stringReader, XmlRunSettingsUtilities.ReaderSettings);
+ var reader = XmlReader.Create(stringReader, RunSettingsUtilities.ReaderSettings);
reader.Read();
var adapterSettings = MSTestAdapterSettings.ToSettings(reader);
adapterSettings.DeployTestSourceDependencies.Should().BeTrue();
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs
index 568ee31d94..f87ab95723 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/ReflectionOperationsTests.cs
@@ -4,6 +4,7 @@
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Helpers;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
@@ -237,9 +238,9 @@ public void GetTestPropertiesAsTraitsShouldIncludeTestPropertiesAtMethodLevel()
{
_attributeMockingHelper.AddCustomAttribute(typeof(TestPropertyAttribute), [new TestPropertyAttribute("MethodKey", "MethodValue")], MemberTypes.Method);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
- actual.Should().BeEquivalentTo(new[] { new Trait("MethodKey", "MethodValue") }, options => options.WithStrictOrdering());
+ actual.Should().BeEquivalentTo(new[] { new TestTrait("MethodKey", "MethodValue") }, options => options.WithStrictOrdering());
}
///
@@ -249,9 +250,9 @@ public void GetTestPropertiesAsTraitsShouldIncludeTestPropertiesAtClassLevel()
{
_attributeMockingHelper.AddCustomAttribute(typeof(TestPropertyAttribute), [new TestPropertyAttribute("ClassKey", "ClassValue")], MemberTypes.TypeInfo);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
- actual.Should().BeEquivalentTo(new[] { new Trait("ClassKey", "ClassValue") }, options => options.WithStrictOrdering());
+ actual.Should().BeEquivalentTo(new[] { new TestTrait("ClassKey", "ClassValue") }, options => options.WithStrictOrdering());
}
///
@@ -262,9 +263,9 @@ public void GetTestPropertiesAsTraitsShouldIncludeTestPropertiesAtAllLevels()
_attributeMockingHelper.AddCustomAttribute(typeof(TestPropertyAttribute), [new TestPropertyAttribute("MethodKey", "MethodValue")], MemberTypes.Method);
_attributeMockingHelper.AddCustomAttribute(typeof(TestPropertyAttribute), [new TestPropertyAttribute("ClassKey", "ClassValue")], MemberTypes.TypeInfo);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
- actual.Should().BeEquivalentTo(new[] { new Trait("MethodKey", "MethodValue"), new Trait("ClassKey", "ClassValue") }, options => options.WithStrictOrdering());
+ actual.Should().BeEquivalentTo(new[] { new TestTrait("MethodKey", "MethodValue"), new TestTrait("ClassKey", "ClassValue") }, options => options.WithStrictOrdering());
}
///
@@ -274,9 +275,9 @@ public void GetTestPropertiesAsTraitsShouldIncludeMultipleTestPropertiesAtMethod
{
_attributeMockingHelper.AddCustomAttribute(typeof(TestPropertyAttribute), [new TestPropertyAttribute("Key1", "Value1"), new TestPropertyAttribute("Key2", "Value2")], MemberTypes.Method);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
- actual.Should().BeEquivalentTo(new[] { new Trait("Key1", "Value1"), new Trait("Key2", "Value2") }, options => options.WithStrictOrdering());
+ actual.Should().BeEquivalentTo(new[] { new TestTrait("Key1", "Value1"), new TestTrait("Key2", "Value2") }, options => options.WithStrictOrdering());
}
///
@@ -286,7 +287,7 @@ public void GetTestPropertiesAsTraitsShouldReturnEmptyWhenNoTestProperties()
{
_attributeMockingHelper.AddCustomAttribute(typeof(TestCategoryBaseAttribute), [new TestCategoryAttribute("SomeCategory")], MemberTypes.Method);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(_method.Object);
actual.Should().BeEmpty();
}
@@ -577,7 +578,7 @@ public void GetTestPropertiesAsTraitsShouldNotIncludeClassLevelAttributesWhenRef
[new TestPropertyAttribute("ClassKey", "ClassValue")],
MemberTypes.TypeInfo);
- Trait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(methodWithNullReflectedType.Object);
+ TestTrait[] actual = _reflectionOperations.GetTestPropertiesAsTraits(methodWithNullReflectedType.Object);
actual.Should().BeEmpty();
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs
index 6062851605..3a6bf32c98 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestContextImplementationTests.cs
@@ -9,8 +9,8 @@
using AwesomeAssertions;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
+using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Moq;
@@ -28,7 +28,7 @@ public class TestContextImplementationTests : TestContainer
private TestContextImplementation _testContextImplementation = null!;
- private TestContextImplementation CreateTestContextImplementation(IMessageLogger? messageLogger = null)
+ private TestContextImplementation CreateTestContextImplementation(IAdapterMessageLogger? messageLogger = null)
=> new(_testMethod.Object, null, _properties, messageLogger, null);
public void TestContextConstructorShouldInitializeProperties()
@@ -346,19 +346,19 @@ public void GetResultFilesShouldReturnListOfAddedResultFiles()
public void DisplayMessageShouldForwardToIMessageLogger()
{
- var messageLoggerMock = new Mock(MockBehavior.Strict);
+ var messageLoggerMock = new Mock(MockBehavior.Strict);
messageLoggerMock
- .Setup(l => l.SendMessage(It.IsAny(), It.IsAny()));
+ .Setup(l => l.SendMessage(It.IsAny(), It.IsAny()));
_testContextImplementation = CreateTestContextImplementation(messageLoggerMock.Object);
_testContextImplementation.DisplayMessage(MessageLevel.Informational, "InfoMessage");
_testContextImplementation.DisplayMessage(MessageLevel.Warning, "WarningMessage");
_testContextImplementation.DisplayMessage(MessageLevel.Error, "ErrorMessage");
- messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Informational, "InfoMessage"), Times.Once);
- messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Warning, "WarningMessage"), Times.Once);
- messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Error, "ErrorMessage"), Times.Once);
+ messageLoggerMock.Verify(x => x.SendMessage(MessageLevel.Informational, "InfoMessage"), Times.Once);
+ messageLoggerMock.Verify(x => x.SendMessage(MessageLevel.Warning, "WarningMessage"), Times.Once);
+ messageLoggerMock.Verify(x => x.SendMessage(MessageLevel.Error, "ErrorMessage"), Times.Once);
}
public void GetAndClearOutput_ShouldReturnContentThenClearBuffer()
@@ -399,7 +399,7 @@ public void GetAndClearTrace_ShouldReturnContentThenClearBuffer()
public void WritesFromBackgroundThreadShouldNotThrow()
{
- TestContextImplementation testContextImplementation = CreateTestContextImplementation(new Mock().Object);
+ TestContextImplementation testContextImplementation = CreateTestContextImplementation(new Mock().Object);
var t = new Thread(() =>
{
for (int i = 0; i < 100; i++)
@@ -704,12 +704,12 @@ public void CloneForDataDrivenIterationShouldCopyTestRunCount()
public void CloneForDataDrivenIterationShouldShareMessageLogger()
{
- var messageLoggerMock = new Mock();
+ var messageLoggerMock = new Mock();
_testContextImplementation = CreateTestContextImplementation(messageLoggerMock.Object);
TestContextImplementation clone = _testContextImplementation.CloneForDataDrivenIteration();
clone.DisplayMessage(MessageLevel.Informational, "from-clone");
- messageLoggerMock.Verify(x => x.SendMessage(TestMessageLevel.Informational, "from-clone"), Times.Once);
+ messageLoggerMock.Verify(x => x.SendMessage(MessageLevel.Informational, "from-clone"), Times.Once);
}
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs
index c88f094732..97f30a2bd2 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Services/TestDeploymentTests.cs
@@ -4,12 +4,11 @@
#if !WINDOWS_UWP && !WIN_UI
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities;
using Moq;
@@ -111,15 +110,12 @@ public void CleanupShouldNotDeleteDirectoriesIfRunSettingsSpecifiesSo()
MSTestSettingsProvider mstestSettingsProvider = new();
mstestSettingsProvider.Load(reader);
- TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
testDeployment.Cleanup();
@@ -128,15 +124,12 @@ public void CleanupShouldNotDeleteDirectoriesIfRunSettingsSpecifiesSo()
public void CleanupShouldDeleteRootDeploymentDirectory()
{
- TestCase testCase = GetTestCase(typeof(DeploymentUtilityTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(DeploymentUtilityTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
// Act.
testDeployment.Cleanup();
@@ -152,15 +145,12 @@ public void CleanupShouldDeleteRootDeploymentDirectory()
public void GetDeploymentDirectoryShouldReturnDeploymentOutputDirectory()
{
- TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
// Act.
testDeployment.GetDeploymentDirectory().Should().Be(testRunDirectories.OutDirectory);
@@ -172,14 +162,14 @@ public void GetDeploymentDirectoryShouldReturnDeploymentOutputDirectory()
public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseButHasDeploymentItems()
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), "path/to/asm.dll");
+ UnitTestElement testCase = CreateTestElement("path/to/asm.dll");
KeyValuePair[] kvpArray =
[
new KeyValuePair(
DefaultDeploymentItemPath,
DefaultDeploymentItemOutputDirectory)
];
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray);
+ testCase.DeploymentItems = kvpArray;
var testDeployment = new TestDeployment(
new DeploymentItemUtility(_mockReflectionOperations.Object),
@@ -194,7 +184,7 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseButHasDeployme
mstestSettingsProvider.Load(reader);
// Deployment should not happen
- testDeployment.Deploy(new List { testCase }, null, null!).Should().BeFalse();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(null, null), new Mock().Object).Should().BeFalse();
// Deployment directories should not be created
testDeployment.GetDeploymentDirectory().Should().BeNull();
@@ -202,8 +192,8 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseButHasDeployme
public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseAndHasNoDeploymentItems()
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), "path/to/asm.dll");
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, null);
+ UnitTestElement testCase = CreateTestElement("path/to/asm.dll");
+ testCase.DeploymentItems = null;
var testDeployment = new TestDeployment(
new DeploymentItemUtility(_mockReflectionOperations.Object),
new DeploymentUtility(),
@@ -217,7 +207,7 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseAndHasNoDeploy
mstestSettingsProvider.Load(reader);
// Deployment should not happen
- testDeployment.Deploy(new List { testCase }, null, null!).Should().BeFalse();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(null, null), new Mock().Object).Should().BeFalse();
// Deployment directories should get created on .NET Framework for compat, but not on .NET Core
#if NETFRAMEWORK
@@ -229,8 +219,8 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToFalseAndHasNoDeploy
public void DeployShouldReturnFalseWhenDeploymentEnabledSetToTrueButHasNoDeploymentItems()
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), "path/to/asm.dll");
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, null);
+ UnitTestElement testCase = CreateTestElement("path/to/asm.dll");
+ testCase.DeploymentItems = null;
var testDeployment = new TestDeployment(
new DeploymentItemUtility(_mockReflectionOperations.Object),
new DeploymentUtility(),
@@ -244,7 +234,7 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToTrueButHasNoDeploym
mstestSettingsProvider.Load(reader);
// Deployment should not happen
- testDeployment.Deploy(new List { testCase }, null, null!).Should().BeFalse();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(null, null), new Mock().Object).Should().BeFalse();
// Deployment directories should get created on .NET Framework for compat, but not on .NET Core
#if NETFRAMEWORK
@@ -257,14 +247,14 @@ public void DeployShouldReturnFalseWhenDeploymentEnabledSetToTrueButHasNoDeploym
// TODO: This test has to have mocks (tracked by https://github.com/microsoft/testfx/issues/8086). It actually deploys stuff and we cannot assume that all the dependencies get copied over to bin\debug.
internal void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeploymentItems()
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), typeof(TestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = CreateTestElement(typeof(TestDeploymentTests).Assembly.Location);
KeyValuePair[] kvpArray =
[
new KeyValuePair(
DefaultDeploymentItemPath,
DefaultDeploymentItemOutputDirectory)
];
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray);
+ testCase.DeploymentItems = kvpArray;
var testDeployment = new TestDeployment(
new DeploymentItemUtility(_mockReflectionOperations.Object),
new DeploymentUtility(),
@@ -278,7 +268,7 @@ internal void DeployShouldReturnTrueWhenDeploymentEnabledSetToTrueAndHasDeployme
mstestSettingsProvider.Load(reader);
// Deployment should happen
- testDeployment.Deploy(new List { testCase }, null, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(null, null), new Mock().Object).Should().BeTrue();
// Deployment directories should get created
testDeployment.GetDeploymentDirectory().Should().NotBeNull();
@@ -309,15 +299,12 @@ public void GetDeploymentInformationShouldReturnAppBaseDirectoryIfRunDirectoryIs
public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceIsNull()
{
// Arrange.
- TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
// Act.
IDictionary properties = TestDeployment.GetDeploymentInformation(null);
@@ -339,15 +326,12 @@ public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceI
public void GetDeploymentInformationShouldReturnRunDirectoryInformationIfSourceIsNotNull()
{
// Arrange.
- TestCase testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
+ UnitTestElement testCase = GetTestCase(typeof(TestDeploymentTests).Assembly.Location);
// Setup mocks.
TestDeployment testDeployment = CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories);
- var mockRunContext = new Mock();
- mockRunContext.Setup(rc => rc.TestRunDirectory).Returns(testRunDirectories.RootDeploymentDirectory);
-
- testDeployment.Deploy(new List { testCase }, mockRunContext.Object, new Mock().Object).Should().BeTrue();
+ testDeployment.Deploy(new List { testCase }, new DeploymentContext(testRunDirectories.RootDeploymentDirectory, null), new Mock().Object).Should().BeTrue();
// Act.
IDictionary properties = TestDeployment.GetDeploymentInformation(typeof(TestDeploymentTests).Assembly.Location);
@@ -383,20 +367,23 @@ private void SetupDeploymentItems(ICustomAttributeProvider attributeProvider, Ke
ru => ru.GetAttributes(attributeProvider)).Returns(deploymentItemAttributes);
}
- private static TestCase GetTestCase(string source)
+ private static UnitTestElement GetTestCase(string source)
{
- var testCase = new TestCase("A.C.M", new Uri("executor://testExecutor"), source);
+ UnitTestElement testCase = CreateTestElement(source);
KeyValuePair[] kvpArray =
[
new KeyValuePair(
DefaultDeploymentItemPath,
DefaultDeploymentItemOutputDirectory)
];
- testCase.SetPropertyValue(DeploymentItemUtilityTests.DeploymentItemsProperty, kvpArray);
+ testCase.DeploymentItems = kvpArray;
return testCase;
}
+ private static UnitTestElement CreateTestElement(string source)
+ => new(new TestMethod("M", "C", source, displayName: null));
+
private TestDeployment CreateAndSetupDeploymentRelatedUtilities(out TestRunDirectories testRunDirectories)
{
string currentExecutingFolder = Path.GetDirectoryName(typeof(TestDeploymentTests).Assembly.Location)!;
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs
index 82126a12c3..baf709a8e7 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/TestableImplementations/TestablePlatformServiceProvider.cs
@@ -4,7 +4,6 @@
using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Moq;
@@ -41,7 +40,12 @@ internal class TestablePlatformServiceProvider : IPlatformServiceProvider
public IFileOperations FileOperations => MockFileOperations.Object;
- public ITraceLogger AdapterTraceLogger { get => MockTraceLogger.Object; set => throw new NotSupportedException(); }
+ // The getter always returns the mock so tests can verify trace logging. The setter is a no-op:
+ // MSTest.TestAdapter's [ModuleInitializer] (MSTestExecutor.SetPlatformLogger) legitimately assigns
+ // PlatformServiceProvider.Instance.AdapterTraceLogger when that module is first touched (e.g. via the
+ // ToAdapterMessageLogger bridge), so this double must tolerate the assignment exactly as the real
+ // PlatformServiceProvider does, rather than throwing.
+ public ITraceLogger AdapterTraceLogger { get => MockTraceLogger.Object; set => _ = value; }
#if !WINDOWS_UWP && !WIN_UI
public ITestDeployment TestDeployment => MockTestDeployment.Object;
@@ -69,7 +73,7 @@ public IReflectionOperations ReflectionOperations
public int GetTestContextCallCount { get; private set; }
- public ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IMessageLogger messageLogger, UnitTestOutcome outcome)
+ public ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFullName, IDictionary properties, IAdapterMessageLogger messageLogger, UnitTestOutcome outcome)
{
GetTestContextCallCount++;
var testContextImpl = new TestContextImplementation(testMethod, testClassFullName, properties, messageLogger, testRunCancellationToken: null);
@@ -77,7 +81,7 @@ public ITestContext GetTestContext(ITestMethod? testMethod, string? testClassFul
return testContextImpl;
}
- public ITestSourceHost CreateTestSourceHost(string source, TestPlatform.ObjectModel.Adapter.IRunSettings? runSettings) => MockTestSourceHost.Object;
+ public ITestSourceHost CreateTestSourceHost(string source, string? settingsXml) => MockTestSourceHost.Object;
public void SetupMockReflectionOperations()
{
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentItemUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentItemUtilityTests.cs
index c1a11d0c2b..10086d9e2d 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentItemUtilityTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentItemUtilityTests.cs
@@ -4,12 +4,12 @@
#if !WINDOWS_UWP && !WIN_UI
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Moq;
@@ -19,13 +19,6 @@ namespace MSTestAdapter.PlatformServices.UnitTests.Utilities;
public class DeploymentItemUtilityTests : TestContainer
{
- internal static readonly TestProperty DeploymentItemsProperty = TestProperty.Register(
- "MSTestDiscoverer.DeploymentItems",
- "DeploymentItems",
- typeof(KeyValuePair[]),
- TestPropertyAttributes.Hidden,
- typeof(TestCase));
-
private readonly Mock _mockReflectionOperations;
private readonly DeploymentItemUtility _deploymentItemUtility;
private readonly ICollection _warnings;
@@ -391,22 +384,24 @@ public void IsValidDeploymentItemShouldReturnTrueForAValidDeploymentItem()
public void HasDeployItemsShouldReturnFalseForNoDeploymentItems()
{
- TestCase testCase = new("A.C.M", new Uri("executor://testExecutor"), "A");
- testCase.SetPropertyValue(DeploymentItemsProperty, null);
+ var testCase = new UnitTestElement(new TestMethod("M", "C", "A", displayName: null))
+ {
+ DeploymentItems = null,
+ };
DeploymentItemUtility.HasDeploymentItems(testCase).Should().BeFalse();
}
public void HasDeployItemsShouldReturnTrueWhenDeploymentItemsArePresent()
{
- TestCase testCase = new("A.C.M", new Uri("executor://testExecutor"), "A");
+ var testCase = new UnitTestElement(new TestMethod("M", "C", "A", displayName: null));
KeyValuePair[] kvpArray =
[
new KeyValuePair(
_defaultDeploymentItemPath,
_defaultDeploymentItemOutputDirectory)
];
- testCase.SetPropertyValue(DeploymentItemsProperty, kvpArray);
+ testCase.DeploymentItems = kvpArray;
DeploymentItemUtility.HasDeploymentItems(testCase).Should().BeTrue();
}
diff --git a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs
index c66fcba062..4f7d0d9e59 100644
--- a/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs
+++ b/test/UnitTests/MSTestAdapter.PlatformServices.UnitTests/Utilities/DeploymentUtilityTests.cs
@@ -4,14 +4,12 @@
#if !WINDOWS_UWP && !WIN_UI
using AwesomeAssertions;
+using Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Deployment;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Resources;
using Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Utilities;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using Moq;
@@ -29,8 +27,7 @@ public class DeploymentUtilityTests : TestContainer
private readonly Mock _mockReflectionOperations;
private readonly Mock _mockFileUtility;
private readonly Mock _mockAssemblyUtility;
- private readonly Mock _mockRunContext;
- private readonly Mock _mockTestExecutionRecorder;
+ private readonly Mock