Add some checks for merged test groups (#89521)
authorMark Plesko <markples@microsoft.com>
Fri, 28 Jul 2023 18:39:19 +0000 (11:39 -0700)
committerGitHub <noreply@github.com>
Fri, 28 Jul 2023 18:39:19 +0000 (11:39 -0700)
- CLRTest.Execute.Batch.targets
  - Fix CLRTestExecutionArguments (allows passing a filter argument to <mergedgroup>.cmd
  - Fix indenting in generated script
- XUnitWrapperGenerator
  - Move some generated code into (shared) ITestInfo.cs
  - Also display exception on stdout on failure
  - Add check that projects in merged test groups don't contain entry points
    - Can be a sign of code that is expected to run but won't be
    - Or can be a [Fact]/etc-less entry point in a ReqProcIso project but would then need to be fixed if ReqProcIso were removed
- Directory.Build.targets
  - Run XUnitWrapperGenerator on test projects in merged groups
  - Can't simply replace the conditional because we have XUnit-style tests outside of merged groups
- Update tests to conform to above checks plus a bit of opportunistic cleanup

22 files changed:
src/tests/Common/CLRTest.Execute.Batch.targets
src/tests/Common/XUnitWrapperGenerator/Descriptors.cs [new file with mode: 0644]
src/tests/Common/XUnitWrapperGenerator/ITestInfo.cs
src/tests/Common/XUnitWrapperGenerator/OptionsHelper.cs
src/tests/Common/XUnitWrapperGenerator/RoslynUtils.cs [new file with mode: 0644]
src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.cs
src/tests/Common/XUnitWrapperGenerator/XUnitWrapperGenerator.props
src/tests/Common/XUnitWrapperLibrary/TestSummary.cs
src/tests/Directory.Build.targets
src/tests/JIT/Methodical/delegate/GSDelegate.cs
src/tests/JIT/Methodical/delegate/GSDelegate.csproj
src/tests/JIT/Methodical/delegate/VirtualDelegate.cs
src/tests/JIT/Methodical/delegate/VirtualDelegate.csproj
src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M01/b08046cs/SyncGCHole.cs
src/tests/JIT/Regression/CLR-x86-JIT/V1.2-M01/b08046cs/b08046cs.csproj
src/tests/JIT/Regression/JitBlue/Runtime_75832/Runtime_75832.cs
src/tests/JIT/Regression/JitBlue/Runtime_82535/Runtime_82535.cs
src/tests/JIT/Regression/JitBlue/Runtime_82535/Runtime_82535.csproj
src/tests/JIT/jit64/hfa/main/dll/hfa_nested_f64_interop_cpp.csproj
src/tests/JIT/opt/Devirtualization/box2.cs
src/tests/JIT/opt/Devirtualization/box2.csproj
src/tests/issues.targets

index eeea1e3..90ba718 100644 (file)
@@ -164,8 +164,8 @@ IF NOT "%CLRTestExitCode%"=="%CLRTestExpectedExitCode%" (
 :TakeLock
 md %lockFolder%
 IF NOT "!ERRORLEVEL!"=="0" (
-timeout /t 10 /nobreak
-goto :TakeLock
+  timeout /t 10 /nobreak
+  goto :TakeLock
 )
 Exit /b 2
 
@@ -403,15 +403,17 @@ IF /I [%1] == [-%(Identity)] set cond=1
 IF /I [%1] == [/%(Identity)] set cond=1
 IF %cond% EQU 1 (
 %(Command)
-shift
-IF /I [%(HasParam)] == [true] shift
-goto NextArg
+    shift
+    IF /I [%(HasParam)] == [true] shift
+    goto NextArg
 )','
 ')
 
+:ExtraArgs
 if NOT "%1" == "" (
-set CLRTestExecutionArguments=%*
-goto :ArgsDone
+    set CLRTestExecutionArguments=%CLRTestExecutionArguments% %1
+    shift
+    goto :ExtraArgs
 )
 
 goto ArgsDone
diff --git a/src/tests/Common/XUnitWrapperGenerator/Descriptors.cs b/src/tests/Common/XUnitWrapperGenerator/Descriptors.cs
new file mode 100644 (file)
index 0000000..41105a8
--- /dev/null
@@ -0,0 +1,21 @@
+// 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;
+
+public static class Descriptors
+{
+    public static readonly DiagnosticDescriptor XUWG1001 =
+        new DiagnosticDescriptor(
+            "XUW1001",
+            "Projects in merged tests group should not have entry points",
+            "Projects in merged tests group should not have entry points. Convert to Facts or Theories.",
+            "XUnitWrapperGenerator",
+            DiagnosticSeverity.Warning,
+            isEnabledByDefault: true);
+}
index 8b844be..4b0648a 100644 (file)
@@ -415,22 +415,18 @@ public sealed class WrapperLibraryTestSummaryReporting : ITestReporterWrapper
             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($"{_summaryLocalIdentifier}.ReportStartingTest({test.TestNameExpression}, System.Console.Out);");
                 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});");
+                                 + $" stopwatch.Elapsed - testStart, {_outputRecorderIdentifier}.GetTestOutput(), System.Console.Out, tempLogSw, statsCsvSw);");
             }
             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});");
