static void Main(string[] args)
{
OutputDirectory = CommandLine.GetProperty("multinode.output-directory") ?? string.Empty;
TestRunSystem = ActorSystem.Create("TestRunnerLogging");
SinkCoordinator = TestRunSystem.ActorOf(Props.Create<SinkCoordinator>(), "sinkCoordinator");
var listenAddress = IPAddress.Parse(CommandLine.GetPropertyOrDefault("multinode.listen-address", "127.0.0.1"));
var listenPort = CommandLine.GetInt32OrDefault("multinode.listen-port", 6577);
var listenEndpoint = new IPEndPoint(listenAddress, listenPort);
var specName = CommandLine.GetPropertyOrDefault("multinode.spec", "");
var tcpLogger = TestRunSystem.ActorOf(Props.Create(() => new TcpLoggingServer(SinkCoordinator)), "TcpLogger");
TestRunSystem.Tcp().Tell(new Tcp.Bind(tcpLogger, listenEndpoint));
var assemblyName = Path.GetFullPath(args[0]);
EnableAllSinks(assemblyName);
PublishRunnerMessage(String.Format("Running MultiNodeTests for {0}", assemblyName));
using (var controller = new XunitFrontController(assemblyName))
{
using (var discovery = new Discovery())
{
controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery());
discovery.Finished.WaitOne();
foreach (var test in discovery.Tests.Reverse())
{
if (!string.IsNullOrEmpty(test.Value.First().SkipReason))
{
PublishRunnerMessage(string.Format("Skipping test {0}. Reason - {1}", test.Value.First().MethodName, test.Value.First().SkipReason));
continue;
}
if (!string.IsNullOrWhiteSpace(specName) && !test.Value[0].MethodName.Contains(specName))
continue;
PublishRunnerMessage(string.Format("Starting test {0}", test.Value.First().MethodName));
var processes = new List<Process>();
StartNewSpec(test.Value);
foreach (var nodeTest in test.Value)
{
//Loop through each test, work out number of nodes to run on and kick off process
var process = new Process();
processes.Add(process);
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "Akka.NodeTestRunner.exe";
process.StartInfo.Arguments =
$@"-Dmultinode.test-assembly=""{assemblyName}"" -Dmultinode.test-class=""{
nodeTest.TypeName}"" -Dmultinode.test-method=""{nodeTest.MethodName
}"" -Dmultinode.max-nodes={test.Value.Count} -Dmultinode.server-host=""{"localhost"
}"" -Dmultinode.host=""{"localhost"}"" -Dmultinode.index={nodeTest.Node - 1
} -Dmultinode.listen-address={listenAddress} -Dmultinode.listen-port={listenPort}";
var nodeIndex = nodeTest.Node;
//TODO: might need to do some validation here to avoid the 260 character max path error on Windows
var folder = Directory.CreateDirectory(Path.Combine(OutputDirectory, nodeTest.MethodName));
var logFilePath = Path.Combine(folder.FullName, "node" + nodeIndex + ".txt");
var fileActor =
TestRunSystem.ActorOf(Props.Create(() => new FileSystemAppenderActor(logFilePath)));
process.OutputDataReceived += (sender, eventArgs) =>
{
if(eventArgs?.Data != null)
fileActor.Tell(eventArgs.Data);
};
var closureTest = nodeTest;
process.Exited += (sender, eventArgs) =>
{
if (process.ExitCode == 0)
{
ReportSpecPassFromExitCode(nodeIndex, closureTest.TestName);
}
};
process.Start();
process.BeginOutputReadLine();
PublishRunnerMessage(string.Format("Started node {0} on pid {1}", nodeTest.Node, process.Id));
}
foreach (var process in processes)
{
process.WaitForExit();
var exitCode = process.ExitCode;
process.Close();
}
PublishRunnerMessage("Waiting 3 seconds for all messages from all processes to be collected.");
Thread.Sleep(TimeSpan.FromSeconds(3));
FinishSpec();
}
}
}
Console.WriteLine("Complete");
PublishRunnerMessage("Waiting 5 seconds for all messages from all processes to be collected.");
Thread.Sleep(TimeSpan.FromSeconds(5));
CloseAllSinks();
//Block until all Sinks have been terminated.
TestRunSystem.WhenTerminated.Wait(TimeSpan.FromMinutes(1));
if (Debugger.IsAttached)
{
Console.ReadLine(); //block when debugging
}
//Return the proper exit code
Environment.Exit(ExitCodeContainer.ExitCode);
}