Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5b07073
Abstract VSTest message logging in PlatformServices (Phase 0/1)
Jul 1, 2026
40cabbb
Abstract VSTest result reporting in PlatformServices (Phase 5)
Evangelink Jul 1, 2026
f05621d
Address review comments on Phase 1 logging PR
Jul 2, 2026
3f85af0
Clarify ITestResultRecorder remarks re: platform boundary
Evangelink Jul 2, 2026
7fd1fcc
Merge remote-tracking branch 'origin/dev/amauryleve/abstract-vstest-r…
Jul 2, 2026
f1e1f36
Abstract VSTest test filtering in PlatformServices (Phase 2 of platfo…
Evangelink Jul 3, 2026
f846008
Abstract VSTest discovery sink in PlatformServices (Phase 3 of platfo…
Evangelink Jul 3, 2026
4f02b6e
Abstract VSTest execution input in PlatformServices (Phase 4 of platf…
Evangelink Jul 3, 2026
98ec16e
Neutralize deployment input in PlatformServices (Phase 6a of platform…
Evangelink Jul 3, 2026
371124a
Neutralize test result recording in PlatformServices (Phase 6b) (#9579)
Evangelink Jul 3, 2026
274e479
Neutralize test message logging in PlatformServices execution engine …
Evangelink Jul 3, 2026
1a37ffa
Neutralize run-settings input in PlatformServices host layer (Phase 6c2)
Jul 3, 2026
dc4c4d8
Move test-case filter parsing to adapter boundary (Phase 6d-1)
Jul 3, 2026
7caa937
Remove VSTest run/discovery context from PlatformServices (Phase 6d-2)
Jul 5, 2026
668335a
Remove IFrameworkHandle from the PlatformServices execution engine (P…
Jul 5, 2026
1e61c13
Relocate VSTest logger/sink bridges to the adapter (Phase 6e-2)
Evangelink Jul 5, 2026
ab2cc59
Neutralize the trait type on UnitTestElement (Phase 6e-3a)
Evangelink Jul 5, 2026
1ffa5fa
Relocate the UnitTestElement<->TestCase conversion to the adapter (Ph…
Evangelink Jul 5, 2026
fbcafef
Neutralize runsettings parsing helpers (Phase 6e-4a)
Evangelink Jul 5, 2026
24e471c
Resolve the object-model assembly by name in AppDomain wiring (Phase …
Evangelink Jul 5, 2026
8261580
Resolve source-host anchor assemblies by name (Phase 6e-4c1)
Evangelink Jul 5, 2026
e6dc77c
Reimplement the source assembly-reference check without ObjectModel (…
Evangelink Jul 5, 2026
9197a3f
Vendor a neutral SuspendCodeCoverage in PlatformServices (Phase 6e-4c3)
Evangelink Jul 5, 2026
16552c6
Drop the ObjectModel package reference from PlatformServices (Phase 7)
Evangelink Jul 5, 2026
ce6569b
Handle null/empty public key tokens in ArePublicKeyTokensEqual
Evangelink Jul 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/Adapter/MSTest.TestAdapter/AdapterTestProperties.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// The VSTest object-model <see cref="TestProperty"/> 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.
/// </summary>
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<string, string>[]), 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

/// <summary>
/// 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.
/// </summary>
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
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// 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 <c>TestContext</c>.
/// </summary>
/// <remarks>
/// 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
/// <c>TestProperty</c> object model. It is expected to move entirely into the adapter layer once the platform
/// services no longer reference the VSTest object model.
/// </remarks>
internal static class TcmTestPropertiesProvider
{
/// <summary>
/// Gets the host execution context properties from a test case, keyed by the host property identifier.
/// </summary>
/// <param name="testCase">Test case.</param>
/// <returns>The properties, or <see langword="null"/> when the test case is null or carries no test case id.</returns>
public static IReadOnlyDictionary<string, object?>? GetTcmProperties(TestPlatformObjectModel.TestCase? testCase)
{
// Return empty properties when testCase is null or when test case id is zero.
if (testCase == null ||
testCase.GetPropertyValue<int>(AdapterTestProperties.TestCaseIdProperty, default) == 0)
{
return null;
}

var tcmProperties = new Dictionary<string, object?>(capacity: 15)
{
// Step 1: Add common properties.
[AdapterTestProperties.TestRunIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.TestRunIdProperty, default),
[AdapterTestProperties.TestPlanIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.TestPlanIdProperty, default),
[AdapterTestProperties.BuildConfigurationIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.BuildConfigurationIdProperty, default),
[AdapterTestProperties.BuildDirectoryProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.BuildDirectoryProperty, default),
[AdapterTestProperties.BuildFlavorProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.BuildFlavorProperty, default),
[AdapterTestProperties.BuildNumberProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.BuildNumberProperty, default),
[AdapterTestProperties.BuildPlatformProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.BuildPlatformProperty, default),
[AdapterTestProperties.BuildUriProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.BuildUriProperty, default),
[AdapterTestProperties.TfsServerCollectionUrlProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.TfsServerCollectionUrlProperty, default),
[AdapterTestProperties.TfsTeamProjectProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.TfsTeamProjectProperty, default),
[AdapterTestProperties.IsInLabEnvironmentProperty.Id] = testCase.GetPropertyValue<bool>(AdapterTestProperties.IsInLabEnvironmentProperty, default),

// Step 2: Add test case specific properties.
[AdapterTestProperties.TestCaseIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.TestCaseIdProperty, default),
[AdapterTestProperties.TestConfigurationIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.TestConfigurationIdProperty, default),
[AdapterTestProperties.TestConfigurationNameProperty.Id] = testCase.GetPropertyValue<string>(AdapterTestProperties.TestConfigurationNameProperty, default),
[AdapterTestProperties.TestPointIdProperty.Id] = testCase.GetPropertyValue<int>(AdapterTestProperties.TestPointIdProperty, default),
};

return tcmProperties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<string>(EngineConstants.ParameterTypesProperty, null));
var testMethod = new TestMethod(testCase.GetManagedMethod(), testCase.GetHierarchy(), name, testClassName!, source, testCase.DisplayName, testCase.GetPropertyValue<string>(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<string[]>(EngineConstants.TestDynamicDataProperty, null);
string[]? data = testCase.GetPropertyValue<string[]>(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<string[]>(EngineConstants.WorkItemIdsProperty, null);
string[]? workItemIds = testCase.GetPropertyValue<string[]>(AdapterTestProperties.WorkItemIdsProperty, null);
if (workItemIds is { Length: > 0 })
{
testElement.WorkItemIds = workItemIds;
}

#if !WINDOWS_UWP && !WIN_UI
KeyValuePair<string, string>[]? deploymentItems = testCase.GetPropertyValue<KeyValuePair<string, string>[]>(EngineConstants.DeploymentItemsProperty, null);
KeyValuePair<string, string>[]? deploymentItems = testCase.GetPropertyValue<KeyValuePair<string, string>[]>(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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
{
Expand Down
Loading