Skip to content
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
45 changes: 38 additions & 7 deletions src/core/Akka.MultiNodeTestRunner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,20 @@ class Program
/// otherwise all tests will be executed
/// </description>
/// </item>
/// <item>
/// <term>-Dmultinode.include={include filter}</term>
/// <description>
/// Setting this flag means that only tests which matches the comma separated wildcard
/// filter will be executed, otherwise all tests will be executed
/// </description>
/// </item>
/// <item>
/// <term>-Dmultinode.exclude={exclude filter}</term>
/// <description>
/// Setting this flag means that only tests which does not match the comma separated
/// wildcard filter will be executed, otherwise all tests will be executed
/// </description>
/// </item>
/// </list>
/// </summary>
static void Main(string[] args)
Expand All @@ -130,7 +144,7 @@ static void Main(string[] args)
var suiteName = Path.GetFileNameWithoutExtension(Path.GetFullPath(args[0].Trim('"')));
var teamCityFormattingOn = CommandLine.GetPropertyOrDefault("multinode.teamcity", "false");
if (!Boolean.TryParse(teamCityFormattingOn, out TeamCityFormattingOn))
throw new ArgumentException("Invalid argument provided for -Dteamcity");
throw new ArgumentException("Invalid argument provided for -Dmultinode.teamcity");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch


var listenAddress = IPAddress.Parse(CommandLine.GetPropertyOrDefault("multinode.listen-address", "127.0.0.1"));
var listenPort = CommandLine.GetInt32OrDefault("multinode.listen-port", 6577);
Expand All @@ -143,6 +157,9 @@ static void Main(string[] args)
if (clearOutputDirectory > 0 && Directory.Exists(OutputDirectory))
Directory.Delete(OutputDirectory, true);

var include = new WildcardPatterns(CommandLine.GetPropertyOrDefault("multinode.include", null), true);
var exclude = new WildcardPatterns(CommandLine.GetPropertyOrDefault("multinode.exclude", null), false);