+                                 + $" stopwatch.Elapsed - testStart, ex, {_outputRecorderIdentifier}.GetTestOutput(), System.Console.Out, tempLogSw, statsCsvSw);");
             }
         }
         builder.AppendLine("else");
index fa92071..9b9dd10 100644 (file)
@@ -6,6 +6,7 @@ namespace XUnitWrapperGenerator;
 
 public static class OptionsHelper
 {
+    private const string InMergedTestDirectoryOption = "build_property.InMergedTestDirectory";
     private const string IsMergedTestRunnerAssemblyOption = "build_property.IsMergedTestRunnerAssembly";
     private const string PriorityOption = "build_property.Priority";
     private const string RuntimeFlavorOption = "build_property.RuntimeFlavor";
@@ -29,6 +30,8 @@ public static class OptionsHelper
                 ? result : 0;
     }
 
+    internal static bool InMergedTestDirectory(this AnalyzerConfigOptions options) => options.GetBoolOption(InMergedTestDirectoryOption);
+
     internal static bool IsMergedTestRunnerAssembly(this AnalyzerConfigOptions options) => options.GetBoolOption(IsMergedTestRunnerAssemblyOption);
 
     internal static int? Priority(this AnalyzerConfigOptions options) => options.GetIntOption(PriorityOption);
diff --git a/src/tests/Common/XUnitWrapperGenerator/RoslynUtils.cs b/src/tests/Common/XUnitWrapperGenerator/RoslynUtils.cs
new file mode 100644 (file)
index 0000000..d87e2a9
--- /dev/null
@@ -0,0 +1,37 @@
+// 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.Text;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace XUnitWrapperGenerator
+{
+    internal class RoslynUtils
+    {
+        /// <summary>
+        ///   Returns the Main method that would serve as the entry point of the assembly, ignoring
+        ///   whether the current target is an executable.
+        /// </summary>
+        /// <remarks>
+        ///   Replacement for CSharpCompilation.GetEntryPoint() which only works for executables.
+        ///   Replacement for its helpers that are internal.
+        ///
+        ///   Intended for the analyzer that is trying to find Main methods that won't be called in
+        ///   merged test groups. Ignores details such as SynthesizedSimpleProgramEntryPointSymbol.
+        ///   Ignores top-level statements as (1) in exes, they will generate an error for conflicting
+        ///   with the auto-generated main, and (2) in libs, they will generate an error for existing
+        ///   at all.
+        /// </remarks>
+        internal static IEnumerable<IMethodSymbol> GetPossibleEntryPoints(Compilation comp, CancellationToken cancellationToken)
+            => comp
+                .GetSymbolsWithName(WellKnownMemberNames.EntryPointMethodName, SymbolFilter.Member)
+                .OfType<IMethodSymbol>()
+                .Where(m => m.IsStatic && !m.IsAbstract && !m.IsVirtual);
+    }
+}
index cb2ae27..ce0d9d9 100644 (file)
@@ -5,6 +5,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -15,6 +16,22 @@ using Microsoft.CodeAnalysis.Diagnostics;
 
 namespace XUnitWrapperGenerator;
 
