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

Node runner should ignore runs that are not started by MNTR #93

Merged
merged 11 commits into from
Oct 11, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Akka.MultiNode.TestAdapter.SampleTests;
using Akka.MultiNode.TestAdapter.SampleTests.Metadata;
using Akka.MultiNode.TestAdapter.Tests.Helpers;
using Akka.Remote.TestKit;
using FluentAssertions;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Xunit;
Expand All @@ -17,6 +18,7 @@ public MultiNodeTestExecutorSpec()
{
_sampleTestsAssemblyPath = Path.GetFullPath(SampleTestsMetadata.AssemblyFileName);
File.Exists(_sampleTestsAssemblyPath).Should().BeTrue($"Assemblies with samples should exist at {_sampleTestsAssemblyPath}");
CommandLine.Initialize(new []{"-Dmultinode.test-runner=\"multinode\""});
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<TargetFramework>$(NetStandardLibVersion)</TargetFramework>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>8.0</LangVersion>
<!-- <EnableDefaultCompileItems>false</EnableDefaultCompileItems> -->
<!-- <GenerateAssemblyInfo>false</GenerateAssemblyInfo> -->
<!-- <NuspecFile>Akka.MultiNode.TestAdapter.nuspec</NuspecFile> -->
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion src/Akka.MultiNode.TestAdapter/Internal/NodeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class NodeTest
public string Role { get; set; }
public MultiNodeTest Test { get; set; }

public string Name => $"{Test.TestName}_node{Node}[{Role}]";
public string Name => $"{Test.MethodName}_node{Node}[{Role}]";
}
}

6 changes: 0 additions & 6 deletions src/Akka.MultiNode.TestAdapter/MultiNodeTestAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Linq;
using System.Xml;
using Akka.MultiNode.TestAdapter.Internal;
using Akka.MultiNode.TestAdapter.Internal.Environment;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
Expand All @@ -27,11 +26,6 @@ namespace Akka.MultiNode.TestAdapter
[ExtensionUri(Constants.ExecutorUriString)]
public class MultiNodeTestAdapter : ITestDiscoverer, ITestExecutor
{
public MultiNodeTestAdapter()
{
MultiNodeEnvironment.Initialize();
}

/// <summary>
/// Discovers the tests available from the provided container.
/// </summary>
Expand Down
9 changes: 7 additions & 2 deletions src/Akka.MultiNode.TestAdapter/MultiNodeTestResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ public TestStatus Status {
{
if (!string.IsNullOrWhiteSpace(Test.SkipReason))
return TestStatus.Skipped;
return NodeResults.Any(result => result.Result == TestStatus.Failed) ? TestStatus.Failed : TestStatus.Passed;

if(NodeResults.Any(result => result.Result == TestStatus.Failed))
return TestStatus.Failed;
if (NodeResults.Any(result => result.Result == TestStatus.Skipped))
return TestStatus.Skipped;
return TestStatus.Passed;
}
}
/// <summary>
Expand All @@ -57,7 +62,7 @@ public TestStatus Status {
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder($"Test {Test.TestName}: {Status}");
var sb = new StringBuilder($"Test {Test.MethodName}: {Status}");
if (Test.SkipReason != null)
sb.Append(" Skipped: ").Append(Test.SkipReason);
foreach (var node in NodeResults)
Expand Down
94 changes: 59 additions & 35 deletions src/Akka.MultiNode.TestAdapter/MultiNodeTestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@
using System.Threading.Tasks;
using Akka.Actor;
using Akka.IO;
using Akka.MultiNode.TestAdapter.Helpers;
using Akka.MultiNode.TestAdapter.Internal;
using Akka.MultiNode.TestAdapter.Internal.Environment;
using Akka.MultiNode.TestAdapter.Internal.Persistence;
using Akka.MultiNode.TestAdapter.Internal.Sinks;
using Akka.MultiNode.TestAdapter.Internal.TrxReporter;
using Akka.MultiNode.TestAdapter.NodeRunner;
using Akka.Remote.TestKit;
using Akka.Util;
using Xunit;
using ErrorMessage = Xunit.Sdk.ErrorMessage;

Expand Down Expand Up @@ -100,7 +97,6 @@ private void Initialize(string assemblyPath, MultiNodeTestRunnerOptions options)
EnableAllSinks(assemblyPath, options);

// Set MNTR environment for correct tests discovery
MultiNodeEnvironment.Initialize();
}

public MultiNodeTestResult ExecuteSpec(MultiNodeTest test, MultiNodeTestRunnerOptions options)
Expand All @@ -113,6 +109,7 @@ public MultiNodeTestResult ExecuteSpec(MultiNodeTest test, MultiNodeTestRunnerOp
: _tcpLogger.Ask<int>(TcpLoggingServer.GetBoundPort.Instance).Result;

TestStarted?.Invoke(test);
Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, "1");
try
{
if (options.SpecNames != null &&
Expand All @@ -132,10 +129,19 @@ public MultiNodeTestResult ExecuteSpec(MultiNodeTest test, MultiNodeTestRunnerOp
var nodes = test.Nodes;

var result = RunSpec(options, test, listenPort);
if(result.Status == MultiNodeTestResult.TestStatus.Failed)
TestFailed?.Invoke(result);
else
TestPassed?.Invoke(result);
switch (result.Status)
{
case MultiNodeTestResult.TestStatus.Passed:
TestPassed?.Invoke(result);
return result;
case MultiNodeTestResult.TestStatus.Failed:
TestFailed?.Invoke(result);
return result;
case MultiNodeTestResult.TestStatus.Skipped:
TestSkipped?.Invoke(test, "Must be run using Akka.MultiNode.TestAdapter");
return null;
}

return result;
}
catch (Exception e)
Expand All @@ -144,6 +150,10 @@ public MultiNodeTestResult ExecuteSpec(MultiNodeTest test, MultiNodeTestRunnerOp
PublishRunnerMessage(e.Message);
return null;
}
finally
{
Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, null);
}
}

/// <summary>
Expand Down Expand Up @@ -173,17 +183,23 @@ public void Dispose()
/// </summary>
public static (List<MultiNodeTest> Tests, List<ErrorMessage> Errors) DiscoverSpecs(string assemblyPath)
{
MultiNodeEnvironment.Initialize();

using (var controller = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyPath))
Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, "1");
try
{
using (var discovery = new Discovery(assemblyPath))
using (var controller = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyPath))
{
controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery());
discovery.Finished.WaitOne();
return (discovery.MultiNodeTests, discovery.Errors);
using (var discovery = new Discovery(assemblyPath))
{
controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery());
discovery.Finished.WaitOne();
return (discovery.MultiNodeTests, discovery.Errors);
}
}
}
finally
{
Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, null);
}
}

