Skip to content

Commit

Permalink
Pure Xunit implementation (#105)
Browse files Browse the repository at this point in the history
* First working prototype, code already cleaned up

* Fix json deserialization, shorten output names

* Bring back MultiNodeFactAttribute

* Fix output sinks, socket port assignment, runner optimization

* Turn on test coordinator, make sure that the test multinode sample project use the correct fact attribute
  • Loading branch information
Arkatufus committed Nov 8, 2021
1 parent bebe537 commit 5047db5
Show file tree
Hide file tree
Showing 82 changed files with 2,408 additions and 1,945 deletions.
1 change: 0 additions & 1 deletion NuGet.Config
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="myget.org/F/xunit" value="https://www.myget.org/F/xunit/api/v3/index.json" protocolVersion="3" />
<add key="myget.org/F/b4ff5f6" value="http://www.myget.org/F/b4ff5f68eccf4f6bbfed74f055f88d8f/api/v3/index.json" protocolVersion="3" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
Expand Down
185 changes: 53 additions & 132 deletions src/Akka.MultiNode.RemoteHost/RemoteHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,166 +4,87 @@
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace Akka.MultiNode.RemoteHost
{
public static class RemoteHost
{
#region Static functions

public static Process Start(Action action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure).process;

public static Process Start(Action<string[]> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure).process;

public static Process Start(Func<int> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure).process;

public static Process Start(Func<string[], int> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure).process;

public static Process Start(Func<Task> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure).process;

public static Process Start(Func<string[], Task> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure).process;

public static Process Start(Func<Task<int>> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure).process;

public static Process Start(Func<string[], Task<int>> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure).process;

public static void Run(Action action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, waitForExit: true);

public static void Run(Action<string[]> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, waitForExit: true);

public static void Run(Func<int> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, waitForExit: true);

public static void Run(Func<string[], int> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, waitForExit: true);

public static void Run(Func<Task> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, waitForExit: true);

public static void Run(Func<string[], Task> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, waitForExit: true);

public static void Run(Func<Task<int>> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, waitForExit: true);

public static void Run(Func<string[], Task<int>> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, waitForExit: true);

public static Task RunAsync(Action action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, returnTask: true).exitedTask;

public static Task RunAsync(Action<string[]> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<int> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<string[], int> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<Task> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<string[], Task> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<Task<int>> action, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), Array.Empty<string>(), configure, returnTask: true).exitedTask;

public static Task RunAsync(Func<string[], Task<int>> action, string[] args, Action<RemoteHostOptions> configure = null)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, returnTask: true).exitedTask;
public static (Process, Task) RunProcessAsync(
Func<string[], Task<int>> action,
string[] args,
Action<RemoteHostOptions> configure = null,
CancellationToken token = default)
=> Start(GetMethodInfo(action), args ?? throw new ArgumentNullException(nameof(args)), configure, token);

private static (Process process, Task exitedTask) Start(
MethodInfo method,
string[] args,
Action<RemoteHostOptions> configure,
bool waitForExit = false,
bool returnTask = false)
CancellationToken token = default)
{
Process process = null;
var process = new Process();
RemoteHostOptions options;
try
{
process = new Process();

var options = new RemoteHostOptions(process.StartInfo);
options = new RemoteHostOptions(process.StartInfo);
ConfigureProcessStartInfoForMethodInvocation(method, args, options.StartInfo);
configure?.Invoke(options);

TaskCompletionSource<bool> tcs = null;
if (returnTask)
{
tcs = new TaskCompletionSource<bool>();
}

if (options.OnExit != null || tcs != null)
}
catch
{
process.Dispose();
throw;
}

var tcs = new TaskCompletionSource<bool>();
if (token != default)
{
token.Register(() =>
{
process.EnableRaisingEvents = true;
process.Exited += (_1, _2) =>
try
{
options.OnExit(process);
if (tcs != null)
{
tcs?.SetResult(true);
process.Dispose();
}
};
}

if (options.OutputDataReceived != null)
{
process.OutputDataReceived += options.OutputDataReceived;
options.StartInfo.RedirectStandardOutput = true;
}

if (options.ErrorDataReceived != null)
{
process.ErrorDataReceived += options.ErrorDataReceived;
options.StartInfo.RedirectStandardError = true;
}
process.Kill();
} catch{}
});
}

process.Start();

if (options.OutputDataReceived != null)
{
process.BeginOutputReadLine();
}
process.EnableRaisingEvents = true;
process.Exited += (_1, _2) =>
{
options.OnExit(process);
if (options.ErrorDataReceived != null)
{
process.BeginErrorReadLine();
}
tcs.SetResult(true);
process.Dispose();
};

if (waitForExit)
{
process.WaitForExit();
}
if (options.OutputDataReceived != null)
{
process.OutputDataReceived += options.OutputDataReceived;
options.StartInfo.RedirectStandardOutput = true;
}

return (process, tcs?.Task);
if (options.ErrorDataReceived != null)
{
process.ErrorDataReceived += options.ErrorDataReceived;
options.StartInfo.RedirectStandardError = true;
}
catch

process.Start();

if (options.OutputDataReceived != null)
{
process?.Dispose();
throw;
process.BeginOutputReadLine();
}
finally

if (options.ErrorDataReceived != null)
{
if (waitForExit)
{
process?.Dispose();
}
process.BeginErrorReadLine();
}

return (process, tcs.Task);
}

private static void ConfigureProcessStartInfoForMethodInvocation(
Expand Down
71 changes: 0 additions & 71 deletions src/Akka.MultiNode.SampleMultiNodeTests/.runsettings

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
<ItemGroup>
<PackageReference Include="Akka.Cluster.TestKit" Version="$(AkkaVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="FluentAssertions" Version="$(FluentAssertionsVersion)" />
<PackageReference Include="Microsoft.TestPlatform.ObjectModel" Version="$(TestSdkVersion)" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Akka.MultiNode.TestAdapter\Akka.MultiNode.TestAdapter.csproj" />
</ItemGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Akka.Cluster.TestKit;
using Akka.Remote.TestKit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Akka.Cluster.TestKit;
using Akka.Remote.TestKit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using Xunit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using Xunit;

[assembly: CollectionBehavior(DisableTestParallelization = true)]
namespace Akka.MultiNode.TestAdapter.SampleTests.Metadata
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Akka.Cluster.TestKit;
using Akka.Remote.TestKit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down Expand Up @@ -33,7 +34,7 @@ private OneNodeFailedMultiNodeSpec(FailedMultiNodeSpecConfig config) : base(conf
}

[MultiNodeFact]
public void Should_fail()
public void One_node_failed_should_fail()
{
RunOn(() => throw new Exception("Spec should fail"), _config.First);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Akka.Cluster.TestKit;
using Akka.Remote.TestKit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Akka.Cluster.TestKit;
using Akka.Remote.TestKit;
using MultiNodeFactAttribute = Akka.MultiNode.TestAdapter.MultiNodeFactAttribute;

namespace Akka.MultiNode.TestAdapter.SampleTests
{
Expand Down

0 comments on commit 5047db5

Please sign in to comment.