+internal struct CompData
+{
+    internal CompData(string assemblyName, IMethodSymbol? entryPoint, IEnumerable<IMethodSymbol> possibleEntryPoints, OutputKind outputKind)
+    {
+        AssemblyName = assemblyName;
+        EntryPoint = entryPoint;
+        PossibleEntryPoints = possibleEntryPoints;
+        OutputKind = outputKind;
+    }
+
+    public string AssemblyName { get; private set; }
+    public IMethodSymbol? EntryPoint { get; private set; }
+    public IEnumerable<IMethodSymbol> PossibleEntryPoints { get; private set; }
+    public OutputKind OutputKind { get; private set; }
+}
+
 [Generator]
 public sealed class XUnitWrapperGenerator : IIncrementalGenerator
 {
@@ -58,9 +75,11 @@ public sealed class XUnitWrapperGenerator : IIncrementalGenerator
             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);
+        var compData = context.CompilationProvider.Select((comp, ct) => new CompData(
+            assemblyName: comp.Assembly.MetadataName,
+            entryPoint: comp.GetEntryPoint(ct),
+            possibleEntryPoints: RoslynUtils.GetPossibleEntryPoints(comp, ct),
+            outputKind: comp.Options.OutputKind));
 
         var testsInSource =
             methodsInSource
@@ -112,40 +131,65 @@ public sealed class XUnitWrapperGenerator : IIncrementalGenerator
             .Collect()
             .Combine(context.AnalyzerConfigOptionsProvider)
             .Combine(aliasMap)
-            .Combine(assemblyName)
-            .Combine(alwaysWriteEntryPoint),
+            .Combine(compData),
             static (context, data) =>
             {
-                var ((((methods, configOptions), aliasMap), assemblyName), alwaysWriteEntryPoint) = data;
+                var (((methods, configOptions), aliasMap), compData) = data;
 
-                if (methods.Length == 0 && !alwaysWriteEntryPoint)
+                bool inMergedTestDirectory = configOptions.GlobalOptions.InMergedTestDirectory();
+                if (inMergedTestDirectory)
                 {
-                    // 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;
+                    CheckNoEntryPoint(context, compData);
                 }
 
-                bool isMergedTestRunnerAssembly = configOptions.GlobalOptions.IsMergedTestRunnerAssembly();
-                configOptions.GlobalOptions.TryGetValue("build_property.TargetOS", out string? targetOS);
-
-                if (isMergedTestRunnerAssembly)
+                if (compData.OutputKind != OutputKind.ConsoleApplication)
                 {
-                    if (targetOS?.ToLowerInvariant() is "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst" or "android" or "browser")
-                    {
-                        context.AddSource("XHarnessRunner.g.cs", GenerateXHarnessTestRunner(methods, aliasMap, assemblyName));
-                    }
-                    else
-                    {
-                        context.AddSource("FullRunner.g.cs", GenerateFullTestRunner(methods, aliasMap, assemblyName));
-                    }
+                    return;
                 }
-                else
+
+                bool alwaysWriteEntryPoint = (compData.EntryPoint is null);
+                if (methods.IsEmpty && !alwaysWriteEntryPoint)
                 {
-                    context.AddSource("SimpleRunner.g.cs", GenerateStandaloneSimpleTestRunner(methods, aliasMap));
+                    // 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;
                 }
+
+                AddRunnerSource(context, methods, configOptions, aliasMap, compData);
             });
     }
 