Props coordinatorProps;
switch (reporter.ToLowerInvariant())
{
Expand Down Expand Up @@ -221,25 +238,39 @@ static void Main(string[] args)
{
foreach (var test in discovery.Tests.Reverse())
{
if (!string.IsNullOrEmpty(test.Value.First().SkipReason))
var node = test.Value.First();
if (!string.IsNullOrEmpty(node.SkipReason))
{
PublishRunnerMessage($"Skipping test {test.Value.First().MethodName}. Reason - {test.Value.First().SkipReason}");
PublishRunnerMessage($"Skipping test {node.MethodName}. Reason - {node.SkipReason}");
continue;
}

if (!string.IsNullOrWhiteSpace(specName) &&
CultureInfo.InvariantCulture.CompareInfo.IndexOf(test.Value.First().TestName,
CultureInfo.InvariantCulture.CompareInfo.IndexOf(node.TestName,
specName,
CompareOptions.IgnoreCase) < 0)
{
PublishRunnerMessage($"Skipping [{test.Value.First().MethodName}] (Filtering)");
PublishRunnerMessage($"Skipping [{node.MethodName}] (Filtering)");
continue;
}

// include filter
if (!include.IsMatch(node.MethodName))
{
PublishRunnerMessage($"Skipping [{node.MethodName}] (Include filter)");
continue;
}

if (exclude.IsMatch(node.MethodName))
{
PublishRunnerMessage($"Skipping [{node.MethodName}] (Exclude filter)");
continue;
}

var processes = new List<Process>();

PublishRunnerMessage($"Starting test {test.Value.First().MethodName}");
Console.Out.WriteLine($"Starting test {test.Value.First().MethodName}");
PublishRunnerMessage($"Starting test {node.MethodName}");
Console.Out.WriteLine($"Starting test {node.MethodName}");

StartNewSpec(test.Value);
#if CORECLR
Expand Down
35 changes: 31 additions & 4 deletions src/core/Akka.MultiNodeTestRunner/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,40 @@ This README explains how to run the `Akka.MultiNodeTestRunner` to execute any `M

## Running the MultiNodeTestRunner

Right now the only options for running the `MultiNodeTestRunner` is to build from the Akka.NET source and manually copy the binaries out of `src\core\Akka.MultiNodeTestRunner\bin\[Debug|Release]`:
Right now the only options for running the `MultiNodeTestRunner` is to build from the Akka.NET source and manually copy the binaries out of `src\core\Akka.MultiNodeTestRunner\bin\[Debug|Release]\[Platform name]`, where `[Platform name]` can be either `net5.0`, `net471`, or `netcoreapp3.1`:

![MultiNodeTestRunner binaries](../../../documentation/wiki/images/multinode-teskit/multi-node-testrunner-binaries.png)

The `Akka.MultiNodeTestRunner` process requires only one argument - the full path or name of the assembly containing `MultiNodeSpec` tests.

C:> Akka.MultiNodeTestRunner.exe [assembly name]
## Commandline Arguments

```
C:> Akka.MultiNodeTestRunner.exe [assembly name]
[-Dmultinode.output-directory={dir-path}]
[-Dmultinode.failed-specs-directory={folder-name}]
[-Dmultinode.loglevel={DEBUG|INFO|WARNING|ERROR}]
[-Dmultinode.listen-address={ip-address}]
[-Dmultinode.listen-port={ip-port}]
[-Dmultinode.reporter={trx|teamcity|console}]
[-Dmultinode.clear-output={0|1}]
[-Dmultinode.spec={spec-filter}]
[-Dmultinode.include={include-filter}]
[-Dmultinode.exclude={exclude-filter}]
```

- __assembly name__ : The full path or name of the assembly containing `MultiNodeSpec` tests.
- __-Dmultinode.output-directory__ : Folder where the test report will be exported. Default value is the current working directory.
- __-Dmultinode.failed-specs-directory__ : Folder name inside the output directory where failed test log will be exported, if a test should fail. Default value is `FAILED_SPECS_LOG`.
- __-Dmultinode.loglevel__ : Sets the minimum reported log level used within the test. Valid values are `DEBUG`, `INFO`, `WARNING`, and `ERROR`. Default value is `WARNING`.
- __-Dmultinode.listen-address__ : The TCP/IP address the multi node test runner should listen for test node reports/logs. Default value is `127.0.0.1`.
- __-Dmultinode.listen-port__ : The TCP/IP port the multi node test runner should listen for test node reports/logs. Default value is `6577`.
- __-Dmultinode.reporter__ : The report type this runner should export in. Valid values are `trx`, `teamcity`, and `console`. Note that report files are exported to the current directory for `trx`. Default value is `console`.
- __-Dmultinode.clear-output__ : This flag will clear the output folder before any test is run when it is set to 1. Default value is 0.
- __-Dmultinode.spec__ : Apply a filter to the test class names within the dll. Any fully qualified test class name that contains this string will run. Default value is (all).
- __-Dmultinode.include__ : A "`,`" (comma) separted list of wildcard pattern to be matched and included in the tests. Default value is `*` (all). The filter is applied on the name of the test method.
- __-Dmultinode.exclude__ : A "`,`" (comma) separted list of wildcard pattern to be matched and excluded in the tests. Default value is (none). The filter is applied on the name of the test method.

## Deprecated Commandline Arguments
- __-Dmultinode.teamcity__ : This argument is no longer processed. Use __-Dmultinode.reporter__ with `teamcity` value instead.

### Built-in Tests for Akka.Cluster

Expand Down
41 changes: 41 additions & 0 deletions src/core/Akka.MultiNodeTestRunner/WildcardPatterns.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Akka.MultiNodeTestRunner
{
public class WildcardPatterns
{
private readonly Regex[] _regex;

public WildcardPatterns(string patternCsv, bool matchAllByDefault)
{
if (string.IsNullOrWhiteSpace(patternCsv))
{
if (matchAllByDefault)
{
patternCsv = "*";
}
else
{
_regex = null;
return;
}
}
var patterns = patternCsv.Split(',');

_regex = patterns
.Select(pattern =>
Regex.Escape(pattern.Trim())
.Replace("\\?", ".")
.Replace("\\*", ".*"))
.Select(clean => new Regex($"^{clean}$", RegexOptions.Compiled | RegexOptions.IgnoreCase))
.ToArray();
}

public bool IsMatch(string input) => _regex?.Any(rx => rx.IsMatch(input)) ?? false;
}
}