aspnetcore/src/Components/test/E2ETest/Infrastructure/SeleniumStandaloneServer.cs

146 lines
4.3 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Components.E2ETest.Infrastructure
{
class SeleniumStandaloneServer
{
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(30);
private static readonly object _instanceCreationLock = new object();
private static SeleniumStandaloneServer _instance;
public Uri Uri { get; }
public static SeleniumStandaloneServer Instance
{
get
{
lock (_instanceCreationLock)
{
if (_instance == null)
{
_instance = new SeleniumStandaloneServer();
}
}
return _instance;
}
}
private SeleniumStandaloneServer()
{
var port = FindAvailablePort();
Uri = new UriBuilder("http", "localhost", port, "/wd/hub").Uri;
var psi = new ProcessStartInfo
{
FileName = "npm",
Arguments = $"run selenium-standalone start -- -- -port {port}",
RedirectStandardOutput = true,
RedirectStandardError = true,
};
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
psi.FileName = "cmd";
psi.Arguments = $"/c npm {psi.Arguments}";
}
var process = Process.Start(psi);
var builder = new StringBuilder();
process.OutputDataReceived += LogOutput;
process.ErrorDataReceived += LogOutput;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// The Selenium sever has to be up for the entirety of the tests and is only shutdown when the application (i.e. the test) exits.
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
{
if (!process.HasExited)
{
process.KillTree(TimeSpan.FromSeconds(10));
process.Dispose();
}
};
void LogOutput(object sender, DataReceivedEventArgs e)
{
lock (builder)
{
builder.AppendLine(e.Data);
}
}
var waitForStart = Task.Run(async () =>
{
var httpClient = new HttpClient
{
Timeout = TimeSpan.FromSeconds(1),
};
while (true)
{
try
{
var responseTask = httpClient.GetAsync(Uri);
var response = await responseTask;
if (response.StatusCode == HttpStatusCode.OK)
{
return;
}
}
catch (OperationCanceledException)
{
}
await Task.Delay(1000);
}
});
try
{
waitForStart.TimeoutAfter(Timeout).Wait(1000);
}
catch (Exception ex)
{
string output;
lock (builder)
{
output = builder.ToString();
}
throw new InvalidOperationException($"Failed to start selenium sever. {Environment.NewLine}{output}", ex.GetBaseException());
}
}
static int FindAvailablePort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
try
{
listener.Start();
return ((IPEndPoint)listener.LocalEndpoint).Port;
}
finally
{
listener.Stop();
}
}
}
}