+    private static void AddRunnerSource(SourceProductionContext context, ImmutableArray<ITestInfo> methods, AnalyzerConfigOptionsProvider configOptions, ImmutableDictionary<string, string> aliasMap, CompData compData)
+    {
+        bool isMergedTestRunnerAssembly = configOptions.GlobalOptions.IsMergedTestRunnerAssembly();
+        configOptions.GlobalOptions.TryGetValue("build_property.TargetOS", out string? targetOS);
+        string assemblyName = compData.AssemblyName;
+
+        if (isMergedTestRunnerAssembly)
+        {
+            if (targetOS?.ToLowerInvariant() is "ios" or "iossimulator" or "tvos" or "tvossimulator" or "maccatalyst" or "android" or "browser")
+            {
+                context.AddSource("XHarnessRunner.g.cs", GenerateXHarnessTestRunner(methods, aliasMap, assemblyName));
+            }
+            else
+            {
+                context.AddSource("FullRunner.g.cs", GenerateFullTestRunner(methods, aliasMap, assemblyName));
+            }
+        }
+        else
+        {
+            context.AddSource("SimpleRunner.g.cs", GenerateStandaloneSimpleTestRunner(methods, aliasMap));
+        }
+    }
+
+    private static void CheckNoEntryPoint(SourceProductionContext context, CompData compData)
+    {
+        foreach (IMethodSymbol entryPoint in compData.PossibleEntryPoints)
+        {
+            context.ReportDiagnostic(Diagnostic.Create(Descriptors.XUWG1001, entryPoint.Locations[0]));
+        }
+    }
+
     private static void AppendAliasMap(CodeBuilder builder, ImmutableDictionary<string, string> aliasMap)
     {
         bool didOutput = false;
@@ -312,7 +356,7 @@ public sealed class XUnitWrapperGenerator : IIncrementalGenerator
             builder.AppendLine("System.Collections.Generic.HashSet<string> testExclusionList = XUnitWrapperLibrary.TestFilter.LoadTestExclusionList();");
             builder.AppendLine($@"return await XHarnessRunnerLibrary.RunnerEntryPoint.RunTests(RunTests, ""{assemblyName}"", args.Length != 0 ? args[0] : null, testExclusionList);");
         }
-        builder.AppendLine("catch(System.Exception ex)");
+        builder.AppendLine("catch (System.Exception ex)");
         using (builder.NewBracesScope())
         {
             builder.AppendLine("System.Console.WriteLine(ex.ToString());");
@@ -435,7 +479,7 @@ public sealed class XUnitWrapperGenerator : IIncrementalGenerator
                         builder.Append(testInfo.GenerateTestExecution(reporter));
                     }
                 }
-                builder.AppendLine("catch(System.Exception ex)");
+                builder.AppendLine("catch (System.Exception ex)");
                 using (builder.NewBracesScope())
                 {
                     builder.AppendLine("System.Console.WriteLine(ex.ToString());");
index 651a40c..067c31f 100644 (file)
@@ -6,6 +6,7 @@
     <CompilerVisibleProperty Include="TargetArchitecture" />
     <CompilerVisibleProperty Include="Priority" />
     <!-- Properties that influence test harness generation -->
+    <CompilerVisibleProperty Include="InMergedTestDirectory" />
     <CompilerVisibleProperty Include="IsMergedTestRunnerAssembly" />
     <CompilerVisibleProperty Include="TestFilter" />
     <CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="IsOutOfProcessTestAssembly" />
index 96636cc..adf4e1a 100644 (file)
@@ -120,11 +120,18 @@ public class TestSummary
         tempLogSw.WriteLine("</assembly>");
     }
 
+    public void ReportStartingTest(string name, TextWriter outTw)
+    {
+        outTw.WriteLine("{0:HH:mm:ss.fff} Running test: {1}", System.DateTime.Now, name);
+        outTw.Flush();
+    }
+
     public void ReportPassedTest(string name,
                                  string containingTypeName,
                                  string methodName,
                                  TimeSpan duration,
                                  string output,
+                                 TextWriter outTw,
                                  StreamWriter tempLogSw,
                                  StreamWriter statsCsvSw)
     {
@@ -133,8 +140,10 @@ public class TestSummary
         var result = new TestResult(name, containingTypeName, methodName, duration, null, null, output);
         _testResults.Add(result);
 
+        outTw.WriteLine($"{0:HH:mm:ss.fff} Passed test: {1}", System.DateTime.Now, name);
         statsCsvSw.WriteLine($"{TotalTests},{PassedTests},{FailedTests},{SkippedTests}");
         tempLogSw.WriteLine(result.ToXmlString());
+        outTw.Flush();
         statsCsvSw.Flush();
         tempLogSw.Flush();
     }
@@ -145,6 +154,7 @@ public class TestSummary
                                  TimeSpan duration,
                                  Exception ex,
                                  string output,
