Skip to content

Commit

Permalink
update network port detection for E2E tests
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviersolau committed Oct 6, 2024
1 parent 64a6a59 commit a35a067
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 39 deletions.
7 changes: 7 additions & 0 deletions src/libs/SoloX.CodeQuality.Playwright/IPlaywrightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,12 @@ public interface IPlaywrightTest : IAsyncDisposable
/// <param name="pageSetupHandler">Page setup handler to initialize page environment before it is display (or null).</param>
/// <returns>async task.</returns>
Task GotoPageAsync(string relativePath, Func<IPage, Task> testHandler, string? traceName = null, Func<IPage, Task>? pageSetupHandler = null);

/// <summary>
/// Get Url used to bind Playwright and host to test.
/// </summary>
#pragma warning disable CA1056 // URI-like properties should not be strings
string Url { get; }
#pragma warning restore CA1056 // URI-like properties should not be strings
}
}
81 changes: 66 additions & 15 deletions src/libs/SoloX.CodeQuality.Playwright/PlaywrightDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public class PlaywrightDriver : IAsyncDisposable

private static readonly string[] INSTALL_ARGUMENTS = new[] { "install" };

private static readonly object InstallLock = new object();
private static readonly object LockSync = new object();
private static bool playwrightInstalled;

private readonly BrowserNewContextOptions? browserNewContextOptions;
private readonly TracingStartOptions? tracingStartOptions;
Expand Down Expand Up @@ -86,31 +87,78 @@ public async Task InitializeAsync(BrowserTypeLaunchOptions? browserTypeLaunchOpt
/// </summary>
private static void InstallPlaywright()
{
InstallPlaywrightDeps();
InstallPlaywrightBin();
lock (LockSync)
{
if (playwrightInstalled)
{
return;
}

var localApplicationData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PlaywrightDriver");

if (!Directory.Exists(localApplicationData))
{
Directory.CreateDirectory(localApplicationData);
}

var lockFile = Path.Combine(localApplicationData, "PlaywrightInstallLock");

var timeout = 60;

while (!TryWriteLockFile(lockFile))
{
Thread.Sleep(1000);
timeout--;
if (timeout < 0)
{
throw new PlaywrightException($"Unable to lock for Playwright install (Timeout)");
}
}

try
{
InstallPlaywrightDeps();
InstallPlaywrightBin();
}
finally
{
playwrightInstalled = true;
File.Delete(lockFile);
}
}
}

private static bool TryWriteLockFile(string lockFile)
{
try
{
using var file = new FileStream(lockFile, FileMode.CreateNew, FileAccess.Write);
file.WriteByte(0);
file.Flush();
file.Close();
}
catch (IOException)
{
return false;
}
return true;
}

private static void InstallPlaywrightDeps()
{
lock (InstallLock)
var exitCode = Microsoft.Playwright.Program.Main(INSTALL_DEPS_ARGUMENTS);
if (exitCode != 0)
{
var exitCode = Microsoft.Playwright.Program.Main(INSTALL_DEPS_ARGUMENTS);
if (exitCode != 0)
{
throw new PlaywrightException($"Playwright exited with code {exitCode} on install-deps");
}
throw new PlaywrightException($"Playwright exited with code {exitCode} on install-deps");
}
}

private static void InstallPlaywrightBin()
{
lock (InstallLock)
var exitCode = Microsoft.Playwright.Program.Main(INSTALL_ARGUMENTS);
if (exitCode != 0)
{
var exitCode2 = Microsoft.Playwright.Program.Main(INSTALL_ARGUMENTS);
if (exitCode2 != 0)
{
throw new PlaywrightException($"Playwright exited with code {exitCode2} on install");
}
throw new PlaywrightException($"Playwright exited with code {exitCode} on install");
}
}

Expand Down Expand Up @@ -140,6 +188,9 @@ public async Task GotoPageAsync(string url, Func<IPage, Task> testHandler, Brows
catch (PlaywrightException)
{
retry--;

await Task.Delay(1000).ConfigureAwait(false);

if (retry == 0)
{
throw;
Expand Down
55 changes: 32 additions & 23 deletions src/libs/SoloX.CodeQuality.Playwright/PlaywrightTestBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ private sealed class PlaywrightTest : IPlaywrightTest

private bool isDisposed;

public string Url => this.url;

internal PlaywrightTest(
Browser browser,
string url,
Expand Down Expand Up @@ -358,28 +360,6 @@ private sealed class PortStore
{
private readonly HashSet<int> usedPorts = [];

/// <summary>
/// Setup port store and prob all TCP port already in use.
/// </summary>
public PortStore()
{
// Get used port with Netstat like command.
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
var tcpListeners = ipGlobalProperties.GetActiveTcpListeners();

foreach (var tcpEndPoint in tcpListeners)
{
this.usedPorts.Add(tcpEndPoint.Port);
}

var tcpConnections = ipGlobalProperties.GetActiveTcpConnections();

foreach (var tcpConnection in tcpConnections)
{
this.usedPorts.Add(tcpConnection.LocalEndPoint.Port);
}
}

public int GetPort(PortRange portRange)
{
#pragma warning disable CA5394 // Do not use insecure randomness
Expand All @@ -388,14 +368,20 @@ public int GetPort(PortRange portRange)

lock (this.usedPorts)
{
while (!this.usedPorts.Add(port))
var systemPort = ProbUsedPorts();

var allUsedPorts = new HashSet<int>(systemPort.Union(this.usedPorts));

while (allUsedPorts.Contains(port))
{
port++;
if (port >= portRange.EndPort)
{
port = portRange.StartPort;
}
}

this.usedPorts.Add(port);
}
return port;
}
Expand All @@ -407,6 +393,29 @@ public void Release(int port)
this.usedPorts.Remove(port);
}
}

public static HashSet<int> ProbUsedPorts()
{
HashSet<int> usedSystemPorts = [];

// Get used port with Netstat like command.
var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
var tcpListeners = ipGlobalProperties.GetActiveTcpListeners();

foreach (var tcpEndPoint in tcpListeners)
{
usedSystemPorts.Add(tcpEndPoint.Port);
}

var tcpConnections = ipGlobalProperties.GetActiveTcpConnections();

foreach (var tcpConnection in tcpConnections)
{
usedSystemPorts.Add(tcpConnection.LocalEndPoint.Port);
}

return usedSystemPorts;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public PlaywrightTestBuilderLocalTest()
.WithLocalHost(localHostBuilder =>
{
localHostBuilder
.UsePortRange(new PortRange(5000, 6000))
.UsePortRange(new PortRange(6000, 7000))
.UseWebHostWithWwwRoot(path, "home.html")
.UseWebHostBuilder(builder =>
{
Expand Down

0 comments on commit a35a067

Please sign in to comment.