From cad6e06a83a5be37cf00fb021d287d486dc9f2bd Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Wed, 16 Oct 2019 18:50:08 +0200 Subject: [PATCH] [Templating][Fixes #15048] Fixes build hang in template tests caused by an unbound blocking collection (#15058) --- .../test/Helpers/AspNetProcess.cs | 30 +++++++++++++++---- .../test/Helpers/ProcessEx.cs | 4 ++- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ProjectTemplates/test/Helpers/AspNetProcess.cs b/src/ProjectTemplates/test/Helpers/AspNetProcess.cs index deb0c199d2..6222a5e8a1 100644 --- a/src/ProjectTemplates/test/Helpers/AspNetProcess.cs +++ b/src/ProjectTemplates/test/Helpers/AspNetProcess.cs @@ -60,7 +60,7 @@ namespace Templates.Test.Helpers Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables); if (hasListeningUri) { - ListeningUri = GetListeningUri(output); + ListeningUri = GetListeningUri(output) ?? throw new InvalidOperationException("Couldn't find the listening URL."); } } @@ -175,16 +175,15 @@ namespace Templates.Test.Helpers { // Wait until the app is accepting HTTP requests output.WriteLine("Waiting until ASP.NET application is accepting connections..."); - var listeningMessage = Process - .OutputLinesAsEnumerable - .Where(line => line != null) - .FirstOrDefault(line => line.Trim().StartsWith(ListeningMessagePrefix, StringComparison.Ordinal)); + var listeningMessage = GetListeningMessage(); if (!string.IsNullOrEmpty(listeningMessage)) { listeningMessage = listeningMessage.Trim(); // Verify we have a valid URL to make requests to - var listeningUrlString = listeningMessage.Substring(ListeningMessagePrefix.Length); + var listeningUrlString = listeningMessage.Substring(listeningMessage.IndexOf( + ListeningMessagePrefix, StringComparison.Ordinal) + ListeningMessagePrefix.Length); + output.WriteLine($"Detected that ASP.NET application is accepting connections on: {listeningUrlString}"); listeningUrlString = listeningUrlString.Substring(0, listeningUrlString.IndexOf(':')) + "://localhost" + @@ -199,6 +198,25 @@ namespace Templates.Test.Helpers } } + private string GetListeningMessage() + { + try + { + return Process + // This will timeout at most after 5 minutes. + .OutputLinesAsEnumerable + .Where(line => line != null) + // This used to do StartsWith, but this is less strict and can prevent issues (very rare) where + // console logging interleaves with other console output in a bad way. For example: + // dbugNow listening on: http://127.0.0.1:12857 + .FirstOrDefault(line => line.Trim().Contains(ListeningMessagePrefix, StringComparison.Ordinal)); + } + catch (OperationCanceledException) + { + return null; + } + } + private bool IsSuccessStatusCode(HttpResponseMessage response) { return response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.Redirect; diff --git a/src/ProjectTemplates/test/Helpers/ProcessEx.cs b/src/ProjectTemplates/test/Helpers/ProcessEx.cs index 2f3cbbc361..517c23e7a8 100644 --- a/src/ProjectTemplates/test/Helpers/ProcessEx.cs +++ b/src/ProjectTemplates/test/Helpers/ProcessEx.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Internal; using Xunit.Abstractions; @@ -26,6 +27,7 @@ namespace Templates.Test.Helpers private readonly object _pipeCaptureLock = new object(); private BlockingCollection _stdoutLines; private TaskCompletionSource _exited; + private CancellationTokenSource _stdoutLinesCancellationSource = new CancellationTokenSource(TimeSpan.FromMinutes(5)); public ProcessEx(ITestOutputHelper output, Process proc) { @@ -71,7 +73,7 @@ namespace Templates.Test.Helpers } } - public IEnumerable OutputLinesAsEnumerable => _stdoutLines.GetConsumingEnumerable(); + public IEnumerable OutputLinesAsEnumerable => _stdoutLines.GetConsumingEnumerable(_stdoutLinesCancellationSource.Token); public int ExitCode => _process.ExitCode;