+                                 TextWriter outTw,
                                  StreamWriter tempLogSw,
                                  StreamWriter statsCsvSw)
     {
@@ -153,8 +163,11 @@ public class TestSummary
         var result = new TestResult(name, containingTypeName, methodName, duration, ex, null, output);
         _testResults.Add(result);
 
+        outTw.WriteLine(ex);
+        outTw.WriteLine("{0:HH:mm:ss.fff} Failed test: {1}", System.DateTime.Now, name);
         statsCsvSw.WriteLine($"{TotalTests},{PassedTests},{FailedTests},{SkippedTests}");
         tempLogSw.WriteLine(result.ToXmlString());
+        outTw.Flush();
         statsCsvSw.Flush();
         tempLogSw.Flush();
     }
index c8b7799..7c0dbf9 100644 (file)
     </ItemGroup>
   </Target>
 
-  <PropertyGroup Condition="'$(Language)' == 'C#' and ('$(BuildAsStandalone)' == 'true' or '$(RequiresProcessIsolation)' == 'true' or '$(IsMergedTestRunnerAssembly)' == 'true')">
+  <PropertyGroup Condition="'$(Language)' == 'C#' and ('$(BuildAsStandalone)' == 'true' or '$(RequiresProcessIsolation)' == 'true' or '$(InMergedTestDirectory)' == 'true' or '$(IsMergedTestRunnerAssembly)' == 'true')">
     <ReferenceXUnitWrapperGenerator Condition="'$(ReferenceXUnitWrapperGenerator)' == ''">true</ReferenceXUnitWrapperGenerator>
   </PropertyGroup>
 
index cbf80ad..ee68adb 100644 (file)
@@ -3,7 +3,7 @@
 
 using System;
 using System.Reflection;
