-
Notifications
You must be signed in to change notification settings - Fork 5.3k
src/tests tree test xunit-based source generated runner #60846
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1bbfc9c
6523a11
6143a48
9b9ae95
54e93eb
14ca067
149ffe2
23b7975
623cc48
a20864e
4a7ebe9
f5009c8
59960aa
191ac42
a2bdb78
e597fec
e783f9e
c978193
270144b
4c73d6f
e81deda
4f9dcbb
d5961f6
4d412d0
377b8a0
835f56d
c84c5d1
b7fb243
d49c1f4
1b19bed
040877c
d736995
a77d13c
04650fd
8ef73bd
5881fa1
eccf29a
ccd08a2
db68ce4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…rue</RequiresProcessIsolation> in a separate process using the cmd/sh build scripts.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,7 +32,24 @@ public void Initialize(IncrementalGeneratorInitializationContext context) | |
| return visitor.Visit(data.Right.GetAssemblyOrModuleSymbol(data.Left))!; | ||
| }); | ||
|
|
||
| var assemblyName = context.CompilationProvider.Select((comp, ct) => comp.Assembly.MetadataName); | ||
| 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)); | ||
|
|
||
|
|
@@ -48,12 +65,16 @@ public void Initialize(IncrementalGeneratorInitializationContext context) | |
| return aliasMap.ToImmutable(); | ||
| }).WithComparer(new ImmutableDictionaryValueComparer<string, string>(EqualityComparer<string>.Default)); | ||
|
|
||
| var assemblyName = context.CompilationProvider.Select((comp, ct) => comp.Assembly.MetadataName); | ||
|
|
||
| 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(aliasMap) | ||
| .Combine(context.AnalyzerConfigOptionsProvider) | ||
| .Combine(assemblyName), | ||
|
|
@@ -318,6 +339,7 @@ private static IEnumerable<ITestInfo> GetTestMethodInfosForMethod(IMethodSymbol | |
| if (options.GlobalOptions.RuntimeFlavor() != "CoreCLR") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be a big problem to postpone the platform / runtime check to the actual runtime execution? We can do it in a future change but ideally we should be able to use the same managed build for Mono and CoreCLR (I don't believe we're doing that now, we have two different legs for that purpose but that's also cost that could be reduced). |
||
| { | ||
| // If we're building tests not for CoreCLR, we can skip handling the specifics of the SkipOnCoreClrAttribute. | ||
| testInfos = ImmutableArray<ITestInfo>.Empty; | ||
| break; | ||
| } | ||
|
|
||
|
|
@@ -508,7 +530,12 @@ private static ImmutableArray<ITestInfo> DecorateWithSkipOnPlatform(ImmutableArr | |
| options.GlobalOptions.TryGetValue("build_property.TargetOS", out string? targetOS); | ||
| Xunit.TestPlatforms targetPlatform = GetPlatformForTargetOS(targetOS); | ||
|
|
||
| if (platformsToSkip.HasFlag(targetPlatform)) | ||
| 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; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be a big problem to postpone the platform / runtime check to the actual runtime execution? We can do it in a future change but ideally we should be able to use the same managed build for Mono and CoreCLR (I don't believe we're doing that now, we have two different legs for that purpose but that's also cost that could be reduced).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only mechanism known today to detect runtime flavor at runtime easily is though reflection, which is very heavyweight for the src/tests test tree. I'm trying to keep the platform checks as cheap as possible so as to not make the "bring up" tests in the JIT significantly less useful, which is why I try to hard-code them at generation time when possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I think it's OK to leave as is for now, I can look into it as part of the new-style Helix publishing; in particular I believe we're setting up about two dozens of environment variables that get set on the Helix execution machine and these include target architecture and OS so I guess we could use these for the runtime checks. We can also add an arbitrary number of additional environment variables as we need to express any of the execution characteristics.