Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to communicate out of the runner #24

Merged
merged 2 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sample/SampleMauiApp/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public static MauiApp CreateMauiApp()
#if ENABLE_AUTO_START
.EnableAutoStart(true)
#endif
.AddConsoleResultChannel()
.AddTestAssembly(typeof(MauiProgram).Assembly)
.AddTestAssemblies(typeof(SampleXunitTestProject.UnitTests).Assembly)
.AddTestAssemblies(typeof(SampleNUnitTestProject.UnitTests).Assembly)
Expand Down
62 changes: 62 additions & 0 deletions scripts/New-PortListener.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
function New-PortListener {
[CmdletBinding(DefaultParameterSetName = 'All')]
param (
[parameter(Mandatory = $false, HelpMessage = "Enter the tcp port you want to use to listen on, for example 3389", parameterSetName = "TCP")]
[ValidatePattern('^[0-9]+$')]
[ValidateRange(0, 65535)]
[int]$Port,

[string]$Output
)

$Global:ProgressPreference = 'SilentlyContinue' # Hide GUI output

$testPort = Test-NetConnection -ComputerName localhost -Port $Port -WarningAction SilentlyContinue -ErrorAction Stop
if ($testPort.TcpTestSucceeded -ne $True) {
Write-Host ("TCP port {0} is available, continuing..." -f $Port) -ForegroundColor Green
}
else {
Write-Warning ("TCP Port {0} is already listening, aborting..." -f $Port)
return
}

# Start TCP Server
# Used procedure from https://riptutorial.com/powershell/example/18117/tcp-listener
$ipendpoint = New-Object System.Net.IPEndPoint([ipaddress]::Any, $Port)
$listener = New-Object System.Net.Sockets.TcpListener $ipendpoint
$listener.Start()

Write-Host ("Now listening on TCP port {0}, press Escape to stop listening" -f $Port) -ForegroundColor Green
while ($true) {
Write-Host ("Waiting for an incoming connection...") -ForegroundColor Green
while (!$listener.Pending()) {
if ($host.UI.RawUI.KeyAvailable) {
$key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp,IncludeKeyDown")
if ($key.VirtualKeyCode -eq 27) {
$listener.Stop()
Write-Host ("Stopped listening on TCP port {0}" -f $Port) -ForegroundColor Green
return
}
}
Start-Sleep -Milliseconds 1000
}
$client = $listener.AcceptTcpClient()

Write-Host ("Connection established, reading data...") -ForegroundColor Green
$text = ""
$stream = $client.GetStream()
$bytes = New-Object System.Byte[] 1024
while (($i = $stream.Read($bytes,0,$bytes.Length)) -ne 0){
$EncodedText = New-Object System.Text.ASCIIEncoding
$data = $EncodedText.GetString($bytes,0, $i)
$text += $data
Write-Output $data
}
$stream.close()
$client.Close()

if ($Output) {
$text | Set-Content $Output
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public class VisualTestRunnerConfigurationBuilder : IVisualTestRunnerConfigurati
{
readonly MauiAppBuilder _appHostBuilder;
readonly List<Assembly> _testAssemblies = new();
readonly List<IResultChannel> _resultChannels = new();
bool _autoStart;
bool _autoTerminate;

Expand All @@ -31,9 +32,12 @@ void IVisualTestRunnerConfigurationBuilder.EnableAutoStart(bool autoTerminate)
_autoTerminate = autoTerminate;
}

void IVisualTestRunnerConfigurationBuilder.AddResultChannel(IResultChannel resultChannel) =>
_resultChannels.Add(resultChannel);

IVisualTestRunnerConfiguration IVisualTestRunnerConfigurationBuilder.Build() =>
Build();

public VisualTestRunnerConfiguration Build() =>
new(_testAssemblies, _autoStart, _autoTerminate);
new(_testAssemblies, new CompositeResultChannel(_resultChannels), _autoStart, _autoTerminate);
}
6 changes: 5 additions & 1 deletion src/DeviceRunners.VisualRunners.NUnit/NUnitTestListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ namespace DeviceRunners.VisualRunners.NUnit;
class NUnitTestListener : ITestListener
{
readonly IReadOnlyDictionary<ITest, NUnitTestCaseInfo> _testCases;
readonly IResultChannel? _resultChannel;

public NUnitTestListener(IReadOnlyDictionary<ITest, NUnitTestCaseInfo> testCases)//, Action<string> logger, string assemblyDisplayName, bool showDiagnostics)
public NUnitTestListener(IReadOnlyDictionary<ITest, NUnitTestCaseInfo> testCases, IResultChannel? resultChannel)//, Action<string> logger, string assemblyDisplayName, bool showDiagnostics)
{
_testCases = testCases ?? throw new ArgumentNullException(nameof(testCases));
_resultChannel = resultChannel;

// if (showDiagnostics && logger != null)
// {
Expand All @@ -37,6 +39,8 @@ public void TestFinished(ITestResult result)

var info = new NUnitTestResultInfo(testCase, result);
testCase.ReportResult(info);

_resultChannel?.RecordResult(info);
}

public void TestOutput(TestOutput output)
Expand Down
30 changes: 12 additions & 18 deletions src/DeviceRunners.VisualRunners.NUnit/NUnitTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ namespace DeviceRunners.VisualRunners.NUnit;
public class NUnitTestRunner : ITestRunner
{
readonly AsyncLock _executionLock = new();

readonly IVisualTestRunnerConfiguration _options;
readonly IDiagnosticsManager? _diagnosticsManager;

public NUnitTestRunner(IDiagnosticsManager? diagnosticsManager = null, ILogger<NUnitTestDiscoverer>? logger = null)
public NUnitTestRunner(IVisualTestRunnerConfiguration options, IDiagnosticsManager? diagnosticsManager = null)
{
_options = options;
_diagnosticsManager = diagnosticsManager;
}

Expand All @@ -32,20 +35,10 @@ public async Task RunTestsAsync(IEnumerable<ITestAssemblyInfo> testAssemblies, C
{
using (await _executionLock.LockAsync())
{
// message ??= runInfos.Count > 1 || runInfos.FirstOrDefault()?.TestCases.Count > 1
// ? "Run Multiple Tests"
// : runInfos.FirstOrDefault()?.TestCases.FirstOrDefault()?.DisplayName;

// _logger.LogTestStart(message);
await using var autoclosing = new AutoClosingResultChannel(_options.ResultChannel);
await autoclosing.EnsureOpenAsync();

try
{
await AsyncUtils.RunAsync(() => RunTests(testAssemblies, cancellationToken));
}
finally
{
// _logger.LogTestComplete();
}
await AsyncUtils.RunAsync(() => RunTests(testAssemblies, cancellationToken));
}
}

Expand Down Expand Up @@ -127,10 +120,11 @@ void RunTests(NUnitTestAssemblyInfo assemblyInfo, IList<IDisposable> pendingLock
// var executionOptions = TestFrameworkOptions.ForExecution(assemblyInfo.Configuration);

var listener = new NUnitTestListener(
nunitTestCases);//,
// d => _diagnosticsManager?.PostDiagnosticMessage(d),
// assemblyInfo.AssemblyFileName,
// true);
nunitTestCases,
_options?.ResultChannel);//,
// d => _diagnosticsManager?.PostDiagnosticMessage(d),
// assemblyInfo.AssemblyFileName,
// true);

// IExecutionSink resultsSink = new DelegatingExecutionSummarySink(deviceExecSink, () => cancellationToken.IsCancellationRequested);

Expand Down
6 changes: 5 additions & 1 deletion src/DeviceRunners.VisualRunners.Xunit/DeviceExecutionSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ namespace DeviceRunners.VisualRunners.Xunit;
class DeviceExecutionSink : TestMessageSink
{
readonly IReadOnlyDictionary<ITestCase, XunitTestCaseInfo> _testCases;
readonly IResultChannel? _resultChannel;

public DeviceExecutionSink(IReadOnlyDictionary<ITestCase, XunitTestCaseInfo> testCases)
public DeviceExecutionSink(IReadOnlyDictionary<ITestCase, XunitTestCaseInfo> testCases, IResultChannel? resultChannel)
{
_testCases = testCases ?? throw new ArgumentNullException(nameof(testCases));
_resultChannel = resultChannel;

Execution.TestFailedEvent += HandleTestFailed;
Execution.TestPassedEvent += HandleTestPassed;
Expand Down Expand Up @@ -39,5 +41,7 @@ void RecordResult(ITestResultMessage testResult, TestResultStatus status)

var result = new XunitTestResultInfo(testCase, testResult, status);
testCase.ReportResult(result);

_resultChannel?.RecordResult(result);
}
}
27 changes: 8 additions & 19 deletions src/DeviceRunners.VisualRunners.Xunit/XunitTestRunner.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
using System.Reflection;

using Microsoft.Extensions.Logging;

using Xunit;

namespace DeviceRunners.VisualRunners.Xunit;

public class XunitTestRunner : ITestRunner
{
readonly AsyncLock _executionLock = new();

readonly IVisualTestRunnerConfiguration _options;
readonly IDiagnosticsManager? _diagnosticsManager;

public XunitTestRunner(IDiagnosticsManager? diagnosticsManager = null, ILogger<XunitTestDiscoverer>? logger = null)
public XunitTestRunner(IVisualTestRunnerConfiguration options, IDiagnosticsManager? diagnosticsManager = null)
{
_options = options;
_diagnosticsManager = diagnosticsManager;
}

Expand All @@ -32,20 +31,10 @@ public async Task RunTestsAsync(IEnumerable<ITestAssemblyInfo> testAssemblies, C
{
using (await _executionLock.LockAsync())
{
// message ??= runInfos.Count > 1 || runInfos.FirstOrDefault()?.TestCases.Count > 1
// ? "Run Multiple Tests"
// : runInfos.FirstOrDefault()?.TestCases.FirstOrDefault()?.DisplayName;
await using var autoclosing = new AutoClosingResultChannel(_options.ResultChannel);
await autoclosing.EnsureOpenAsync();

// _logger.LogTestStart(message);

try
{
await AsyncUtils.RunAsync(() => RunTests(testAssemblies, cancellationToken));
}
finally
{
// _logger.LogTestComplete();
}
await AsyncUtils.RunAsync(() => RunTests(testAssemblies, cancellationToken));
}
}

Expand Down Expand Up @@ -126,7 +115,7 @@ void RunTests(XunitTestAssemblyInfo assemblyInfo, IList<IDisposable> pendingLock

var executionOptions = TestFrameworkOptions.ForExecution(assemblyInfo.Configuration);

var deviceExecSink = new DeviceExecutionSink(xunitTestCases);
var deviceExecSink = new DeviceExecutionSink(xunitTestCases, _options.ResultChannel);

IExecutionSink resultsSink = new DelegatingExecutionSummarySink(deviceExecSink, () => cancellationToken.IsCancellationRequested);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public interface IVisualTestRunnerConfiguration
/// </summary>
IReadOnlyList<Assembly> TestAssemblies { get; }

IResultChannel? ResultChannel { get; }

bool AutoStart { get; }

bool AutoTerminate { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ void AddTestPlatform<TTestDiscoverer, TTestRunner>()

void EnableAutoStart(bool autoTerminate = false);

void AddResultChannel(IResultChannel resultChannel);

IVisualTestRunnerConfiguration Build();
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace DeviceRunners.VisualRunners;

public class AutoClosingResultChannel : IAsyncDisposable
{
readonly IResultChannel? _channel;
bool _weOpened;

public AutoClosingResultChannel(IResultChannel? channel)
{
_channel = channel;
}

public async Task EnsureOpenAsync()
{
if (_channel is null || _channel.IsOpen)
return;

await _channel.OpenChannel();
_weOpened = true;
}

public async ValueTask DisposeAsync()
{
if (_weOpened && _channel is not null && _channel.IsOpen)
{
await _channel.CloseChannel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace DeviceRunners.VisualRunners;

public class CompositeResultChannel : IResultChannel
{
readonly IReadOnlyList<IResultChannel> _resultChannels;

public CompositeResultChannel(IEnumerable<IResultChannel> resultChannels)
{
_resultChannels = resultChannels.ToList();
}

public bool IsOpen { get; private set; }

public async Task CloseChannel()
{
foreach (var channel in _resultChannels)
{
await channel.CloseChannel();
}

IsOpen = false;
}

public async Task<bool> OpenChannel(string? message = null)
{
var success = true;
foreach (var channel in _resultChannels)
{
var s = await channel.OpenChannel(message);
success = success || s;
}
IsOpen = true;
return success;
}

public void RecordResult(ITestResultInfo testResult)
{
foreach (var channel in _resultChannels)
{
channel.RecordResult(testResult);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

namespace DeviceRunners.VisualRunners;

class ConsoleResultChannel : TextWriterResultChannel
{
public ConsoleResultChannel()
: base(new TextResultChannelFormatter())
{
}

protected override TextWriter CreateWriter() => Console.Out;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Diagnostics;
using System.Text;

namespace DeviceRunners.VisualRunners;

class DebugResultChannel : TextWriterResultChannel
{
public DebugResultChannel()
: base(new TextResultChannelFormatter())
{
}

protected override TextWriter CreateWriter() => new DebugTextWriter();

class DebugTextWriter : TextWriter
{
public override Encoding Encoding => Encoding.Default;

public override void Write(char value) => Debug.Write(value);

public override void Write(string? value) => Debug.Write(value);

public override void WriteLine() => Debug.WriteLine(string.Empty);

public override void WriteLine(string? value) => Debug.WriteLine(value);

public override void WriteLine(string format, object?[] args) => Debug.WriteLine(format, args);
}
}
12 changes: 12 additions & 0 deletions src/DeviceRunners.VisualRunners/Testing/Channels/IResultChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DeviceRunners.VisualRunners;

public interface IResultChannel
{
bool IsOpen { get; }

Task<bool> OpenChannel(string? message = null);

void RecordResult(ITestResultInfo testResult);

Task CloseChannel();
}
Loading
Loading