-
+using Xunit;
 
 public interface IGetContents<T> {
     (string, int, T) GetContents();
@@ -23,30 +23,26 @@ public class Program {
 
     public delegate (string, int, T) MyDelegate<T>(IGetContents<T> arg);
 
-    public static int Main(string[] args)
+    [Fact]
+    public static int TestEntryPoint()
     {
         int retVal = 100;
 
-        try {
-            MyStruct<string> myStruct = new MyStruct<string>();
-            myStruct.s = "test1";
-            myStruct.a = 42;
-            myStruct.t = "test2";
-
-            MethodInfo mi = typeof(IGetContents<string>).GetMethod("GetContents");
-            MyDelegate<string> func = (MyDelegate<string>)mi.CreateDelegate(typeof(MyDelegate<string>));
-
-            (string c1, int c2, string c3) = func(myStruct);
-            if (c1 != "test1")
-                retVal = 1;
-            if (c2 != 42)
-                retVal = 2;
-            if (c3 != "test2")
-                retVal = 3;
-        } catch (Exception e) {
-            Console.WriteLine(e);
+        MyStruct<string> myStruct = new MyStruct<string>();
+        myStruct.s = "test1";
+        myStruct.a = 42;
+        myStruct.t = "test2";
+
+        MethodInfo mi = typeof(IGetContents<string>).GetMethod("GetContents");
+        MyDelegate<string> func = (MyDelegate<string>)mi.CreateDelegate(typeof(MyDelegate<string>));
+
+        (string c1, int c2, string c3) = func(myStruct);
+        if (c1 != "test1")
             retVal = 1;
-        }
+        if (c2 != 42)
+            retVal = 2;
+        if (c3 != "test2")
+            retVal = 3;
 
         return retVal;
     }
index 96112c6..b47c3e8 100644 (file)
@@ -1,13 +1,9 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <PropertyGroup>
     <DebugType>PdbOnly</DebugType>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="GSDelegate.cs" />
+    <Compile Include="$(MSBuildProjectName).cs" />
   </ItemGroup>
 </Project>
index 5a28c68..f912580 100644 (file)
@@ -1,22 +1,19 @@
 // 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.Runtime.InteropServices;
+using Xunit;
 
 public class VirtualDelegate
 {
-    public static int Main () {
+    [Fact]
+    public static int TestEntryPoint() {
         int retVal = 100;
-        try {
-            var del = (Func<string, string>)Delegate.CreateDelegate (typeof (Func<string, string>), null, typeof (object).GetMethod ("ToString"));
-            if (del ("FOO") != "FOO")
-                retVal = 1;
-        } catch(Exception e) {
-            Console.WriteLine(e);
+
+        var del = (Func<string, string>)Delegate.CreateDelegate(typeof(Func<string, string>), null, typeof(object).GetMethod ("ToString"));
+        if (del("FOO") != "FOO")
             retVal = 1;
-        }
         
         return retVal;
         
index c8f15c2..cddd1e5 100644 (file)
@@ -1,9 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <PropertyGroup>
     <DebugType>PdbOnly</DebugType>
     <Optimize>True</Optimize>
   </PropertyGroup>
index 2e6d5ab..75ab923 100644 (file)
@@ -2,12 +2,18 @@
 // src\tests\JIT\Regression\CLR-x86-JIT\V1.2-M01\b08046\SyncGCHole.il
 //
 // Changes:
-// - Remove the [Fact] from Main because it eventually leads to
-//   "CS7022 The entry point of the program is global code; ignoring 'Main()' entry point."
-//   [Fact] will be added again as part of the test merging work.
-// - Remove the Environment.Exit call at the end of Main.  Exit doesn't wait
+// - Rename Main to TestEntryPoint (standard change for merged test groups).
+// - Remove the Environment.Exit call at the end of Main. Exit doesn't wait
 //   for foreground threads to complete, so the test becomes a race that is
 //   typically lost.
+// - Write a local static instead of Environment.ExitCode for compatibility
+//   with merged test groups.
+// - Don't allow a successful thread to overwrite the exit value of a failing
+//   one. Retain the writes for successes (to 'Ignored') to keep the shape of
+//   the code as similar to the original as possible. It is unclear what
+//   aspect of the code caused the original problem.
+// - Don't bother catching the exception in the outer method as the test
+//   infrastructure will handle it.
 
 using System;
 using System.Runtime.CompilerServices;
@@ -29,26 +35,26 @@ class ExternalClass
 
 public class ExternalException : Exception
 {
-    public static void Main()
+    public static int ExitCode { get; set; }
+    public static int Ignored { get; set; }
+
+    [Fact]
+    public static int TestEntryPoint()
     {
+        ExitCode = 100;
+
         ExternalException v1 = new ExternalException();
 
         for (int v2 = 0; v2 < 10; v2++)
         {
             Thread v0 = new Thread(new ThreadStart(v1.runtest));
-
-            try
-            {
-                v0.Start();
-            }
-            catch (Exception)
-            {
-                Console.WriteLine("Exception was caught in main");
-            }
+            v0.Start();
         }
+
+        return ExitCode;
     }
 
-    public void runtest()
+    private void runtest()
     {
         int v0 = 0;
 
@@ -86,22 +92,22 @@ public class ExternalException : Exception
         {
             lock(this)
             {
-                Console.WriteLine("TryCatch Test Passed");
-                Environment.ExitCode = 100;
+                Console.WriteLine("TryCatch Thread Passed");
+                ExternalException.Ignored = 100;
             }
         }
         else
         {
             lock(this)
             {
-                Console.WriteLine("TryCatch Test Failed");
+                Console.WriteLine("TryCatch Thread Failed");
                 Console.WriteLine(0);
-                Environment.ExitCode = 1;
+                ExternalException.ExitCode = 1;
             }
         }
     }
 
-    public void recurse(int counter)
+    private void recurse(int counter)
     {
         char[] v0 = new char[100];
 
index 0b13f79..a77f49f 100644 (file)
@@ -1,7 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <!-- Needed for Environment.Exit -->
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <CLRTestPriority>1</CLRTestPriority>
   </PropertyGroup>
   <PropertyGroup>
index edfcc9f..1e7dd41 100644 (file)
@@ -8,30 +8,11 @@ using Xunit;
 public class Runtime_75832
 {
     [Fact]
-    public static int TestEntryPoint()
-    {
-        try
-        {
-            Test(0);
-            Console.WriteLine("FAIL: No exception thrown");
-        }
-        catch (DivideByZeroException)
-        {
-            return 100;
-        }
-        catch (Exception ex)
-        {
-            Console.WriteLine("FAIL: Caught {0}", ex.GetType().Name);
-        }
-        
-        return 101;
-    }
+    public static void TestEntryPoint()
+        => Assert.Throws<DivideByZeroException>(() => Test(0));
 
     [MethodImpl(MethodImplOptions.NoInlining)]
-    private static void Test(int i)
-    {
-        GetAction()(100 / i);
-    }
+    private static void Test(int i) => GetAction()(100 / i);
 
     [MethodImpl(MethodImplOptions.NoInlining)]
     private static Action<int> GetAction() => null;
index 110ea1d..88710be 100644 (file)
 // The .NET Foundation licenses this file to you under the MIT license.
 using System;
 using System.Runtime.CompilerServices;
+using Xunit;
 
 public class Program
 {
-       public Program()
-       {
-       }
-
-       static int Main(string[] args)
-       {
+    [Fact]
+    public static int TestEntryPoint()
+    {
         Foo currentFoo;
 
-               Bacon defaultBacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
-               currentFoo = new Foo();
+        Bacon defaultBacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
+        currentFoo = new Foo();
         try {
             currentFoo.GetBar().m_Bacon = defaultBacon;
         } catch (NullReferenceException) {
             return 100;
         }
         return 101;
-       }
+    }
 }
 
 public class Foo
 {
-       private Bar m_Bar;
-       public Bar GetBar()
-       {
-               return m_Bar;
-       }
+    private Bar m_Bar;
+    public Bar GetBar()
+    {
+        return m_Bar;
+    }
 }
 
 
 public class Bar
 {
-       public Bacon m_Bacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
+    public Bacon m_Bacon = new Bacon(-180, 180, true, false, 300f, 0.1f, 0.1f, "Foo", false);
 }
 
 public struct Bacon
 {
-       public float Value;
-       public enum FooEnum
-       {
-               One,
-               Two
-       };
-
-       public FooEnum m_FooEnum;
-       public float m_f1;
-       public float m_f2;
-       public float m_f3;
-       public string m_s1;
-       public float m_f8;
-       public bool m_bool1;
-       public float m_f4;
-       public float m_f5;
-       public bool m_bool2;
-       public FooBar m_FooBar;
-
-       float m_f6;
-       float m_f7;
-       int m_i1;
-
-       public bool bool3 { get; set; }
-
-       public bool bool4 { get; set; }
-
-       public interface IFooInterface
-       {
-               float GetFooValue(int foo);
-       }
-
-       IFooInterface m_FooProvider;
-       int m_i2;
-
-       public Bacon(
-               float minValue, float maxValue, bool wrap, bool rangeLocked,
-               float maxSpeed, float accelTime, float decelTime,
-               string name, bool invert)
-       {
-               m_f4 = minValue;
-               m_f5 = maxValue;
-               m_bool2 = wrap;
-               bool3 = rangeLocked;
-
-               bool4 = false;
-               m_FooBar = new FooBar(false, 1, 2);
-
-               m_FooEnum = FooEnum.One;
-               m_f1 = maxSpeed;
-               m_f2 = accelTime;
-               m_f3 = decelTime;
-               Value = (minValue + maxValue) / 2;
-               m_s1 = name;
-               m_f8 = 0;
-               m_bool1 = invert;
-
-               m_f6 = 0f;
-               m_FooProvider = null;
-               m_i2 = 0;
-               m_f7 = 0;
-               m_i1 = 0;
-       }
-
-       public struct FooBar
-       {
-               public bool m_FooBar_bool1;
-               public float m_FooBar_f1;
-               public float m_FooBar_f2;
-
-               float m_FooBar_f3;
-               float m_FooBar_f4;
-               float m_FooBar_f5;
-               int m_FooBar_i1;
-               int m_FooBar_i2;
-
-               public FooBar(bool b1, float f1, float f2)
-               {
-                       m_FooBar_bool1 = b1;
-                       m_FooBar_f1 = f1;
-                       m_FooBar_f2 = f2;
-                       m_FooBar_f4 = 0;
-                       m_FooBar_f5 = 0;
-                       m_FooBar_i1 = m_FooBar_i2 = -1;
-                       m_FooBar_f3 = 0;
-               }
-       }
+    public float Value;
+    public enum FooEnum
+    {
+        One,
+        Two
+    };
+
+    public FooEnum m_FooEnum;
+    public float m_f1;
+    public float m_f2;
+    public float m_f3;
+    public string m_s1;
+    public float m_f8;
+    public bool m_bool1;
+    public float m_f4;
+    public float m_f5;
+    public bool m_bool2;
+    public FooBar m_FooBar;
+
+    float m_f6;
+    float m_f7;
+    int m_i1;
+
+    public bool bool3 { get; set; }
+
+    public bool bool4 { get; set; }
+
+    public interface IFooInterface
+    {
+        float GetFooValue(int foo);
+    }
+
+    IFooInterface m_FooProvider;
+    int m_i2;
+
+    public Bacon(
+        float minValue, float maxValue, bool wrap, bool rangeLocked,
+        float maxSpeed, float accelTime, float decelTime,
+        string name, bool invert)
+    {
+        m_f4 = minValue;
+        m_f5 = maxValue;
+        m_bool2 = wrap;
+        bool3 = rangeLocked;
+
+        bool4 = false;
+        m_FooBar = new FooBar(false, 1, 2);
+
+        m_FooEnum = FooEnum.One;
+        m_f1 = maxSpeed;
+        m_f2 = accelTime;
+        m_f3 = decelTime;
+        Value = (minValue + maxValue) / 2;
+        m_s1 = name;
+        m_f8 = 0;
+        m_bool1 = invert;
+
+        m_f6 = 0f;
+        m_FooProvider = null;
+        m_i2 = 0;
+        m_f7 = 0;
+        m_i1 = 0;
+    }
+
+    public struct FooBar
+    {
+        public bool m_FooBar_bool1;
+        public float m_FooBar_f1;
+        public float m_FooBar_f2;
+
+        float m_FooBar_f3;
+        float m_FooBar_f4;
+        float m_FooBar_f5;
+        int m_FooBar_i1;
+        int m_FooBar_i2;
+
+        public FooBar(bool b1, float f1, float f2)
+        {
+            m_FooBar_bool1 = b1;
+            m_FooBar_f1 = f1;
+            m_FooBar_f2 = f2;
+            m_FooBar_f4 = 0;
+            m_FooBar_f5 = 0;
+            m_FooBar_i1 = m_FooBar_i2 = -1;
+            m_FooBar_f3 = 0;
+        }
+    }
 }
index 23a7bac..1e568f6 100644 (file)
@@ -1,6 +1,5 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <OutputType>Library</OutputType>
     <Optimize>True</Optimize>
   </PropertyGroup>
   <ItemGroup>
index d82380d..c470c81 100644 (file)
@@ -1,7 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <!-- Needed for CMakeProjectReference -->
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
     <OutputType>Library</OutputType>
     <CLRTestKind>BuildOnly</CLRTestKind>
     <GenerateRunScript>false</GenerateRunScript>
index 8837ee5..91f9568 100644 (file)
@@ -4,10 +4,11 @@
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using Xunit;
 
-class Program
+public class Program
 {
-    static async Task<int> Main()
+    static async Task TestTask()
     {
         for (int i = 0; i < 10; i++)
         {
@@ -17,7 +18,8 @@ class Program
             // extra context argument.
             await new ValueTask<string>(Task.Delay(1).ContinueWith(_ => default(string))).ConfigureAwait(false);
         }
-
-        return 100;
     }
+
+    [Fact]
+    public static void TestEntryPoint() => Task.Run(TestTask).Wait();
 }
index 8d5913d..e86759e 100644 (file)
@@ -1,9 +1,5 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <!-- Needed for async Main -->
-    <RequiresProcessIsolation>true</RequiresProcessIsolation>
-  </PropertyGroup>
-  <PropertyGroup>
     <DebugType>None</DebugType>
     <Optimize>True</Optimize>
   </PropertyGroup>
index b1d9c5a..35c1a1c 100644 (file)
@@ -69,6 +69,9 @@
         <ExcludeList Include="$(XunitTestBinBase)/GC/Regressions/Github/Runtime_76219/Runtime_76219/*">
             <Issue>https://github.com/dotnet/runtime/issues/78899</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/JitBlue/Runtime_82535/Runtime_82535/*">
+          <Issue>https://github.com/dotnet/runtime/issues/89585</Issue>
+        </ExcludeList>
     </ItemGroup>
 
     <!-- All Unix targets on all runtimes -->