private List<MultiNodeTestResult> DiscoverAndRunSpecs(string assemblyPath, MultiNodeTestRunnerOptions options)
Expand Down Expand Up @@ -226,12 +242,20 @@ private List<MultiNodeTestResult> DiscoverAndRunSpecs(string assemblyPath, Multi

// Run test on several nodes and report results
var result = RunSpec(options, test, listenPort);
if(result.Status == MultiNodeTestResult.TestStatus.Failed)
TestFailed?.Invoke(result);
else
TestPassed?.Invoke(result);

testResults.Add(result);
switch (result.Status)
{
case MultiNodeTestResult.TestStatus.Failed:
TestFailed?.Invoke(result);
testResults.Add(result);
break;
case MultiNodeTestResult.TestStatus.Passed:
TestPassed?.Invoke(result);
testResults.Add(result);
break;
case MultiNodeTestResult.TestStatus.Skipped:
TestSkipped?.Invoke(test, test.SkipReason);
continue;
}
}
catch (Exception e)
{
Expand All @@ -250,7 +274,7 @@ private MultiNodeTestResult RunSpec(MultiNodeTestRunnerOptions options, MultiNod

var timelineCollector = TestRunSystem.ActorOf(Props.Create(() => new TimelineLogCollectorActor()));
//TODO: might need to do some validation here to avoid the 260 character max path error on Windows
var folder = Directory.CreateDirectory(Path.Combine(options.OutputDirectory, test.TestName));
var folder = Directory.CreateDirectory(Path.Combine(options.OutputDirectory, test.MethodName));
var testOutputDir = folder.FullName;

var testResult = new MultiNodeTestResult(test);
Expand All @@ -270,7 +294,8 @@ private MultiNodeTestResult RunSpec(MultiNodeTestRunnerOptions options, MultiNod
$@"-Dmultinode.role=""{nodeTest.Role}""",
$@"-Dmultinode.listen-address={options.ListenAddress}",
$@"-Dmultinode.listen-port={listenPort}",
$@"-Dmultinode.test-assembly=""{test.AssemblyPath}"""
$@"-Dmultinode.test-assembly=""{test.AssemblyPath}""",
"-Dmultinode.test-runner=\"multinode\""
};

// Configure process for node
Expand Down Expand Up @@ -319,7 +344,7 @@ private void DumpAggregatedSpecLogs(

if (result.Status == MultiNodeTestResult.TestStatus.Failed)
{
var failedSpecPath = Path.GetFullPath(Path.Combine(options.OutputDirectory, options.FailedSpecsDirectory, $"{result.Test.TestName}.txt"));
var failedSpecPath = Path.GetFullPath(Path.Combine(options.OutputDirectory, options.FailedSpecsDirectory, $"{result.Test.MethodName}.txt"));
var dumpFailureArtifactTask = timelineCollector.Ask<Done>(new TimelineLogCollectorActor.DumpToFile(failedSpecPath));
dumpTasks.Add(dumpFailureArtifactTask);
result.Attachments.Add(new MultiNodeTestResult.Attachment{Title = "Fail log", Path = failedSpecPath});
Expand All @@ -339,9 +364,12 @@ private void WaitForNodeExit(MultiNodeTestResult result, List<(NodeTest, Process
process.WaitForExit();
Console.WriteLine($"Process for test {test.Name} finished with code {process.ExitCode}");
var nodeResult = result.NodeResults.First(n => n.Index == test.Node);
nodeResult.Result = process.ExitCode == 0
? MultiNodeTestResult.TestStatus.Passed
: MultiNodeTestResult.TestStatus.Failed;
nodeResult.Result = process.ExitCode switch
{
0 => MultiNodeTestResult.TestStatus.Passed,
2 => MultiNodeTestResult.TestStatus.Skipped,
_ => MultiNodeTestResult.TestStatus.Failed
};
}
}
finally
Expand All @@ -366,7 +394,7 @@ private Process StartNodeProcess(
var nodeIndex = nodeTest.Node;
var nodeRole = nodeTest.Role;
var logFilePath = Path.GetFullPath(Path.Combine(specFolder.FullName, $"node{nodeIndex}__{nodeRole}__{_platformName}.txt"));
var nodeInfo = new TimelineLogCollectorActor.NodeInfo(nodeIndex, nodeRole, _platformName, nodeTest.Test.TestName);
var nodeInfo = new TimelineLogCollectorActor.NodeInfo(nodeIndex, nodeRole, _platformName, nodeTest.Test.MethodName);
var fileActor = TestRunSystem.ActorOf(Props.Create(() => new FileSystemAppenderActor(logFilePath)));
result.Attachments.Add(new MultiNodeTestResult.Attachment{Title = $"Node {nodeIndex} [{nodeRole}]", Path = logFilePath});

Expand All @@ -377,7 +405,7 @@ private Process StartNodeProcess(
{
if (p.ExitCode == 0)
{
ReportSpecPassFromExitCode(nodeIndex, nodeRole, closureTest.Test.TestName);
ReportSpecPassFromExitCode(nodeIndex, nodeRole, closureTest.Test.MethodName);
}
};
opt.OutputDataReceived = (sender, eventArgs) =>
Expand Down Expand Up @@ -493,12 +521,8 @@ MessageSink CreateVisualizerFileSink()
return new FileSystemMessageSink(visualizerProps);
}

var fileSystemSink = CommandLine.GetProperty("multinode.enable-filesink");
if (!string.IsNullOrEmpty(fileSystemSink))
{
SinkCoordinator.Tell(new SinkCoordinator.EnableSink(CreateJsonFileSink()));
SinkCoordinator.Tell(new SinkCoordinator.EnableSink(CreateVisualizerFileSink()));
}
SinkCoordinator.Tell(new SinkCoordinator.EnableSink(CreateJsonFileSink()));
SinkCoordinator.Tell(new SinkCoordinator.EnableSink(CreateVisualizerFileSink()));
}

private void AbortTcpLoggingServer()
Expand Down
12 changes: 9 additions & 3 deletions src/Akka.MultiNode.TestAdapter/NodeRunner/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
using System.Threading;
using Akka.Actor;
using Akka.IO;
using Akka.MultiNode.TestAdapter.Internal.Environment;
using Akka.MultiNode.TestAdapter.Internal.Sinks;
using Akka.Remote.TestKit;
using Xunit;
Expand All @@ -32,10 +31,15 @@ public int Execute(string[] args)
var maxProcessWaitTimeout = TimeSpan.FromMinutes(5);
IActorRef logger = null;

Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, "1");
try
{
CommandLine.Initialize(args);

var runner = CommandLine.GetPropertyOrDefault("multinode.test-runner", null);
if (runner != "multinode")
return 2;

var nodeIndex = CommandLine.GetInt32("multinode.index");
var nodeRole = CommandLine.GetProperty("multinode.role");
var assemblyFileName = CommandLine.GetProperty("multinode.test-assembly");
Expand All @@ -53,8 +57,6 @@ public int Execute(string[] args)
var tcpClient = logger = system.ActorOf<RunnerTcpClient>();
system.Tcp().Tell(new Tcp.Connect(listenEndpoint), tcpClient);

MultiNodeEnvironment.Initialize();

// In NetCore, if the assembly file hasn't been touched,
// XunitFrontController would fail loading external assemblies and its dependencies.

Expand Down Expand Up @@ -182,6 +184,10 @@ public int Execute(string[] args)
Environment.Exit(1); //signal failure
return 1;
}
finally
{
Environment.SetEnvironmentVariable(MultiNodeFactAttribute.MultiNodeTestEnvironmentName, null);
}

void FlushLogMessages()
{
Expand Down
2 changes: 1 addition & 1 deletion src/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<NetStandardLibVersion>netstandard2.0</NetStandardLibVersion>
<FluentAssertionsVersion>6.1.0</FluentAssertionsVersion>
<FsCheckVersion>2.9.0</FsCheckVersion>
<AkkaVersion>1.4.26</AkkaVersion>
<AkkaVersion>1.4.27</AkkaVersion>
<AkkaPackageTags>akka;actors;actor model;Akka;concurrency</AkkaPackageTags>
</PropertyGroup>
<PropertyGroup>
Expand Down