diff --git a/src/tests/Common/Assert.cs b/src/tests/Common/Assert.cs
index a4ab602f7f9801..1593326e92b2f6 100644
--- a/src/tests/Common/Assert.cs
+++ b/src/tests/Common/Assert.cs
@@ -12,6 +12,12 @@
 
 namespace Xunit
 {
+    /// <summary>
+    /// A copy of the Xunit.FactAttribute type for assemblies that reference System.Private.CoreLib directly.
+    /// </summary>
+    public sealed class FactAttribute : Attribute
+    {}
+
     /// <summary>
     ///    A collection of helper classes to test various conditions within
     /// unit tests. If the condition being tested is not met, an exception
diff --git a/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj b/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
index ec3c6699118784..61ea72fa3ba176 100644
--- a/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
+++ b/src/tests/Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj
@@ -15,6 +15,11 @@
     <Compile Include="Utilities.cs" />
     <Compile Include="HostPolicyMock.cs" />
     <Compile Include="XPlatformUtils.cs" />
+    <Compile Include="CoreClrConfigurationDetection.cs" />
+    <Compile Include="OutOfProcessTest.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\Coreclr.TestWrapper\CoreclrTestWrapperLib.cs" Link="CoreclrTestWrapperLib.cs" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/tests/Common/CoreCLRTestLibrary/CoreClrConfigurationDetection.cs b/src/tests/Common/CoreCLRTestLibrary/CoreClrConfigurationDetection.cs
new file mode 100644
index 00000000000000..3fb8adccf02233
--- /dev/null
+++ b/src/tests/Common/CoreCLRTestLibrary/CoreClrConfigurationDetection.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+using System;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+
+namespace TestLibrary;
+
+public static class CoreClrConfigurationDetection
+{
+    public static bool IsJitStress => !string.Equals(GetEnvironmentVariableValue("JitStress"), "0", StringComparison.InvariantCulture);
+    public static bool IsJitStressRegs => !string.Equals(GetEnvironmentVariableValue("JitStressRegs"), "0", StringComparison.InvariantCulture);
+    public static bool IsJitMinOpts => string.Equals(GetEnvironmentVariableValue("JITMinOpts"), "1", StringComparison.InvariantCulture);
+    public static bool IsTailCallStress => string.Equals(GetEnvironmentVariableValue("TailcallStress"), "1", StringComparison.InvariantCulture);
+    public static bool IsZapDisable => string.Equals(GetEnvironmentVariableValue("ZapDisable"), "1", StringComparison.InvariantCulture);
+    public static bool IsGCStress3 => CompareGCStressModeAsLower(GetEnvironmentVariableValue("GCStress"), "0x3", "3");
+    public static bool IsGCStressC => CompareGCStressModeAsLower(GetEnvironmentVariableValue("GCStress"), "0xC", "C");
+
+    public static bool IsGCStress => GetEnvironmentVariableValue("GCStress") != string.Empty;
+
+    public static bool IsCheckedRuntime => AssemblyConfigurationEquals("Checked");
+    public static bool IsReleaseRuntime => AssemblyConfigurationEquals("Release");
+    public static bool IsDebugRuntime => AssemblyConfigurationEquals("Debug");
+
+    public static bool IsStressTest =>
+        IsGCStress ||
+        IsZapDisable ||
+        IsTailCallStress ||
+        IsJitStressRegs ||
+        IsJitStress ||
+        IsJitMinOpts;
+
+    private static string GetEnvironmentVariableValue(string name) =>
+        Environment.GetEnvironmentVariable("DOTNET_" + name) ?? Environment.GetEnvironmentVariable("COMPlus_" + name) ?? "0";
+
+    private static bool AssemblyConfigurationEquals(string configuration)
+    {
+        AssemblyConfigurationAttribute assemblyConfigurationAttribute = typeof(string).Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>();
+
+        return assemblyConfigurationAttribute != null &&
+            string.Equals(assemblyConfigurationAttribute.Configuration, configuration, StringComparison.InvariantCulture);
+    }
+
+    private static bool CompareGCStressModeAsLower(string value, string first, string second)
+    {
+        value = value.ToLowerInvariant();
+        return string.Equals(value, first.ToLowerInvariant(), StringComparison.InvariantCulture) ||
+            string.Equals(value, second.ToLowerInvariant(), StringComparison.InvariantCulture) ||
+            string.Equals(value, "0xf", StringComparison.InvariantCulture) ||
+            string.Equals(value, "f", StringComparison.InvariantCulture);
+    }
+}
diff --git a/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs
new file mode 100644
index 00000000000000..e96378339b8b7b
--- /dev/null
+++ b/src/tests/Common/CoreCLRTestLibrary/OutOfProcessTest.cs
@@ -0,0 +1,132 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using CoreclrTestLib;
+using Xunit;
+
+namespace TestLibrary
+{
+    public static class OutOfProcessTest
+    {
+        internal static bool runningInWindows;
+        internal static string reportBase;
+        internal static string testBinaryBase;
+        internal static string helixUploadRoot;
+
+        static OutOfProcessTest()
+        {
+            reportBase = Directory.GetCurrentDirectory();
+            testBinaryBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
+            helixUploadRoot = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT");
+            if (!String.IsNullOrEmpty(helixUploadRoot))
+            {
+                reportBase = Path.Combine(Path.GetFullPath(helixUploadRoot), "Reports");
+            }
+
+            if (String.IsNullOrEmpty(reportBase))
+            {
+                reportBase = Path.Combine(testBinaryBase, "Reports");
+            }
+            else
+            {
+                reportBase = Path.GetFullPath(reportBase);
+            }
+        }
+
+        public static void RunOutOfProcessTest(string basePath, string assemblyPath)
+        {
+            int ret = -100;
+            string outputFile = System.IO.Path.GetFullPath(reportBase + assemblyPath + "output.txt");
+            string errorFile = System.IO.Path.GetFullPath(reportBase + assemblyPath + "error.txt");
+            string outputDir = System.IO.Path.GetDirectoryName(outputFile)!;
+            string testExecutable = null;
+            Exception infraEx = null;
+
+            try
+            {
+                CoreclrTestWrapperLib wrapper = new CoreclrTestWrapperLib();
+
+                if (OperatingSystem.IsWindows())
+                {
+                    testExecutable = Path.Combine(basePath, Path.ChangeExtension(assemblyPath, ".cmd"));
+                }
+                else
+                {
+                    testExecutable = Path.Combine(basePath, Path.ChangeExtension(assemblyPath.Replace("\\", "/"), ".sh"));
+                }
+
+                System.IO.Directory.CreateDirectory(reportBase + Path.GetDirectoryName(assemblyPath));
+
+                ret = wrapper.RunTest(testExecutable, outputFile, errorFile, Assembly.GetEntryAssembly()!.FullName!, testBinaryBase, outputDir);
+            }
+            catch (Exception ex)
+            {
+                infraEx = ex;
+            }
+
+            if (infraEx != null)
+            {
+                Assert.True(false, "Test Infrastructure Failure: " + infraEx.ToString());
+            }
+            else
+            {
+                List<string> testOutput = new List<string>();
+
+                try
+                {
+                    testOutput.AddRange(System.IO.File.ReadAllLines(errorFile));
+                }
+                catch (Exception ex)
+                {
+                    testOutput.Add("Unable to read error file: " + errorFile);
+                    testOutput.Add(ex.ToString());
+                }
+
+                testOutput.Add(string.Empty);
+                testOutput.Add("Return code:      " + ret);
+                testOutput.Add("Raw output file:      " + outputFile);
+                testOutput.Add("Raw output:");
+
+                try
+                {
+                    testOutput.AddRange(System.IO.File.ReadAllLines(outputFile));
+                }
+                catch (Exception ex)
+                {
+                    testOutput.Add("Unable to read output file: " + outputFile);
+                    testOutput.Add(ex.ToString());
+                }
+
+                testOutput.Add("To run the test:");
+                testOutput.Add("Set up CORE_ROOT and run.");
+                testOutput.Add("> " + testExecutable);
+
+                var unicodeControlCharsRegex = new Regex("%5C%5Cp{C}+");
+
+                // Remove all characters that have no visual or spatial representation.
+                for (int i = 0; i < testOutput.Count; i++)
+                {
+                    string line = testOutput[i];
+                    line = unicodeControlCharsRegex.Replace(line, string.Empty);
+                    testOutput[i] = line;
+                }
+
+                foreach (string line in testOutput)
+                {
+                    Console.WriteLine(line);
+                }
+
+                Assert.True(ret == CoreclrTestWrapperLib.EXIT_SUCCESS_CODE, string.Join(Environment.NewLine, testOutput));
+            }
+        }
+    }
+}
diff --git a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
index da13ad2492e676..91e7f8c461c196 100644
--- a/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
+++ b/src/tests/Common/Coreclr.TestWrapper/CoreclrTestWrapperLib.cs
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 //
+#nullable disable
 
 using System;
 using System.Collections.Generic;
@@ -196,7 +197,7 @@ public class CoreclrTestWrapperLib
     {
         public const int EXIT_SUCCESS_CODE = 0;
         public const string TIMEOUT_ENVIRONMENT_VAR = "__TestTimeout";
-        
+
         // Default timeout set to 10 minutes
         public const int DEFAULT_TIMEOUT_MS = 1000 * 60 * 10;
 
@@ -289,7 +290,7 @@ public int RunTest(string executable, string outputFile, string errorFile, strin
             Debug.Assert(outputFile != errorFile);
 
             int exitCode = -100;
-            
+
             // If a timeout was given to us by an environment variable, use it instead of the default
             // timeout.
             string environmentVar = Environment.GetEnvironmentVariable(TIMEOUT_ENVIRONMENT_VAR);
@@ -387,7 +388,5 @@ public int RunTest(string executable, string outputFile, string errorFile, strin
 
             return exitCode;
         }
-
-        
     }
 }
diff --git a/src/tests/Common/Directory.Build.props b/src/tests/Common/Directory.Build.props
index 99bbac2cdaa729..965600c743fdaf 100644
--- a/src/tests/Common/Directory.Build.props
+++ b/src/tests/Common/Directory.Build.props
@@ -1,4 +1,8 @@
 <Project>
+  <PropertyGroup>
+    <IsTestsCommonProject>true</IsTestsCommonProject>
+  </PropertyGroup>
+
   <!-- SDK Style projects auto-magically include this file. -->
   <Import Project="..\Directory.Build.props" />
 </Project>
diff --git a/src/tests/Common/ILTestRunner/ILTestRunner.csproj b/src/tests/Common/ILTestRunner/ILTestRunner.csproj
new file mode 100644
index 00000000000000..bbb4b7f2a7c290
--- /dev/null
+++ b/src/tests/Common/ILTestRunner/ILTestRunner.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <IsMergedTestRunnerAssembly>true</IsMergedTestRunnerAssembly>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="$(RepoRoot)/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+    <ProjectReference Include="$(RepoRoot)/src/tests/Common/XUnitWrapperLibrary/XUnitWrapperLibrary.csproj" />
+  </ItemGroup>
+
+  <Import Project="$(RepoRoot)/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props" />
+
+  <ItemGroup>
+    <ProjectReference Include="$(RepoRoot)\src\tests\JIT\IL_Conformance\Old\directed\AutoInit.ilproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs b/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
new file mode 100644
index 00000000000000..633f534e72efdb
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
@@ -0,0 +1,309 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace XUnitWrapperGenerator;
+
+interface ITestInfo
+{
+    string TestNameExpression { get; }
+    string DisplayNameForFiltering { get; }
+    string Method { get; }
+    string ContainingType { get; }
+
+    string GenerateTestExecution(ITestReporterWrapper testReporterWrapper);
+}
+
+interface ITestReporterWrapper
+{
+    string WrapTestExecutionWithReporting(string testExecution, ITestInfo test);
+
+    string GenerateSkippedTestReporting(ITestInfo skippedTest);
+}
+
+sealed class BasicTestMethod : ITestInfo
+{
+    public BasicTestMethod(IMethodSymbol method, string externAlias, ImmutableArray<string> arguments = default, string? displayNameExpression = null)
+    {
+        var args = arguments.IsDefaultOrEmpty ? "" : string.Join(", ", arguments);
+        ContainingType = method.ContainingType.ToDisplayString(XUnitWrapperGenerator.FullyQualifiedWithoutGlobalNamespace);
+        Method = method.Name;
+        DisplayNameForFiltering = $"{ContainingType}.{Method}({args})";
+        TestNameExpression = displayNameExpression ?? $"\"{externAlias}::{ContainingType}.{Method}({args})\"";
+        if (method.IsStatic)
+        {
+            ExecutionStatement = $"{externAlias}::{ContainingType}.{Method}({args});";
+        }
+        else
+        {
+            ExecutionStatement = $"using ({externAlias}::{ContainingType} obj = new()) obj.{Method}({args});";
+        }
+    }
+
+    public string TestNameExpression { get; }
+    public string DisplayNameForFiltering { get; }
+    public string Method { get; }
+    public string ContainingType { get; }
+    private string ExecutionStatement { get; }
+
+    public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+    {
+        return testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+    }
+
+    public override bool Equals(object obj)
+    {
+        return obj is BasicTestMethod other
+            && TestNameExpression == other.TestNameExpression
+            && Method == other.Method
+            && ContainingType == other.ContainingType
+            && ExecutionStatement == other.ExecutionStatement;
+    }
+}
+sealed class LegacyStandaloneEntryPointTestMethod : ITestInfo
+{
+    public LegacyStandaloneEntryPointTestMethod(IMethodSymbol method, string externAlias)
+    {
+        ContainingType = method.ContainingType.ToDisplayString(XUnitWrapperGenerator.FullyQualifiedWithoutGlobalNamespace);
+        Method = method.Name;
+        TestNameExpression = $"\"{externAlias}::{ContainingType}.{Method}()\"";
+        DisplayNameForFiltering = $"{ContainingType}.{Method}()";
+        ExecutionStatement = $"Xunit.Assert.Equal(100, {externAlias}::{ContainingType}.{Method}());";
+    }
+
+    public string TestNameExpression { get; }
+    public string DisplayNameForFiltering { get; }
+
+    public string Method { get; }
+    public string ContainingType { get; }
+    private string ExecutionStatement { get; }
+
+    public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+    {
+        return testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+    }
+
+    public override bool Equals(object obj)
+    {
+        return obj is LegacyStandaloneEntryPointTestMethod other
+            && TestNameExpression == other.TestNameExpression
+            && Method == other.Method
+            && ContainingType == other.ContainingType; ;
+    }
+}
+
+sealed class ConditionalTest : ITestInfo
+{
+    private ITestInfo _innerTest;
+    private string _condition;
+    public ConditionalTest(ITestInfo innerTest, string condition)
+    {
+        _innerTest = innerTest;
+        _condition = condition;
+        TestNameExpression = innerTest.TestNameExpression;
+        DisplayNameForFiltering = innerTest.DisplayNameForFiltering;
+        Method = innerTest.Method;
+        ContainingType = innerTest.ContainingType;
+    }
+
+    public ConditionalTest(ITestInfo innerTest, Xunit.TestPlatforms platform)
+        : this(innerTest, GetPlatformConditionFromTestPlatform(platform))
+    {
+    }
+
+    public string TestNameExpression { get; }
+
+    public string DisplayNameForFiltering { get; }
+
+    public string Method { get; }
+    public string ContainingType { get; }
+
+    public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+    {
+        return $"if ({_condition}) {{ {_innerTest.GenerateTestExecution(testReporterWrapper)} }} else {{ {testReporterWrapper.GenerateSkippedTestReporting(_innerTest)} }}";
+    }
+
+    public override bool Equals(object obj)
+    {
+        return obj is ConditionalTest other
+            && TestNameExpression == other.TestNameExpression
+            && Method == other.Method
+            && ContainingType == other.ContainingType
+            && _condition == other._condition
+            && _innerTest.Equals(other._innerTest);
+    }
+
+    private static string GetPlatformConditionFromTestPlatform(Xunit.TestPlatforms platform)
+    {
+        List<string> platformCheckConditions = new();
+        if (platform.HasFlag(Xunit.TestPlatforms.Windows))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsWindows()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.Linux))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsLinux()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.OSX))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsMacOS()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.illumos))
+        {
+            platformCheckConditions.Add(@"global::System.OperatingSystem.IsOSPlatform(""illumos"")");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.Solaris))
+        {
+            platformCheckConditions.Add(@"global::System.OperatingSystem.IsOSPlatform(""Solaris"")");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.Android))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsAndroid()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.iOS))
+        {
+            platformCheckConditions.Add("(global::System.OperatingSystem.IsIOS() && !global::System.OperatingSystem.IsMacCatalyst())");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.tvOS))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsTvOS()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.MacCatalyst))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsMacCatalyst()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.Browser))
+        {
+            platformCheckConditions.Add("global::System.OperatingSystem.IsBrowser()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.FreeBSD))
+        {
+            platformCheckConditions.Add(@"global::System.OperatingSystem.IsFreeBSD()");
+        }
+        if (platform.HasFlag(Xunit.TestPlatforms.NetBSD))
+        {
+            platformCheckConditions.Add(@"global::System.OperatingSystem.IsOSPlatform(""NetBSD"")");
+        }
+        return string.Join(" || ", platformCheckConditions);
+    }
+}
+
+sealed class MemberDataTest : ITestInfo
+{
+    private ITestInfo _innerTest;
+    private string _memberInvocation;
+    private string _loopVarIdentifier;
+    public MemberDataTest(ISymbol referencedMember, ITestInfo innerTest, string externAlias, string argumentLoopVarIdentifier)
+    {
+        TestNameExpression = innerTest.TestNameExpression;
+        Method = innerTest.Method;
+        ContainingType = innerTest.ContainingType;
+        DisplayNameForFiltering = $"{ContainingType}.{Method}(...)";
+        _innerTest = innerTest;
+        _loopVarIdentifier = argumentLoopVarIdentifier;
+
+        string containingType = referencedMember.ContainingType.ToDisplayString(XUnitWrapperGenerator.FullyQualifiedWithoutGlobalNamespace);
+        _memberInvocation = referencedMember switch
+        {
+            IPropertySymbol { IsStatic: true } => $"{externAlias}::{containingType}.{referencedMember.Name}",
+            IMethodSymbol { IsStatic: true, Parameters: { Length: 0 } } => $"{externAlias}::{containingType}.{referencedMember.Name}()",
+            _ => throw new ArgumentException()
+        };
+    }
+
+    public string TestNameExpression { get; }
+    public string DisplayNameForFiltering { get; }
+    public string Method { get; }
+    public string ContainingType { get; }
+
+    public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+    {
+        return $@"
+foreach (object[] {_loopVarIdentifier} in {_memberInvocation})
+{{
+    {_innerTest.GenerateTestExecution(testReporterWrapper)}
+}}
+";
+    }
+
+    public override bool Equals(object obj)
+    {
+        return obj is MemberDataTest other
+            && TestNameExpression == other.TestNameExpression
+            && Method == other.Method
+            && ContainingType == other.ContainingType
+            && _innerTest.Equals(other._innerTest);
+    }
+}
+
+sealed class OutOfProcessTest : ITestInfo
+{
+    public OutOfProcessTest(string displayName, string relativeAssemblyPath)
+    {
+        Method = displayName;
+        DisplayNameForFiltering = displayName;
+        TestNameExpression = $"@\"{displayName}\"";
+        ExecutionStatement = $@"TestLibrary.OutOfProcessTest.RunOutOfProcessTest(typeof(Program).Assembly.Location, @""{relativeAssemblyPath}"");";
+    }
+
+    public string TestNameExpression { get; }
+
+    public string DisplayNameForFiltering { get; }
+
+    public string Method { get; }
+
+    public string ContainingType => "OutOfProcessTest";
+
+    private string ExecutionStatement { get; }
+
+    public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper) => testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+}
+
+sealed class NoTestReporting : ITestReporterWrapper
+{
+    public string WrapTestExecutionWithReporting(string testExecution, ITestInfo test) => testExecution;
+
+    public string GenerateSkippedTestReporting(ITestInfo skippedTest) => string.Empty;
+}
+
+sealed class WrapperLibraryTestSummaryReporting : ITestReporterWrapper
+{
+    private string _summaryLocalIdentifier;
+    private readonly string _filterLocalIdentifier;
+
+    public WrapperLibraryTestSummaryReporting(string summaryLocalIdentifier, string filterLocalIdentifier)
+    {
+        _summaryLocalIdentifier = summaryLocalIdentifier;
+        _filterLocalIdentifier = filterLocalIdentifier;
+    }
+
+    public string WrapTestExecutionWithReporting(string testExecutionExpression, ITestInfo test)
+    {
+        StringBuilder builder = new();
+        builder.AppendLine($"if ({_filterLocalIdentifier} is null || {_filterLocalIdentifier}.ShouldRunTest(@\"{test.ContainingType}.{test.Method}\", {test.TestNameExpression}))");
+        builder.AppendLine("{");
+
+        builder.AppendLine($"TimeSpan testStart = stopwatch.Elapsed;");
+        builder.AppendLine("try {");
+        builder.AppendLine(testExecutionExpression);
+        builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart);");
+        builder.AppendLine("}");
+        builder.AppendLine("catch (System.Exception ex) {");
+        builder.AppendLine($"{_summaryLocalIdentifier}.ReportFailedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\", stopwatch.Elapsed - testStart, ex);");
+        builder.AppendLine("}");
+
+        builder.AppendLine("}");
+        return builder.ToString();
+    }
+
+    public string GenerateSkippedTestReporting(ITestInfo skippedTest)
+    {
+        return $"{_summaryLocalIdentifier}.ReportSkippedTest({skippedTest.TestNameExpression}, \"{skippedTest.ContainingType}\", \"{skippedTest.Method}\", TimeSpan.Zero, string.Empty);";
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/ImmutableDictionaryValueComparer.cs b/src/tests/Common/XUnitWrapperGenerator/ImmutableDictionaryValueComparer.cs
new file mode 100644
index 00000000000000..ec69869e08044a
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/ImmutableDictionaryValueComparer.cs
@@ -0,0 +1,39 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Text;
+
+namespace XUnitWrapperGenerator;
+
+internal class ImmutableDictionaryValueComparer<TKey, TValue> : IEqualityComparer<ImmutableDictionary<TKey, TValue>>
+    where TKey : notnull
+{
+    private readonly IEqualityComparer<TValue> _valueComparer;
+
+    public ImmutableDictionaryValueComparer(IEqualityComparer<TValue> valueComparer)
+    {
+        _valueComparer = valueComparer;
+    }
+
+    public bool Equals(ImmutableDictionary<TKey, TValue> x, ImmutableDictionary<TKey, TValue> y)
+    {
+        if (x.Count != y.Count)
+        {
+            return false;
+        }
+
+        foreach (var pair in x)
+        {
+            if (!y.TryGetValue(pair.Key, out TValue? value) || !_valueComparer.Equals(value, pair.Value))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int GetHashCode(ImmutableDictionary<TKey, TValue> obj) => throw new NotImplementedException();
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
new file mode 100644
index 00000000000000..f3af8c32441b8a
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace XUnitWrapperGenerator;
+
+public static class OptionsHelper
+{
+    private const string ReferenceSystemPrivateCoreLibOption = "build_property.ReferenceSystemPrivateCoreLib";
+    private const string IsMergedTestRunnerAssemblyOption = "build_property.IsMergedTestRunnerAssembly";
+    private const string PriorityOption = "build_property.Priority";
+    private const string RuntimeFlavorOption = "build_property.RuntimeFlavor";
+    private const string IsOutOfProcessTestAssemblyOption = "build_metadata.AdditionalFiles.IsOutOfProcessTestAssembly";
+    private const string TestFilterOption = "build_metadata.AdditionalFiles.TestFilter";
+    private const string TestAssemblyRelativePathOption = "build_metadata.AdditionalFiles.TestAssemblyRelativePath";
+    private const string TestDisplayNameOption = "build_metadata.AdditionalFiles.TestDisplayName";
+
+    private static bool GetBoolOption(this AnalyzerConfigOptions options, string key)
+    {
+        return options.TryGetValue(key, out string? value)
+            && bool.TryParse(value, out bool result)
+            && result;
+    }
+
+    private static int? GetIntOption(this AnalyzerConfigOptions options, string key)
+    {
+        return options.TryGetValue(key, out string? value)
+            && int.TryParse(value, out int result)
+                ? result : 0;
+    }
+
+    internal static bool ReferenceSystemPrivateCoreLib(this AnalyzerConfigOptions options) => options.GetBoolOption(ReferenceSystemPrivateCoreLibOption);
+
+    internal static bool IsMergedTestRunnerAssembly(this AnalyzerConfigOptions options) => options.GetBoolOption(IsMergedTestRunnerAssemblyOption);
+
+    internal static int? Priority(this AnalyzerConfigOptions options) => options.GetIntOption(PriorityOption);
+
+    internal static string RuntimeFlavor(this AnalyzerConfigOptions options) => options.TryGetValue(RuntimeFlavorOption, out string? flavor) ? flavor : "CoreCLR";
+
+    internal static bool IsOutOfProcessTestAssembly(this AnalyzerConfigOptions options) => options.GetBoolOption(IsOutOfProcessTestAssemblyOption);
+
+    internal static string? TestFilter(this AnalyzerConfigOptions options) => options.TryGetValue(TestFilterOption, out string? filter) ? filter : null;
+
+    internal static string? TestAssemblyRelativePath(this AnalyzerConfigOptions options) => options.TryGetValue(TestAssemblyRelativePathOption, out string? flavor) ? flavor : null;
+
+    internal static string? TestDisplayName(this AnalyzerConfigOptions options) => options.TryGetValue(TestDisplayNameOption, out string? flavor) ? flavor : null;
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/RuntimeConfiguration.cs b/src/tests/Common/XUnitWrapperGenerator/RuntimeConfiguration.cs
new file mode 100644
index 00000000000000..74f3da88fa416d
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/RuntimeConfiguration.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Xunit
+{
+    [Flags]
+    public enum RuntimeConfiguration
+    {
+        Any = ~0,
+        Checked = 1,
+        Debug = 1 << 1,
+        Release = 1 << 2
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/RuntimeTestModes.cs b/src/tests/Common/XUnitWrapperGenerator/RuntimeTestModes.cs
new file mode 100644
index 00000000000000..b8c32c93654956
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/RuntimeTestModes.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Xunit
+{
+    [Flags]
+    public enum RuntimeTestModes
+    {
+        // Disable always when using coreclr runtime.
+        Any = ~0,
+
+        // We're running regular tests with no runtime stress modes
+        RegularRun = 1,
+
+        // JitStress, JitStressRegs, JitMinOpts and TailcallStress enable
+        // various modes in the JIT that cause us to exercise more code paths,
+        // and generate different kinds of code
+        JitStress = 1 << 1, // COMPlus_JitStress is set.
+        JitStressRegs = 1 << 2, // COMPlus_JitStressRegs is set.
+        JitMinOpts = 1 << 3, // COMPlus_JITMinOpts is set.
+        TailcallStress = 1 << 4, // COMPlus_TailcallStress is set.
+
+        // ZapDisable says to not use NGEN or ReadyToRun images.
+        // This means we JIT everything.
+        ZapDisable = 1 << 5, // COMPlus_ZapDisable is set.
+
+        // GCStress3 forces a GC at various locations, typically transitions
+        // to/from the VM from managed code. 
+        GCStress3 = 1 << 6,  // COMPlus_GCStress includes mode 0x3.
+
+        // GCStressC forces a GC at every JIT-generated code instruction,
+        // including in NGEN/ReadyToRun code.
+        GCStressC = 1 << 7, // COMPlus_GCStress includes mode 0xC.
+        AnyGCStress = GCStress3 | GCStressC // Disable when any GCStress is exercised.
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/SymbolExtensions.cs b/src/tests/Common/XUnitWrapperGenerator/SymbolExtensions.cs
new file mode 100644
index 00000000000000..6dc122ba04b554
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/SymbolExtensions.cs
@@ -0,0 +1,23 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace XUnitWrapperGenerator;
+
+internal static class SymbolExtensions
+{
+    public static IEnumerable<AttributeData> GetAttributesOnSelfAndContainingSymbols(this ISymbol symbol)
+    {
+        for (ISymbol? containing = symbol; containing is not null; containing = containing.ContainingSymbol)
+        {
+            foreach (var attr in containing.GetAttributes())
+            {
+                yield return attr;
+            }
+        }
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/TargetFrameworkMonikers.cs b/src/tests/Common/XUnitWrapperGenerator/TargetFrameworkMonikers.cs
new file mode 100644
index 00000000000000..255e28493dc4de
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/TargetFrameworkMonikers.cs
@@ -0,0 +1,15 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Xunit
+{
+    [Flags]
+    public enum TargetFrameworkMonikers
+    {
+        Netcoreapp = 1,
+        NetFramework = 2,
+        Any = ~0
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/TestPlatforms.cs b/src/tests/Common/XUnitWrapperGenerator/TestPlatforms.cs
new file mode 100644
index 00000000000000..b84e9f250045ec
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/TestPlatforms.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Xunit
+{
+    [Flags]
+    public enum TestPlatforms
+    {
+        Windows = 1,
+        Linux = 2,
+        OSX = 4,
+        FreeBSD = 8,
+        NetBSD = 16,
+        illumos= 32,
+        Solaris = 64,
+        iOS = 128,
+        tvOS = 256,
+        Android = 512,
+        Browser = 1024,
+        MacCatalyst = 2048,
+        AnyUnix = FreeBSD | Linux | NetBSD | OSX | illumos | Solaris | iOS | tvOS | MacCatalyst | Android | Browser,
+        Any = ~0
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/TestRuntimes.cs b/src/tests/Common/XUnitWrapperGenerator/TestRuntimes.cs
new file mode 100644
index 00000000000000..1e821c10a64897
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/TestRuntimes.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Xunit
+{
+    [Flags]
+    public enum TestRuntimes
+    {
+        CoreCLR = 1,
+        Mono = 2,
+        Any = ~0
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
new file mode 100644
index 00000000000000..bd8aed55080f98
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
@@ -0,0 +1,609 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace XUnitWrapperGenerator;
+
+[Generator]
+public sealed class XUnitWrapperGenerator : IIncrementalGenerator
+{
+    public void Initialize(IncrementalGeneratorInitializationContext context)
+    {
+        var methodsInSource = context.SyntaxProvider.CreateSyntaxProvider(
+                static (node, ct) =>
+                    node.IsKind(SyntaxKind.MethodDeclaration)
+                        && node is MethodDeclarationSyntax method
+                        && method.AttributeLists.Count > 0,
+                static (context, ct) => (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node)!);
+
+        var methodsInReferencedAssemblies = context.MetadataReferencesProvider.Combine(context.CompilationProvider).SelectMany((data, ct) =>
+        {
+            ExternallyReferencedTestMethodsVisitor visitor = new();
+            return visitor.Visit(data.Right.GetAssemblyOrModuleSymbol(data.Left))!;
+        });
+
+        var outOfProcessTests = context.AdditionalTextsProvider.Combine(context.AnalyzerConfigOptionsProvider).SelectMany((data, ct) =>
+        {
+            var (file, options) = data;
+
+            AnalyzerConfigOptions fileOptions = options.GetOptions(file);
+
+            if (fileOptions.IsOutOfProcessTestAssembly())
+            {
+                string? assemblyPath = fileOptions.TestAssemblyRelativePath();
+                string? testDisplayName = fileOptions.TestDisplayName();
+                if (assemblyPath is not null && testDisplayName is not null)
+                {
+                    return ImmutableArray.Create<ITestInfo>(new OutOfProcessTest(testDisplayName, assemblyPath));
+                }
+            }
+
+            return ImmutableArray<ITestInfo>.Empty;
+        });
+
+        var allMethods = methodsInSource.Collect().Combine(methodsInReferencedAssemblies.Collect()).SelectMany((methods, ct) => methods.Left.AddRange(methods.Right));
+
+        var aliasMap = context.CompilationProvider.Select((comp, ct) =>
+        {
+            var aliasMap = ImmutableDictionary.CreateBuilder<string, string>();
+            aliasMap.Add(comp.Assembly.MetadataName, "global");
+            foreach (var reference in comp.References)
+            {
+                aliasMap.Add(comp.GetAssemblyOrModuleSymbol(reference)!.MetadataName, reference.Properties.Aliases.FirstOrDefault() ?? "global");
+            }
+
+            return aliasMap.ToImmutable();
+        }).WithComparer(new ImmutableDictionaryValueComparer<string, string>(EqualityComparer<string>.Default));
+
+        var assemblyName = context.CompilationProvider.Select((comp, ct) => comp.Assembly.MetadataName);
+
+        var alwaysWriteEntryPoint = context.CompilationProvider.Select((comp, ct) => comp.Options.OutputKind == OutputKind.ConsoleApplication && comp.GetEntryPoint(ct) is null);
+
+        context.RegisterImplementationSourceOutput(
+            allMethods
+            .Combine(context.AnalyzerConfigOptionsProvider)
+            .Combine(aliasMap)
+            .SelectMany((data, ct) => ImmutableArray.CreateRange(GetTestMethodInfosForMethod(data.Left.Left, data.Left.Right, data.Right)))
+            .Collect()
+            .Combine(outOfProcessTests.Collect())
+            .Select((tests, ct) => tests.Left.AddRange(tests.Right))
+            .Combine(context.AnalyzerConfigOptionsProvider)
+            .Select((data, ct) =>
+            {
+                var (tests, options) = data;
+                var filter = new XUnitWrapperLibrary.TestFilter(options.GlobalOptions.TestFilter() ?? "");
+                return (ImmutableArray.CreateRange(tests.Where(test => filter.ShouldRunTest($"{test.ContainingType}.{test.Method}", test.DisplayNameForFiltering, Array.Empty<string>()))), options);
+            })
+            .Combine(aliasMap)
+            .Combine(assemblyName)
+            .Combine(alwaysWriteEntryPoint),
+            static (context, data) =>
+            {
+                var ((((methods, configOptions), aliasMap), assemblyName), alwaysWriteEntryPoint) = data;
+
+                if (methods.Length == 0 && !alwaysWriteEntryPoint)
+                {
+                    // If we have no test methods, assume that this project is not migrated to the new system yet
+                    // and that we shouldn't generate a no-op Main method.
+                    return;
+                }
+
+                bool referenceCoreLib = configOptions.GlobalOptions.ReferenceSystemPrivateCoreLib();
+
+                bool isMergedTestRunnerAssembly = configOptions.GlobalOptions.IsMergedTestRunnerAssembly();
+
+                // TODO: add error (maybe in MSBuild that referencing CoreLib directly from a merged test runner is not supported)
+                if (isMergedTestRunnerAssembly && !referenceCoreLib)
+                {
+                    context.AddSource("FullRunner.g.cs", GenerateFullTestRunner(methods, aliasMap, assemblyName));
+                }
+                else
+                {
+                    string consoleType = referenceCoreLib ? "Internal.Console" : "System.Console";
+                    context.AddSource("SimpleRunner.g.cs", GenerateStandaloneSimpleTestRunner(methods, aliasMap, consoleType));
+                }
+            });
+    }
+
+    private static string GenerateFullTestRunner(ImmutableArray<ITestInfo> testInfos, ImmutableDictionary<string, string> aliasMap, string assemblyName)
+    {
+        // For simplicity, we'll use top-level statements for the generated Main method.
+        StringBuilder builder = new();
+        ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter");
+        builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
+
+        builder.AppendLine("XUnitWrapperLibrary.TestFilter filter = args.Length != 0 ? new XUnitWrapperLibrary.TestFilter(args[0]) : null;");
+        builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new();");
+        builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = new();");
+
+        foreach (ITestInfo test in testInfos)
+        {
+            builder.AppendLine(test.GenerateTestExecution(reporter));
+        }
+
+        builder.AppendLine($@"System.IO.File.WriteAllText(""{assemblyName}.testResults.xml"", summary.GetTestResultOutput());");
+
+        return builder.ToString();
+    }
+
+    private static string GenerateStandaloneSimpleTestRunner(ImmutableArray<ITestInfo> testInfos, ImmutableDictionary<string, string> aliasMap, string consoleType)
+    {
+        // For simplicity, we'll use top-level statements for the generated Main method.
+        ITestReporterWrapper reporter = new NoTestReporting();
+        StringBuilder builder = new();
+        builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
+        builder.AppendLine("try {");
+        builder.AppendLine(string.Join("\n", testInfos.Select(m => m.GenerateTestExecution(reporter))));
+        builder.AppendLine($"}} catch(System.Exception ex) {{ {consoleType}.WriteLine(ex.ToString()); return 101; }}");
+        builder.AppendLine("return 100;");
+        return builder.ToString();
+    }
+
+    private class ExternallyReferencedTestMethodsVisitor : SymbolVisitor<IEnumerable<IMethodSymbol>>
+    {
+        public override IEnumerable<IMethodSymbol>? VisitAssembly(IAssemblySymbol symbol)
+        {
+            return Visit(symbol.GlobalNamespace);
+        }
+
+        public override IEnumerable<IMethodSymbol>? VisitModule(IModuleSymbol symbol)
+        {
+            return Visit(symbol.GlobalNamespace);
+        }
+
+        public override IEnumerable<IMethodSymbol>? VisitNamespace(INamespaceSymbol symbol)
+        {
+            foreach (var type in symbol.GetMembers())
+            {
+                foreach (var result in Visit(type) ?? Array.Empty<IMethodSymbol>())
+                {
+                    yield return result;
+                }
+            }
+        }
+
+        public override IEnumerable<IMethodSymbol>? VisitNamedType(INamedTypeSymbol symbol)
+        {
+            if (symbol.DeclaredAccessibility == Accessibility.Public)
+            {
+                foreach (var type in symbol.GetMembers())
+                {
+                    foreach (var result in Visit(type) ?? Array.Empty<IMethodSymbol>())
+                    {
+                        yield return result;
+                    }
+                }
+            }
+        }
+
+        public override IEnumerable<IMethodSymbol>? VisitMethod(IMethodSymbol symbol)
+        {
+            if (symbol.DeclaredAccessibility == Accessibility.Public
+                && symbol.GetAttributes().Any(attr => attr.AttributeClass?.ContainingNamespace.Name == "Xunit"))
+            {
+                return new[] { symbol };
+            }
+            return Array.Empty<IMethodSymbol>();
+        }
+    }
+
+    private static IEnumerable<ITestInfo> GetTestMethodInfosForMethod(IMethodSymbol method, AnalyzerConfigOptionsProvider options, ImmutableDictionary<string, string> aliasMap)
+    {
+        bool factAttribute = false;
+        bool theoryAttribute = false;
+        List<AttributeData> theoryDataAttributes = new();
+        List<AttributeData> filterAttributes = new();
+        foreach (var attr in method.GetAttributesOnSelfAndContainingSymbols())
+        {
+            switch (attr.AttributeClass?.ToDisplayString())
+            {
+                case "Xunit.ConditionalFactAttribute":
+                    filterAttributes.Add(attr);
+                    factAttribute = true;
+                    break;
+                case "Xunit.FactAttribute":
+                    factAttribute = true;
+                    break;
+                case "Xunit.ConditionalTheoryAttribute":
+                    filterAttributes.Add(attr);
+                    theoryAttribute = true;
+                    break;
+                case "Xunit.TheoryAttribute":
+                    theoryAttribute = true;
+                    break;
+                case "Xunit.ConditionalClassAttribute":
+                case "Xunit.SkipOnPlatformAttribute":
+                case "Xunit.ActiveIssueAttribute":
+                case "Xunit.OuterloopAttribute":
+                case "Xunit.PlatformSpecificAttribute":
+                case "Xunit.SkipOnMonoAttribute":
+                case "Xunit.SkipOnTargetFrameworkAttribute":
+                case "Xunit.SkipOnCoreClrAttribute":
+                    filterAttributes.Add(attr);
+                    break;
+                case "Xunit.InlineDataAttribute":
+                case "Xunit.MemberDataAttribute":
+                    theoryDataAttributes.Add(attr);
+                    break;
+            }
+        }
+
+        ImmutableArray<ITestInfo> testInfos = ImmutableArray<ITestInfo>.Empty;
+
+        if (factAttribute)
+        {
+            if (!method.Parameters.IsEmpty)
+            {
+                // todo: emit diagnostic
+            }
+            else if (method.IsStatic && method.ReturnType.SpecialType == SpecialType.System_Int32)
+            {
+                // Support the old executable-based test design where an int return of 100 is success.
+                testInfos = ImmutableArray.Create((ITestInfo)new LegacyStandaloneEntryPointTestMethod(method, aliasMap[method.ContainingAssembly.MetadataName]));
+            }
+            else
+            {
+                testInfos = ImmutableArray.Create((ITestInfo)new BasicTestMethod(method, aliasMap[method.ContainingAssembly.MetadataName]));
+            }
+        }
+        else if (theoryAttribute)
+        {
+            testInfos = CreateTestCases(method, theoryDataAttributes, aliasMap[method.ContainingAssembly.MetadataName]);
+        }
+
+        foreach (var filterAttribute in filterAttributes)
+        {
+            switch (filterAttribute.AttributeClass!.ToDisplayString())
+            {
+                case "Xunit.ConditionalFactAttribute":
+                case "Xunit.ConditionalTheoryAttribute":
+                case "Xunit.ConditionalClassAttribute":
+                    {
+                        ITypeSymbol conditionType;
+                        ImmutableArray<TypedConstant> conditionMembers;
+                        if (filterAttribute.AttributeConstructor!.Parameters.Length == 1)
+                        {
+                            conditionType = method.ContainingType;
+                            conditionMembers = filterAttribute.ConstructorArguments[0].Values;
+                        }
+                        else
+                        {
+                            Debug.Assert(filterAttribute.AttributeConstructor!.Parameters.Length == 2);
+                            conditionType = (ITypeSymbol)filterAttribute.ConstructorArguments[0].Value!;
+                            conditionMembers = filterAttribute.ConstructorArguments[1].Values;
+                        }
+                        testInfos = DecorateWithUserDefinedCondition(
+                            testInfos,
+                            conditionType,
+                            conditionMembers,
+                            aliasMap[conditionType.ContainingAssembly.MetadataName]);
+                        break;
+                    }
+                case "Xunit.OuterloopAttribute":
+                    if (options.GlobalOptions.Priority() == 0)
+                    {
+                        // If we aren't building the outerloop/Pri 1 test suite, then this attribute acts like an
+                        // [ActiveIssue] attribute (it has the same shape).
+                        goto case "Xunit.ActiveIssueAttribute";
+                    }
+                    break;
+                case "Xunit.ActiveIssueAttribute":
+                    if (filterAttribute.AttributeConstructor!.Parameters.Length == 3)
+                    {
+                        ITypeSymbol conditionType = (ITypeSymbol)filterAttribute.ConstructorArguments[1].Value!;
+                        testInfos = DecorateWithUserDefinedCondition(
+                            testInfos,
+                            conditionType,
+                            filterAttribute.ConstructorArguments[2].Values,
+                            aliasMap[conditionType.ContainingAssembly.MetadataName]);
+                        break;
+                    }
+                    else if (filterAttribute.AttributeConstructor.Parameters.Length == 4)
+                    {
+                        testInfos = FilterForSkippedRuntime(
+                            FilterForSkippedTargetFrameworkMonikers(
+                                DecorateWithSkipOnPlatform(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!, options),
+                                (int)filterAttribute.ConstructorArguments[2].Value!),
+                            (int)filterAttribute.ConstructorArguments[3].Value!, options);
+                    }
+                    else
+                    {
+                        switch (filterAttribute.AttributeConstructor.Parameters[1].Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
+                        {
+                            case "global::Xunit.TestPlatforms":
+                                testInfos = DecorateWithSkipOnPlatform(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!, options);
+                                break;
+                            case "global::Xunit.TestRuntimes":
+                                testInfos = FilterForSkippedRuntime(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!, options);
+                                break;
+                            case "global::Xunit.TargetFrameworkMonikers":
+                                testInfos = FilterForSkippedTargetFrameworkMonikers(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!);
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+                    break;
+                case "Xunit.SkipOnMonoAttribute":
+                    if (options.GlobalOptions.RuntimeFlavor() != "Mono")
+                    {
+                        // If we're building tests not for Mono, we can skip handling the specifics of the SkipOnMonoAttribute.
+                        continue;
+                    }
+                    testInfos = DecorateWithSkipOnPlatform(testInfos, (int)filterAttribute.ConstructorArguments[1].Value!, options);
+                    break;
+                case "Xunit.SkipOnPlatformAttribute":
+                    testInfos = DecorateWithSkipOnPlatform(testInfos, (int)filterAttribute.ConstructorArguments[0].Value!, options);
+                    break;
+                case "Xunit.PlatformSpecificAttribute":
+                    testInfos = DecorateWithSkipOnPlatform(testInfos, ~(int)filterAttribute.ConstructorArguments[0].Value!, options);
+                    break;
+                case "Xunit.SkipOnTargetFrameworkAttribute":
+                    testInfos = FilterForSkippedTargetFrameworkMonikers(testInfos, (int)filterAttribute.ConstructorArguments[0].Value!);
+                    break;
+                case "Xunit.SkipOnCoreClrAttribute":
+                    if (options.GlobalOptions.RuntimeFlavor() != "CoreCLR")
+                    {
+                        // If we're building tests not for CoreCLR, we can skip handling the specifics of the SkipOnCoreClrAttribute.
+                        continue;
+                    }
+
+                    Xunit.TestPlatforms skippedTestPlatforms = 0;
+                    Xunit.RuntimeConfiguration skippedConfigurations = 0;
+                    Xunit.RuntimeTestModes skippedTestModes = 0;
+
+                    for (int i = 1; i < filterAttribute.AttributeConstructor!.Parameters.Length; i++)
+                    {
+                        ReadSkippedInformationFromSkipOnCoreClrAttributeArgument(filterAttribute, i);
+                    }
+
+                    void ReadSkippedInformationFromSkipOnCoreClrAttributeArgument(AttributeData filterAttribute, int argumentIndex)
+                    {
+                        int argumentValue = (int)filterAttribute.ConstructorArguments[argumentIndex].Value!;
+                        switch (filterAttribute.AttributeConstructor!.Parameters[argumentIndex].Type.ToDisplayString())
+                        {
+                            case "Xunit.TestPlatforms":
+                                skippedTestPlatforms = (Xunit.TestPlatforms)argumentValue;
+                                break;
+                            case "Xunit.RuntimeTestModes":
+                                skippedTestModes = (Xunit.RuntimeTestModes)argumentValue;
+                                break;
+                            case "Xunit.RuntimeConfiguration":
+                                skippedConfigurations = (Xunit.RuntimeConfiguration)argumentValue;
+                                break;
+                            default:
+                                break;
+                        }
+                    }
+
+                    if (skippedTestModes == Xunit.RuntimeTestModes.Any)
+                    {
+                        testInfos = FilterForSkippedRuntime(testInfos, (int)Xunit.TestRuntimes.CoreCLR, options);
+                    }
+                    testInfos = DecorateWithSkipOnPlatform(testInfos, (int)skippedTestPlatforms, options);
+                    testInfos = DecorateWithSkipOnCoreClrConfiguration(testInfos, skippedTestModes, skippedConfigurations);
+
+                    break;
+            }
+        }
+
+        return testInfos;
+    }
+
+    private static ImmutableArray<ITestInfo> DecorateWithSkipOnCoreClrConfiguration(ImmutableArray<ITestInfo> testInfos, Xunit.RuntimeTestModes skippedTestModes, Xunit.RuntimeConfiguration skippedConfigurations)
+    {
+        const string ConditionClass = "TestLibrary.CoreClrConfigurationDetection";
+        List<string> conditions = new();
+        if (skippedConfigurations.HasFlag(Xunit.RuntimeConfiguration.Debug | Xunit.RuntimeConfiguration.Checked | Xunit.RuntimeConfiguration.Release))
+        {
+            // If all configurations are skipped, just skip the test as a whole
+            return ImmutableArray<ITestInfo>.Empty;
+        }
+
+        if (skippedConfigurations.HasFlag(Xunit.RuntimeConfiguration.Debug))
+        {
+            conditions.Add($"!{ConditionClass}.IsDebugRuntime");
+        }
+        if (skippedConfigurations.HasFlag(Xunit.RuntimeConfiguration.Checked))
+        {
+            conditions.Add($"!{ConditionClass}.IsCheckedRuntime");
+        }
+        if (skippedConfigurations.HasFlag(Xunit.RuntimeConfiguration.Release))
+        {
+            conditions.Add($"!{ConditionClass}.IsReleaseRuntime");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.RegularRun))
+        {
+            conditions.Add($"{ConditionClass}.IsStressTest");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.JitStress))
+        {
+            conditions.Add($"!{ConditionClass}.IsJitStress");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.JitStressRegs))
+        {
+            conditions.Add($"!{ConditionClass}.IsJitStressRegs");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.JitMinOpts))
+        {
+            conditions.Add($"!{ConditionClass}.IsJitMinOpts");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.TailcallStress))
+        {
+            conditions.Add($"!{ConditionClass}.IsTailcallStress");
+        }
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.ZapDisable))
+        {
+            conditions.Add($"!{ConditionClass}.IsZapDisable");
+        }
+
+        if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.AnyGCStress))
+        {
+            conditions.Add($"!{ConditionClass}.IsGCStress");
+        }
+        else if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.GCStress3))
+        {
+            conditions.Add($"!{ConditionClass}.IsGCStress3");
+        }
+        else if (skippedTestModes.HasFlag(Xunit.RuntimeTestModes.GCStressC))
+        {
+            conditions.Add($"!{ConditionClass}.IsGCStressC");
+        }
+
+        return ImmutableArray.CreateRange<ITestInfo>(testInfos.Select(t => new ConditionalTest(t, string.Join(" && ", conditions))));
+    }
+
+    private static ImmutableArray<ITestInfo> FilterForSkippedTargetFrameworkMonikers(ImmutableArray<ITestInfo> testInfos, int v)
+    {
+        var tfm = (Xunit.TargetFrameworkMonikers)v;
+
+        if (tfm.HasFlag(Xunit.TargetFrameworkMonikers.Netcoreapp))
+        {
+            return ImmutableArray<ITestInfo>.Empty;
+        }
+        else
+        {
+            return testInfos;
+        }
+    }
+
+    private static ImmutableArray<ITestInfo> CreateTestCases(IMethodSymbol method, List<AttributeData> theoryDataAttributes, string alias)
+    {
+        var testCasesBuilder = ImmutableArray.CreateBuilder<ITestInfo>();
+        foreach (var attr in theoryDataAttributes)
+        {
+            switch (attr.AttributeClass!.ToDisplayString())
+            {
+                case "Xunit.InlineDataAttribute":
+                    {
+                        var args = attr.ConstructorArguments[0].Values;
+                        if (method.Parameters.Length != args.Length)
+                        {
+                            // Emit diagnostic
+                            continue;
+                        }
+                        var argsAsCode = ImmutableArray.CreateRange(args.Select(a => a.ToCSharpString()));
+                        testCasesBuilder.Add(new BasicTestMethod(method, alias, arguments: argsAsCode));
+                        break;
+                    }
+                case "Xunit.MemberDataAttribute":
+                    {
+                        string? memberName = (string?)attr.ConstructorArguments[0].Value;
+                        if (string.IsNullOrEmpty(memberName))
+                        {
+                            // Emit diagnostic
+                            continue;
+                        }
+                        INamedTypeSymbol memberType = method.ContainingType;
+                        if (attr.NamedArguments.FirstOrDefault(p => p.Key == "MemberType").Value.Value is INamedTypeSymbol memberTypeOverride)
+                        {
+                            memberType = memberTypeOverride;
+                        }
+                        var membersByName = memberType.GetMembers(memberName!);
+                        if (membersByName.Length != 1)
+                        {
+                            // Emit diagnostic
+                            continue;
+                        }
+                        const string argumentVariableIdentifier = "testArguments";
+                        // The display name for the test is an interpolated string that includes the arguments.
+                        string displayNameOverride = $@"$""{alias}::{method.ContainingType.ToDisplayString(FullyQualifiedWithoutGlobalNamespace)}.{method.Name}({{string.Join("","", {argumentVariableIdentifier})}})""";
+                        var argsAsCode = method.Parameters.Select((p, i) => $"({p.Type.ToDisplayString()}){argumentVariableIdentifier}[{i}]").ToImmutableArray();
+                        testCasesBuilder.Add(new MemberDataTest(membersByName[0], new BasicTestMethod(method, alias, argsAsCode, displayNameOverride), alias, argumentVariableIdentifier));
+                        break;
+                    }
+                default:
+                    break;
+            }
+        }
+        return testCasesBuilder.ToImmutable();
+    }
+
+    private static ImmutableArray<ITestInfo> FilterForSkippedRuntime(ImmutableArray<ITestInfo> testInfos, int skippedRuntimeValue, AnalyzerConfigOptionsProvider options)
+    {
+        Xunit.TestRuntimes skippedRuntimes = (Xunit.TestRuntimes)skippedRuntimeValue;
+        string runtimeFlavor = options.GlobalOptions.RuntimeFlavor();
+        if (runtimeFlavor == "Mono" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.Mono))
+        {
+            return ImmutableArray<ITestInfo>.Empty;
+        }
+        else if (runtimeFlavor == "CoreCLR" && skippedRuntimes.HasFlag(Xunit.TestRuntimes.CoreCLR))
+        {
+            return ImmutableArray<ITestInfo>.Empty;
+        }
+        return testInfos;
+    }
+
+    private static ImmutableArray<ITestInfo> DecorateWithSkipOnPlatform(ImmutableArray<ITestInfo> testInfos, int v, AnalyzerConfigOptionsProvider options)
+    {
+        Xunit.TestPlatforms platformsToSkip = (Xunit.TestPlatforms)v;
+        options.GlobalOptions.TryGetValue("build_property.TargetOS", out string? targetOS);
+        Xunit.TestPlatforms targetPlatform = GetPlatformForTargetOS(targetOS);
+
+        if (platformsToSkip == 0)
+        {
+            // In this case, we don't need to skip any platforms
+            return testInfos;
+        }
+        else if (platformsToSkip.HasFlag(targetPlatform))
+        {
+            // If the target platform is skipped, then we don't have any tests to emit.
+            return ImmutableArray<ITestInfo>.Empty;
+        }
+        else if (targetPlatform.HasFlag(platformsToSkip))
+        {
+            // If our target platform encompases one or more of the skipped platforms,
+            // emit a runtime platform check here.
+            Xunit.TestPlatforms platformsToEnableTest = targetPlatform & ~platformsToSkip;
+            return ImmutableArray.CreateRange(testInfos.Select(t => (ITestInfo)new ConditionalTest(t, platformsToEnableTest)));
+        }
+        else
+        {
+            // The target platform is not mentioned in the attribute, just run it as-is.
+            return testInfos;
+        }
+
+        static Xunit.TestPlatforms GetPlatformForTargetOS(string? targetOS)
+        {
+            return targetOS?.ToLowerInvariant() switch
+            {
+                "windows" => Xunit.TestPlatforms.Windows,
+                "linux" => Xunit.TestPlatforms.Linux,
+                "osx" => Xunit.TestPlatforms.OSX,
+                "illumos" => Xunit.TestPlatforms.illumos,
+                "solaris" => Xunit.TestPlatforms.Solaris,
+                "android" => Xunit.TestPlatforms.Android,
+                "ios" => Xunit.TestPlatforms.iOS,
+                "tvos" => Xunit.TestPlatforms.tvOS,
+                "maccatalyst" => Xunit.TestPlatforms.MacCatalyst,
+                "browser" => Xunit.TestPlatforms.Browser,
+                "freebsd" => Xunit.TestPlatforms.FreeBSD,
+                "netbsd" => Xunit.TestPlatforms.NetBSD,
+                null or "" or "anyos" => Xunit.TestPlatforms.Any,
+                _ => 0
+            };
+        }
+    }
+
+    private static ImmutableArray<ITestInfo> DecorateWithUserDefinedCondition(
+        ImmutableArray<ITestInfo> testInfos,
+        ITypeSymbol conditionType,
+        ImmutableArray<TypedConstant> values,
+        string externAlias)
+    {
+        string condition = string.Join("&&", values.Select(v => $"{externAlias}::{conditionType.ToDisplayString(FullyQualifiedWithoutGlobalNamespace)}.{v.Value}"));
+        return ImmutableArray.CreateRange<ITestInfo>(testInfos.Select(m => new ConditionalTest(m, condition)));
+    }
+
+    public static readonly SymbolDisplayFormat FullyQualifiedWithoutGlobalNamespace = SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.csproj b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.csproj
new file mode 100644
index 00000000000000..fe22933c6ae690
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.csproj
@@ -0,0 +1,19 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard2.0</TargetFramework>
+    <Nullable>enable</Nullable>
+    <EnableDefaultItems>true</EnableDefaultItems>
+    <IsRoslynComponent>true</IsRoslynComponent>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis" Version="$(MicrosoftCodeAnalysisVersion)" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisCSharpVersion)" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="$(MicrosoftCodeAnalysisAnalyzersVersion)" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Compile Include="../XUnitWrapperLibrary/TestFilter.cs" Link="TestFilter.cs" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props
new file mode 100644
index 00000000000000..cf35f35cb24d10
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props
@@ -0,0 +1,24 @@
+﻿<Project>
+  <ItemGroup>
+    <!-- Properties used for filtering -->
+    <CompilerVisibleProperty Include="RuntimeFlavor" />
+    <CompilerVisibleProperty Include="TargetOS" />
+    <CompilerVisibleProperty Include="TargetArchitecture" />
+    <CompilerVisibleProperty Include="Priority" />
+    <!-- Properties that influence test harness generation -->
+    <CompilerVisibleProperty Include="ReferenceSystemPrivateCoreLib" />
+    <CompilerVisibleProperty Include="IsMergedTestRunnerAssembly" />
+    <CompilerVisibleProperty Include="TestFilter" />
+    <CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="IsOutOfProcessTestAssembly" />
+    <CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="TestAssemblyRelativePath" />
+    <CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="TestDisplayName" />
+  </ItemGroup>
+
+  <Target Name="AddOutOfProcessTestAssembliesToCompilation" AfterTargets="ResolveProjectReferences">
+    <ItemGroup>
+      <OutOfProcessTests IsOutOfProcessTestAssembly="true" TestAssemblyRelativePath="$([MSBuild]::MakeRelative('$(OutputPath)', '%(Identity)'))" TestDisplayName="$([MSBuild]::MakeRelative('$(TestBinDir)', '%(Identity)'))" />
+      <!-- Change the extension to .cmd to match the existing test names for out-of-proc test runs. -->
+      <AdditionalFiles Include="@(OutOfProcessTests)" TestDisplayName="$([System.IO.Path]::ChangeExtension('%(TestDisplayName)', '.cmd'))" />
+    </ItemGroup>
+  </Target>
+</Project>
diff --git a/src/tests/Common/XUnitWrapperLibrary/TestFilter.cs b/src/tests/Common/XUnitWrapperLibrary/TestFilter.cs
new file mode 100644
index 00000000000000..b290307e6ff77e
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperLibrary/TestFilter.cs
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+namespace XUnitWrapperLibrary;
+
+public class TestFilter
+{
+    interface ISearchClause
+    {
+        bool IsMatch(string fullyQualifiedName, string displayName, string[] traits);
+    }
+
+    enum TermKind
+    {
+        FullyQualifiedName,
+        DisplayName
+    }
+    sealed class NameClause : ISearchClause
+    {
+        public NameClause(TermKind kind, string filter, bool substring)
+        {
+            Kind = kind;
+            Filter = filter;
+            Substring = substring;
+        }
+        public TermKind Kind { get; }
+        public string Filter { get; }
+        public bool Substring { get; }
+
+        public bool IsMatch(string fullyQualifiedName, string displayName, string[] traits)
+        {
+            string stringToSearch = Kind switch
+            {
+                TermKind.FullyQualifiedName => fullyQualifiedName,
+                TermKind.DisplayName => displayName,
+                _ => throw new InvalidOperationException()
+            };
+
+            if (Substring)
+            {
+                return stringToSearch.Contains(Filter);
+            }
+            return stringToSearch == Filter;
+        }
+    }
+
+    sealed class AndClause : ISearchClause
+    {
+        private ISearchClause _left;
+        private ISearchClause _right;
+
+        public AndClause(ISearchClause left, ISearchClause right)
+        {
+            _left = left;
+            _right = right;
+        }
+
+        public bool IsMatch(string fullyQualifiedName, string displayName, string[] traits) => _left.IsMatch(fullyQualifiedName, displayName, traits) && _right.IsMatch(fullyQualifiedName, displayName, traits);
+    }
+
+    sealed class OrClause : ISearchClause
+    {
+        private ISearchClause _left;
+        private ISearchClause _right;
+
+        public OrClause(ISearchClause left, ISearchClause right)
+        {
+            _left = left;
+            _right = right;
+        }
+
+        public bool IsMatch(string fullyQualifiedName, string displayName, string[] traits) => _left.IsMatch(fullyQualifiedName, displayName, traits) || _right.IsMatch(fullyQualifiedName, displayName, traits);
+    }
+
+    sealed class NotClause : ISearchClause
+    {
+        private ISearchClause _inner;
+
+        public NotClause(ISearchClause inner)
+        {
+            _inner = inner;
+        }
+
+        public bool IsMatch(string fullyQualifiedName, string displayName, string[] traits) => !_inner.IsMatch(fullyQualifiedName, displayName, traits);
+    }
+
+    private ISearchClause? _filter;
+
+    public TestFilter(string filterString)
+    {
+        if (filterString.IndexOfAny(new[] { '!', '(', ')', '~', '=' }) != -1)
+        {
+            throw new ArgumentException("Complex test filter expressions are not supported today. The only filters supported today are the simple form supported in 'dotnet test --filter' (substrings of the test's fully qualified name). If further filtering options are desired, file an issue on dotnet/runtime for support.", nameof(filterString));
+        }
+        _filter = new NameClause(TermKind.FullyQualifiedName, filterString, substring: true);
+    }
+
+    public bool ShouldRunTest(string fullyQualifiedName, string displayName, string[]? traits = null) => _filter is null ? true : _filter.IsMatch(fullyQualifiedName, displayName, traits ?? Array.Empty<string>());
+}
diff --git a/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs b/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs
new file mode 100644
index 00000000000000..f7e47ebdeb619b
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperLibrary/TestSummary.cs
@@ -0,0 +1,95 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+namespace XUnitWrapperLibrary;
+
+public class TestSummary
+{
+    readonly record struct TestResult(string Name, string ContainingTypeName, string MethodName, TimeSpan Duration, Exception? Exception, string? SkipReason);
+
+    private int _numPassed = 0;
+
+    private int _numFailed = 0;
+
+    private int _numSkipped = 0;
+
+    private readonly List<TestResult> _testResults = new();
+
+    private DateTime _testRunStart = DateTime.Now;
+
+    public void ReportPassedTest(string name, string containingTypeName, string methodName, TimeSpan duration)
+    {
+        _numPassed++;
+        _testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, null));
+    }
+
+    public void ReportFailedTest(string name, string containingTypeName, string methodName, TimeSpan duration, Exception ex)
+    {
+        _numFailed++;
+        _testResults.Add(new TestResult(name, containingTypeName, methodName, duration, ex, null));
+    }
+
+    public void ReportSkippedTest(string name, string containingTypeName, string methodName, TimeSpan duration, string reason)
+    {
+        _numSkipped++;
+        _testResults.Add(new TestResult(name, containingTypeName, methodName, duration, null, reason));
+    }
+
+    public string GetTestResultOutput()
+    {
+        double totalRunSeconds = (DateTime.Now - _testRunStart).TotalSeconds;
+        // using StringBuilder here for simplicity of loaded IL.
+        StringBuilder resultsFile = new();
+        resultsFile.AppendLine("<assemblies>");
+        resultsFile.AppendLine($@"
+<assembly
+    name=""""
+    test-framework=""XUnitWrapperGenerator-generated-runner""
+    run-date=""{_testRunStart.ToString("yyy-mm-dd")}""
+    run-time=""{_testRunStart.ToString("hh:mm:ss")}""
+    time=""{totalRunSeconds}""
+    total=""{_testResults.Count}""
+    passed=""{_numPassed}""
+    failed=""{_numFailed}""
+    skipped=""{_numSkipped}""
+    errors=""0"">");
+
+        resultsFile.AppendLine($@"
+<collection
+    name=""Collection""
+    time=""{totalRunSeconds}""
+    total=""{_testResults.Count}""
+    passed=""{_numPassed}""
+    failed=""{_numFailed}""
+    skipped=""{_numSkipped}""
+    errors=""0""
+>");
+
+        foreach (var test in _testResults)
+        {
+            resultsFile.Append($@"<test name=""{test.Name}"" type=""{test.ContainingTypeName}"" method=""{test.MethodName}"" time=""{test.Duration}"" ");
+            if (test.Exception is not null)
+            {
+                resultsFile.AppendLine($@"result=""Fail""><failure exception-type=""{test.Exception.GetType()}""><message><![CDATA[{test.Exception.Message}]]></message><stack-trace><![CDATA[{test.Exception.StackTrace}]]></stack-trace></failure></test>");
+            }
+            else if (test.SkipReason is not null)
+            {
+                resultsFile.AppendLine($@"result=""Skip""><reason><![CDATA[{test.SkipReason}]]></reason></test>");
+            }
+            else
+            {
+                resultsFile.AppendLine(@" result=""Pass"" />");
+            }
+        }
+
+        resultsFile.AppendLine("</collection>");
+        resultsFile.AppendLine("</assembly>");
+        resultsFile.AppendLine("</assemblies>");
+
+        return resultsFile.ToString();
+    }
+}
diff --git a/src/tests/Common/XUnitWrapperLibrary/XUnitWrapperLibrary.csproj b/src/tests/Common/XUnitWrapperLibrary/XUnitWrapperLibrary.csproj
new file mode 100644
index 00000000000000..4d2fc0407701c1
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperLibrary/XUnitWrapperLibrary.csproj
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Library</OutputType>
+    <CLRTestKind>BuildOnly</CLRTestKind>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <GenerateRunScript>false</GenerateRunScript>
+    <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework>
+    <EnableDefaultItems>true</EnableDefaultItems>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+</Project>
diff --git a/src/tests/Common/XunitBase.cs b/src/tests/Common/XunitBase.cs
deleted file mode 100644
index abaa1bb1406d73..00000000000000
--- a/src/tests/Common/XunitBase.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.Threading;
-using Xunit.Runners;
-
-namespace Xunit
-{
-    public abstract class XunitBase
-    {
-        private static object consoleLock = new object();
-
-        private static ManualResetEvent finished = new ManualResetEvent(false);
-        
-        private static int result = 100;
-
-        public int RunTests()
-        {
-            var runner = AssemblyRunner.WithoutAppDomain(GetType().Assembly.Location);
-            runner.OnDiscoveryComplete = OnDiscoveryComplete;
-            runner.OnExecutionComplete = OnExecutionComplete;
-            runner.OnTestFailed = OnTestFailed;
-            runner.OnTestSkipped = OnTestSkipped;
-
-            Console.WriteLine("Discovering...");
-
-            runner.Start();
-
-            finished.WaitOne();
-            finished.Dispose();
-
-            return result;
-        }
-
-        private static void OnDiscoveryComplete(DiscoveryCompleteInfo info)
-        {
-            lock (consoleLock)
-                Console.WriteLine($"Running {info.TestCasesToRun} of {info.TestCasesDiscovered} tests...");
-        }
-
-        private static void OnExecutionComplete(ExecutionCompleteInfo info)
-        {
-            lock (consoleLock)
-                Console.WriteLine($"Finished: {info.TotalTests} tests in {Math.Round(info.ExecutionTime, 3)}s ({info.TestsFailed} failed, {info.TestsSkipped} skipped)");
-
-            finished.Set();
-        }
-
-        private static void OnTestFailed(TestFailedInfo info)
-        {
-            lock (consoleLock)
-            {
-                Console.ForegroundColor = ConsoleColor.Red;
-
-                Console.WriteLine("[FAIL] {0}: {1}", info.TestDisplayName, info.ExceptionMessage);
-                if (info.ExceptionStackTrace != null)
-                    Console.WriteLine(info.ExceptionStackTrace);
-
-                Console.ResetColor();
-            }
-
-            result = 101;
-        }
-
-        private static void OnTestSkipped(TestSkippedInfo info)
-        {
-            lock (consoleLock)
-            {
-                Console.ForegroundColor = ConsoleColor.Yellow;
-                Console.WriteLine("[SKIP] {0}: {1}", info.TestDisplayName, info.SkipReason);
-                Console.ResetColor();
-            }
-        }
-    }
-}
diff --git a/src/tests/Common/external/external.csproj b/src/tests/Common/external/external.csproj
index 2f7bb3abb8b3c0..3040614a467970 100644
--- a/src/tests/Common/external/external.csproj
+++ b/src/tests/Common/external/external.csproj
@@ -63,6 +63,7 @@
     <PackageToInclude Include="Microsoft.CodeAnalysis.CSharp"/>
     <PackageToInclude Include="Microsoft.CodeAnalysis.VisualBasic"/>
     <PackageToInclude Include="CommandLineParser"/>
+    <PackageToInclude Include="Microsoft.DotNet.XUnitExtensions" />
 
     <PackageReference Include="Microsoft.CodeAnalysis.Compilers" Version="1.1.1" />
     <PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
@@ -71,6 +72,7 @@
     <PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
     <PackageReference Include="xunit" Version="$(XUnitVersion)" />
     <PackageReference Include="Microsoft.DotNet.XUnitConsoleRunner" Version="$(MicrosoftDotNetXUnitConsoleRunnerVersion)" GeneratePathProperty="True" />
+    <PackageReference Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
     <PackageReference Include="xunit.runner.utility" Version="$(XUnitVersion)" />
   </ItemGroup>
 
diff --git a/src/tests/Directory.Build.props b/src/tests/Directory.Build.props
index 51cd548023ac78..12dbe872646f6c 100644
--- a/src/tests/Directory.Build.props
+++ b/src/tests/Directory.Build.props
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <LibrariesConfiguration>Release</LibrariesConfiguration>
   </PropertyGroup>
-  
+
   <Import Project="$(MSBuildThisFileDirectory)\Common\dir.sdkbuild.props" Condition="'$(UsingMicrosoftNETSdk)' == 'true'"  />
   <Import Project="$(MSBuildThisFileDirectory)\Common\dir.common.props" Condition="'$(UsingMicrosoftNETSdk)' != 'true'"  />
 
@@ -179,4 +179,9 @@
   <PropertyGroup Condition="'$(TargetOS)' == 'Browser'">
     <CLRTestMSBuildArgs>/p:MSBuildEnableWorkloadResolver=false</CLRTestMSBuildArgs>
   </PropertyGroup>
+
+  <PropertyGroup Condition="'$(IsTestsCommonProject)' != 'true'">
+    <BuildAsStandalone Condition="'$(BuildAsStandalone)' == ''">true</BuildAsStandalone>
+    <OutputType>Exe</OutputType>
+  </PropertyGroup>
 </Project>
diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets
index 2866110fb4c360..1061519b6b34e0 100644
--- a/src/tests/Directory.Build.targets
+++ b/src/tests/Directory.Build.targets
@@ -408,6 +408,32 @@
 
   </Target>
 
+  <Target Name="GetRequiresProcessIsolation" Returns="@(ProjectRequiringProcessIsolation)">
+    <ItemGroup Condition="'$(RequiresProcessIsolation)' == 'true'">
+      <ProjectRequiringProcessIsolation Include="$(MSBuildProjectFullPath)" />
+    </ItemGroup>
+  </Target>
+
+  <Target Name="DiscoverProcessIsolatedTestProjectReferences" BeforeTargets="AssignProjectConfiguration">
+    <MSBuild Projects="@(ProjectReference)" Targets="GetRequiresProcessIsolation" SkipNonexistentTargets="true">
+      <Output TaskParameter="TargetOutputs" ItemName="ReferencedProjectRequiringProcessIsolation" />
+    </MSBuild>
+
+    <ItemGroup>
+      <ProjectReference Remove="@(ReferencedProjectRequiringProcessIsolation)" />
+      <ProjectReference Include="@(ReferencedProjectRequiringProcessIsolation)" OutputItemType="OutOfProcessTests" BuildReference="true" ReferenceOutputAssembly="false" />
+    </ItemGroup>
+  </Target>
+
+  <PropertyGroup Condition="'$(Language)' == 'C#' and ('$(BuildAsStandalone)' == 'true' or '$(RequiresProcessIsolation)' == 'true' or '$(IsMergedTestAssembly)' == 'true')">
+    <ReferenceXUnitWrapperGenerator Condition="'$(ReferenceXUnitWrapperGenerator)' == ''">true</ReferenceXUnitWrapperGenerator>
+  </PropertyGroup>
+
+  <ItemGroup Condition="'$(ReferenceXUnitWrapperGenerator)' == 'true'">
+    <ProjectReference Include="$(RepoRoot)/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+  </ItemGroup>
+
+  <Import Project="$(RepoRoot)/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props" />
   <Import Project="$(RepoRoot)eng/liveBuilds.targets" />
   <Import Project="$(MSBuildProjectFullPath).targets" Condition="Exists('$(MSBuildProjectFullPath).targets')"/>
 
diff --git a/src/tests/Interop/ICastable/Castable.cs b/src/tests/Interop/ICastable/Castable.cs
index ee45820c9c808b..3a20177a36e5ec 100644
--- a/src/tests/Interop/ICastable/Castable.cs
+++ b/src/tests/Interop/ICastable/Castable.cs
@@ -4,9 +4,20 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Xunit;
 
 using Console = Internal.Console;
 
+namespace Xunit
+{
+    // Include an inline definition of the SkipOnMonoAttribute type as tests that reference CoreLib
+    // only reference CoreLib and don't reference any other assemblies.
+    public class SkipOnMonoAttribute : Attribute
+    {
+        public SkipOnMonoAttribute(string reason, int testPlatforms = ~0) { }
+    }
+}
+
 public interface IRetArg<T>
 {
     T ReturnArg(T t);
@@ -114,22 +125,17 @@ public RuntimeTypeHandle GetImplType(RuntimeTypeHandle interfaceType)
         return default(RuntimeTypeHandle);
     }
 }
- 
 
-public class Program
+public class CastableTests
 {
-    private static bool passed = true;
-
     public static void Assert(bool value, string message)
     {
-        if (!value)
-        {
-            Console.WriteLine("FAIL! " + message);
-            passed = false;
-        }
+        Xunit.Assert.True(value, message);
     }
 
-    public static int Main()
+    [Fact]
+    [SkipOnMono("ICastable is unsupported on Mono")]
+    public static void Test()
     {
         //Console.WriteLine("Execution started. Attach debugger and press enter.");
         //Console.ReadLine();
@@ -145,7 +151,7 @@ public static int Main()
                      { typeof(IExtra), null }, //we should never use it
                  }
             );
-            
+
             // testing simple cases
             Assert(implProxy is IRetThis, "implProxy should be castable to IRetThis via is");
             Assert(!(implProxy is IUnimplemented), "implProxy should not be castable to IUnimplemented via is");
@@ -158,7 +164,7 @@ public static int Main()
             Assert(!(implProxy is IUnimplemented), "implProxy should not be castable to IUnimplemented via is");
             Assert((implProxy as IUnimplemented) == null, "implProxy should not be castable to IUnimplemented via as");
 
-            
+
             // testing generics
             IRetArg<string> retArgStr = (IRetArg<string>)implProxy;
             Assert(retArgStr.ReturnArg("hohoho") == "hohoho", "retArgStr.ReturnArg() should return arg");
@@ -184,7 +190,7 @@ public static int Main()
             {
                 var _ = (IRetThis)nullCastable;
                 Assert(false, "Exceptions should be thrown from IsInstanceOfInterface");
-            } 
+            }
             catch (CastableException) {}
 
             Assert(!(nullCastable is IRetThis), "null castable shouldn't be allowed to be casted to anything");
@@ -198,7 +204,7 @@ public static int Main()
                 var r = (IRetThis)badCastable;
                 r.ReturnThis();
                 Assert(false, "Exceptions should be thrown from ReturnThis()");
-            } 
+            }
             catch (EntryPointNotFoundException) {}
 
             //delegate testing
@@ -212,17 +218,5 @@ public static int Main()
        {
             Assert(false, e.ToString());
        }
-
-       if (passed)
-       {
-            Console.WriteLine("Test PASSED!");
-            return 100;
-       }
-       else
-       {
-            Console.WriteLine("Test FAILED!");
-            return -1;
-       }
-        
     }
 }
diff --git a/src/tests/Interop/ICastable/Castable.csproj b/src/tests/Interop/ICastable/Castable.csproj
index 6a0eb0bcfe5472..7007b694eb2133 100644
--- a/src/tests/Interop/ICastable/Castable.csproj
+++ b/src/tests/Interop/ICastable/Castable.csproj
@@ -1,9 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
     <!-- Internal.Console is not in the CoreLib reference assemblies.  ICastable is CoreCLR-only  -->
     <ReferenceSystemPrivateCoreLib>true</ReferenceSystemPrivateCoreLib>
-    <CLRTestTargetUnsupported Condition="'$(TargetOS)' == 'Browser' Or '$(TargetOS)' == 'Android' Or '$(TargetOS)' == 'iOS' Or '$(TargetOS)' == 'iOSSimulator'">true</CLRTestTargetUnsupported>
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="Castable.cs" />
diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj
index dd746d4181fb87..6fcc5f2e6ac204 100644
--- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj
+++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj
@@ -9,6 +9,5 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="ICustomMarshaler.cs" />
-    <Compile Include="..\..\..\Common\XunitBase.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj
index 3c152f7cb31b83..febed8fcc2aa01 100644
--- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj
+++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj
@@ -10,6 +10,5 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="ICustomMarshaler.cs" />
-    <Compile Include="..\..\..\Common\XunitBase.cs" />
   </ItemGroup>
 </Project>
diff --git a/src/tests/Interop/PInvoke/Decimal/DecimalTest.cs b/src/tests/Interop/PInvoke/Decimal/DecimalTest.cs
index a10193b9d83608..40afd032ab1b67 100644
--- a/src/tests/Interop/PInvoke/Decimal/DecimalTest.cs
+++ b/src/tests/Interop/PInvoke/Decimal/DecimalTest.cs
@@ -10,26 +10,8 @@ public class DecimalTest
     private const int StartingIntValue = 42;
     private const int NewIntValue = 18;
 
-    public static int Main()
-    {
-        try
-        {
-            RunDecimalTests();
-            RunLPDecimalTests();
-            if (OperatingSystem.IsWindows())
-            {
-                RunCurrencyTests();
-            }
-        }
-        catch (System.Exception ex)
-        {
-            Console.WriteLine(ex.ToString());
-            return 101;
-        }
-        return 100;
-    }
-
-    private static void RunDecimalTests()
+    [Fact]
+    public static void RunDecimalTests()
     {
         Assert.Equal((decimal)StartingIntValue, DecimalTestNative.CreateDecimalFromInt(StartingIntValue));
 
@@ -56,7 +38,8 @@ private static void RunDecimalTests()
         DecimalTestNative.PassThroughDecimalToCallback((decimal)NewIntValue, d => Assert.Equal((decimal)NewIntValue, d));
     }
 
-    private static void RunLPDecimalTests()
+    [Fact]
+    public static void RunLPDecimalTests()
     {
         Assert.Equal((decimal)StartingIntValue, DecimalTestNative.CreateLPDecimalFromInt(StartingIntValue));
 
@@ -72,7 +55,9 @@ private static void RunLPDecimalTests()
         DecimalTestNative.PassThroughLPDecimalToCallback((decimal)NewIntValue, d => Assert.Equal((decimal)NewIntValue, d));
     }
 
-    private static void RunCurrencyTests()
+    [Fact]
+    [PlatformSpecific(TestPlatforms.Windows)]
+    public static void RunCurrencyTests()
     {
         Assert.Throws<MarshalDirectiveException>(() => DecimalTestNative.CreateCurrencyFromInt(StartingIntValue));
 
diff --git a/src/tests/Interop/PInvoke/Decimal/DecimalTest.csproj b/src/tests/Interop/PInvoke/Decimal/DecimalTest.csproj
index 78c3de24d972d1..e868b25ab24053 100644
--- a/src/tests/Interop/PInvoke/Decimal/DecimalTest.csproj
+++ b/src/tests/Interop/PInvoke/Decimal/DecimalTest.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-  </PropertyGroup>
   <ItemGroup>
     <Compile Include="*.cs" />
   </ItemGroup>
diff --git a/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.cs b/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.cs
index d7cedae8110fad..2f79b9b20173eb 100644
--- a/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.cs
+++ b/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.cs
@@ -11,23 +11,8 @@ public class Vector2_3_4Test
     private const int StartingIntValue = 42;
     private const int NewIntValue = 18;
 
-    public static int Main()
-    {
-        try
-        {
-            RunVector2Tests();
-            RunVector3Tests();
-            RunVector4Tests();
-        }
-        catch (System.Exception ex)
-        {
-            Console.WriteLine(ex);
-            return 101;
-        }
-        return 100;
-    }
-
-    private static void RunVector2Tests()
+    [Fact]
+    public static void RunVector2Tests()
     {
         Console.WriteLine($"Running {nameof(RunVector2Tests)}... ");
         float X = StartingIntValue;
@@ -69,7 +54,8 @@ private static void RunVector2Tests()
         }));
     }
 
-    private static void RunVector3Tests()
+    [Fact]
+    public static void RunVector3Tests()
     {
         Console.WriteLine($"Running {nameof(RunVector3Tests)}... ");
         float X = StartingIntValue;
@@ -111,7 +97,8 @@ private static void RunVector3Tests()
         }));
     }
 
-    private static void RunVector4Tests()
+    [Fact]
+    public static void RunVector4Tests()
     {
         Console.WriteLine($"Running {nameof(RunVector4Tests)}... ");
         float X = StartingIntValue;
diff --git a/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.csproj b/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.csproj
index 78c3de24d972d1..e868b25ab24053 100644
--- a/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.csproj
+++ b/src/tests/Interop/PInvoke/Vector2_3_4/Vector2_3_4.csproj
@@ -1,7 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-  </PropertyGroup>
   <ItemGroup>
     <Compile Include="*.cs" />
   </ItemGroup>
diff --git a/src/tests/JIT/IL_Conformance/Old/directed/AutoInit.il b/src/tests/JIT/IL_Conformance/Old/directed/AutoInit.il
index 9e2dc256dbe439..609fb1276213fa 100644
--- a/src/tests/JIT/IL_Conformance/Old/directed/AutoInit.il
+++ b/src/tests/JIT/IL_Conformance/Old/directed/AutoInit.il
@@ -3,11 +3,21 @@
 
 
 .assembly extern legacy library mscorlib {}
-.assembly autoinit{}
+.assembly AutoInit {}
+.assembly extern xunit.core
+{
+	.publickeytoken = (
+		8d 05 b1 bb 7a 6f db 6c
+	)
+	.ver 2:4:2:0
+}
 
 .class public auto_init {
 
-.method public static int32 main(class [mscorlib]System.String[]) {
+.method public static int32 main() {
+    .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = (
+            01 00 00 00
+        )
 .zeroinit
 .entrypoint
 .locals	(int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32,int32)
diff --git a/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.cs b/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.cs
index 1fc60de83ad968..a2e57fee7a3987 100644
--- a/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.cs
+++ b/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.cs
@@ -3,10 +3,9 @@
 
 
 using System;
-using System.Runtime.CompilerServices;
+using Xunit;
 
-
-// This test represent deep execution tree that could case C stack overflow 
+// This test represent deep execution tree that could case C stack overflow
 // in recursive tree walkers. It should work if the compiler spills deep tree periodically or
 // recursive walkers are replaced with non-recursive versions.
 class GitHub_10215
@@ -461,6 +460,10 @@ private static int CalcBigExpressionWithoutStores()
 
     }
 
+    // Use the [Fact] attribute directly on Main like an IL test since we can't run the generator on this project.
+    // The above expression creates a deep enough syntax tree that running a tree traversal of the syntax causes a stack overflow.
+    // As a result, running any generator on this project causes the compilation to crash.
+    [Fact]
     public static int Main()
     {
         if (CalcBigExpressionWithoutStores() == 100)
diff --git a/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.csproj b/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.csproj
index 656bedde102452..742e20eb6e5ad4 100644
--- a/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.csproj
+++ b/src/tests/JIT/Regression/JitBlue/GitHub_10215/GitHub_10215.csproj
@@ -2,6 +2,8 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <CLRTestPriority>1</CLRTestPriority>
+    <!-- Explicitly don't reference the generator here as the source here blows the stack whenever a SyntaxVisitor traverses it (which happens as part of the generator execution). -->
+    <ReferenceXUnitWrapperGenerator>false</ReferenceXUnitWrapperGenerator>
   </PropertyGroup>
   <PropertyGroup>
     <DebugType />
diff --git a/src/tests/JIT/superpmi/superpmicollect.csproj b/src/tests/JIT/superpmi/superpmicollect.csproj
index c3c7fae8808fc8..86ddca45522001 100644
--- a/src/tests/JIT/superpmi/superpmicollect.csproj
+++ b/src/tests/JIT/superpmi/superpmicollect.csproj
@@ -25,17 +25,14 @@
   <ItemGroup>
     <_SpmiTestProjects Include="..\Performance\CodeQuality\Bytemark\Bytemark.csproj" />
     <_SpmiTestProjects Include="..\Methodical\fp\exgen\10w5d_cs_do.csproj" />
-    <_SpmiTestProjects Update="@(_SpmiTestProjects)">
-      <Properties>OutputPath=$(OutputPath)\%(FileName)</Properties>
-    </_SpmiTestProjects>
+    <ProjectReference Include="@(_SpmiTestProjects)">
+      <Targets>Build</Targets>
+      <OutputItemType>_SpmiTest</OutputItemType>
+      <TargetPath>%(FileName)</TargetPath>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
   </ItemGroup>
 
-  <!-- This target builds the executables and test running scripts for the _SpmiTestProjects
-       to the output directory of the current project. -->
-  <Target Name="_BuildSpmiTestProjects" BeforeTargets="Build">
-    <MSBuild Projects="@(_SpmiTestProjects)" />
-  </Target>
-
   <!-- _SpmiTestNamesFile contains the file names of the _SpmiTestProjects. -->
   <PropertyGroup>
     <_SpmiTestNamesFile>$(IntermediateOutputPath)\SpmiTestNames.txt</_SpmiTestNamesFile>
@@ -55,4 +52,14 @@
       <LogicalName>SpmiTestNames</LogicalName>
     </EmbeddedResource>
   </ItemGroup>
+
+  <Target Name="_CopySpmiTestsToOutput" DependsOnTargets="ResolveProjectReferences" BeforeTargets="AssignTargetPaths">
+    <Copy SourceFiles="@(_SpmiTest)" DestinationFolder="$([System.IO.Path]::GetDirectoryName('$(OutputPath)'))/$([System.IO.Path]::GetFileName('%(FileName)'))" SkipUnchangedFiles="True" />
+  </Target>
+
+  <!-- This target builds the executables and test running scripts for the _SpmiTestProjects
+      to the output directory of the current project. -->
+  <Target Name="_BuildSpmiTestProjectScripts" BeforeTargets="CreateExecuteScript">
+    <MSBuild Projects="@(_SpmiTestProjects)" Targets="CreateExecuteScript" Properties="OutputPath=$(OutputPath)\%(FileName)" />
+  </Target>
 </Project>
diff --git a/src/tests/JIT/superpmi/superpmicollect.csproj.targets b/src/tests/JIT/superpmi/superpmicollect.csproj.targets
deleted file mode 100644
index ca98b9666a04a4..00000000000000
--- a/src/tests/JIT/superpmi/superpmicollect.csproj.targets
+++ /dev/null
@@ -1,7 +0,0 @@
-<Project>
-  <!-- This target builds the executables and test running scripts for the _SpmiTestProjects
-       to the output directory of the current project. -->
-  <Target Name="_BuildSpmiTestProjectScripts" BeforeTargets="CreateExecuteScript">
-    <MSBuild Projects="@(_SpmiTestProjects)" Targets="CreateExecuteScript" />
-  </Target>
-</Project>
diff --git a/src/tests/build.proj b/src/tests/build.proj
index 7a161270572bdb..0519c409f1d7db 100644
--- a/src/tests/build.proj
+++ b/src/tests/build.proj
@@ -25,6 +25,8 @@
     <RestoreProjects Include="Common\test_dependencies_fs\test_dependencies.fsproj" />
     <RestoreProjects Include="Common\test_dependencies\test_dependencies.csproj" />
     <RestoreProjects Include="Common\CoreCLRTestLibrary\CoreCLRTestLibrary.csproj" />
+    <RestoreProjects Include="Common\XUnitWrapperGenerator\XUnitWrapperGenerator.csproj" />
+    <RestoreProjects Include="Common\XUnitWrapperLibrary\XUnitWrapperLibrary.csproj" />
     <RestoreProjects Include="Common\external\external.csproj" />
     <RestoreProjects Include="Common\ilasm\ilasm.ilproj" />
   </ItemGroup>
diff --git a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs
index 5b697d5802759a..d804dd63a0f4ca 100644
--- a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs
+++ b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTester.cs
@@ -9,7 +9,7 @@
 
 namespace IlasmPortablePdbTests
 {
-    public class IlasmPortablePdbTester : XunitBase
+    public class IlasmPortablePdbTester : IDisposable
     {
         private const string CoreRoot = "CORE_ROOT";
         private const string IlasmFileName = "ilasm";
@@ -36,7 +36,7 @@ public void TestPortablePdbDebugDirectory(string ilSource)
         {
             var ilasm = IlasmPortablePdbTesterCommon.GetIlasmFullPath(CoreRootVar, IlasmFile);
             IlasmPortablePdbTesterCommon.Assemble(ilasm, ilSource, TestDir, out string dll, out string pdb);
-            
+
             using (var peStream = new FileStream(dll, FileMode.Open, FileAccess.Read))
             {
                 using (var peReader = new PEReader(peStream))
@@ -102,7 +102,7 @@ public void TestPortablePdbDocuments()
                         var portablePdbMdReader = pdbReaderProvider.GetMetadataReader();
                         Assert.NotNull(portablePdbMdReader);
                         Assert.Equal(expected.Count, portablePdbMdReader.Documents.Count);
-                        
+
                         int i = 0;
                         foreach (var documentHandle in portablePdbMdReader.Documents)
                         {
@@ -246,7 +246,7 @@ public void TestPortablePdbLocalScope(string ilSource)
                                 Assert.Equal(expected[i].Length, localScope.Length);
                                 var variableHandles = localScope.GetLocalVariables();
                                 Assert.Equal(expected[i].Variables.Count, variableHandles.Count);
-                                
+
                                 int j = 0;
                                 foreach (var variableHandle in localScope.GetLocalVariables())
                                 {
@@ -255,7 +255,7 @@ public void TestPortablePdbLocalScope(string ilSource)
                                     var variableName = portablePdbMdReader.GetString(variable.Name);
                                     Assert.Equal(expected[i].Variables[j].Name, variableName);
                                     Assert.Equal(expected[i].Variables[j].Index, variable.Index);
-                                    Assert.Equal(expected[i].Variables[j].IsDebuggerHidden, 
+                                    Assert.Equal(expected[i].Variables[j].IsDebuggerHidden,
                                         variable.Attributes == LocalVariableAttributes.DebuggerHidden);
                                     j++;
                                 }
@@ -268,10 +268,7 @@ public void TestPortablePdbLocalScope(string ilSource)
                 }
             }
         }
-        
-        public static int Main(string[] args)
-        {
-            return new IlasmPortablePdbTester().RunTests();
-        }
+
+        public void Dispose() {}
     }
 }
diff --git a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj
index d5759248ef8ee4..b490324b35c166 100644
--- a/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj
+++ b/src/tests/ilasm/PortablePdb/IlasmPortablePdbTests.csproj
@@ -6,7 +6,6 @@
     <Compile Include="IlasmPortablePdbTester.cs" />
     <Compile Include="IlasmPortablePdbTesterCommon.cs" />
     <Compile Include="IlasmPortablePdbTesterTypes.cs" />
-    <Compile Include="..\..\Common\XunitBase.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Remove="Resources\**" />
diff --git a/src/tests/ilverify/ILMethodTester.cs b/src/tests/ilverify/ILMethodTester.cs
index 83865a982c0d84..1dea2366dcb5d7 100644
--- a/src/tests/ilverify/ILMethodTester.cs
+++ b/src/tests/ilverify/ILMethodTester.cs
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection.Metadata;
@@ -16,7 +17,7 @@ public class ILMethodTester
         [Theory(DisplayName = "")]
         [MemberData(nameof(TestDataLoader.GetMethodsWithValidIL), MemberType = typeof(TestDataLoader))]
         [Trait("", "Valid IL Tests")]
-        void TestMethodsWithValidIL(ValidILTestCase validIL)
+        public static void TestMethodsWithValidIL(ValidILTestCase validIL)
         {
             var results = Verify(validIL);
             Assert.Empty(results);
@@ -25,7 +26,7 @@ void TestMethodsWithValidIL(ValidILTestCase validIL)
         [Theory(DisplayName = "")]
         [MemberData(nameof(TestDataLoader.GetMethodsWithInvalidIL), MemberType = typeof(TestDataLoader))]
         [Trait("", "Invalid IL Tests")]
-        void TestMethodsWithInvalidIL(InvalidILTestCase invalidIL)
+        public static void TestMethodsWithInvalidIL(InvalidILTestCase invalidIL)
         {
             IEnumerable<VerificationResult> results = null;
 
diff --git a/src/tests/ilverify/ILTypeVerificationTester.cs b/src/tests/ilverify/ILTypeVerificationTester.cs
index 902e612ff76129..2e35b6f63ebb29 100644
--- a/src/tests/ilverify/ILTypeVerificationTester.cs
+++ b/src/tests/ilverify/ILTypeVerificationTester.cs
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection.Metadata;
@@ -11,12 +12,12 @@
 
 namespace ILVerification.Tests
 {
-    public class ILTypeVerificationTester : XunitBase
+    public class ILTypeVerificationTester
     {
         [Theory(DisplayName = "")]
         [MemberData(nameof(TestDataLoader.GetTypesWithValidType), MemberType = typeof(TestDataLoader))]
         [Trait("", "Valid type implementation tests")]
-        private void TestValidTypes(ValidTypeTestCase validType)
+        public static void TestValidTypes(ValidTypeTestCase validType)
         {
             IEnumerable<VerificationResult> results = Verify(validType);
             Assert.Empty(results);
@@ -25,7 +26,7 @@ private void TestValidTypes(ValidTypeTestCase validType)
         [Theory(DisplayName = "")]
         [MemberData(nameof(TestDataLoader.GetTypesWithInvalidType), MemberType = typeof(TestDataLoader))]
         [Trait("", "Invalid type implementation tests")]
-        private void TestInvalidTypes(InvalidTypeTestCase invalidType)
+        public static void TestInvalidTypes(InvalidTypeTestCase invalidType)
         {
             IEnumerable<VerificationResult> results = null;
 
@@ -65,10 +66,5 @@ private static IEnumerable<VerificationResult> Verify(TestCase testCase)
             });
             return verifier.Verify(module.PEReader, typeHandle);
         }
-
-        public static int Main(string[] args)
-        {
-            return new ILTypeVerificationTester().RunTests();
-        }
     }
 }
diff --git a/src/tests/ilverify/ILVerification.Tests.csproj b/src/tests/ilverify/ILVerificationTests.csproj
similarity index 89%
rename from src/tests/ilverify/ILVerification.Tests.csproj
rename to src/tests/ilverify/ILVerificationTests.csproj
index 779ef5ed4e5b27..049390bad80f48 100644
--- a/src/tests/ilverify/ILVerification.Tests.csproj
+++ b/src/tests/ilverify/ILVerificationTests.csproj
@@ -12,13 +12,11 @@
     <Compile Include="ILMethodTester.cs" />
     <Compile Include="ILTypeVerificationTester.cs" />
     <Compile Include="TestDataLoader.cs" />
-    <Compile Include="..\Common\XunitBase.cs" />
   </ItemGroup>
 
   <Import Project="..\..\coreclr\tools\ILVerification\ILVerification.projitems" />
 
   <ItemGroup>
-    <PackageReference Include="xunit" Version="$(XUnitVersion)" />
     <PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
   </ItemGroup>
 
diff --git a/src/tests/ilverify/TestDataLoader.cs b/src/tests/ilverify/TestDataLoader.cs
index 52231124b307e1..7823215c221f22 100644
--- a/src/tests/ilverify/TestDataLoader.cs
+++ b/src/tests/ilverify/TestDataLoader.cs
@@ -289,7 +289,7 @@ protected override PEReader ResolveCore(string simpleName)
         }
     }
 
-    abstract class TestCase : IXunitSerializable
+    public abstract class TestCase : IXunitSerializable
     {
         public string TestName { get; set; }
         public string TypeName { get; set; }
@@ -324,12 +324,12 @@ public override string ToString()
     /// <summary>
     /// Describes a test case with a method that contains valid IL
     /// </summary>
-    class ValidILTestCase : TestCase { }
+    public class ValidILTestCase : TestCase { }
 
     /// <summary>
     /// Describes a test case with a method that contains invalid IL with the expected VerifierErrors
     /// </summary>
-    class InvalidILTestCase : TestCase
+    public class InvalidILTestCase : TestCase
     {
         public List<VerifierError> ExpectedVerifierErrors { get; set; }
 
@@ -369,9 +369,9 @@ private static string GetErrorsString(List<VerifierError> errors)
         }
     }
 
-    class ValidTypeTestCase : TestCase { }
+    public class ValidTypeTestCase : TestCase { }
 
-    class InvalidTypeTestCase : TestCase
+    public class InvalidTypeTestCase : TestCase
     {
         public List<VerifierError> ExpectedVerifierErrors { get; set; }
 
diff --git a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTester.cs b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTester.cs
index 155fd38a78662e..674c6652515478 100644
--- a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTester.cs
+++ b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTester.cs
@@ -6,13 +6,13 @@
 
 namespace R2RDumpTests
 {
-    public class R2RDumpTester : XunitBase
+    public class R2RDumpTester
     {
         private const string CoreRoot = "CORE_ROOT";
         private const string R2RDumpRelativePath = "R2RDump";
         private const string R2RDumpFile = "R2RDump.dll";
         private const string CoreRunFileName = "corerun";
-        
+
         public static string FindExePath(string exe)
         {
             if (OperatingSystem.IsWindows())
@@ -37,7 +37,8 @@ public static string FindExePath(string exe)
         }
 
         [Fact]
-        public void DumpCoreLib()
+        [SkipOnMono("Ready-To-Run is a CoreCLR-only feature", TestPlatforms.Any)]
+        public static void DumpCoreLib()
         {
             string CoreRootVar = Environment.GetEnvironmentVariable(CoreRoot);
             bool IsUnix = !OperatingSystem.IsWindows();
@@ -73,10 +74,5 @@ public void DumpCoreLib()
                 Assert.True(!failed);
             }
         }
-
-        public static int Main(string[] args)
-        {
-            return new R2RDumpTester().RunTests();
-        }
     }
-}
\ No newline at end of file
+}
diff --git a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj
index 539d8bcf9029b3..dc513e6d2b63cd 100644
--- a/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj
+++ b/src/tests/readytorun/r2rdump/FrameworkTests/R2RDumpTests.csproj
@@ -9,14 +9,10 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="R2RDumpTester.cs" />
-    <Compile Include="..\..\..\Common\XunitBase.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Remove="Resources\**" />
     <EmbeddedResource Remove="Resources\**" />
     <None Remove="Resources\**" />
   </ItemGroup>
-  <ItemGroup>
-    <PackageReference Include="xunit" Version="$(XUnitVersion)" />
-  </ItemGroup>
 </Project>
