diff --git a/Datadog.Trace.OSX.slnf b/Datadog.Trace.OSX.slnf
index 1511c78d2145..3cfc61e70b40 100644
--- a/Datadog.Trace.OSX.slnf
+++ b/Datadog.Trace.OSX.slnf
@@ -123,6 +123,10 @@
"tracer\\test\\test-applications\\integrations\\Samples.Msmq\\Samples.Msmq.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTests2\\Samples.MSTestTests2.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsRetries\\Samples.MSTestTestsRetries.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsNativeRetries\\Samples.MSTestTestsNativeRetries.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsNativeRetriesMtp\\Samples.MSTestTestsNativeRetriesMtp.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsDiscovery\\Samples.MSTestTestsDiscovery.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.TestOptimizationShutdown\\Samples.TestOptimizationShutdown.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTests\\Samples.MSTestTests.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MySqlConnector\\Samples.MySqlConnector.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MySql\\Samples.MySql.csproj",
diff --git a/Datadog.Trace.Samples.g.sln b/Datadog.Trace.Samples.g.sln
index 8dd6eb9e92ad..d15c34ca779c 100644
--- a/Datadog.Trace.Samples.g.sln
+++ b/Datadog.Trace.Samples.g.sln
@@ -461,6 +461,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.LifetimeManager.Ter
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Ocelot.DistributedTracing", "tracer\test\test-applications\integrations\Samples.Ocelot.DistributedTracing\Samples.Ocelot.DistributedTracing.csproj", "{08C6510D-42DC-C8B4-CC79-26349F2D6DBB}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsNativeRetries", "tracer\test\test-applications\integrations\Samples.MSTestTestsNativeRetries\Samples.MSTestTestsNativeRetries.csproj", "{C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsNativeRetriesMtp", "tracer\test\test-applications\integrations\Samples.MSTestTestsNativeRetriesMtp\Samples.MSTestTestsNativeRetriesMtp.csproj", "{1C29692A-19EB-48CD-BC67-73D503FAB794}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsDiscovery", "tracer\test\test-applications\integrations\Samples.MSTestTestsDiscovery\Samples.MSTestTestsDiscovery.csproj", "{8625421C-B637-4EFB-95AC-64D324DFB9DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.TestOptimizationShutdown", "tracer\test\test-applications\integrations\Samples.TestOptimizationShutdown\Samples.TestOptimizationShutdown.csproj", "{207A6285-EDAF-4DBE-9232-D677483ED619}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1123,6 +1131,22 @@ Global
{08C6510D-42DC-C8B4-CC79-26349F2D6DBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08C6510D-42DC-C8B4-CC79-26349F2D6DBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08C6510D-42DC-C8B4-CC79-26349F2D6DBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9518425A-36A5-4B8F-B0B8-6137DB88441D} = {8CEC2042-F11C-49F5-A674-2355793B600A}
@@ -1309,5 +1333,9 @@ Global
{021D714E-1764-D76B-15B5-C9114FE934C2} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{0E3E1069-80FF-99C9-D29F-936D96D5F516} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{08C6510D-42DC-C8B4-CC79-26349F2D6DBB} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {1C29692A-19EB-48CD-BC67-73D503FAB794} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {207A6285-EDAF-4DBE-9232-D677483ED619} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
EndGlobalSection
EndGlobal
diff --git a/Datadog.Trace.TestOptimization.slnf b/Datadog.Trace.TestOptimization.slnf
index c7ae1aac69c9..baca06e12b8d 100644
--- a/Datadog.Trace.TestOptimization.slnf
+++ b/Datadog.Trace.TestOptimization.slnf
@@ -31,6 +31,10 @@
"tracer\\test\\test-applications\\integrations\\Samples.CIVisibilityIpc\\Samples.CIVisibilityIpc.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTests2\\Samples.MSTestTests2.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsRetries\\Samples.MSTestTestsRetries.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsNativeRetries\\Samples.MSTestTestsNativeRetries.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsNativeRetriesMtp\\Samples.MSTestTestsNativeRetriesMtp.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.MSTestTestsDiscovery\\Samples.MSTestTestsDiscovery.csproj",
+ "tracer\\test\\test-applications\\integrations\\Samples.TestOptimizationShutdown\\Samples.TestOptimizationShutdown.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.MSTestTests\\Samples.MSTestTests.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.NUnitTestsRetries\\Samples.NUnitTestsRetries.csproj",
"tracer\\test\\test-applications\\integrations\\Samples.NUnitTests\\Samples.NUnitTests.csproj",
diff --git a/Datadog.Trace.sln b/Datadog.Trace.sln
index 8dd1047bbd81..97fda61aa520 100644
--- a/Datadog.Trace.sln
+++ b/Datadog.Trace.sln
@@ -657,12 +657,36 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Datadog.AutoInstrumentation
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Datadog.AutoInstrumentation.Generator.Cli", "tracer\src\Datadog.AutoInstrumentation.Generator.Cli\Datadog.AutoInstrumentation.Generator.Cli.csproj", "{8D722514-8BAD-4069-84D0-15797026E0ED}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsNativeRetries", "tracer\test\test-applications\integrations\Samples.MSTestTestsNativeRetries\Samples.MSTestTestsNativeRetries.csproj", "{C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsNativeRetriesMtp", "tracer\test\test-applications\integrations\Samples.MSTestTestsNativeRetriesMtp\Samples.MSTestTestsNativeRetriesMtp.csproj", "{1C29692A-19EB-48CD-BC67-73D503FAB794}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.MSTestTestsDiscovery", "tracer\test\test-applications\integrations\Samples.MSTestTestsDiscovery\Samples.MSTestTestsDiscovery.csproj", "{8625421C-B637-4EFB-95AC-64D324DFB9DF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.TestOptimizationShutdown", "tracer\test\test-applications\integrations\Samples.TestOptimizationShutdown\Samples.TestOptimizationShutdown.csproj", "{207A6285-EDAF-4DBE-9232-D677483ED619}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {207A6285-EDAF-4DBE-9232-D677483ED619}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C29692A-19EB-48CD-BC67-73D503FAB794}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF}.Release|Any CPU.Build.0 = Release|Any CPU
{91B6272F-5780-4C94-8071-DBBA7B4F67F3}.Debug|Any CPU.ActiveCfg = Debug|Win32
{91B6272F-5780-4C94-8071-DBBA7B4F67F3}.Debug|Any CPU.Build.0 = Debug|Win32
{91B6272F-5780-4C94-8071-DBBA7B4F67F3}.Release|Any CPU.ActiveCfg = Release|Win32
@@ -1614,6 +1638,10 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
+ {207A6285-EDAF-4DBE-9232-D677483ED619} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {C4FF3DF9-9A1B-48F3-BECE-74F9B4572373} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {1C29692A-19EB-48CD-BC67-73D503FAB794} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
+ {8625421C-B637-4EFB-95AC-64D324DFB9DF} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{91B6272F-5780-4C94-8071-DBBA7B4F67F3} = {9E5F0022-0A50-40BF-AC6A-C3078585ECAB}
{C0C8D381-D6B9-4C76-9428-F40F2FA93A9A} = {9E5F0022-0A50-40BF-AC6A-C3078585ECAB}
{5DFDF781-F24C-45B1-82EF-9125875A80A4} = {9E5F0022-0A50-40BF-AC6A-C3078585ECAB}
diff --git a/tracer/build/PackageVersionsGeneratorDefinitions.json b/tracer/build/PackageVersionsGeneratorDefinitions.json
index b595caf901a7..6faeaffa9b8b 100644
--- a/tracer/build/PackageVersionsGeneratorDefinitions.json
+++ b/tracer/build/PackageVersionsGeneratorDefinitions.json
@@ -667,6 +667,8 @@
"2.0.*",
"2.*.*",
"3.*.*",
+ "4.3.3",
+ "4.4.0",
"4.*.*"
],
"VersionConditions": [{
@@ -684,6 +686,8 @@
"2.0.*",
"2.*.*",
"3.*.*",
+ "4.3.3",
+ "4.4.0",
"4.*.*"
],
"VersionConditions": [{
@@ -701,6 +705,8 @@
"2.0.*",
"2.*.*",
"3.*.*",
+ "4.3.3",
+ "4.4.0",
"4.*.*"
],
"VersionConditions": [{
@@ -708,6 +714,37 @@
"ExcludeTargetFrameworks": ["netcoreapp2.1", "netcoreapp3.0", "netcoreapp3.1", "net5.0", "net6.0", "net7.0"]
}]
},
+ {
+ "IntegrationName": "MSTestNativeRetries",
+ "SampleProjectName": "Samples.MSTestTestsNativeRetries",
+ "NugetPackageSearchName": "MSTest.TestFramework",
+ "MinVersion": "4.4.0",
+ "MaxVersionExclusive": "5.0.0",
+ "SpecificVersions": [
+ "4.4.0"
+ ]
+ },
+ {
+ "IntegrationName": "MSTestNativeRetriesMtp",
+ "SampleProjectName": "Samples.MSTestTestsNativeRetriesMtp",
+ "NugetPackageSearchName": "MSTest.TestFramework",
+ "MinVersion": "4.4.0",
+ "MaxVersionExclusive": "5.0.0",
+ "SpecificVersions": [
+ "4.4.0"
+ ]
+ },
+ {
+ "IntegrationName": "MSTestDiscovery",
+ "SampleProjectName": "Samples.MSTestTestsDiscovery",
+ "NugetPackageSearchName": "MSTest.TestFramework",
+ "MinVersion": "4.3.3",
+ "MaxVersionExclusive": "5.0.0",
+ "SpecificVersions": [
+ "4.3.3",
+ "4.4.0"
+ ]
+ },
{
"IntegrationName": "Kafka",
"SampleProjectName": "Samples.Kafka",
diff --git a/tracer/build/PackageVersionsLatestMajors.g.props b/tracer/build/PackageVersionsLatestMajors.g.props
index 48e744cb6567..c89baa177482 100644
--- a/tracer/build/PackageVersionsLatestMajors.g.props
+++ b/tracer/build/PackageVersionsLatestMajors.g.props
@@ -5262,6 +5262,72 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsDiscovery
+
ApiVersion=1.9.3;RestoreRecursive=false;BuildProjectReferences=false
net48
diff --git a/tracer/build/PackageVersionsLatestMinors.g.props b/tracer/build/PackageVersionsLatestMinors.g.props
index a938d60b5389..f1c1c7e8a028 100644
--- a/tracer/build/PackageVersionsLatestMinors.g.props
+++ b/tracer/build/PackageVersionsLatestMinors.g.props
@@ -27726,6 +27726,96 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsDiscovery
+
ApiVersion=1.4.4;RestoreRecursive=false;BuildProjectReferences=false
net48
diff --git a/tracer/build/PackageVersionsLatestSpecific.g.props b/tracer/build/PackageVersionsLatestSpecific.g.props
index 6cf4df6a9029..28271836a548 100644
--- a/tracer/build/PackageVersionsLatestSpecific.g.props
+++ b/tracer/build/PackageVersionsLatestSpecific.g.props
@@ -6210,6 +6210,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTests
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net48
@@ -6342,6 +6348,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTests
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net8.0
@@ -6366,6 +6378,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTests
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net9.0
@@ -6390,6 +6408,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTests
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net10.0
@@ -6414,6 +6438,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests2
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTests2
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net48
@@ -6546,6 +6576,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests2
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTests2
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net8.0
@@ -6570,6 +6606,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests2
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTests2
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net9.0
@@ -6594,6 +6636,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTests2
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTests2
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net10.0
@@ -6618,6 +6666,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsRetries
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net48
@@ -6750,6 +6804,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsRetries
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net8.0
@@ -6774,6 +6834,12 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsRetries
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net9.0
@@ -6798,12 +6864,108 @@ NOTE: This code was generated by the GeneratePackageVersions tool. To safely
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsRetries
+
ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
net10.0
None
Samples.MSTestTestsRetries
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetries
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsNativeRetriesMtp
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net48
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net8.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net9.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.3.3;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsDiscovery
+
+
+ ApiVersion=4.4.0;RestoreRecursive=false;BuildProjectReferences=false
+ net10.0
+ None
+ Samples.MSTestTestsDiscovery
+
ApiVersion=1.4.4;RestoreRecursive=false;BuildProjectReferences=false
net48
diff --git a/tracer/build/supported_calltargets.g.json b/tracer/build/supported_calltargets.g.json
index d6b06f349d2a..2ec7601b820f 100644
--- a/tracer/build/supported_calltargets.g.json
+++ b/tracer/build/supported_calltargets.g.json
@@ -10926,6 +10926,28 @@
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
+ {
+ "IntegrationName": "MsTestV2",
+ "AssemblyName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter",
+ "TargetTypeName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestClassInfo",
+ "TargetMethodName": "RunClassCleanup",
+ "TargetReturnType": "System.String",
+ "TargetParameterTypes": [],
+ "MinimumVersion": {
+ "Item1": 14,
+ "Item2": 0,
+ "Item3": 0
+ },
+ "MaximumVersion": {
+ "Item1": 14,
+ "Item2": 65535,
+ "Item3": 65535
+ },
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.TestClassInfoRunClassCleanupIntegrationV2",
+ "IntegrationKind": 0,
+ "IsAdoNetIntegration": false,
+ "InstrumentationCategory": 1
+ },
{
"IntegrationName": "MsTestV2",
"AssemblyName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter",
@@ -11948,6 +11970,55 @@
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
+ {
+ "IntegrationName": "MsTestV2",
+ "AssemblyName": "MSTestAdapter.PlatformServices",
+ "TargetTypeName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodInfo",
+ "TargetMethodName": "GetRetryAttribute",
+ "TargetReturnType": "Microsoft.VisualStudio.TestTools.UnitTesting.RetryBaseAttribute",
+ "TargetParameterTypes": [],
+ "MinimumVersion": {
+ "Item1": 4,
+ "Item2": 4,
+ "Item3": 0
+ },
+ "MaximumVersion": {
+ "Item1": 4,
+ "Item2": 65535,
+ "Item3": 65535
+ },
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.TestMethodInfoGetRetryAttributeIntegration",
+ "IntegrationKind": 0,
+ "IsAdoNetIntegration": false,
+ "InstrumentationCategory": 1
+ },
+ {
+ "IntegrationName": "MsTestV2",
+ "AssemblyName": "MSTestAdapter.PlatformServices",
+ "TargetTypeName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.Execution.TestMethodRunner",
+ "TargetMethodName": "ExecuteAsync",
+ "TargetReturnType": "System.Threading.Tasks.Task`1[Microsoft.VisualStudio.TestTools.UnitTesting.TestResult[]]",
+ "TargetParameterTypes": [
+ "System.String",
+ "System.String",
+ "System.String",
+ "System.String"
+ ],
+ "MinimumVersion": {
+ "Item1": 4,
+ "Item2": 4,
+ "Item3": 0
+ },
+ "MaximumVersion": {
+ "Item1": 4,
+ "Item2": 65535,
+ "Item3": 65535
+ },
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.TestMethodRunnerExecuteAsyncIntegrationV4_4",
+ "IntegrationKind": 0,
+ "IsAdoNetIntegration": false,
+ "InstrumentationCategory": 1
+ },
{
"IntegrationName": "MsTestV2",
"AssemblyName": "MSTestAdapter.PlatformServices",
@@ -12253,7 +12324,7 @@
"Item2": 65535,
"Item3": 65535
},
- "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.UnitTestRunnerRunSingleTestAsyncIntegrationV4_3_3",
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.UnitTestRunnerRunSingleTestAsyncIntegrationV4_4",
"IntegrationKind": 0,
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
@@ -12277,7 +12348,7 @@
},
"MaximumVersion": {
"Item1": 4,
- "Item2": 65535,
+ "Item2": 3,
"Item3": 65535
},
"InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.UnitTestDiscovererSendTestCasesIntegrationV4",
@@ -12285,6 +12356,55 @@
"IsAdoNetIntegration": false,
"InstrumentationCategory": 1
},
+ {
+ "IntegrationName": "MsTestV2",
+ "AssemblyName": "MSTestAdapter.PlatformServices",
+ "TargetTypeName": "Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.UnitTestDiscoverer",
+ "TargetMethodName": "SendTestCasesAsync",
+ "TargetReturnType": "System.Threading.Tasks.Task",
+ "TargetParameterTypes": [
+ "System.Collections.Generic.IEnumerable`1[Microsoft.VisualStudio.TestPlatform.MSTest.TestAdapter.ObjectModel.UnitTestElement]",
+ "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.IUnitTestElementSink",
+ "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.ITestElementFilterProvider",
+ "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.Interface.IAdapterMessageLogger"
+ ],
+ "MinimumVersion": {
+ "Item1": 4,
+ "Item2": 4,
+ "Item3": 0
+ },
+ "MaximumVersion": {
+ "Item1": 4,
+ "Item2": 65535,
+ "Item3": 65535
+ },
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.UnitTestDiscovererSendTestCasesAsyncIntegrationV4_4",
+ "IntegrationKind": 0,
+ "IsAdoNetIntegration": false,
+ "InstrumentationCategory": 1
+ },
+ {
+ "IntegrationName": "MsTestV2",
+ "AssemblyName": "MSTestAdapter.PlatformServices",
+ "TargetTypeName": "Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices.TestSourceHost",
+ "TargetMethodName": "SetupHost",
+ "TargetReturnType": "System.Void",
+ "TargetParameterTypes": [],
+ "MinimumVersion": {
+ "Item1": 4,
+ "Item2": 3,
+ "Item3": 3
+ },
+ "MaximumVersion": {
+ "Item1": 4,
+ "Item2": 65535,
+ "Item3": 65535
+ },
+ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2.TestSourceHostSetupHostIntegration",
+ "IntegrationKind": 0,
+ "IsAdoNetIntegration": false,
+ "InstrumentationCategory": 1
+ },
{
"IntegrationName": "MySql",
"AssemblyName": "MySql.Data",
diff --git a/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml b/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml
index 1be2f4ce3834..2f6d0958e91e 100644
--- a/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml
+++ b/tracer/src/Datadog.Trace.Trimming/build/Datadog.Trace.Trimming.xml
@@ -1182,7 +1182,9 @@
+
+
diff --git a/tracer/src/Datadog.Trace/Ci/Test.cs b/tracer/src/Datadog.Trace/Ci/Test.cs
index ce4ff133e155..c161ae394ceb 100644
--- a/tracer/src/Datadog.Trace/Ci/Test.cs
+++ b/tracer/src/Datadog.Trace/Ci/Test.cs
@@ -31,10 +31,17 @@ public sealed class Test
private static readonly HashSet OpenedTests = new();
private readonly ITestOptimization _testOptimization;
private readonly Scope _scope;
+ private readonly object _executionLock = new();
private readonly Test? _priorTest;
private Coverage.CoverageSessionHandle? _coverageSessionHandle;
private int _finished;
- private List>? _onCloseActions;
+ private bool _finishingExecution;
+ private bool _executionFinished;
+ private bool _closeRequested;
+ private TimeSpan? _executionDuration;
+ // Execution callbacks (for example, Selenium RUM flush) must run before leaving the attempt context,
+ // even when MSTest keeps the span open until its retry policy selects the final result.
+ private List>? _executionCompletedActions;
internal Test(TestSuite suite, string name, DateTimeOffset? startDate)
: this(suite, name, startDate, default, 0)
@@ -495,19 +502,154 @@ public void Close(TestStatus status, TimeSpan? duration)
/// Duration of the test suite
/// In case
public void Close(TestStatus status, TimeSpan? duration, string? skipReason)
+ => Close(status, duration, skipReason, beforeClose: null);
+
+ ///
+ /// Assigns remaining tags and closes the test under the same guard as shutdown.
+ /// The callback is skipped if another caller has already closed the test.
+ ///
+ internal void Close(TestStatus status, TimeSpan? duration, string? skipReason, Action? beforeClose)
{
- if (Interlocked.Exchange(ref _finished, 1) == 1)
+ lock (_executionLock)
+ {
+ if (IsClosed)
+ {
+ _testOptimization.Log.Warning("Test.Close() was already called before.");
+ return;
+ }
+
+ _closeRequested = true;
+ try
+ {
+ beforeClose?.Invoke(this);
+ }
+ finally
+ {
+ // A completion callback can request Close on this same thread. The outer
+ // execution completion will close the span after all callbacks have returned.
+ if (!_finishingExecution)
+ {
+ try
+ {
+ FinishExecution(status, duration, skipReason);
+ }
+ finally
+ {
+ CompleteClose();
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Captures the attempt's duration and outcome and finishes its coverage and callbacks once.
+ /// The test remains open until Close is called with its final tags already assigned.
+ ///
+ ///
+ /// Only instrumentation that owns the remaining test lifetime may call this method.
+ /// It must restore the active execution context and arrange a later Close, including error paths.
+ /// The scope is not disposed here. Shutdown can still find the test and use its captured duration.
+ ///
+ internal void UnsafeFinishExecution(TestStatus status, TimeSpan duration, string? skipReason)
+ {
+ lock (_executionLock)
+ {
+ try
+ {
+ FinishExecution(status, duration, skipReason);
+ }
+ finally
+ {
+ if (_closeRequested && !_finishingExecution)
+ {
+ CompleteClose();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Captures status and duration once and ends coverage and execution callbacks.
+ /// Requires the execution lock; reentrant Close waits until callbacks have completed.
+ ///
+ private void FinishExecution(TestStatus status, TimeSpan? duration, string? skipReason)
+ {
+ if (_executionFinished || _finishingExecution)
{
- _testOptimization.Log.Warning("Test.Close() was already called before.");
return;
}
- var scope = _scope;
- var tags = (TestSpanTags)scope.Span.Tags;
+ _finishingExecution = true;
+ _executionDuration = duration ?? _scope.Span.Context.TraceContext.Clock.ElapsedSince(_scope.Span.StartTime);
+ var tags = (TestSpanTags)_scope.Span.Tags;
+ try
+ {
+ // Record the attempt outcome and update suite failure and skip accounting.
+ switch (status)
+ {
+ case TestStatus.Pass:
+ tags.Status = TestTags.StatusPass;
+ break;
+ case TestStatus.Fail:
+ tags.Status = TestTags.StatusFail;
+ Suite.Tags.Status = TestTags.StatusFail;
+ break;
+ case TestStatus.Skip:
+ tags.Status = TestTags.StatusSkip;
+ tags.SkipReason = skipReason;
+ if (tags.SkipReason == IntelligentTestRunnerTags.SkippedByReason)
+ {
+ tags.SkippedByIntelligentTestRunner = "true";
+ var moduleName = tags.Bundle ?? tags.Module;
+ _testOptimization.SkippableFeature?.RecordTestSkippedByItr(Suite.Module.Tags.SessionId, moduleName);
+ Suite.Tags.AddIntelligentTestRunnerSkippingCount(1);
+ TelemetryFactory.Metrics.RecordCountCIVisibilityITRSkipped(MetricTags.CIVisibilityTestingEventType.Test);
+ }
+ else
+ {
+ tags.SkippedByIntelligentTestRunner = "false";
+ }
+
+ break;
+ }
- // Calculate duration beforehand
- duration ??= scope.Span.Context.TraceContext.Clock.ElapsedSince(scope.Span.StartTime);
+ if (tags.Unskippable is not null && string.Equals(tags.Unskippable, "true", StringComparison.OrdinalIgnoreCase))
+ {
+ TelemetryFactory.Metrics.RecordCountCIVisibilityITRUnskippable(MetricTags.CIVisibilityTestingEventType.Test);
+ }
+
+ if (tags.ForcedRun is not null && string.Equals(tags.ForcedRun, "true", StringComparison.OrdinalIgnoreCase))
+ {
+ TelemetryFactory.Metrics.RecordCountCIVisibilityITRForcedRun(MetricTags.CIVisibilityTestingEventType.Test);
+ }
+
+ try
+ {
+ FinishCoverage(tags, status);
+ }
+ finally
+ {
+ RunCompletionCallbacks();
+ }
+ }
+ finally
+ {
+ _executionFinished = true;
+ _finishingExecution = false;
+ if (ReferenceEquals(Current, this))
+ {
+ Current = null;
+ }
+ }
+ }
+ ///
+ /// Ends the exact coverage session captured at test creation, even after an async context switch.
+ ///
+ private void FinishCoverage(TestSpanTags tags, TestStatus status)
+ {
+ var scope = _scope;
// The immutable coverage handle must be claimed once because Close() may be called concurrently.
var coverageSessionHandle = Interlocked.Exchange(ref _coverageSessionHandle, null);
var coverageEnded = coverageSessionHandle is null || !coverageSessionHandle.IsValid;
@@ -541,91 +683,96 @@ public void Close(TestStatus status, TimeSpan? duration, string? skipReason)
coverageSessionHandle?.AbortIncomplete(Coverage.GlobalCoverageFailureReason.TestCloseBeforeCoverage);
}
}
+ }
- // Set status
- switch (status)
+ ///
+ /// Runs each execution callback once, allowing remaining callbacks to run if one fails.
+ ///
+ private void RunCompletionCallbacks()
+ {
+ var callbacks = _executionCompletedActions;
+ _executionCompletedActions = null;
+ if (callbacks is null)
{
- case TestStatus.Pass:
- tags.Status = TestTags.StatusPass;
- break;
- case TestStatus.Fail:
- tags.Status = TestTags.StatusFail;
- Suite.Tags.Status = TestTags.StatusFail;
- break;
- case TestStatus.Skip:
- tags.Status = TestTags.StatusSkip;
- tags.SkipReason = skipReason;
- if (tags.SkipReason == IntelligentTestRunnerTags.SkippedByReason)
- {
- tags.SkippedByIntelligentTestRunner = "true";
- var moduleName = tags.Bundle ?? tags.Module;
- _testOptimization.SkippableFeature?.RecordTestSkippedByItr(Suite.Module.Tags.SessionId, moduleName);
- Suite.Tags.AddIntelligentTestRunnerSkippingCount(1);
- TelemetryFactory.Metrics.RecordCountCIVisibilityITRSkipped(MetricTags.CIVisibilityTestingEventType.Test);
- }
- else
- {
- tags.SkippedByIntelligentTestRunner = "false";
- }
-
- break;
+ return;
}
- if (tags.Unskippable is not null && string.Equals(tags.Unskippable, "true", StringComparison.OrdinalIgnoreCase))
+ foreach (var callback in callbacks)
{
- TelemetryFactory.Metrics.RecordCountCIVisibilityITRUnskippable(MetricTags.CIVisibilityTestingEventType.Test);
+ try
+ {
+ callback(this);
+ }
+ catch (Exception ex)
+ {
+ _testOptimization.Log.Error(ex, "Error completing a test callback.");
+ }
}
+ }
- if (tags.ForcedRun is not null && string.Equals(tags.ForcedRun, "true", StringComparison.OrdinalIgnoreCase))
+ ///
+ /// Finishes the span with its captured duration, disposes its scope, and removes the active test.
+ /// Requires the execution lock and completed execution callbacks.
+ ///
+ private void CompleteClose()
+ {
+ if (Interlocked.Exchange(ref _finished, 1) == 1)
{
- TelemetryFactory.Metrics.RecordCountCIVisibilityITRForcedRun(MetricTags.CIVisibilityTestingEventType.Test);
+ return;
}
- if (_onCloseActions is not null)
+ var tags = (TestSpanTags)_scope.Span.Tags;
+ try
{
- foreach (var action in _onCloseActions)
+ try
{
- action(this);
+ _scope.Span.Finish(_executionDuration!.Value);
+ }
+ finally
+ {
+ _scope.Dispose();
}
- _onCloseActions.Clear();
+ // Record closure after final retry tags are available.
+ if (TelemetryHelper.GetEventTypeWithCodeOwnerAndSupportedCiAndBenchmarkAndEarlyFlakeDetection(
+ MetricTags.CIVisibilityTestingEventType.Test,
+ tags.Type == TestTags.TypeBenchmark,
+ tags.TestIsNew == "true",
+ tags.EarlyFlakeDetectionTestAbortReason == "slow",
+ !StringUtil.IsNullOrEmpty(tags.BrowserDriver),
+ tags.IsRumActive == "true") is { } eventTypeWithMetadata)
+ {
+ var retryReasonTag = tags.TestRetryReason switch
+ {
+ TestTags.TestRetryReasonEfd => MetricTags.CIVisibilityTestingEventTypeRetryReason.EarlyFlakeDetection,
+ TestTags.TestRetryReasonAtr => MetricTags.CIVisibilityTestingEventTypeRetryReason.AutomaticTestRetry,
+ _ => MetricTags.CIVisibilityTestingEventTypeRetryReason.None
+ };
+
+ var quarantinedOrDisabled = tags.IsQuarantined == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.IsQuarantined :
+ tags.IsDisabled == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.IsDisabled :
+ MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.None;
+ var attemptToFix = tags.IsAttemptToFix == "true" ? (tags.HasFailedAllRetries == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.AttemptToFixHasFailedAllRetries : MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.IsAttemptToFix) : MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.None;
+
+ TelemetryFactory.Metrics.RecordCountCIVisibilityEventFinished(
+ TelemetryHelper.GetTelemetryTestingFrameworkEnum(tags.Framework),
+ eventTypeWithMetadata,
+ retryReasonTag,
+ quarantinedOrDisabled,
+ attemptToFix);
+ }
}
-
- scope.Span.Finish(duration.Value);
- scope.Dispose();
-
- if (TelemetryHelper.GetEventTypeWithCodeOwnerAndSupportedCiAndBenchmarkAndEarlyFlakeDetection(
- MetricTags.CIVisibilityTestingEventType.Test,
- tags.Type == TestTags.TypeBenchmark,
- tags.TestIsNew == "true",
- tags.EarlyFlakeDetectionTestAbortReason == "slow",
- !StringUtil.IsNullOrEmpty(tags.BrowserDriver),
- tags.IsRumActive == "true") is { } eventTypeWithMetadata)
+ finally
{
- var retryReasonTag = tags.TestRetryReason switch
- {
- TestTags.TestRetryReasonEfd => MetricTags.CIVisibilityTestingEventTypeRetryReason.EarlyFlakeDetection,
- TestTags.TestRetryReasonAtr => MetricTags.CIVisibilityTestingEventTypeRetryReason.AutomaticTestRetry,
- _ => MetricTags.CIVisibilityTestingEventTypeRetryReason.None
- };
-
- var quarantinedOrDisabled = tags.IsQuarantined == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.IsQuarantined :
- tags.IsDisabled == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.IsDisabled :
- MetricTags.CIVisibilityTestingEventTypeTestManagementQuarantinedOrDisabled.None;
- var attemptToFix = tags.IsAttemptToFix == "true" ? (tags.HasFailedAllRetries == "true" ? MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.AttemptToFixHasFailedAllRetries : MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.IsAttemptToFix) : MetricTags.CIVisibilityTestingEventTypeTestManagementAttemptToFix.None;
-
- TelemetryFactory.Metrics.RecordCountCIVisibilityEventFinished(
- TelemetryHelper.GetTelemetryTestingFrameworkEnum(tags.Framework),
- eventTypeWithMetadata,
- retryReasonTag,
- quarantinedOrDisabled,
- attemptToFix);
- }
+ if (ReferenceEquals(Current, this))
+ {
+ Current = null;
+ }
- Current = null;
- lock (OpenedTests)
- {
- OpenedTests.Remove(this);
+ lock (OpenedTests)
+ {
+ OpenedTests.Remove(this);
+ }
}
_testOptimization.Log.Debug("######### Test Closed: {Name} ({Suite} | {Module}) | {Status}", Name, Suite.Name, Suite.Module.Name, tags.Status);
@@ -651,9 +798,22 @@ internal void SetName(string name)
((TestSpanTags)_scope.Span.Tags).Name = name;
}
- internal void AddOnCloseAction(Action action)
+ ///
+ /// Registers a callback that runs once when execution finishes, before the span is closed.
+ /// Final retry tags may still be pending. Callbacks registered after execution finishes are rejected.
+ ///
+ internal void AddOnExecutionCompletedAction(Action action)
{
- _onCloseActions ??= [];
- _onCloseActions.Add(action);
+ lock (_executionLock)
+ {
+ if (_executionFinished || _finishingExecution)
+ {
+ _testOptimization.Log.Warning("Cannot register a completion callback after test execution has finished.");
+ return;
+ }
+
+ _executionCompletedActions ??= [];
+ _executionCompletedActions.Add(action);
+ }
}
}
diff --git a/tracer/src/Datadog.Trace/Ci/TestOptimization.cs b/tracer/src/Datadog.Trace/Ci/TestOptimization.cs
index d145c43a12dc..838a0e612a46 100644
--- a/tracer/src/Datadog.Trace/Ci/TestOptimization.cs
+++ b/tracer/src/Datadog.Trace/Ci/TestOptimization.cs
@@ -236,6 +236,10 @@ public void Initialize()
return;
}
+ // Agent discovery can initialize the tracer. Register first so test sessions close before its writer.
+ LifetimeManager.Instance.AddAsyncShutdownTask(ShutdownAsync);
+ cd.Debug("Added shutdown task");
+
if (_enablement.InferredEnabled)
{
PropagateCiVisibilityEnvironmentVariable();
@@ -266,9 +270,6 @@ public void Initialize()
Log.Information("TestOptimization: EVP Proxy was enabled with mode: {Mode}", TracerManagement.EventPlatformProxySupport);
}
- LifetimeManager.Instance.AddAsyncShutdownTask(ShutdownAsync);
- cd.Debug("Added shutdown task");
-
var tracerSettings = settings.TracerSettings;
Log.Debug("TestOptimization: Setting up the test session name to: {TestSessionName}", settings.TestSessionName);
Log.Debug("TestOptimization: Setting up the service name to: {ServiceName}", tracerSettings.Manager.InitialMutableSettings.ServiceName);
diff --git a/tracer/src/Datadog.Trace/Ci/TestSuite.cs b/tracer/src/Datadog.Trace/Ci/TestSuite.cs
index 749e45d1e5c4..ceea8ae939f9 100644
--- a/tracer/src/Datadog.Trace/Ci/TestSuite.cs
+++ b/tracer/src/Datadog.Trace/Ci/TestSuite.cs
@@ -109,6 +109,12 @@ internal static IReadOnlyCollection ActiveTestSuites
internal TestSuiteSpanTags Tags => (TestSuiteSpanTags)_span.Tags;
+ ///
+ /// Gets a value indicating whether closing has started. Framework callbacks can report the same cleanup
+ /// failure again after closing the suite; those callbacks must leave its final tags alone.
+ ///
+ internal bool IsClosed => Volatile.Read(ref _finished) != 0;
+
///
/// Sets a string tag into the test
///
diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IMsTestContext.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IMsTestContext.cs
new file mode 100644
index 000000000000..9c44b6219f67
--- /dev/null
+++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IMsTestContext.cs
@@ -0,0 +1,27 @@
+//
+// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
+// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
+//
+#nullable enable
+
+using System;
+using Datadog.Trace.DuckTyping;
+
+namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2;
+
+internal interface IMsTestContext : IDuckType
+{
+ string? TestDisplayName { get; }
+
+ int TestRunCount { get; set; }
+
+ object CloneForDataDrivenIteration();
+
+ void SetTestData(object[]? arguments);
+
+ void SetDisplayName(string? displayName);
+
+ IDisposable SetCurrentTestContext(object context);
+
+ void Dispose();
+}
diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IRetryResult.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IRetryResult.cs
new file mode 100644
index 000000000000..9eef362dc152
--- /dev/null
+++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/IRetryResult.cs
@@ -0,0 +1,14 @@
+//
+// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
+// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
+//
+#nullable enable
+
+using System.Collections;
+
+namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2;
+
+internal interface IRetryResult
+{
+ IList? TryGetLast();
+}
diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfoWithRetry.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfoWithRetry.cs
new file mode 100644
index 000000000000..b8a22179e769
--- /dev/null
+++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestMethodInfoWithRetry.cs
@@ -0,0 +1,16 @@
+//
+// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
+// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
+//
+#nullable enable
+
+namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2;
+
+internal interface ITestMethodInfoWithRetry
+{
+ object? RetryAttribute { get; }
+
+ object TestContext { get; set; }
+
+ void SetArguments(object[]? arguments);
+}
diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResultV4_4.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResultV4_4.cs
new file mode 100644
index 000000000000..f0ce0dba5b4f
--- /dev/null
+++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/ITestResultV4_4.cs
@@ -0,0 +1,24 @@
+//
+// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
+// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
+//
+#nullable enable
+
+using System;
+
+namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2;
+
+internal interface ITestResultV4_4 : ITestResult
+{
+ int RetryAttemptNumber { get; set; }
+
+ bool IsSupersededRetryAttempt { get; set; }
+
+ Guid ExecutionId { get; set; }
+
+ Guid ParentExecId { get; set; }
+
+ int DatarowIndex { get; set; }
+
+ object? AssociatedUnitTestElement { get; set; }
+}
diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestExecution.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestExecution.cs
new file mode 100644
index 000000000000..a675640dcdd8
--- /dev/null
+++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Testing/MsTestV2/MsTestExecution.cs
@@ -0,0 +1,538 @@
+//
+// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
+// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
+//
+#nullable enable
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Datadog.Trace.Ci;
+using Datadog.Trace.Ci.Tags;
+using Datadog.Trace.ClrProfiler.CallTarget;
+using Datadog.Trace.ClrProfiler.CallTarget.Handlers;
+using Datadog.Trace.DuckTyping;
+
+namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.Testing.MsTestV2;
+
+///
+/// Owns one runner invocation, including its data rows and native and Datadog retries.
+/// Attempt execution ends immediately; spans close only after the native policy selects its result.
+///
+internal sealed class MsTestExecution
+{
+ private static readonly AsyncLocal CurrentExecution = new();
+ private static readonly ConditionalWeakTable