diff --git a/src/tests/Common/XUnitWrapperGenerator/CodeBuilder.cs b/src/tests/Common/XUnitWrapperGenerator/CodeBuilder.cs
new file mode 100644
index 0000000000000..1477f544de348
--- /dev/null
+++ b/src/tests/Common/XUnitWrapperGenerator/CodeBuilder.cs
@@ -0,0 +1,228 @@
+// 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.Text;
+using static System.FormattableString;
+
+namespace XUnitWrapperGenerator;
+
+///
+/// A helper class for generating indented code. Indentation is automatically added to lines.
+/// Trailing whitespace is removed from lines.
+///
+[DebuggerDisplay("Code = {_code}")]
+public class CodeBuilder {
+ private readonly Stack _indentLevels;
+ private string _currentIndentString;
+ private readonly int _indentSize;
+ private readonly StringBuilder _code;
+ private const int DefaultAdditionalIndent = 1;
+
+ private sealed class IndentationContext : IDisposable {
+ private CodeBuilder Builder { get; }
+ private bool _disposed;
+ private string? EndLine { get; }
+
+ ///
+ /// Constructor.
+ ///
+ /// The associated with this object.
+ /// The number of indentation levels to add.
+ /// Line to add after disposing the indentation context
+ public IndentationContext(CodeBuilder builder, uint additionalIndent = DefaultAdditionalIndent, string? endLine = null) {
+ Builder = builder;
+ _disposed = false;
+ EndLine = endLine;
+ Builder.PushIndent(additionalIndent);
+ }
+
+ ///
+ /// Performs cleanup actions at the end of the lifetime.
+ /// This involves decreasing the level of indentation on
+ /// the object that was used to
+ /// construct this .
+ ///
+ public void Dispose() {
+ if (_disposed) return;
+ Builder.PopIndent();
+ if (EndLine != null) Builder.AppendLine(EndLine);
+ _disposed = true;
+ }
+ }
+
+ ///
+ /// Constructor.
+ ///
+ /// The number of spaces each level of indentation adds.
+ public CodeBuilder(uint indentSize = 4) {
+ _indentLevels = new Stack();
+ _indentSize = Convert.ToInt32(indentSize);
+ _currentIndentString = "";
+ _code = new StringBuilder();
+ _indentLevels.Push(0);
+ }
+
+ public bool IsEmpty => _code.Length == 0;
+
+ public static CodeBuilder Create(string initialCode) {
+ var code = new CodeBuilder();
+ code.Append(initialCode);
+ return code;
+ }
+
+ public static CodeBuilder CreateNewLine(string initialCode) {
+ var code = new CodeBuilder();
+ code.Append(initialCode);
+ code.AppendLine();
+ return code;
+ }
+
+ ///
+ /// Push a new indent level.
+ ///
+ /// The amount of indentation to add.
+ public void PushIndent(uint additionalIndent = DefaultAdditionalIndent) {
+ int existingIndent = _indentLevels.Peek();
+ var newIndent = (int) (existingIndent + additionalIndent);
+ _indentLevels.Push(newIndent);
+ _currentIndentString = new string(' ', newIndent * _indentSize);
+ }
+
+ ///
+ /// Pop an indent level (and restore the indent level to before the last call to ).
+ ///
+ public void PopIndent() {
+ _indentLevels.Pop();
+ _currentIndentString = new string(' ', _indentLevels.Peek() * _indentSize);
+ }
+
+ private bool AtStartOfLine() {
+ if (_code.Length == 0) {
+ return true;
+ }
+
+ return _code[_code.Length - 1] == '\n';
+ }
+
+ private void Append(string code, bool allowLeadingWhiteSpace) {
+ if (string.IsNullOrEmpty(code)) return;
+
+ string[] lines = code.Split('\n');
+
+ // Do entire check first to avoid a partial write in the case of failure
+ if (!allowLeadingWhiteSpace) {
+ for (int i = 0; i < lines.Length; ++i) {
+ if ((i > 0 || AtStartOfLine())
+ && (lines[i].Length > 0) && char.IsWhiteSpace(lines[i][0])) {
+ throw new ArgumentException(Invariant($@"Whitespace (0x{(int)lines[i][0]:x2}) at start of line {i} in input '{code}'"));
+ }
+ }
+ }
+
+ for (int i = 0; i < lines.Length; ++i) {
+ if (i != 0) AppendLine();
+
+ string line = lines[i];
+ if (AtStartOfLine() && !string.IsNullOrWhiteSpace(line)) _code.Append(_currentIndentString);
+ _code.Append(line);
+ }
+ }
+
+ ///
+ /// Append the given code. The currently active indentation level is applied at newlines.
+ ///
+ /// Thrown when a line already contains leading whitespace
+ /// The code to append.
+ public void Append(string code) => Append(code, allowLeadingWhiteSpace: false);
+
+ ///
+ /// Append the given, already-indented code. The currently active indentation level is also applied at newlines.
+ ///
+ /// The code to append.
+ public void AppendIndented(string code) => Append(code, allowLeadingWhiteSpace: true);
+
+ ///
+ /// Append the given, already-indented code. The currently active indentation level is also applied at newlines.
+ ///
+ /// The code to append.
+ public void Append(CodeBuilder code) => AppendIndented(code.GetCode());
+
+ ///
+ /// Append the given code followed by a line terminator. The currently active indentation level is applied at newlines.
+ ///
+ /// The line to append.
+ public void AppendLine(string codeLine) {
+ Append(codeLine);
+ AppendLine();
+ }
+
+ /// Append a blank line.
+ public void AppendLine() {
+ int lastToKeep;
+ for (lastToKeep = _code.Length - 1; lastToKeep >= 0; --lastToKeep) {
+ if (_code[lastToKeep] == '\n' || !char.IsWhiteSpace(_code[lastToKeep])) {
+ break;
+ }
+ }
+ _code.Length = lastToKeep + 1;
+ _code.AppendLine();
+ }
+
+ ///
+ /// Appends a block of code using the current indentation.
+ ///
+ /// The code block.
+ public void AppendBlock(string block) {
+ if (block == null) throw new ArgumentNullException(nameof(block));
+
+ using (var reader = new StringReader(block)) {
+ string line = reader.ReadLine();
+ if (line == null) return;
+
+ AppendIndented(line);
+ while ((line = reader.ReadLine()) != null) {
+ AppendLine();
+ AppendIndented(line);
+ }
+ }
+ }
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// String to add before the braces and indentation context. If non-empty, then a new line will be
+ /// appended after it.
+ ///
+ /// Number of indentation levels to add.
+ /// A new .
+ public IDisposable NewScope(string? introduction = null, uint additionalIndent = DefaultAdditionalIndent) {
+ if (!string.IsNullOrEmpty(introduction)) {
+ this.AppendLine(introduction!);
+ }
+ return new IndentationContext(this, additionalIndent);
+ }
+
+ ///
+ /// Creates a new with an introduction and
+ /// surrounded by braces.
+ ///
+ /// String to add before the braces and indentation context.
+ /// Number of indentation levels to add.
+ /// A new .
+ public IDisposable NewBracesScope(string? introduction = null, uint additionalIndent = DefaultAdditionalIndent) {
+ if (!string.IsNullOrEmpty(introduction)) {
+ this.Append(introduction!);
+ }
+ this.AppendLine(string.IsNullOrEmpty(introduction) ? "{" : " {");
+ return new IndentationContext(this, additionalIndent: additionalIndent, endLine: "}");
+ }
+
+ /// Returns the built-up code.
+ /// The built-up code.
+ public string GetCode() => _code.ToString();
+}
diff --git a/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs b/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
index 81e07deb6041e..afbfeb30a566b 100644
--- a/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
+++ b/src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
@@ -17,12 +17,12 @@ interface ITestInfo
string Method { get; }
string ContainingType { get; }
- string GenerateTestExecution(ITestReporterWrapper testReporterWrapper);
+ CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper);
}
interface ITestReporterWrapper
{
- string WrapTestExecutionWithReporting(string testExecution, ITestInfo test);
+ CodeBuilder WrapTestExecutionWithReporting(CodeBuilder testExecution, ITestInfo test);
string GenerateSkippedTestReporting(ITestInfo skippedTest);
}
@@ -52,9 +52,9 @@ public BasicTestMethod(IMethodSymbol method, string externAlias, ImmutableArray<
public string ContainingType { get; }
private string ExecutionStatement { get; }
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
{
- return testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+ return testReporterWrapper.WrapTestExecutionWithReporting(CodeBuilder.CreateNewLine(ExecutionStatement), this);
}
public override bool Equals(object obj)
@@ -84,9 +84,9 @@ public LegacyStandaloneEntryPointTestMethod(IMethodSymbol method, string externA
public string ContainingType { get; }
private string ExecutionStatement { get; }
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
{
- return testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+ return testReporterWrapper.WrapTestExecutionWithReporting(CodeBuilder.CreateNewLine(ExecutionStatement), this);
}
public override bool Equals(object obj)
@@ -124,9 +124,20 @@ public ConditionalTest(ITestInfo innerTest, Xunit.TestPlatforms platform)
public string Method { get; }
public string ContainingType { get; }
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
{
- return $"if ({_condition}) {{ {_innerTest.GenerateTestExecution(testReporterWrapper)} }} else {{ {testReporterWrapper.GenerateSkippedTestReporting(_innerTest)} }}";
+ CodeBuilder builder = new();
+ builder.AppendLine($"if ({_condition})");
+ using (builder.NewBracesScope())
+ {
+ builder.Append(_innerTest.GenerateTestExecution(testReporterWrapper));
+ }
+ builder.AppendLine($"else");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine(testReporterWrapper.GenerateSkippedTestReporting(_innerTest));
+ }
+ return builder;
}
public override bool Equals(object obj)
@@ -222,14 +233,16 @@ public MemberDataTest(ISymbol referencedMember, ITestInfo innerTest, string exte
public string Method { get; }
public string ContainingType { get; }
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
{
- return $@"
-foreach (object[] {_loopVarIdentifier} in {_memberInvocation})
-{{
- {_innerTest.GenerateTestExecution(testReporterWrapper)}
-}}
-";
+ CodeBuilder builder = new();
+ builder.AppendLine();
+ builder.AppendLine($@"foreach (object[] {_loopVarIdentifier} in {_memberInvocation})");
+ using (builder.NewBracesScope())
+ {
+ builder.Append(_innerTest.GenerateTestExecution(testReporterWrapper));
+ }
+ return builder;
}
public override bool Equals(object obj)
@@ -249,31 +262,35 @@ public OutOfProcessTest(string displayName, string relativeAssemblyPath)
Method = displayName;
DisplayNameForFiltering = displayName;
TestNameExpression = $"@\"{displayName}\"";
- ExecutionStatement = $@"
-if (TestLibrary.OutOfProcessTest.OutOfProcessTestsSupported)
-{{
-TestLibrary.OutOfProcessTest.RunOutOfProcessTest(typeof(Program).Assembly.Location, @""{relativeAssemblyPath}"");
-}}
-";
+ RelativeAssemblyPath = relativeAssemblyPath;
+ ExecutionStatement = new CodeBuilder();
+ ExecutionStatement.AppendLine();
+ ExecutionStatement.AppendLine("if (TestLibrary.OutOfProcessTest.OutOfProcessTestsSupported)");
+ using (ExecutionStatement.NewBracesScope())
+ {
+ ExecutionStatement.AppendLine($@"TestLibrary.OutOfProcessTest.RunOutOfProcessTest(typeof(Program).Assembly.Location, @""{relativeAssemblyPath}"");");
+ }
}
public string TestNameExpression { get; }
public string DisplayNameForFiltering { get; }
+ private string RelativeAssemblyPath { get; }
+
public string Method { get; }
public string ContainingType => "OutOfProcessTest";
- private string ExecutionStatement { get; }
+ private CodeBuilder ExecutionStatement { get; }
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper) => testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper) => testReporterWrapper.WrapTestExecutionWithReporting(ExecutionStatement, this);
public override bool Equals(object obj)
{
return obj is OutOfProcessTest other
&& DisplayNameForFiltering == other.DisplayNameForFiltering
- && ExecutionStatement == other.ExecutionStatement;
+ && RelativeAssemblyPath == other.RelativeAssemblyPath;
}
}
@@ -295,10 +312,10 @@ public TestWithCustomDisplayName(ITestInfo inner, string displayName)
public string ContainingType => _inner.ContainingType;
- public string GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
+ public CodeBuilder GenerateTestExecution(ITestReporterWrapper testReporterWrapper)
{
ITestReporterWrapper dummyInnerWrapper = new NoTestReporting();
- string innerExecution = _inner.GenerateTestExecution(dummyInnerWrapper);
+ CodeBuilder innerExecution = _inner.GenerateTestExecution(dummyInnerWrapper);
return testReporterWrapper.WrapTestExecutionWithReporting(innerExecution, this);
}
@@ -312,7 +329,7 @@ public override bool Equals(object obj)
sealed class NoTestReporting : ITestReporterWrapper
{
- public string WrapTestExecutionWithReporting(string testExecution, ITestInfo test) => testExecution;
+ public CodeBuilder WrapTestExecutionWithReporting(CodeBuilder testExecution, ITestInfo test) => testExecution;
public string GenerateSkippedTestReporting(ITestInfo skippedTest) => string.Empty;
}
@@ -330,38 +347,41 @@ public WrapperLibraryTestSummaryReporting(string summaryLocalIdentifier, string
_outputRecorderIdentifier = outputRecorderIdentifier;
}
- public string WrapTestExecutionWithReporting(string testExecutionExpression, ITestInfo test)
+ public CodeBuilder WrapTestExecutionWithReporting(CodeBuilder testExecutionExpression, ITestInfo test)
{
- StringBuilder builder = new();
+ CodeBuilder builder = new();
builder.AppendLine($"if ({_filterLocalIdentifier} is null || {_filterLocalIdentifier}.ShouldRunTest(@\"{test.ContainingType}.{test.Method}\","
+ $" {test.TestNameExpression}))");
- builder.AppendLine("{");
-
- builder.AppendLine($"System.TimeSpan testStart = stopwatch.Elapsed;");
- builder.AppendLine("try {");
- builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Running test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
- builder.AppendLine($"{_outputRecorderIdentifier}.ResetTestOutput();");
- builder.AppendLine(testExecutionExpression);
-
- builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\","
- + $" stopwatch.Elapsed - testStart, {_outputRecorderIdentifier}.GetTestOutput(), tempLogSw, statsCsvSw);");
-
- builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Passed test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
- builder.AppendLine("}");
- builder.AppendLine("catch (System.Exception ex) {");
-
- builder.AppendLine($"{_summaryLocalIdentifier}.ReportFailedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\","
- + $" stopwatch.Elapsed - testStart, ex, {_outputRecorderIdentifier}.GetTestOutput(), tempLogSw, statsCsvSw);");
-
- builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Failed test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
- builder.AppendLine("}");
-
- builder.AppendLine("}");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($"System.TimeSpan testStart = stopwatch.Elapsed;");
+ builder.AppendLine("try");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Running test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
+ builder.AppendLine($"{_outputRecorderIdentifier}.ResetTestOutput();");
+ builder.Append(testExecutionExpression);
+
+ builder.AppendLine($"{_summaryLocalIdentifier}.ReportPassedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\","
+ + $" stopwatch.Elapsed - testStart, {_outputRecorderIdentifier}.GetTestOutput(), tempLogSw, statsCsvSw);");
+
+ builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Passed test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
+ }
+ builder.AppendLine("catch (System.Exception ex)");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($"{_summaryLocalIdentifier}.ReportFailedTest({test.TestNameExpression}, \"{test.ContainingType}\", @\"{test.Method}\","
+ + $" stopwatch.Elapsed - testStart, ex, {_outputRecorderIdentifier}.GetTestOutput(), tempLogSw, statsCsvSw);");
+
+ builder.AppendLine($"System.Console.WriteLine(\"{{0:HH:mm:ss.fff}} Failed test: {{1}}\", System.DateTime.Now, {test.TestNameExpression});");
+ }
+ }
builder.AppendLine("else");
- builder.AppendLine("{");
- builder.AppendLine(GenerateSkippedTestReporting(test));
- builder.AppendLine("}");
- return builder.ToString();
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine(GenerateSkippedTestReporting(test));
+ }
+ return builder;
}
public string GenerateSkippedTestReporting(ITestInfo skippedTest)
diff --git a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
index ae1d12d57b0f8..3f4e8ae532829 100644
--- a/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
+++ b/src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
@@ -147,19 +147,40 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
});
}
+ private static void AppendAliasMap(CodeBuilder builder, ImmutableDictionary aliasMap)
+ {
+ bool didOutput = false;
+ foreach (string alias in aliasMap.Values.Where(alias => alias != "global").OrderBy(a => a))
+ {
+ builder.AppendLine($"extern alias {alias};");
+ didOutput = true;
+ }
+ if (didOutput)
+ {
+ builder.AppendLine();
+ }
+ }
+
private static string GenerateFullTestRunner(ImmutableArray testInfos, ImmutableDictionary aliasMap, string assemblyName)
{
// For simplicity, we'll use top-level statements for the generated Main method.
- StringBuilder builder = new();
- builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
+ CodeBuilder builder = new();
+ AppendAliasMap(builder, aliasMap);
+
builder.AppendLine("System.Collections.Generic.HashSet testExclusionList = XUnitWrapperLibrary.TestFilter.LoadTestExclusionList();");
+ builder.AppendLine();
- builder.Append("\n"); // Make the FullRunner.g.cs file a bit more readable.
builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.tempLog.xml""))");
- builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.tempLog.xml"");");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.tempLog.xml"");");
+ }
builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.testStats.csv""))");
- builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.testStats.csv"");");
- builder.Append("\n");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.testStats.csv"");");
+ }
+ builder.AppendLine();
builder.AppendLine("XUnitWrapperLibrary.TestFilter filter = new (args, testExclusionList);");
// builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new(TestCount.Count);");
@@ -167,154 +188,220 @@ private static string GenerateFullTestRunner(ImmutableArray testInfos
builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();");
builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder = new(System.Console.Out);");
builder.AppendLine("System.Console.SetOut(outputRecorder);");
+ builder.AppendLine();
- builder.Append("\n");
builder.AppendLine($@"using (System.IO.StreamWriter tempLogSw = System.IO.File.AppendText(""{assemblyName}.tempLog.xml""))");
- builder.AppendLine($@"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(""{assemblyName}.testStats.csv"")){{");
- builder.AppendLine("statsCsvSw.WriteLine($\"{TestCount.Count},0,0,0\");");
+ builder.AppendLine($@"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(""{assemblyName}.testStats.csv""))");
- ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
-
- StringBuilder testExecutorBuilder = new();
- int testsLeftInCurrentTestExecutor = 0;
- int currentTestExecutor = 0;
+ CodeBuilder testExecutorBuilder = new();
int totalTestsEmitted = 0;
- if (testInfos.Length > 0)
+ using (builder.NewBracesScope())
{
- // Break tests into groups of 50 so that we don't create an unreasonably large main method
- // Excessively large methods are known to take a long time to compile, and use excessive stack
- // leading to test failures.
- foreach (ITestInfo test in testInfos)
+ builder.AppendLine("statsCsvSw.WriteLine($\"{TestCount.Count},0,0,0\");");
+
+ ITestReporterWrapper reporter =
+ new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
+
+ int testsLeftInCurrentTestExecutor = 0;
+ int currentTestExecutor = 0;
+
+ if (testInfos.Length > 0)
{
- if (testsLeftInCurrentTestExecutor == 0)
+ // Break tests into groups of 50 so that we don't create an unreasonably large main method
+ // Excessively large methods are known to take a long time to compile, and use excessive stack
+ // leading to test failures.
+ foreach (ITestInfo test in testInfos)
{
- if (currentTestExecutor != 0)
+ if (testsLeftInCurrentTestExecutor == 0)
+ {
+ if (currentTestExecutor != 0)
+ {
+ testExecutorBuilder.PopIndent();
+ testExecutorBuilder.AppendLine("}");
+ testExecutorBuilder.AppendLine();
+ }
+
+ currentTestExecutor++;
+ testExecutorBuilder.AppendLine($"void TestExecutor{currentTestExecutor}(System.IO.StreamWriter tempLogSw, System.IO.StreamWriter statsCsvSw)");
+ testExecutorBuilder.AppendLine("{");
+ testExecutorBuilder.PushIndent();
+
+ builder.AppendLine($"TestExecutor{currentTestExecutor}(tempLogSw, statsCsvSw);");
+ testsLeftInCurrentTestExecutor = 50; // Break test executors into groups of 50, which empirically seems to work well
+ }
+ else
{
- testExecutorBuilder.AppendLine("}");
+ testExecutorBuilder.AppendLine();
}
- currentTestExecutor++;
- testExecutorBuilder.AppendLine($"void TestExecutor{currentTestExecutor}(System.IO.StreamWriter tempLogSw, System.IO.StreamWriter statsCsvSw) {{");
- builder.AppendLine($"TestExecutor{currentTestExecutor}(tempLogSw, statsCsvSw);");
- testsLeftInCurrentTestExecutor = 50; // Break test executors into groups of 50, which empirically seems to work well
+ testExecutorBuilder.Append(test.GenerateTestExecution(reporter));
+ totalTestsEmitted++;
+ testsLeftInCurrentTestExecutor--;
}
- testExecutorBuilder.AppendLine(test.GenerateTestExecution(reporter));
- totalTestsEmitted++;
- testsLeftInCurrentTestExecutor--;
+ testExecutorBuilder.PopIndent();
+ testExecutorBuilder.AppendLine("}");
+ testExecutorBuilder.AppendLine();
}
-
- testExecutorBuilder.AppendLine("}");
}
-
- // Closing the 'using' statements that stream the temporary files.
- builder.AppendLine("}\n");
+ builder.AppendLine();
builder.AppendLine($@"string testResults = summary.GetTestResultOutput(""{assemblyName}"");");
builder.AppendLine($@"string workitemUploadRoot = System.Environment.GetEnvironmentVariable(""HELIX_WORKITEM_UPLOAD_ROOT"");");
- builder.AppendLine($@"if (workitemUploadRoot != null) System.IO.File.WriteAllText(System.IO.Path.Combine(workitemUploadRoot, ""{assemblyName}.testResults.xml.txt""), testResults);");
+ builder.AppendLine($@"if (workitemUploadRoot != null)");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"System.IO.File.WriteAllText(System.IO.Path.Combine(workitemUploadRoot, ""{assemblyName}.testResults.xml.txt""), testResults);");
+ }
+ builder.AppendLine();
+
builder.AppendLine($@"System.IO.File.WriteAllText(""{assemblyName}.testResults.xml"", testResults);");
builder.AppendLine("return 100;");
+ builder.AppendLine();
builder.Append(testExecutorBuilder);
builder.AppendLine("public static class TestCount { public const int Count = " + totalTestsEmitted.ToString() + "; }");
- return builder.ToString();
+ return builder.GetCode();
}
private static string GenerateXHarnessTestRunner(ImmutableArray testInfos, ImmutableDictionary aliasMap, string assemblyName)
{
// For simplicity, we'll use top-level statements for the generated Main method.
- StringBuilder builder = new();
- builder.AppendLine(string.Join("\n", aliasMap.Values.Where(alias => alias != "global").Select(alias => $"extern alias {alias};")));
+ CodeBuilder builder = new();
+ AppendAliasMap(builder, aliasMap);
+
builder.AppendLine("System.Collections.Generic.HashSet testExclusionList = XUnitWrapperLibrary.TestFilter.LoadTestExclusionList();");
+ builder.AppendLine();
- builder.AppendLine("try {");
- builder.AppendLine($@"return await XHarnessRunnerLibrary.RunnerEntryPoint.RunTests(RunTests, ""{assemblyName}"", args.Length != 0 ? args[0] : null, testExclusionList);");
- builder.AppendLine("} catch(System.Exception ex) { System.Console.WriteLine(ex.ToString()); return 101; }");
+ builder.AppendLine("try");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"return await XHarnessRunnerLibrary.RunnerEntryPoint.RunTests(RunTests, ""{assemblyName}"", args.Length != 0 ? args[0] : null, testExclusionList);");
+ }
+ builder.AppendLine("catch(System.Exception ex)");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine("System.Console.WriteLine(ex.ToString());");
+ builder.AppendLine("return 101;");
+ }
+ builder.AppendLine();
builder.AppendLine("static XUnitWrapperLibrary.TestSummary RunTests(XUnitWrapperLibrary.TestFilter filter)");
- builder.AppendLine("{");
- builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new();");
- builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = new();");
- builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder = new(System.Console.Out);");
- builder.AppendLine("System.Console.SetOut(outputRecorder);");
-
- builder.Append("\n");
- builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.tempLog.xml""))");
- builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.tempLog.xml"");");
- builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.testStats.csv""))");
- builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.testStats.csv"");");
- builder.Append("\n");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine("XUnitWrapperLibrary.TestSummary summary = new();");
+ builder.AppendLine("System.Diagnostics.Stopwatch stopwatch = new();");
+ builder.AppendLine("XUnitWrapperLibrary.TestOutputRecorder outputRecorder = new(System.Console.Out);");
+ builder.AppendLine("System.Console.SetOut(outputRecorder);");
+ builder.AppendLine();
- ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
+ builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.tempLog.xml""))");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.tempLog.xml"");");
+ }
+ builder.AppendLine($@"if (System.IO.File.Exists(""{assemblyName}.testStats.csv""))");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine($@"System.IO.File.Delete(""{assemblyName}.testStats.csv"");");
+ }
+ builder.AppendLine();
- StringBuilder testExecutorBuilder = new();
- int testsLeftInCurrentTestExecutor = 0;
- int currentTestExecutor = 0;
+ ITestReporterWrapper reporter = new WrapperLibraryTestSummaryReporting("summary", "filter", "outputRecorder");
- // Open the stream writer for the temp log.
- builder.AppendLine($@"using (System.IO.StreamWriter tempLogSw = System.IO.File.AppendText(""{assemblyName}.templog.xml""))");
- builder.AppendLine($@"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(""{assemblyName}.testStats.csv"")){{");
- builder.AppendLine($"statsCsvSw.WriteLine(\"{testInfos.Length},0,0,0\");");
+ CodeBuilder testExecutorBuilder = new();
+ int testsLeftInCurrentTestExecutor = 0;
+ int currentTestExecutor = 0;
- if (testInfos.Length > 0)
- {
- // Break tests into groups of 50 so that we don't create an unreasonably large main method
- // Excessively large methods are known to take a long time to compile, and use excessive stack
- // leading to test failures.
- foreach (ITestInfo test in testInfos)
+ // Open the stream writer for the temp log.
+ builder.AppendLine($@"using (System.IO.StreamWriter tempLogSw = System.IO.File.AppendText(""{assemblyName}.templog.xml""))");
+ builder.AppendLine($@"using (System.IO.StreamWriter statsCsvSw = System.IO.File.AppendText(""{assemblyName}.testStats.csv""))");
+ using (builder.NewBracesScope())
{
- if (testsLeftInCurrentTestExecutor == 0)
+ builder.AppendLine($"statsCsvSw.WriteLine(\"{testInfos.Length},0,0,0\");");
+
+ if (testInfos.Length > 0)
{
- if (currentTestExecutor != 0)
- testExecutorBuilder.AppendLine("}");
-
- currentTestExecutor++;
- testExecutorBuilder.AppendLine($"static void TestExecutor{currentTestExecutor}("
- + "XUnitWrapperLibrary.TestSummary summary, "
- + "XUnitWrapperLibrary.TestFilter filter, "
- + "XUnitWrapperLibrary.TestOutputRecorder outputRecorder, "
- + "System.Diagnostics.Stopwatch stopwatch, "
- + "System.IO.StreamWriter tempLogSw, "
- + "System.IO.StreamWriter statsCsvSw){");
-
- builder.AppendLine($"TestExecutor{currentTestExecutor}(summary, filter, outputRecorder, stopwatch, tempLogSw, statsCsvSw);");
- testsLeftInCurrentTestExecutor = 50; // Break test executors into groups of 50, which empirically seems to work well
- }
+ // Break tests into groups of 50 so that we don't create an unreasonably large main method
+ // Excessively large methods are known to take a long time to compile, and use excessive stack
+ // leading to test failures.
+ foreach (ITestInfo test in testInfos)
+ {
+ if (testsLeftInCurrentTestExecutor == 0)
+ {
+ if (currentTestExecutor != 0)
+ {
+ testExecutorBuilder.PopIndent();
+ testExecutorBuilder.AppendLine("}");
+ testExecutorBuilder.AppendLine();
+ }
+
+ currentTestExecutor++;
+ testExecutorBuilder.AppendLine($"static void TestExecutor{currentTestExecutor}("
+ + "XUnitWrapperLibrary.TestSummary summary, "
+ + "XUnitWrapperLibrary.TestFilter filter, "
+ + "XUnitWrapperLibrary.TestOutputRecorder outputRecorder, "
+ + "System.Diagnostics.Stopwatch stopwatch, "
+ + "System.IO.StreamWriter tempLogSw, "
+ + "System.IO.StreamWriter statsCsvSw)");
+ testExecutorBuilder.AppendLine("{");
+ testExecutorBuilder.PushIndent();
+
+ builder.AppendLine($"TestExecutor{currentTestExecutor}(summary, filter, outputRecorder, stopwatch, tempLogSw, statsCsvSw);");
+ testsLeftInCurrentTestExecutor = 50; // Break test executors into groups of 50, which empirically seems to work well
+ }
+ else
+ {
+ testExecutorBuilder.AppendLine();
+ }
- testExecutorBuilder.AppendLine(test.GenerateTestExecution(reporter));
- testsLeftInCurrentTestExecutor--;
+ testExecutorBuilder.Append(test.GenerateTestExecution(reporter));
+ testsLeftInCurrentTestExecutor--;
+ }
+
+ testExecutorBuilder.PopIndent();
+ testExecutorBuilder.AppendLine("}");
+ testExecutorBuilder.AppendLine();
+ }
}
+ builder.AppendLine("return summary;");
- testExecutorBuilder.AppendLine("}");
- builder.AppendLine("}");
+ builder.Append(testExecutorBuilder);
}
- builder.AppendLine("return summary;");
- builder.AppendLine("}");
-
- builder.Append(testExecutorBuilder);
-
- return builder.ToString();
+ return builder.GetCode();
}
private static string GenerateStandaloneSimpleTestRunner(ImmutableArray testInfos, ImmutableDictionary 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("namespace __GeneratedMainWrapper;");
- builder.AppendLine("class __GeneratedMainWrapper {");
- builder.AppendLine("public static int Main() {");
- builder.AppendLine("try {");
- builder.AppendLine(string.Join("\n", testInfos.Select(m => m.GenerateTestExecution(reporter))));
- builder.AppendLine("} catch(System.Exception ex) { System.Console.WriteLine(ex.ToString()); return 101; }");
- builder.AppendLine("return 100;");
- builder.AppendLine("}");
- builder.AppendLine("}");
- return builder.ToString();
+ CodeBuilder builder = new();
+ AppendAliasMap(builder, aliasMap);
+ builder.AppendLine("class __GeneratedMainWrapper");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine("public static int Main()");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine("try");
+ using (builder.NewBracesScope())
+ {
+ foreach (ITestInfo testInfo in testInfos)
+ {
+ builder.Append(testInfo.GenerateTestExecution(reporter));
+ }
+ }
+ builder.AppendLine("catch(System.Exception ex)");
+ using (builder.NewBracesScope())
+ {
+ builder.AppendLine("System.Console.WriteLine(ex.ToString());");
+ builder.AppendLine("return 101;");
+ }
+ builder.AppendLine("return 100;");
+ }
+ }
+ return builder.GetCode();
}
private class ExternallyReferencedTestMethodsVisitor : SymbolVisitor>
diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets
index e26e9f6388215..81b4fc841a8c8 100644
--- a/src/tests/Directory.Build.targets
+++ b/src/tests/Directory.Build.targets
@@ -13,9 +13,11 @@
true
Exe
+
BuildAndRun
BuildOnly
0
+
$(NoWarn);CS2008
diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes_r.csproj b/src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes.Arm_r.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes_r.csproj
rename to src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes.Arm_r.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes_ro.csproj b/src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes.Arm_ro.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes_ro.csproj
rename to src/tests/JIT/HardwareIntrinsics/Arm/Aes/Aes.Arm_ro.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32_r.csproj b/src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32.Arm_r.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32_r.csproj
rename to src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32.Arm_r.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32_ro.csproj b/src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32.Arm_ro.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32_ro.csproj
rename to src/tests/JIT/HardwareIntrinsics/Arm/Crc32/Crc32.Arm_ro.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
index d81af3a381450..696c3d04a6dd1 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_r.csproj
@@ -1,6 +1,5 @@
- true
20
true
true
@@ -26,6 +25,7 @@
-
+
+
diff --git a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
index cec6dbb86c481..e9fd5bcfd4999 100644
--- a/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
+++ b/src/tests/JIT/HardwareIntrinsics/HardwareIntrinsics_ro.csproj
@@ -1,6 +1,5 @@
- true
20
true
true
@@ -26,7 +25,7 @@
-
-
+
+
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_Vector128_r.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_r.csproj
rename to src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_Vector128_r.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_Vector128_ro.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_ro.csproj
rename to src/tests/JIT/HardwareIntrinsics/X86/AvxVnni_Vector128/MultiplyWideningAndAdd_Vector128_ro.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32_r.csproj b/src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32.X64_r.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32_r.csproj
rename to src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32.X64_r.csproj
diff --git a/src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32_ro.csproj b/src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32.X64_ro.csproj
similarity index 100%
rename from src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32_ro.csproj
rename to src/tests/JIT/HardwareIntrinsics/X86/Sse42.X64/Crc32.X64_ro.csproj
diff --git a/src/tests/JIT/IL_Conformance/IL_Conformance.csproj b/src/tests/JIT/IL_Conformance/IL_Conformance.csproj
index 5e641dbafb2cf..0f7b4f5ff8284 100644
--- a/src/tests/JIT/IL_Conformance/IL_Conformance.csproj
+++ b/src/tests/JIT/IL_Conformance/IL_Conformance.csproj
@@ -1,9 +1,7 @@
-
- true
-
-
+
+
\ No newline at end of file
diff --git a/src/tests/JIT/Methodical/Methodical_d1.csproj b/src/tests/JIT/Methodical/Methodical_d1.csproj
index 1b765ef609e58..0053845f32274 100644
--- a/src/tests/JIT/Methodical/Methodical_d1.csproj
+++ b/src/tests/JIT/Methodical/Methodical_d1.csproj
@@ -1,13 +1,11 @@
-
- true
-
-
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_d2.csproj b/src/tests/JIT/Methodical/Methodical_d2.csproj
index 0ec205658464f..4aca955892f50 100644
--- a/src/tests/JIT/Methodical/Methodical_d2.csproj
+++ b/src/tests/JIT/Methodical/Methodical_d2.csproj
@@ -1,7 +1,4 @@
-
- true
-
@@ -9,6 +6,7 @@
-
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_do.csproj b/src/tests/JIT/Methodical/Methodical_do.csproj
index 002f38b1e12da..96b0976b7ccf4 100644
--- a/src/tests/JIT/Methodical/Methodical_do.csproj
+++ b/src/tests/JIT/Methodical/Methodical_do.csproj
@@ -1,8 +1,7 @@
-
- true
-
-
+
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_others.csproj b/src/tests/JIT/Methodical/Methodical_others.csproj
index 356c3d7587a73..4021ca8992282 100644
--- a/src/tests/JIT/Methodical/Methodical_others.csproj
+++ b/src/tests/JIT/Methodical/Methodical_others.csproj
@@ -1,12 +1,11 @@
-
- true
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_r1.csproj b/src/tests/JIT/Methodical/Methodical_r1.csproj
index 532ce8b581839..1efc37e0cbb0d 100644
--- a/src/tests/JIT/Methodical/Methodical_r1.csproj
+++ b/src/tests/JIT/Methodical/Methodical_r1.csproj
@@ -1,13 +1,11 @@
-
- true
-
-
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_r2.csproj b/src/tests/JIT/Methodical/Methodical_r2.csproj
index 68f110ea6d64a..5e54baf3ed6d7 100644
--- a/src/tests/JIT/Methodical/Methodical_r2.csproj
+++ b/src/tests/JIT/Methodical/Methodical_r2.csproj
@@ -1,7 +1,4 @@
-
- true
-
@@ -9,6 +6,7 @@
-
+
+
diff --git a/src/tests/JIT/Methodical/Methodical_ro.csproj b/src/tests/JIT/Methodical/Methodical_ro.csproj
index a75e209c30a3d..acf22626a849f 100644
--- a/src/tests/JIT/Methodical/Methodical_ro.csproj
+++ b/src/tests/JIT/Methodical/Methodical_ro.csproj
@@ -1,8 +1,7 @@
-
- true
-
-
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj
index 115b67e172e88..65bab37faa42f 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests0-99.csproj
@@ -1,11 +1,12 @@
1
- true
-
-
-
+
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj
index 08f6a1a0c49cb..79c89194d7b2a 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests100-199.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj
index bddda0d0c5b0f..da5a9726e4d05 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1000-1099.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj
index 365bcf7d49c1c..c523d3602f26a 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1100-1199.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj
index 2ad89135c72f0..83f8253f796a6 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1200-1299.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj
index 0f092654b623a..7ca5bfe0f8a02 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1300-1399.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj
index 74cb61ba6e05d..a1daf88b06bd9 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests1400-1599.csproj
@@ -1,11 +1,12 @@
1
- true
-
-
-
+
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj
index f1aeb00ddab3a..8a7a4d4d87679 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests200-299.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj
index 74c158b597550..827c1df18e770 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests300-399.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj
index 6931acb1fa3c2..53c61d8e1196a 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests400-499.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj
index b95348752dd44..b155247e7f38b 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests500-599.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj
index 7efba2ca08d65..36e6d990c3b8c 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests600-699.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj
index 2f82ed1c20e2f..3d0fe1cb38634 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests700-799.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj
index 7ad7e17d7f7ac..c3f8528edcb4f 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests800-899.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj
index 6e952ff28dd47..e049aba25c3a8 100644
--- a/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj
+++ b/src/tests/Loader/classloader/TypeGeneratorTests/TypeGeneratorTests900-999.csproj
@@ -1,10 +1,11 @@
1
- true
-
-
+
+
+
+
diff --git a/src/tests/MergedTestRunner.targets b/src/tests/MergedTestRunner.targets
new file mode 100644
index 0000000000000..258b711537d2b
--- /dev/null
+++ b/src/tests/MergedTestRunner.targets
@@ -0,0 +1,21 @@
+
+
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/tests/baseservices/threading/threading_group1.csproj b/src/tests/baseservices/threading/threading_group1.csproj
index a099f2d9e87c5..15e01f3e3ae1c 100644
--- a/src/tests/baseservices/threading/threading_group1.csproj
+++ b/src/tests/baseservices/threading/threading_group1.csproj
@@ -1,7 +1,4 @@
-
- true
-
@@ -10,7 +7,7 @@
-
-
+
+
\ No newline at end of file
diff --git a/src/tests/baseservices/threading/threading_group2.csproj b/src/tests/baseservices/threading/threading_group2.csproj
index bb5468800359d..bec4e631db702 100644
--- a/src/tests/baseservices/threading/threading_group2.csproj
+++ b/src/tests/baseservices/threading/threading_group2.csproj
@@ -1,7 +1,4 @@
-
- true
-
@@ -11,7 +8,7 @@
-
-
+
+
\ No newline at end of file