Skip to content

Commit cad6e06

Browse files
authored
[Templating][Fixes #15048] Fixes build hang in template tests caused by an unbound blocking collection (#15058)
1 parent 49191f4 commit cad6e06

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

src/ProjectTemplates/test/Helpers/AspNetProcess.cs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public AspNetProcess(
6060
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables);
6161
if (hasListeningUri)
6262
{
63-
ListeningUri = GetListeningUri(output);
63+
ListeningUri = GetListeningUri(output) ?? throw new InvalidOperationException("Couldn't find the listening URL.");
6464
}
6565
}
6666

@@ -175,16 +175,15 @@ private Uri GetListeningUri(ITestOutputHelper output)
175175
{
176176
// Wait until the app is accepting HTTP requests
177177
output.WriteLine("Waiting until ASP.NET application is accepting connections...");
178-
var listeningMessage = Process
179-
.OutputLinesAsEnumerable
180-
.Where(line => line != null)
181-
.FirstOrDefault(line => line.Trim().StartsWith(ListeningMessagePrefix, StringComparison.Ordinal));
178+
var listeningMessage = GetListeningMessage();
182179

183180
if (!string.IsNullOrEmpty(listeningMessage))
184181
{
185182
listeningMessage = listeningMessage.Trim();
186183
// Verify we have a valid URL to make requests to
187-
var listeningUrlString = listeningMessage.Substring(ListeningMessagePrefix.Length);
184+
var listeningUrlString = listeningMessage.Substring(listeningMessage.IndexOf(
185+
ListeningMessagePrefix, StringComparison.Ordinal) + ListeningMessagePrefix.Length);
186+
188187
output.WriteLine($"Detected that ASP.NET application is accepting connections on: {listeningUrlString}");
189188
listeningUrlString = listeningUrlString.Substring(0, listeningUrlString.IndexOf(':')) +
190189
"://localhost" +
@@ -199,6 +198,25 @@ private Uri GetListeningUri(ITestOutputHelper output)
199198
}
200199
}
201200

201+
private string GetListeningMessage()
202+
{
203+
try
204+
{
205+
return Process
206+
// This will timeout at most after 5 minutes.
207+
.OutputLinesAsEnumerable
208+
.Where(line => line != null)
209+
// This used to do StartsWith, but this is less strict and can prevent issues (very rare) where
210+
// console logging interleaves with other console output in a bad way. For example:
211+
// dbugNow listening on: http://127.0.0.1:12857
212+
.FirstOrDefault(line => line.Trim().Contains(ListeningMessagePrefix, StringComparison.Ordinal));
213+
}
214+
catch (OperationCanceledException)
215+
{
216+
return null;
217+
}
218+
}
219+
202220
private bool IsSuccessStatusCode(HttpResponseMessage response)
203221
{
204222
return response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.Redirect;

src/ProjectTemplates/test/Helpers/ProcessEx.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Reflection;
1010
using System.Runtime.InteropServices;
1111
using System.Text;
12+
using System.Threading;
1213
using System.Threading.Tasks;
1314
using Microsoft.Extensions.Internal;
1415
using Xunit.Abstractions;
@@ -26,6 +27,7 @@ internal class ProcessEx : IDisposable
2627
private readonly object _pipeCaptureLock = new object();
2728
private BlockingCollection<string> _stdoutLines;
2829
private TaskCompletionSource<int> _exited;
30+
private CancellationTokenSource _stdoutLinesCancellationSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
2931

3032
public ProcessEx(ITestOutputHelper output, Process proc)
3133
{
@@ -71,7 +73,7 @@ public string Output
7173
}
7274
}
7375

74-
public IEnumerable<string> OutputLinesAsEnumerable => _stdoutLines.GetConsumingEnumerable();
76+
public IEnumerable<string> OutputLinesAsEnumerable => _stdoutLines.GetConsumingEnumerable(_stdoutLinesCancellationSource.Token);
7577

7678
public int ExitCode => _process.ExitCode;
7779

0 commit comments

Comments
 (0)