diff --git a/src/Tools/PrepareTests/TestDiscovery.cs b/src/Tools/PrepareTests/TestDiscovery.cs index 78fe54d00d64a..1205d91f318fd 100644 --- a/src/Tools/PrepareTests/TestDiscovery.cs +++ b/src/Tools/PrepareTests/TestDiscovery.cs @@ -10,6 +10,7 @@ using System.IO.Pipes; using System.Threading.Tasks; using System.Threading; +using System.Text; namespace PrepareTests; internal class TestDiscovery @@ -85,14 +86,22 @@ static bool RunWorker(string dotnetPath, string pathToWorker, string pathToAssem pipeClient.StartInfo.FileName = pathToWorker; } + var errorOutput = new StringBuilder(); + using (var pipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) { // Pass the client process a handle to the server. arguments.Add(pipeServer.GetClientHandleAsString()); pipeClient.StartInfo.Arguments = string.Join(" ", arguments); pipeClient.StartInfo.UseShellExecute = false; + + // Errors will be logged to stderr, redirect to us so we can capture it. + pipeClient.StartInfo.RedirectStandardError = true; + pipeClient.ErrorDataReceived += PipeClient_ErrorDataReceived; pipeClient.Start(); + pipeClient.BeginErrorReadLine(); + pipeServer.DisposeLocalCopyOfClientHandle(); try @@ -120,10 +129,15 @@ static bool RunWorker(string dotnetPath, string pathToWorker, string pathToAssem if (!success) { - Console.WriteLine($"Failed to discover tests in {pathToAssembly}"); + Console.WriteLine($"Failed to discover tests in {pathToAssembly}:{Environment.NewLine}{errorOutput}"); } return success; + + void PipeClient_ErrorDataReceived(object sender, DataReceivedEventArgs e) + { + errorOutput.AppendLine(e.Data); + } } private static List GetAssemblies(string binDirectory, bool isUnix) diff --git a/src/Tools/TestDiscoveryWorker/Program.cs b/src/Tools/TestDiscoveryWorker/Program.cs index 9c5c12375d84e..882ab62b3db1c 100644 --- a/src/Tools/TestDiscoveryWorker/Program.cs +++ b/src/Tools/TestDiscoveryWorker/Program.cs @@ -24,76 +24,85 @@ return ExitFailure; } -using var pipeClient = new AnonymousPipeClientStream(PipeDirection.In, args[0]); -using var sr = new StreamReader(pipeClient); -string? output; - -// Wait for 'sync message' from the server. -do +try { - output = await sr.ReadLineAsync().ConfigureAwait(false); -} -while (!(output?.StartsWith("ASSEMBLY", StringComparison.OrdinalIgnoreCase) == true)); + using var pipeClient = new AnonymousPipeClientStream(PipeDirection.In, args[0]); + using var sr = new StreamReader(pipeClient); + string? output; -if ((output = await sr.ReadLineAsync().ConfigureAwait(false)) is not null) -{ - var assemblyFileName = output; + // Wait for 'sync message' from the server. + do + { + output = await sr.ReadLineAsync().ConfigureAwait(false); + } + while (!(output?.StartsWith("ASSEMBLY", StringComparison.OrdinalIgnoreCase) == true)); -#if NET6_0_OR_GREATER - var resolver = new System.Runtime.Loader.AssemblyDependencyResolver(assemblyFileName); - System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (context, assemblyName) => + if ((output = await sr.ReadLineAsync().ConfigureAwait(false)) is not null) { - var assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); - if (assemblyPath is not null) + var assemblyFileName = output; + +#if NET6_0_OR_GREATER + var resolver = new System.Runtime.Loader.AssemblyDependencyResolver(assemblyFileName); + System.Runtime.Loader.AssemblyLoadContext.Default.Resolving += (context, assemblyName) => { - return context.LoadFromAssemblyPath(assemblyPath); - } + var assemblyPath = resolver.ResolveAssemblyToPath(assemblyName); + if (assemblyPath is not null) + { + return context.LoadFromAssemblyPath(assemblyPath); + } - return null; - }; + return null; + }; #endif - string testDescriptor = Path.GetFileName(assemblyFileName); + string testDescriptor = Path.GetFileName(assemblyFileName); #if NET - testDescriptor += " (.NET Core)"; + testDescriptor += " (.NET Core)"; #else testDescriptor += " (.NET Framework)"; #endif - await Console.Out.WriteLineAsync($"Discovering tests in {testDescriptor}...").ConfigureAwait(false); + await Console.Out.WriteLineAsync($"Discovering tests in {testDescriptor}...").ConfigureAwait(false); - using var xunit = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyFileName, shadowCopy: false); - var configuration = ConfigReader.Load(assemblyFileName); - var sink = new Sink(); - xunit.Find(includeSourceInformation: false, - messageSink: sink, - discoveryOptions: TestFrameworkOptions.ForDiscovery(configuration)); + using var xunit = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyFileName, shadowCopy: false); + var configuration = ConfigReader.Load(assemblyFileName); + var sink = new Sink(); + xunit.Find(includeSourceInformation: false, + messageSink: sink, + discoveryOptions: TestFrameworkOptions.ForDiscovery(configuration)); - var testsToWrite = new HashSet(); - await foreach (var fullyQualifiedName in sink.GetTestCaseNamesAsync()) - { - testsToWrite.Add(fullyQualifiedName); - } + var testsToWrite = new HashSet(); + await foreach (var fullyQualifiedName in sink.GetTestCaseNamesAsync()) + { + testsToWrite.Add(fullyQualifiedName); + } - if (sink.AnyWriteFailures) - { - await Console.Error.WriteLineAsync($"Channel failed to write for '{assemblyFileName}'").ConfigureAwait(false); - return ExitFailure; - } + if (sink.AnyWriteFailures) + { + await Console.Error.WriteLineAsync($"Channel failed to write for '{assemblyFileName}'").ConfigureAwait(false); + return ExitFailure; + } #if NET6_0_OR_GREATER - await Console.Out.WriteLineAsync($"Discovered {testsToWrite.Count} tests in {testDescriptor}").ConfigureAwait(false); + await Console.Out.WriteLineAsync($"Discovered {testsToWrite.Count} tests in {testDescriptor}").ConfigureAwait(false); #else - await Console.Out.WriteLineAsync($"Discovered {testsToWrite.Count} tests in {testDescriptor}").ConfigureAwait(false); + await Console.Out.WriteLineAsync($"Discovered {testsToWrite.Count} tests in {testDescriptor}").ConfigureAwait(false); #endif - var directory = Path.GetDirectoryName(assemblyFileName); - using var fileStream = File.Create(Path.Combine(directory!, "testlist.json")); - await JsonSerializer.SerializeAsync(fileStream, testsToWrite).ConfigureAwait(false); - return ExitSuccess; -} + var directory = Path.GetDirectoryName(assemblyFileName); + using var fileStream = File.Create(Path.Combine(directory!, "testlist.json")); + await JsonSerializer.SerializeAsync(fileStream, testsToWrite).ConfigureAwait(false); + return ExitSuccess; + } -return ExitFailure; + return ExitFailure; +} +catch (Exception ex) +{ + // Write the exception details to stderr so the host process can pick it up. + await Console.Error.WriteLineAsync(ex.ToString()).ConfigureAwait(false); + return 1; +} file class Sink : IMessageSink { diff --git a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index 1d4bf4f98739d..1ce8835d46e43 100644 --- a/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/Core/MSBuild.BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -30,7 +30,9 @@ + +