diff --git a/Akka.MultiNodeTestRunner.sln b/Akka.MultiNodeTestRunner.sln index 1d9a60b..d3ab93a 100644 --- a/Akka.MultiNodeTestRunner.sln +++ b/Akka.MultiNodeTestRunner.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29230.47 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNodeTestRunner", "src\Akka.MultiNodeTestRunner\Akka.MultiNodeTestRunner.csproj", "{E945AABA-2779-41E8-9B43-8898FFD64F22}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNode.TestRunner", "src\Akka.MultiNode.TestRunner\Akka.MultiNode.TestRunner.csproj", "{E945AABA-2779-41E8-9B43-8898FFD64F22}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{79D71264-186B-4F62-8930-35DD9ECCAF3B}" ProjectSection(SolutionItems) = preProject @@ -13,11 +13,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{79D71264 build.sh = build.sh EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNodeTestRunner.Shared", "src\Akka.MultiNodeTestRunner.Shared\Akka.MultiNodeTestRunner.Shared.csproj", "{2F249AFE-6F7E-42CA-9D84-A3914F9DD158}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNode.Shared", "src\Akka.MultiNode.Shared\Akka.MultiNode.Shared.csproj", "{2F249AFE-6F7E-42CA-9D84-A3914F9DD158}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.NodeTestRunner", "src\Akka.NodeTestRunner\Akka.NodeTestRunner.csproj", "{5150BF37-8247-4A47-AFB6-300DD7426DC4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNode.NodeRunner", "src\Akka.MultiNode.NodeRunner\Akka.MultiNode.NodeRunner.csproj", "{5150BF37-8247-4A47-AFB6-300DD7426DC4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNodeTestRunner.Shared.Tests", "src\Akka.MultiNodeTestRunner.Shared.Tests\Akka.MultiNodeTestRunner.Shared.Tests.csproj", "{6E3C7F54-F954-4D4C-B9CA-ACFEF046E036}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.MultiNode.Shared.Tests", "src\Akka.MultiNode.Shared.Tests\Akka.MultiNode.Shared.Tests.csproj", "{6E3C7F54-F954-4D4C-B9CA-ACFEF046E036}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.MultiNode.TestAdapter", "src\Akka.MultiNode.TestAdapter\Akka.MultiNode.TestAdapter.csproj", "{F3037C62-E780-4619-89B3-BA21C7168DFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.MultiNode.SampleMultiNodeTests", "src\Akka.MultiNode.SampleMultiNodeTests\Akka.MultiNode.SampleMultiNodeTests.csproj", "{89D2C131-718F-4C93-9343-72E5BE6F7317}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.MultiNode.TestRunner.Shared", "src\Akka.MultiNode.TestRunner.Shared\Akka.MultiNode.TestRunner.Shared.csproj", "{ECAF9A0E-8ED2-460A-8408-4648F2BC3FB5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.MultiNode.TestAdapter.Tests", "src\Akka.MultiNode.TestAdapter.Tests\Akka.MultiNode.TestAdapter.Tests.csproj", "{A66D60EE-714F-4297-A9BD-222CFD868C28}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -41,6 +49,22 @@ Global {6E3C7F54-F954-4D4C-B9CA-ACFEF046E036}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E3C7F54-F954-4D4C-B9CA-ACFEF046E036}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E3C7F54-F954-4D4C-B9CA-ACFEF046E036}.Release|Any CPU.Build.0 = Release|Any CPU + {F3037C62-E780-4619-89B3-BA21C7168DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3037C62-E780-4619-89B3-BA21C7168DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3037C62-E780-4619-89B3-BA21C7168DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3037C62-E780-4619-89B3-BA21C7168DFA}.Release|Any CPU.Build.0 = Release|Any CPU + {89D2C131-718F-4C93-9343-72E5BE6F7317}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89D2C131-718F-4C93-9343-72E5BE6F7317}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89D2C131-718F-4C93-9343-72E5BE6F7317}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89D2C131-718F-4C93-9343-72E5BE6F7317}.Release|Any CPU.Build.0 = Release|Any CPU + {ECAF9A0E-8ED2-460A-8408-4648F2BC3FB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECAF9A0E-8ED2-460A-8408-4648F2BC3FB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECAF9A0E-8ED2-460A-8408-4648F2BC3FB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECAF9A0E-8ED2-460A-8408-4648F2BC3FB5}.Release|Any CPU.Build.0 = Release|Any CPU + {A66D60EE-714F-4297-A9BD-222CFD868C28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A66D60EE-714F-4297-A9BD-222CFD868C28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A66D60EE-714F-4297-A9BD-222CFD868C28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A66D60EE-714F-4297-A9BD-222CFD868C28}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NuGet.Config b/NuGet.Config index c1584db..e11b891 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,10 +1,12 @@  - - - - + + + - \ No newline at end of file + + + + diff --git a/build.fsx b/build.fsx index 9f51c7a..09b5a5d 100644 --- a/build.fsx +++ b/build.fsx @@ -201,9 +201,9 @@ Target "CreateNuget" (fun _ -> let projects = !! "src/**/*.csproj" -- "src/**/*Tests.csproj" // Don't publish unit tests -- "src/**/*Tests*.csproj" - -- "src/**/*.MultiNodeTestRunner.csproj" - -- "src/**/*.MultiNodeTestRunner.Shared.csproj" - -- "src/**/*.NodeTestRunner.csproj" + -- "src/**/*.MultiNode.TestRunner.csproj" + -- "src/**/*.MultiNode.TestRunner.Shared.csproj" + -- "src/**/*.MultiNode.NodeRunner.csproj" let runSingleProject project = DotNetCli.Pack @@ -219,7 +219,7 @@ Target "CreateNuget" (fun _ -> ) Target "PublishMntr" (fun _ -> - let executableProjects = !! "./src/**/Akka.MultiNodeTestRunner.csproj" + let executableProjects = !! "./src/**/Akka.MultiNode.TestRunner.csproj" executableProjects |> Seq.iter (fun project -> DotNetCli.Restore @@ -251,12 +251,12 @@ Target "PublishMntr" (fun _ -> Target "CreateMntrNuget" (fun _ -> // uses the template file to create a temporary .nuspec file with the correct version - CopyFile "./src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec" "./src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec.template" + CopyFile "./src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec" "./src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec.template" let commonPropsVersionPrefix = XMLRead true "./src/common.props" "" "" "//Project/PropertyGroup/VersionPrefix" |> Seq.head let versionReplacement = List.ofSeq [ "@version@", commonPropsVersionPrefix + (if (not (versionSuffix = "")) then ("-" + versionSuffix) else "") ] - TemplateHelper.processTemplates versionReplacement [ "./src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec" ] + TemplateHelper.processTemplates versionReplacement [ "./src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec" ] - let executableProjects = !! "./src/**/Akka.MultiNodeTestRunner.csproj" + let executableProjects = !! "./src/**/Akka.MultiNode.TestRunner.csproj" executableProjects |> Seq.iter (fun project -> DotNetCli.Pack @@ -269,7 +269,7 @@ Target "CreateMntrNuget" (fun _ -> OutputPath = "\"" + outputNuGet + "\"" } ) ) - DeleteFile "./src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec" + DeleteFile "./src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec" ) Target "PublishNuget" (fun _ -> @@ -356,7 +356,7 @@ Target "Nuget" DoNothing "Build" ==> "RunTests" // nuget dependencies -"Clean" ==> "Build" ==> "CreateMntrNuget" ==> "CreateNuget" +"BuildRelease" ==> "CreateMntrNuget" ==> "CreateNuget" "CreateNuget" ==> "SignPackages" ==> "PublishNuget" ==> "Nuget" // docs diff --git a/global.json b/global.json new file mode 100644 index 0000000..80d84a2 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "msbuild-sdks": { + "MSBuild.Sdk.Extras": "2.0.54" + } +} \ No newline at end of file diff --git a/src/Akka.NodeTestRunner/Akka.NodeTestRunner.csproj b/src/Akka.MultiNode.NodeRunner/Akka.MultiNode.NodeRunner.csproj similarity index 73% rename from src/Akka.NodeTestRunner/Akka.NodeTestRunner.csproj rename to src/Akka.MultiNode.NodeRunner/Akka.MultiNode.NodeRunner.csproj index 2a0ccf8..8e6f508 100644 --- a/src/Akka.NodeTestRunner/Akka.NodeTestRunner.csproj +++ b/src/Akka.MultiNode.NodeRunner/Akka.MultiNode.NodeRunner.csproj @@ -1,17 +1,16 @@  + - Akka.NodeTestRunner + Akka.MultiNode.NodeRunner $(NetFrameworkTestVersion);$(NetCoreTestVersion) Exe + Akka.MultiNode.NodeRunner + true - - - - - + @@ -21,6 +20,10 @@ + + + + $(DefineConstants);CORECLR diff --git a/src/Akka.NodeTestRunner/Discovery.cs b/src/Akka.MultiNode.NodeRunner/Discovery.cs similarity index 97% rename from src/Akka.NodeTestRunner/Discovery.cs rename to src/Akka.MultiNode.NodeRunner/Discovery.cs index e8d083b..dca083c 100644 --- a/src/Akka.NodeTestRunner/Discovery.cs +++ b/src/Akka.MultiNode.NodeRunner/Discovery.cs @@ -11,7 +11,7 @@ using Xunit; using Xunit.Abstractions; -namespace Akka.NodeTestRunner +namespace Akka.MultiNode.NodeRunner { [Serializable] public class Discovery : TestMessageVisitor diff --git a/src/Akka.NodeTestRunner/Program.cs b/src/Akka.MultiNode.NodeRunner/Program.cs similarity index 89% rename from src/Akka.NodeTestRunner/Program.cs rename to src/Akka.MultiNode.NodeRunner/Program.cs index e36351e..20b44ef 100644 --- a/src/Akka.NodeTestRunner/Program.cs +++ b/src/Akka.MultiNode.NodeRunner/Program.cs @@ -10,20 +10,20 @@ using System.Linq; using System.Net; using System.Reflection; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Akka.Actor; using Akka.IO; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Environment; +using Akka.MultiNode.Shared.Sinks; using Akka.Remote.TestKit; using Xunit; + #if CORECLR using System.Runtime.Loader; using Microsoft.Extensions.DependencyModel; #endif -namespace Akka.NodeTestRunner +namespace Akka.MultiNode.NodeRunner { class Program { @@ -50,16 +50,26 @@ static int Main(string[] args) var system = ActorSystem.Create("NoteTestRunner-" + nodeIndex); var tcpClient = _logger = system.ActorOf(); system.Tcp().Tell(new Tcp.Connect(listenEndpoint), tcpClient); + + MultiNodeEnvironment.Initialize(); #if CORECLR // In NetCore, if the assembly file hasn't been touched, // XunitFrontController would fail loading external assemblies and its dependencies. AssemblyLoadContext.Default.Resolving += (assemblyLoadContext, assemblyName) => DefaultOnResolving(assemblyLoadContext, assemblyName, assemblyFileName); var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFileName); - DependencyContext.Load(assembly) + var dependencyContext = DependencyContext.Load(assembly); + if (dependencyContext == null) + { + Console.WriteLine($"Failed to load assembly under .NET Core from {assemblyFileName}. " + + $"Possible reason is that assembly is compiled under .NET Full Framework."); + Environment.Exit(1); + return 1; + } + + dependencyContext .CompileLibraries - .Where(dep => dep.Name.ToLower() - .Contains(assembly.FullName.Split(new[] { ',' })[0].ToLower())) + .Where(dep => dep.Name.ToLower().Contains(assembly.FullName.Split(new[] {','})[0].ToLower())) .Select(dependency => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(dependency.Name))); #endif @@ -72,7 +82,7 @@ static int Main(string[] args) * the Discovery class to actually not find any individual specs to run */ var assemblyName = Path.GetFileName(assemblyFileName); - Console.WriteLine("Running specs for {0} [{1}]", assemblyName, assemblyFileName); + Console.WriteLine("Running specs for {0} [{1}] ", assemblyName, assemblyFileName); using (var discovery = new Discovery(assemblyName, typeName)) { using (var sink = new Sink(nodeIndex, nodeRole, tcpClient)) @@ -139,8 +149,9 @@ static int Main(string[] args) FlushLogMessages(); system.Terminate().Wait(); - Environment.Exit(sink.Passed && !timedOut ? 0 : 1); - return sink.Passed ? 0 : 1; + var retCode = sink.Passed && !timedOut ? 0 : 1; + Environment.Exit(retCode); + return retCode; } } } diff --git a/src/Akka.NodeTestRunner/Properties/AssemblyInfo.cs b/src/Akka.MultiNode.NodeRunner/Properties/AssemblyInfo.cs similarity index 100% rename from src/Akka.NodeTestRunner/Properties/AssemblyInfo.cs rename to src/Akka.MultiNode.NodeRunner/Properties/AssemblyInfo.cs diff --git a/src/Akka.NodeTestRunner/Sink.cs b/src/Akka.MultiNode.NodeRunner/Sink.cs similarity index 96% rename from src/Akka.NodeTestRunner/Sink.cs rename to src/Akka.MultiNode.NodeRunner/Sink.cs index 8e4cb04..64025bc 100644 --- a/src/Akka.NodeTestRunner/Sink.cs +++ b/src/Akka.MultiNode.NodeRunner/Sink.cs @@ -6,16 +6,14 @@ //----------------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Text; using System.Threading; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; using Xunit; using Xunit.Abstractions; using IMessageSink = Xunit.Abstractions.IMessageSink; -namespace Akka.NodeTestRunner +namespace Akka.MultiNode.NodeRunner { #if CORECLR class Sink : IMessageSink, IDisposable @@ -46,6 +44,7 @@ public bool OnMessage(IMessageSinkMessage message) _logger.Tell(resultMessage.Output); Console.WriteLine(resultMessage.Output); } + var testPassed = message as ITestPassed; if (testPassed != null) { diff --git a/src/Akka.MultiNode.SampleMultiNodeTests/Akka.MultiNode.SampleMultiNodeTests.csproj b/src/Akka.MultiNode.SampleMultiNodeTests/Akka.MultiNode.SampleMultiNodeTests.csproj new file mode 100644 index 0000000..c47f264 --- /dev/null +++ b/src/Akka.MultiNode.SampleMultiNodeTests/Akka.MultiNode.SampleMultiNodeTests.csproj @@ -0,0 +1,36 @@ + + + + + $(NetFrameworkTestVersion);$(NetCoreTestVersion) + Akka.MultiNode.TestAdapter.SampleTests + + + + + + + + + + + + + + + + + + + + + + + + + + + $(DefineConstants);RELEASE + + + diff --git a/src/Akka.MultiNode.SampleMultiNodeTests/IgnoredXunitTest.cs b/src/Akka.MultiNode.SampleMultiNodeTests/IgnoredXunitTest.cs new file mode 100644 index 0000000..90b6fe1 --- /dev/null +++ b/src/Akka.MultiNode.SampleMultiNodeTests/IgnoredXunitTest.cs @@ -0,0 +1,14 @@ +using System; +using Xunit; + +namespace Akka.MultiNode.TestAdapter.SampleTests +{ + public class IgnoredXunitTest + { + [Fact(Skip = "This test should be ignored by MNTR")] + public void Ignored_test() + { + throw new Exception("This test should be ignored by MNTR"); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.SampleMultiNodeTests/SampleMultiNodeSpec.cs b/src/Akka.MultiNode.SampleMultiNodeTests/SampleMultiNodeSpec.cs new file mode 100644 index 0000000..01bc929 --- /dev/null +++ b/src/Akka.MultiNode.SampleMultiNodeTests/SampleMultiNodeSpec.cs @@ -0,0 +1,50 @@ +using Akka.Cluster.TestKit; +using Akka.MultiNode.NodeRunner; +using Akka.MultiNode.Shared.Environment; +using Akka.MultiNode.TestRunner.Shared; +using Akka.Remote.TestKit; + +namespace Akka.MultiNode.TestAdapter.SampleTests +{ + public class SampleMultiNodeSpecConfig : MultiNodeConfig + { + public RoleName First { get; } + public RoleName Second { get; } + + public SampleMultiNodeSpecConfig() + { + First = Role("first"); + Second = Role("second"); + + CommonConfig = DebugConfig(true) + .WithFallback(MultiNodeClusterSpec.ClusterConfig()); + } + } + + public class SampleMultiNodeSpec : MultiNodeClusterSpec + { + private readonly SampleMultiNodeSpecConfig _config; + + public SampleMultiNodeSpec() : this(new SampleMultiNodeSpecConfig()) + { + } + + private SampleMultiNodeSpec(SampleMultiNodeSpecConfig config) : base(config, typeof(SampleMultiNodeSpec)) + { + _config = config; + } + + // [MultiNodeFact] + [CustomMultiNodeFact] + public void Should_start_and_join_cluster() + { + RunOn(StartClusterNode, _config.First); + + EnterBarrier("first-started"); + + RunOn(() => Cluster.Join(GetAddress(_config.First)), _config.Second); + + EnterBarrier("after"); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.SampleMultiNodeTests/SampleTestsMetadata.cs b/src/Akka.MultiNode.SampleMultiNodeTests/SampleTestsMetadata.cs new file mode 100644 index 0000000..2f7a6c9 --- /dev/null +++ b/src/Akka.MultiNode.SampleMultiNodeTests/SampleTestsMetadata.cs @@ -0,0 +1,20 @@ +using System.IO; +using System.Reflection; + +namespace Akka.MultiNode.TestAdapter.SampleTests +{ + /// + /// SampleTestsMetadata + /// + public static class SampleTestsMetadata + { + /// + /// Sample tests assembly path + /// + public static string AssemblyPath => typeof(SampleMultiNodeSpec).Assembly.Location; + /// + /// Gets assembly file name + /// + public static string AssemblyFileName => typeof(SampleMultiNodeSpec).Assembly.GetName().Name + ".dll"; + } +} \ No newline at end of file diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj b/src/Akka.MultiNode.Shared.Tests/Akka.MultiNode.Shared.Tests.csproj similarity index 68% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj rename to src/Akka.MultiNode.Shared.Tests/Akka.MultiNode.Shared.Tests.csproj index f1df1de..92b3d2b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/Akka.MultiNodeTestRunner.Shared.Tests.csproj +++ b/src/Akka.MultiNode.Shared.Tests/Akka.MultiNode.Shared.Tests.csproj @@ -2,23 +2,25 @@ - Akka.MultiNodeTestRunner.Shared.Tests + Akka.MultiNode.Shared.Tests $(NetFrameworkTestVersion);$(NetCoreTestVersion) false + Akka.MultiNode.Shared.Tests - - + + + - + diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs b/src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs similarity index 84% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs rename to src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs index 5616f32..2ebe204 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs +++ b/src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs @@ -7,7 +7,7 @@ using Akka.Remote.TestKit; -namespace Akka.MultiNodeTestRunner.Shared.Tests.MultiNodeTestRunnerDiscovery +namespace Akka.MultiNode.Shared.Tests.MultiNodeTestRunnerDiscovery { public class DiscoveryCases { @@ -43,9 +43,9 @@ public DeeplyInheritedConfig() } } - public abstract class DeeplyInheritedBaseSpec + public abstract class DeeplyInheritedBaseSpec : MultiNodeSpec { - public DeeplyInheritedBaseSpec(DeeplyInheritedConfig config) + public DeeplyInheritedBaseSpec(DeeplyInheritedConfig config) : base(config, typeof(DeeplyInheritedBaseSpec)) { } @@ -53,6 +53,9 @@ public DeeplyInheritedBaseSpec(DeeplyInheritedConfig config) public void Dummy() { } + + /// + protected override int InitialParticipantsValueFactory { get; } } public abstract class DeeplyInheritedMediumSpec : DeeplyInheritedBaseSpec @@ -99,10 +102,10 @@ public FloodyConfig() } } - public abstract class FloodyBaseSpec + public abstract class FloodyBaseSpec : MultiNodeSpec { - public FloodyBaseSpec(FloodyConfig config) + public FloodyBaseSpec(FloodyConfig config) : base(config, typeof(FloodyBaseSpec)) { } @@ -110,6 +113,9 @@ public FloodyBaseSpec(FloodyConfig config) public void Dummy() { } + + /// + protected override int InitialParticipantsValueFactory { get; } } public class FloodyChildSpec1 : FloodyBaseSpec @@ -149,9 +155,9 @@ public DiverseConfig() } } - public class DiverseSpec + public class DiverseSpec : MultiNodeSpec { - public DiverseSpec(DiverseConfig config) + public DiverseSpec(DiverseConfig config) : base(config, typeof(DiverseSpec)) { } @@ -159,6 +165,9 @@ public DiverseSpec(DiverseConfig config) public void Dummy() { } + + /// + protected override int InitialParticipantsValueFactory { get; } } } } diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs b/src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs similarity index 96% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs rename to src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs index 99e485e..4671508 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs @@ -9,11 +9,12 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Akka.TestKit; -using Xunit; +using Akka.MultiNode.TestRunner; +using Akka.MultiNode.TestRunner.Shared; using FluentAssertions; +using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests.MultiNodeTestRunnerDiscovery +namespace Akka.MultiNode.Shared.Tests.MultiNodeTestRunnerDiscovery { public class DiscoverySpec { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/NodeDataActorSpec.cs b/src/Akka.MultiNode.Shared.Tests/NodeDataActorSpec.cs similarity index 96% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/NodeDataActorSpec.cs rename to src/Akka.MultiNode.Shared.Tests/NodeDataActorSpec.cs index e7741f4..8d45719 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/NodeDataActorSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/NodeDataActorSpec.cs @@ -6,12 +6,11 @@ //----------------------------------------------------------------------- using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Sinks; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { public class NodeDataActorSpec : TestKit.Xunit2.TestKit { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/NodeMessageHelpers.cs b/src/Akka.MultiNode.Shared.Tests/NodeMessageHelpers.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/NodeMessageHelpers.cs rename to src/Akka.MultiNode.Shared.Tests/NodeMessageHelpers.cs index 620f65e..35d6052 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/NodeMessageHelpers.cs +++ b/src/Akka.MultiNode.Shared.Tests/NodeMessageHelpers.cs @@ -9,10 +9,10 @@ using System.Collections.Generic; using System.Linq; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.Util; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Tests.Utils; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { /// /// Helper class for creating diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/ParsingSpec.cs b/src/Akka.MultiNode.Shared.Tests/ParsingSpec.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/ParsingSpec.cs rename to src/Akka.MultiNode.Shared.Tests/ParsingSpec.cs index 4f52873..2fd4b35 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/ParsingSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/ParsingSpec.cs @@ -9,12 +9,11 @@ using Akka.Actor; using Akka.Configuration; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Sinks; using FluentAssertions; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { /// /// Used to test the 's ability to parse diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs b/src/Akka.MultiNode.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs rename to src/Akka.MultiNode.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs index 4761df2..fbe101a 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/Persistence/JsonPersistentTestRunStoreSpec.cs @@ -8,14 +8,13 @@ using System.IO; using System.Linq; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Persistence; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Persistence; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Sinks; using FluentAssertions; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests.Persistence +namespace Akka.MultiNode.Shared.Tests.Persistence { public class JsonPersistentTestRunStoreSpec : TestKit.Xunit2.TestKit { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/Properties/AssemblyInfo.cs b/src/Akka.MultiNode.Shared.Tests/Properties/AssemblyInfo.cs similarity index 100% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/Properties/AssemblyInfo.cs rename to src/Akka.MultiNode.Shared.Tests/Properties/AssemblyInfo.cs diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/ResultSummaryTests.cs b/src/Akka.MultiNode.Shared.Tests/ResultSummaryTests.cs similarity index 96% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/ResultSummaryTests.cs rename to src/Akka.MultiNode.Shared.Tests/ResultSummaryTests.cs index d55aa44..84cf311 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/ResultSummaryTests.cs +++ b/src/Akka.MultiNode.Shared.Tests/ResultSummaryTests.cs @@ -4,12 +4,13 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Collections.Generic; -using Akka.MultiNodeTestRunner.TrxReporter.Models; +using Akka.MultiNode.Shared.TrxReporter.Models; using FluentAssertions; using Xunit; -namespace Akka.MultiNodeTestRunner.TrxReporter.Tests +namespace Akka.MultiNode.Shared.Tests { public class ResultSummaryTests { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/SpecRunCoordinatorSpec.cs b/src/Akka.MultiNode.Shared.Tests/SpecRunCoordinatorSpec.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/SpecRunCoordinatorSpec.cs rename to src/Akka.MultiNode.Shared.Tests/SpecRunCoordinatorSpec.cs index 2cb43d1..c00a42c 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/SpecRunCoordinatorSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/SpecRunCoordinatorSpec.cs @@ -8,12 +8,11 @@ using System.Collections.Generic; using System.Linq; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Sinks; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { public class SpecRunCoordinatorSpec : TestKit.Xunit2.TestKit { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunCoordinatorSpec.cs b/src/Akka.MultiNode.Shared.Tests/TestRunCoordinatorSpec.cs similarity index 95% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunCoordinatorSpec.cs rename to src/Akka.MultiNode.Shared.Tests/TestRunCoordinatorSpec.cs index 228a097..37303ff 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunCoordinatorSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/TestRunCoordinatorSpec.cs @@ -5,16 +5,14 @@ // //----------------------------------------------------------------------- -using System; using System.Collections.Generic; using System.Linq; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Sinks; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { public class TestRunCoordinatorSpec : TestKit.Xunit2.TestKit { diff --git a/src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunShutdownSpec.cs b/src/Akka.MultiNode.Shared.Tests/TestRunShutdownSpec.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunShutdownSpec.cs rename to src/Akka.MultiNode.Shared.Tests/TestRunShutdownSpec.cs index a564321..02bf50f 100644 --- a/src/Akka.MultiNodeTestRunner.Shared.Tests/TestRunShutdownSpec.cs +++ b/src/Akka.MultiNode.Shared.Tests/TestRunShutdownSpec.cs @@ -7,13 +7,11 @@ using System; using System.Linq; -using System.Threading.Tasks; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.TestKit; +using Akka.MultiNode.Shared.Sinks; using Xunit; -namespace Akka.MultiNodeTestRunner.Shared.Tests +namespace Akka.MultiNode.Shared.Tests { /// /// Used to validate that we can get final reporting on shutdown diff --git a/src/Akka.MultiNode.Shared.Tests/Utils/ContinuousEnumerator.cs b/src/Akka.MultiNode.Shared.Tests/Utils/ContinuousEnumerator.cs new file mode 100644 index 0000000..801c181 --- /dev/null +++ b/src/Akka.MultiNode.Shared.Tests/Utils/ContinuousEnumerator.cs @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2019 Lightbend Inc. +// Copyright (C) 2013-2019 .NET Foundation +// +//----------------------------------------------------------------------- + + using System.Collections; + using System.Collections.Generic; + + namespace Akka.MultiNode.Shared.Tests.Utils +{ + /// + /// Implements a circular around an existing . + /// + /// This allows for continuous read-only iteration over a set. + /// + /// The type of objects to enumerate + /// + /// Duplicates Akka.Utils.ContinuousEnumerator class from Akka.NET https://github.com/akkadotnet/akka.net + /// + internal sealed class ContinuousEnumerator : IEnumerator + { + private readonly IEnumerator _internalEnumerator; + + /// + /// Initializes a new instance of the class. + /// + /// The raw iterator from some object + public ContinuousEnumerator(IEnumerator internalEnumerator) + { + _internalEnumerator = internalEnumerator; + } + + /// + public void Dispose() + { + _internalEnumerator.Dispose(); + } + + /// + public bool MoveNext() + { + if (!_internalEnumerator.MoveNext()) + { + _internalEnumerator.Reset(); + return _internalEnumerator.MoveNext(); + } + return true; + } + + /// + public void Reset() + { + _internalEnumerator.Reset(); + } + + /// + public T Current { get { return _internalEnumerator.Current; } } + + object IEnumerator.Current + { + get { return Current; } + } + } + + /// + /// Extension method class for adding support to any + /// instance within Akka.NET + /// + internal static class ContinuousEnumeratorExtensions + { + /// + /// Provides a instance for . + /// + /// Internally, it just wraps 's internal iterator with circular iteration behavior. + /// + /// TBD + /// TBD + public static ContinuousEnumerator GetContinuousEnumerator(this IEnumerable collection) + { + return new ContinuousEnumerator(collection.GetEnumerator()); + } + } +} + diff --git a/src/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj b/src/Akka.MultiNode.Shared/Akka.MultiNode.Shared.csproj similarity index 85% rename from src/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj rename to src/Akka.MultiNode.Shared/Akka.MultiNode.Shared.csproj index 506c80b..e0db28c 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Akka.MultiNodeTestRunner.Shared.csproj +++ b/src/Akka.MultiNode.Shared/Akka.MultiNode.Shared.csproj @@ -1,13 +1,15 @@  - Akka.MultiNodeTestRunner.Shared - $(NetStandardLibVersion) + Akka.MultiNode.Shared + $(NetStandardLibVersion) false false + Akka.MultiNode.Shared + diff --git a/src/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs b/src/Akka.MultiNode.Shared/CompilerErrorCollection.cs similarity index 90% rename from src/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs rename to src/Akka.MultiNode.Shared/CompilerErrorCollection.cs index 5f87322..d40c950 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/CompilerErrorCollection.cs +++ b/src/Akka.MultiNode.Shared/CompilerErrorCollection.cs @@ -5,12 +5,10 @@ // //----------------------------------------------------------------------- -using System; using System.Collections.Generic; -using System.Reflection; #if CORECLR -namespace System.CodeDom.Compiler +namespace Akka.MultiNode.Shared { public class CompilerErrorCollection : List { diff --git a/src/Akka.MultiNode.Shared/Environment/CustomMultiNodeFactAttribute.cs b/src/Akka.MultiNode.Shared/Environment/CustomMultiNodeFactAttribute.cs new file mode 100644 index 0000000..b0abe31 --- /dev/null +++ b/src/Akka.MultiNode.Shared/Environment/CustomMultiNodeFactAttribute.cs @@ -0,0 +1,25 @@ +using System; +using Xunit; + +namespace Akka.MultiNode.Shared.Environment +{ + // TODO: Remove this after Akka.Cluster.TestKit.MultiNodeFactAttribute will be updated (https://github.com/akkadotnet/akka.net/issues/4188) + public class CustomMultiNodeFactAttribute : FactAttribute + { + /// + /// Set by MultiNodeTestRunner when running multi-node tests + /// + public const string MultiNodeTestEnvironmentName = "__AKKA_MULTI_NODE_ENVIRONMENT"; + + private static readonly Lazy ExecutedByMultiNodeRunner = new Lazy(() => + { + return System.Environment.GetEnvironmentVariable(MultiNodeTestEnvironmentName) != null; + }); + + public override string Skip + { + get => !ExecutedByMultiNodeRunner.Value ? "Must be executed by multi-node test runner" : base.Skip; + set => base.Skip = value; + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.Shared/Environment/MultiNodeEnvironment.cs b/src/Akka.MultiNode.Shared/Environment/MultiNodeEnvironment.cs new file mode 100644 index 0000000..1843e40 --- /dev/null +++ b/src/Akka.MultiNode.Shared/Environment/MultiNodeEnvironment.cs @@ -0,0 +1,16 @@ +namespace Akka.MultiNode.Shared.Environment +{ + /// + /// MultiNodeEnvironment + /// + public static class MultiNodeEnvironment + { + /// + /// Initializes multi-node test environment. Used by and NodeRunner. + /// + public static void Initialize() + { + System.Environment.SetEnvironmentVariable(CustomMultiNodeFactAttribute.MultiNodeTestEnvironmentName, "1"); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNodeTestRunner.Shared/ExitCodeContainer.cs b/src/Akka.MultiNode.Shared/ExitCodeContainer.cs similarity index 88% rename from src/Akka.MultiNodeTestRunner.Shared/ExitCodeContainer.cs rename to src/Akka.MultiNode.Shared/ExitCodeContainer.cs index eb253f2..8b5606b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/ExitCodeContainer.cs +++ b/src/Akka.MultiNode.Shared/ExitCodeContainer.cs @@ -5,9 +5,9 @@ // //----------------------------------------------------------------------- -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared +namespace Akka.MultiNode.Shared { /// /// Global state for hanging onto the exit code used by the process. diff --git a/src/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs b/src/Akka.MultiNode.Shared/Extensions/DateTimeExtension.cs similarity index 77% rename from src/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs rename to src/Akka.MultiNode.Shared/Extensions/DateTimeExtension.cs index aa0bcfe..04a4dcc 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Extensions/DateTimeExtension.cs +++ b/src/Akka.MultiNode.Shared/Extensions/DateTimeExtension.cs @@ -6,14 +6,9 @@ //----------------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; #if CORECLR -namespace Akka.MultiNodeTestRunner.Shared.Extensions +namespace Akka.MultiNode.Shared.Extensions { internal static class DateTimeExtension { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs b/src/Akka.MultiNode.Shared/Extensions/TypeExtension.cs similarity index 81% rename from src/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs rename to src/Akka.MultiNode.Shared/Extensions/TypeExtension.cs index b6c7a68..b6593c7 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Extensions/TypeExtension.cs +++ b/src/Akka.MultiNode.Shared/Extensions/TypeExtension.cs @@ -6,14 +6,10 @@ //----------------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; #if CORECLR -namespace Akka.MultiNodeTestRunner.Shared.Extensions +namespace Akka.MultiNode.Shared.Extensions { internal static class TypeExtension { diff --git a/src/Akka.MultiNodeTestRunner.Shared/NodeTest.cs b/src/Akka.MultiNode.Shared/NodeTest.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared/NodeTest.cs rename to src/Akka.MultiNode.Shared/NodeTest.cs index 7a720a1..f98dba9 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/NodeTest.cs +++ b/src/Akka.MultiNode.Shared/NodeTest.cs @@ -5,7 +5,7 @@ // //----------------------------------------------------------------------- -namespace Akka.MultiNodeTestRunner.Shared +namespace Akka.MultiNode.Shared { public class NodeTest { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/EnumerableExtensions.cs b/src/Akka.MultiNode.Shared/Persistence/EnumerableExtensions.cs similarity index 93% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/EnumerableExtensions.cs rename to src/Akka.MultiNode.Shared/Persistence/EnumerableExtensions.cs index 60c1682..cd91e7f 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/EnumerableExtensions.cs +++ b/src/Akka.MultiNode.Shared/Persistence/EnumerableExtensions.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { public static class EnumerableExtensions { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/FileNameGenerator.cs b/src/Akka.MultiNode.Shared/Persistence/FileNameGenerator.cs similarity index 96% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/FileNameGenerator.cs rename to src/Akka.MultiNode.Shared/Persistence/FileNameGenerator.cs index 850a56c..81dca69 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/FileNameGenerator.cs +++ b/src/Akka.MultiNode.Shared/Persistence/FileNameGenerator.cs @@ -8,7 +8,7 @@ using System; using System.IO; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { public class FileNameGenerator { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/IPersistentTestRunStore.cs b/src/Akka.MultiNode.Shared/Persistence/IPersistentTestRunStore.cs similarity index 86% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/IPersistentTestRunStore.cs rename to src/Akka.MultiNode.Shared/Persistence/IPersistentTestRunStore.cs index ed9f388..68eebad 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/IPersistentTestRunStore.cs +++ b/src/Akka.MultiNode.Shared/Persistence/IPersistentTestRunStore.cs @@ -5,9 +5,9 @@ // //----------------------------------------------------------------------- -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { /// /// Persistent store for saving instances diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/IRetrievableTestRunStore.cs b/src/Akka.MultiNode.Shared/Persistence/IRetrievableTestRunStore.cs similarity index 87% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/IRetrievableTestRunStore.cs rename to src/Akka.MultiNode.Shared/Persistence/IRetrievableTestRunStore.cs index b5584f9..2ccc446 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/IRetrievableTestRunStore.cs +++ b/src/Akka.MultiNode.Shared/Persistence/IRetrievableTestRunStore.cs @@ -5,9 +5,9 @@ // //----------------------------------------------------------------------- -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { /// /// Persistent store for retrieving instances diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/JsonPersistentTestRunStore.cs b/src/Akka.MultiNode.Shared/Persistence/JsonPersistentTestRunStore.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/JsonPersistentTestRunStore.cs rename to src/Akka.MultiNode.Shared/Persistence/JsonPersistentTestRunStore.cs index a4bb992..e8e841b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/JsonPersistentTestRunStore.cs +++ b/src/Akka.MultiNode.Shared/Persistence/JsonPersistentTestRunStore.cs @@ -9,11 +9,11 @@ using System.IO; using System.Reflection; using System.Text; -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { /// /// JavaScript Object Notation (JSON) implementation of the diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItem.cs b/src/Akka.MultiNode.Shared/Persistence/TimelineItem.cs similarity index 95% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItem.cs rename to src/Akka.MultiNode.Shared/Persistence/TimelineItem.cs index 1e1b147..6816a87 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItem.cs +++ b/src/Akka.MultiNode.Shared/Persistence/TimelineItem.cs @@ -7,7 +7,7 @@ using System; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { public class TimelineItem { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItemFactory.cs b/src/Akka.MultiNode.Shared/Persistence/TimelineItemFactory.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItemFactory.cs rename to src/Akka.MultiNode.Shared/Persistence/TimelineItemFactory.cs index 5a0dba7..fc66d8a 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/TimelineItemFactory.cs +++ b/src/Akka.MultiNode.Shared/Persistence/TimelineItemFactory.cs @@ -7,7 +7,7 @@ using System; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { public static class TimelineItemFactory { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerPersistentTestRunStore.cs b/src/Akka.MultiNode.Shared/Persistence/VisualizerPersistentTestRunStore.cs similarity index 90% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerPersistentTestRunStore.cs rename to src/Akka.MultiNode.Shared/Persistence/VisualizerPersistentTestRunStore.cs index e4f7d37..65f3a52 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerPersistentTestRunStore.cs +++ b/src/Akka.MultiNode.Shared/Persistence/VisualizerPersistentTestRunStore.cs @@ -6,10 +6,9 @@ //----------------------------------------------------------------------- using System.IO; +using Akka.MultiNode.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Reporting; - -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { /// /// Stores test run as a html page. diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs b/src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs rename to src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs index 95c807f..e98b562 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs +++ b/src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.Tree.cs @@ -8,9 +8,9 @@ using System; using System.Collections.Generic; using System.Linq; -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { partial class VisualizerRuntimeTemplate { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs b/src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.cs similarity index 99% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs rename to src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.cs index 9d24ad3..bc59aef 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.cs +++ b/src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.cs @@ -16,10 +16,10 @@ // ------------------------------------------------------------------------------ #if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; +using Akka.MultiNode.Shared.Extensions; #endif -namespace Akka.MultiNodeTestRunner.Shared.Persistence +namespace Akka.MultiNode.Shared.Persistence { using System.Linq; using System.Text; diff --git a/src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.tt b/src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.tt similarity index 100% rename from src/Akka.MultiNodeTestRunner.Shared/Persistence/VisualizerRuntimeTemplate.tt rename to src/Akka.MultiNode.Shared/Persistence/VisualizerRuntimeTemplate.tt diff --git a/src/Akka.MultiNodeTestRunner.Shared/Properties/AssemblyInfo.cs b/src/Akka.MultiNode.Shared/Properties/AssemblyInfo.cs similarity index 100% rename from src/Akka.MultiNodeTestRunner.Shared/Properties/AssemblyInfo.cs rename to src/Akka.MultiNode.Shared/Properties/AssemblyInfo.cs diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/MultiNodeMessage.cs b/src/Akka.MultiNode.Shared/Reporting/MultiNodeMessage.cs similarity index 99% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/MultiNodeMessage.cs rename to src/Akka.MultiNode.Shared/Reporting/MultiNodeMessage.cs index cce8c2a..99df610 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/MultiNodeMessage.cs +++ b/src/Akka.MultiNode.Shared/Reporting/MultiNodeMessage.cs @@ -8,7 +8,7 @@ using System; using Akka.Event; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { /// /// Message from an individual node diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/NodeDataActor.cs b/src/Akka.MultiNode.Shared/Reporting/NodeDataActor.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/NodeDataActor.cs rename to src/Akka.MultiNode.Shared/Reporting/NodeDataActor.cs index 682bf45..e5b8dd7 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/NodeDataActor.cs +++ b/src/Akka.MultiNode.Shared/Reporting/NodeDataActor.cs @@ -7,9 +7,9 @@ using System; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { /// /// Actor responsible for processing test messages for an individual node within a multi-node test diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/SpecRunCoordinator.cs b/src/Akka.MultiNode.Shared/Reporting/SpecRunCoordinator.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/SpecRunCoordinator.cs rename to src/Akka.MultiNode.Shared/Reporting/SpecRunCoordinator.cs index 488ad11..18c9d73 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/SpecRunCoordinator.cs +++ b/src/Akka.MultiNode.Shared/Reporting/SpecRunCoordinator.cs @@ -9,9 +9,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { /// /// Actor responsible for organizing the results of an individual spec diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TeamCityLoggerActor.cs b/src/Akka.MultiNode.Shared/Reporting/TeamCityLoggerActor.cs similarity index 84% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/TeamCityLoggerActor.cs rename to src/Akka.MultiNode.Shared/Reporting/TeamCityLoggerActor.cs index c149994..ff21af1 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TeamCityLoggerActor.cs +++ b/src/Akka.MultiNode.Shared/Reporting/TeamCityLoggerActor.cs @@ -6,13 +6,9 @@ //----------------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Akka.Actor; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { public class TeamCityLoggerActor : ReceiveActor { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunCoordinator.cs b/src/Akka.MultiNode.Shared/Reporting/TestRunCoordinator.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunCoordinator.cs rename to src/Akka.MultiNode.Shared/Reporting/TestRunCoordinator.cs index 7005053..ca79879 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunCoordinator.cs +++ b/src/Akka.MultiNode.Shared/Reporting/TestRunCoordinator.cs @@ -10,9 +10,9 @@ using System.Linq; using System.Threading.Tasks; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { /// /// Actor responsible for organizing all of the data for each test run diff --git a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunTree.cs b/src/Akka.MultiNode.Shared/Reporting/TestRunTree.cs similarity index 99% rename from src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunTree.cs rename to src/Akka.MultiNode.Shared/Reporting/TestRunTree.cs index 45abfda..067c021 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Reporting/TestRunTree.cs +++ b/src/Akka.MultiNode.Shared/Reporting/TestRunTree.cs @@ -10,7 +10,7 @@ using System.Linq; using Newtonsoft.Json; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Reporting { /// /// The top of the tree - represents an entire test run. diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs b/src/Akka.MultiNode.Shared/Sinks/ConsoleMessageSinkActor.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs rename to src/Akka.MultiNode.Shared/Sinks/ConsoleMessageSinkActor.cs index a4b73cb..75ce364 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/ConsoleMessageSinkActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/ConsoleMessageSinkActor.cs @@ -9,12 +9,11 @@ using System.Linq; using Akka.Actor; using Akka.Event; +using Akka.MultiNode.Shared.Reporting; #if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; #endif -using Akka.MultiNodeTestRunner.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// implementation that logs all of its output directly to the . diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemAppenderActor.cs b/src/Akka.MultiNode.Shared/Sinks/FileSystemAppenderActor.cs similarity index 87% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemAppenderActor.cs rename to src/Akka.MultiNode.Shared/Sinks/FileSystemAppenderActor.cs index 0f553a1..c162f01 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemAppenderActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/FileSystemAppenderActor.cs @@ -9,7 +9,7 @@ using System.IO; using Akka.Actor; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Actor that just writes dumb messages to the file system - used for capturing @@ -25,7 +25,7 @@ public FileSystemAppenderActor(string fullFilePath) ReceiveAny(o => { - File.AppendAllText(_fullFilePath, o.ToString() + Environment.NewLine); + File.AppendAllText(_fullFilePath, o + System.Environment.NewLine); }); } } diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemMessageSinkActor.cs b/src/Akka.MultiNode.Shared/Sinks/FileSystemMessageSinkActor.cs similarity index 86% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemMessageSinkActor.cs rename to src/Akka.MultiNode.Shared/Sinks/FileSystemMessageSinkActor.cs index 16c13f8..44ac2fa 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/FileSystemMessageSinkActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/FileSystemMessageSinkActor.cs @@ -8,10 +8,10 @@ using System; using System.IO; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Persistence; -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Persistence; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// A file system implementation @@ -64,8 +64,15 @@ protected override void AdditionalReceives() protected override void HandleTestRunTree(TestRunTree tree) { + var filePath = Path.GetFullPath(FileName); + + // Create output dir if not exists + var dir = new DirectoryInfo(Path.GetDirectoryName(filePath)); + if (!dir.Exists) + dir.Create(); + if (_reportStatus) - Console.WriteLine("Writing test state to: {0}", Path.GetFullPath(FileName)); + Console.WriteLine("Writing test state to: {0}", filePath); try { FileStore.SaveTestRun(FileName, tree); @@ -73,7 +80,7 @@ protected override void HandleTestRunTree(TestRunTree tree) catch (Exception ex) //avoid throwing exception back to parent - just continue { if (_reportStatus) - Console.WriteLine("Failed to write test state to {0}. Cause: {1}", Path.GetFullPath(FileName), ex); + Console.WriteLine("Failed to write test state to {0}. Cause: {1}", filePath, ex); } if (_reportStatus) Console.WriteLine("Finished."); diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/IMessageSink.cs b/src/Akka.MultiNode.Shared/Sinks/IMessageSink.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/IMessageSink.cs rename to src/Akka.MultiNode.Shared/Sinks/IMessageSink.cs index 1e4721c..56218d0 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/IMessageSink.cs +++ b/src/Akka.MultiNode.Shared/Sinks/IMessageSink.cs @@ -10,9 +10,8 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Interface used to define destinations for MultiNodeTest messages diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSink.cs b/src/Akka.MultiNode.Shared/Sinks/MessageSink.cs similarity index 99% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSink.cs rename to src/Akka.MultiNode.Shared/Sinks/MessageSink.cs index 228d992..89ae4cd 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSink.cs +++ b/src/Akka.MultiNode.Shared/Sinks/MessageSink.cs @@ -11,9 +11,8 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Abstract base class for all implementations. Includes some methods diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSinkActor.cs b/src/Akka.MultiNode.Shared/Sinks/MessageSinkActor.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSinkActor.cs rename to src/Akka.MultiNode.Shared/Sinks/MessageSinkActor.cs index 5a55da0..bd78bd3 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/MessageSinkActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/MessageSinkActor.cs @@ -6,9 +6,9 @@ //----------------------------------------------------------------------- using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Actor responsible for directing the flow of all messages for each test run. diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/Messages.cs b/src/Akka.MultiNode.Shared/Sinks/Messages.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/Messages.cs rename to src/Akka.MultiNode.Shared/Sinks/Messages.cs index e9bcc07..1afb70b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/Messages.cs +++ b/src/Akka.MultiNode.Shared/Sinks/Messages.cs @@ -8,9 +8,8 @@ using System; using System.Collections.Generic; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { #region Message types diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs b/src/Akka.MultiNode.Shared/Sinks/SinkCoordinator.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs rename to src/Akka.MultiNode.Shared/Sinks/SinkCoordinator.cs index 89199f0..dc87f4c 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/SinkCoordinator.cs +++ b/src/Akka.MultiNode.Shared/Sinks/SinkCoordinator.cs @@ -11,9 +11,8 @@ using System.Threading.Tasks; using Akka.Actor; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Top-level actor responsible for managing all instances. diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/Spec.cs b/src/Akka.MultiNode.Shared/Sinks/Spec.cs similarity index 91% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/Spec.cs rename to src/Akka.MultiNode.Shared/Sinks/Spec.cs index 8c02672..969d20b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/Spec.cs +++ b/src/Akka.MultiNode.Shared/Sinks/Spec.cs @@ -5,19 +5,16 @@ // //----------------------------------------------------------------------- -using System; using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// Message class used for reporting a test pass. /// /// - /// The Akka.MultiNodeTestRunner.Shared.MessageSink depends on the format string + /// The Akka.MultiNode.Shared.MessageSink depends on the format string /// that this class produces, so do not remove or refactor it. /// /// @@ -45,7 +42,7 @@ public override string ToString() /// Message class used for reporting a test fail. /// /// - /// The Akka.MultiNodeTestRunner.Shared.MessageSink depends on the format string + /// The Akka.MultiNode.Shared.MessageSink depends on the format string /// that this class produces, so do not remove or refactor it. /// /// diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/SpecLog.cs b/src/Akka.MultiNode.Shared/Sinks/SpecLog.cs similarity index 95% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/SpecLog.cs rename to src/Akka.MultiNode.Shared/Sinks/SpecLog.cs index c825a95..9ac9eeb 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/SpecLog.cs +++ b/src/Akka.MultiNode.Shared/Sinks/SpecLog.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; -namespace Akka.MultiNodeTestRunner.Shared.Reporting +namespace Akka.MultiNode.Shared.Sinks { /// /// SpecLog diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs b/src/Akka.MultiNode.Shared/Sinks/TeamCityMessageSinkActor.cs similarity index 90% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs rename to src/Akka.MultiNode.Shared/Sinks/TeamCityMessageSinkActor.cs index 4a5acf9..a6823fa 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TeamCityMessageSinkActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/TeamCityMessageSinkActor.cs @@ -6,22 +6,13 @@ //----------------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Akka.Actor; -using Akka.Event; +using Akka.MultiNode.Shared.Reporting; +using JetBrains.TeamCity.ServiceMessages.Write.Special; #if CORECLR -using Akka.MultiNodeTestRunner.Shared.Extensions; #endif -using Akka.MultiNodeTestRunner.Shared.Reporting; -using JetBrains.TeamCity.ServiceMessages; -using JetBrains.TeamCity.ServiceMessages.Write.Special; -using JetBrains.TeamCity.ServiceMessages.Write.Special.Impl.Writer; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { public class TeamCityMessageSinkActor : TestCoordinatorEnabledMessageSink { diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs b/src/Akka.MultiNode.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs rename to src/Akka.MultiNode.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs index 2bc03f1..7f91610 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs +++ b/src/Akka.MultiNode.Shared/Sinks/TestCoordinatorEnabledMessageSink.cs @@ -8,9 +8,9 @@ using System; using System.Threading.Tasks; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Reporting; +using Akka.MultiNode.Shared.Reporting; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { /// /// A implementation that is capable of using a for diff --git a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TimelineLogCollectorActor.cs b/src/Akka.MultiNode.Shared/Sinks/TimelineLogCollectorActor.cs similarity index 98% rename from src/Akka.MultiNodeTestRunner.Shared/Sinks/TimelineLogCollectorActor.cs rename to src/Akka.MultiNode.Shared/Sinks/TimelineLogCollectorActor.cs index 70555e3..e6d3961 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/Sinks/TimelineLogCollectorActor.cs +++ b/src/Akka.MultiNode.Shared/Sinks/TimelineLogCollectorActor.cs @@ -13,10 +13,8 @@ using System.Text.RegularExpressions; using Akka.Actor; using Akka.Event; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.Util.Internal; -namespace Akka.MultiNodeTestRunner.Shared.Sinks +namespace Akka.MultiNode.Shared.Sinks { public class TimelineLogCollectorActor : ReceiveActor { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ErrorInfo.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/ErrorInfo.cs similarity index 86% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ErrorInfo.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/ErrorInfo.cs index 0b5e456..95cbe10 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ErrorInfo.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/ErrorInfo.cs @@ -4,10 +4,11 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class ErrorInfo : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ITestEntity.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/ITestEntity.cs similarity index 90% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ITestEntity.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/ITestEntity.cs index fc4a51a..ea03b07 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ITestEntity.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/ITestEntity.cs @@ -4,9 +4,10 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Xml.Linq; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public interface ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Identifier.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/Identifier.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Identifier.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/Identifier.cs index 9629241..1d6c982 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Identifier.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/Identifier.cs @@ -4,9 +4,10 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public struct Identifier { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Output.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/Output.cs similarity index 87% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Output.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/Output.cs index 77ad4cb..848a661 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Output.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/Output.cs @@ -4,13 +4,14 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class Output : ITestEntity { @@ -24,7 +25,7 @@ public XElement Serialize() { XElement TextElem(string element, List lines) => lines.Count > 0 - ? Elem(element, Text(string.Join(Environment.NewLine, lines))) + ? Elem(element, Text(string.Join(System.Environment.NewLine, lines))) : null; return Elem("Output", diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ResultSummary.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/ResultSummary.cs similarity index 96% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ResultSummary.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/ResultSummary.cs index 74c827d..215b24a 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/ResultSummary.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/ResultSummary.cs @@ -4,13 +4,14 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class ResultSummary : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestEntry.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestEntry.cs similarity index 89% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestEntry.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/TestEntry.cs index 5385934..0995b3c 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestEntry.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestEntry.cs @@ -4,10 +4,11 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class TestEntry : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestList.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestList.cs similarity index 86% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestList.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/TestList.cs index 56ee1fd..b832983 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestList.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestList.cs @@ -4,10 +4,11 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class TestList : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestOutcome.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestOutcome.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestOutcome.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/TestOutcome.cs index ae73b4d..7bc2a87 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestOutcome.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestOutcome.cs @@ -5,7 +5,7 @@ // // ----------------------------------------------------------------------- -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public enum TestOutcome { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestRun.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestRun.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestRun.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/TestRun.cs index 8f166f5..3df4a6a 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestRun.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestRun.cs @@ -4,12 +4,13 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class TestRun : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestSettings.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestSettings.cs similarity index 86% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestSettings.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/TestSettings.cs index bde156b..fd43bd3 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/TestSettings.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/TestSettings.cs @@ -4,10 +4,11 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class TestSettings : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Times.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/Times.cs similarity index 90% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Times.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/Times.cs index 2d99331..88980a9 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/Times.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/Times.cs @@ -4,11 +4,12 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class Times : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTest.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTest.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTest.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTest.cs index 5e3aa63..07c259b 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTest.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTest.cs @@ -4,11 +4,12 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Collections.Generic; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class UnitTest : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTestResult.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTestResult.cs similarity index 95% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTestResult.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTestResult.cs index 1178619..dbb7758 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/UnitTestResult.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/UnitTestResult.cs @@ -4,12 +4,13 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Xml.Linq; -using static Akka.MultiNodeTestRunner.TrxReporter.Models.XmlHelper; +using static Akka.MultiNode.Shared.TrxReporter.Models.XmlHelper; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { public class UnitTestResult : ITestEntity { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/XmlHelper.cs b/src/Akka.MultiNode.Shared/TrxReporter/Models/XmlHelper.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/XmlHelper.cs rename to src/Akka.MultiNode.Shared/TrxReporter/Models/XmlHelper.cs index a66b2b6..7e52bd9 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/Models/XmlHelper.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/Models/XmlHelper.cs @@ -4,11 +4,12 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -namespace Akka.MultiNodeTestRunner.TrxReporter.Models +namespace Akka.MultiNode.Shared.TrxReporter.Models { internal static class XmlHelper { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecEvent.cs b/src/Akka.MultiNode.Shared/TrxReporter/SpecEvent.cs similarity index 93% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecEvent.cs rename to src/Akka.MultiNode.Shared/TrxReporter/SpecEvent.cs index 8d5f833..f2a73a7 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecEvent.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/SpecEvent.cs @@ -4,9 +4,10 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; -namespace Akka.MultiNodeTestRunner.Shared.AzureDevOps +namespace Akka.MultiNode.Shared.TrxReporter { public class SpecEvent { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecSession.cs b/src/Akka.MultiNode.Shared/TrxReporter/SpecSession.cs similarity index 95% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecSession.cs rename to src/Akka.MultiNode.Shared/TrxReporter/SpecSession.cs index 14bb574..9cc2307 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/SpecSession.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/SpecSession.cs @@ -4,11 +4,12 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Collections.Generic; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared.AzureDevOps +namespace Akka.MultiNode.Shared.TrxReporter { public class SpecSession { diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxMessageSink.cs b/src/Akka.MultiNode.Shared/TrxReporter/TrxMessageSink.cs similarity index 85% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxMessageSink.cs rename to src/Akka.MultiNode.Shared/TrxReporter/TrxMessageSink.cs index 311ec5c..138d1c6 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxMessageSink.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/TrxMessageSink.cs @@ -4,16 +4,17 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using Akka.Actor; -using Akka.MultiNodeTestRunner.Shared.Sinks; +using Akka.MultiNode.Shared.Sinks; -namespace Akka.MultiNodeTestRunner.Shared.AzureDevOps +namespace Akka.MultiNode.Shared.TrxReporter { public class TrxMessageSink : MessageSink { public TrxMessageSink(string suiteName) - : base(Props.Create(() => new TrxSinkActor(suiteName, Environment.UserName, Environment.MachineName, true))) + : base(Props.Create(() => new TrxSinkActor(suiteName, System.Environment.UserName, System.Environment.MachineName, true))) { } diff --git a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxSinkActor.cs b/src/Akka.MultiNode.Shared/TrxReporter/TrxSinkActor.cs similarity index 97% rename from src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxSinkActor.cs rename to src/Akka.MultiNode.Shared/TrxReporter/TrxSinkActor.cs index 20de3c8..77fd916 100644 --- a/src/Akka.MultiNodeTestRunner.Shared/TrxReporter/TrxSinkActor.cs +++ b/src/Akka.MultiNode.Shared/TrxReporter/TrxSinkActor.cs @@ -4,16 +4,17 @@ // Copyright (C) 2013-2019 .NET Foundation // // ----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.MultiNodeTestRunner.TrxReporter.Models; +using Akka.MultiNode.Shared.Reporting; +using Akka.MultiNode.Shared.Sinks; +using Akka.MultiNode.Shared.TrxReporter.Models; -namespace Akka.MultiNodeTestRunner.Shared.AzureDevOps +namespace Akka.MultiNode.Shared.TrxReporter { public class TrxSinkActor : TestCoordinatorEnabledMessageSink { diff --git a/src/Akka.MultiNode.TestAdapter.Tests/Akka.MultiNode.TestAdapter.Tests.csproj b/src/Akka.MultiNode.TestAdapter.Tests/Akka.MultiNode.TestAdapter.Tests.csproj new file mode 100644 index 0000000..11d3b15 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/Akka.MultiNode.TestAdapter.Tests.csproj @@ -0,0 +1,20 @@ + + + + + $(NetFrameworkTestVersion);$(NetCoreTestVersion) + + + + + + + + + + + + + + + diff --git a/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeFrameworkHandler.cs b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeFrameworkHandler.cs new file mode 100644 index 0000000..97da2a6 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeFrameworkHandler.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Akka.Util; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Akka.MultiNode.TestAdapter.Tests.Helpers +{ + /// + /// Fake to pass to method + /// + class FakeFrameworkHandler : IFrameworkHandle + { + public List<(TestMessageLevel Level, string Message)> Messages { get; } = new List<(TestMessageLevel Level, string Message)>(); + public List TestResults { get; } = new List(); + public List StartedTestCases { get; } = new List(); + public List<(TestCase TestCase, Option Outcome)> FinishedTestCases { get; } = new List<(TestCase TestCase, Option Outcome)>(); + + /// + public void SendMessage(TestMessageLevel testMessageLevel, string message) + { + Messages.Add((testMessageLevel, message)); + } + + /// + public void RecordResult(TestResult testResult) + { + TestResults.Add(testResult); + } + + /// + public void RecordStart(TestCase testCase) + { + StartedTestCases.Add(testCase); + } + + /// + public void RecordEnd(TestCase testCase, TestOutcome outcome) + { + FinishedTestCases.Add((testCase, outcome)); + } + + /// + public void RecordAttachments(IList attachmentSets) + { + } + + /// + public int LaunchProcessWithDebuggerAttached(string filePath, string workingDirectory, string arguments, + IDictionary environmentVariables) + { + throw new NotImplementedException(); + } + + /// + public bool EnableShutdownAfterTestRun { get; set; } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeRunContext.cs b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeRunContext.cs new file mode 100644 index 0000000..2f4c603 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/FakeRunContext.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; + +namespace Akka.MultiNode.TestAdapter.Tests.Helpers +{ + /// + /// Fake to pass to method + /// + class FakeRunContext : IRunContext + { + public ITestCaseFilterExpression GetTestCaseFilter(IEnumerable supportedProperties, Func propertyProvider) + { + return null; + } + + public IRunSettings RunSettings { get; } + public bool KeepAlive { get; } + public bool InIsolation { get; } + public bool IsDataCollectionEnabled { get; } + public bool IsBeingDebugged { get; } + public string TestRunDirectory { get; } + public string SolutionDirectory { get; } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter.Tests/Helpers/TestCollections.cs b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/TestCollections.cs new file mode 100644 index 0000000..b674159 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/Helpers/TestCollections.cs @@ -0,0 +1,13 @@ +namespace Akka.MultiNode.TestAdapter.Tests.Helpers +{ + /// + /// TestCollections + /// + public static class TestCollections + { + /// + /// Collection of tests that are running MNTR + /// + public const string MultiNode = "MNTR Collection"; + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter.Tests/MultiNodeTestExecutorSpec.cs b/src/Akka.MultiNode.TestAdapter.Tests/MultiNodeTestExecutorSpec.cs new file mode 100644 index 0000000..867d914 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/MultiNodeTestExecutorSpec.cs @@ -0,0 +1,24 @@ +using System.IO; +using Akka.MultiNode.TestAdapter.SampleTests; +using Akka.MultiNode.TestAdapter.Tests.Helpers; +using FluentAssertions; +using Xunit; + +namespace Akka.MultiNode.TestAdapter.Tests +{ + [Collection(TestCollections.MultiNode)] + public class MultiNodeTestExecutorSpec + { + [Fact] + public void Should_run_tests_and_report_results() + { + var sampleTestAssemblyPath = Path.GetFullPath(SampleTestsMetadata.AssemblyFileName); + File.Exists(sampleTestAssemblyPath).Should().BeTrue($"Assemblies with samples should exist at {sampleTestAssemblyPath}"); + + var executor = new MultiNodeTestExecutor(); + var frameworkHandler = new FakeFrameworkHandler(); + executor.RunTests(new []{ sampleTestAssemblyPath }, new FakeRunContext(), frameworkHandler); + frameworkHandler.TestResults.Should().NotBeEmpty(); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter.Tests/TestRunnerSpec.cs b/src/Akka.MultiNode.TestAdapter.Tests/TestRunnerSpec.cs new file mode 100644 index 0000000..46b0789 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter.Tests/TestRunnerSpec.cs @@ -0,0 +1,24 @@ +using System.IO; +using Akka.MultiNode.TestAdapter.SampleTests; +using Akka.MultiNode.TestAdapter.Tests.Helpers; +using Akka.MultiNode.TestRunner.Shared; +using FluentAssertions; +using Xunit; + +namespace Akka.MultiNode.TestAdapter.Tests +{ + [Collection(TestCollections.MultiNode)] + public class TestRunnerSpec + { + [Fact] + public void Should_discover_sample_tests_and_run_them() + { + var sampleTestAssemblyPath = Path.GetFullPath(SampleTestsMetadata.AssemblyFileName); + File.Exists(sampleTestAssemblyPath).Should().BeTrue($"Assembly with samples should exist at {sampleTestAssemblyPath}"); + + var runner = new MultiNodeTestRunner(); + var results = runner.Execute(sampleTestAssemblyPath, MultiNodeTestRunnerOptions.Default); + results.Should().NotBeEmpty(); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter/Akka.MultiNode.TestAdapter.csproj b/src/Akka.MultiNode.TestAdapter/Akka.MultiNode.TestAdapter.csproj new file mode 100644 index 0000000..a0fc24f --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter/Akka.MultiNode.TestAdapter.csproj @@ -0,0 +1,50 @@ + + + + + Akka.MultiNode.TestAdapter + true + embedded + latest + $(NetFrameworkTestVersion);$(NetCoreTestVersion) + true + true + Visual Studio 2017 15.9+ Test Explorer runner for the Akka.NET MultiNode tests + build + 2.4.1 + true + $(NoWarn)1701;1702;NU5105 + Akka.MultiNode.TestAdapter + + + + Akka.MultiNode.TestAdapter + $(DefineConstants);NETFRAMEWORK + + + + Akka.MultiNode.DotNetCore.TestAdapter + $(DefineConstants);NETCOREAPP;CORECLR + + + + $(DefineConstants);RELEASE + + + + + + + + + + + + + + + + + + + diff --git a/src/Akka.MultiNode.TestAdapter/ExecutorMetadata.cs b/src/Akka.MultiNode.TestAdapter/ExecutorMetadata.cs new file mode 100644 index 0000000..a76848c --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter/ExecutorMetadata.cs @@ -0,0 +1,13 @@ +namespace Akka.MultiNode.TestAdapter +{ + /// + /// ExecutorMetadata + /// + public static class ExecutorMetadata + { + /// + /// Executor URI used by this test adapter + /// + public const string ExecutorUri = "executor://MultiNodeExecutor"; + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter/MultiNodeTestDiscoverer.cs b/src/Akka.MultiNode.TestAdapter/MultiNodeTestDiscoverer.cs new file mode 100644 index 0000000..5251eab --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter/MultiNodeTestDiscoverer.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Akka.MultiNode.TestRunner.Shared; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Akka.MultiNode.TestAdapter +{ + /// + /// TestDiscoverer + /// + /// + /// See how it works here: https://github.com/Microsoft/vstest-docs/blob/master/RFCs/0004-Adapter-Extensibility.md + /// + [FileExtension(".dll")] + [DefaultExecutorUri(ExecutorMetadata.ExecutorUri)] + public class TestDiscoverer : ITestDiscoverer + { + /// + /// Discovers the tests available from the provided container. + /// + /// Collection of test containers. + /// Context in which discovery is being performed. + /// Logger used to log messages. + /// Used to send testcases and discovery related events back to Discoverer manager. + public void DiscoverTests(IEnumerable sources, IDiscoveryContext discoveryContext, IMessageLogger logger, ITestCaseDiscoverySink discoverySink) + { + foreach (var assemblyPath in sources) + { + var (specs, errors) = MultiNodeTestRunner.DiscoverSpecs(assemblyPath); + + foreach (var discoveryErrorMessage in errors.SelectMany(e => e.Messages)) + { + logger.SendMessage(TestMessageLevel.Error, discoveryErrorMessage); + } + + foreach (var discoveredSpec in specs) + { + discoverySink.SendTestCase(new TestCase(discoveredSpec.SpecName, new Uri(ExecutorMetadata.ExecutorUri), assemblyPath)); + } + } + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestAdapter/MultiNodeTestExecutor.cs b/src/Akka.MultiNode.TestAdapter/MultiNodeTestExecutor.cs new file mode 100644 index 0000000..2c4a6c6 --- /dev/null +++ b/src/Akka.MultiNode.TestAdapter/MultiNodeTestExecutor.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Akka.MultiNode.TestRunner.Shared; +using Akka.Util; +using Microsoft.VisualStudio.TestPlatform.ObjectModel; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; + +namespace Akka.MultiNode.TestAdapter +{ + /// + /// TestExecutor + /// + /// + /// See how it works here: https://github.com/Microsoft/vstest-docs/blob/master/RFCs/0004-Adapter-Extensibility.md + /// + [ExtensionUri(ExecutorMetadata.ExecutorUri)] + public class MultiNodeTestExecutor : ITestExecutor + { + /// + /// Cancel the execution of the tests. + /// + public void Cancel() + { + // TODO: Implement proper cancellation + } + + /// + /// Runs only the tests specified by parameter 'tests'. + /// + /// + /// ITestExecutor.RunTests with a set of test cases gets called in mostly VS IDE scenarios("Run Selected tests" scenarios) + /// where a discovery operation has already been performed. + /// This would already have the information as to what ITestExecutor can run the test case via a URI property. + /// The platform would then just call into that specific executor to run the test cases. + /// + /// Tests to be run. + /// Context to use when executing the tests. + /// Handle to the framework to record results and to do framework operations. + public void RunTests(IEnumerable tests, IRunContext runContext, IFrameworkHandle frameworkHandle) + { + throw new NotImplementedException("Running from VS is not implemented yet"); + + // This is called from VS "Run Selected tests" command. + // Need to get assembly paths and perform specs filtering by name + List assemblyPaths = null; + + var filteredSpecNames = tests.Select(t => t.FullyQualifiedName).ToList(); + RunTestsWithOptions(assemblyPaths, frameworkHandle, new MultiNodeTestRunnerOptions(specNames: filteredSpecNames)); + } + + /// + /// Runs 'all' the tests present in the specified 'containers'. + /// + /// Path to test container files to look for tests in. + /// Context to use when executing the tests. + /// Handle to the framework to record results and to do framework operations. + public void RunTests(IEnumerable sources, IRunContext runContext, IFrameworkHandle frameworkHandle) + { + RunTestsWithOptions(sources, frameworkHandle, MultiNodeTestRunnerOptions.Default); + } + + private void RunTestsWithOptions(IEnumerable sources, IFrameworkHandle frameworkHandle, MultiNodeTestRunnerOptions options) + { + var testAssemblyPaths = sources.ToList(); + frameworkHandle.SendMessage(TestMessageLevel.Informational, $"Loading tests from assemblies: {string.Join(", ", testAssemblyPaths)}"); + + foreach (var assemblyPath in testAssemblyPaths) + { + TestCase BuildTestCase(string name) => new TestCase(name, new Uri(ExecutorMetadata.ExecutorUri), assemblyPath); + + var testCases = new ConcurrentDictionary(); + try + { + var runner = new MultiNodeTestRunner(); + runner.TestStarted += testName => + { + var testCase = BuildTestCase(testName); + testCases.AddOrUpdate(testName, name => testCase, (name, existingCase) => testCase); + frameworkHandle.RecordStart(testCase); + }; + + runner.TestFinished += testResult => + { + var testCase = testCases[testResult.TestName]; + frameworkHandle.RecordResult(new TestResult(testCase) + { + // TODO: Set other props + Outcome = MapToOutcome(testResult.Status) + }); + frameworkHandle.RecordEnd(testCase, MapToOutcome(testResult.Status)); + }; + + runner.Execute(assemblyPath, options); + } + catch (Exception ex) + { + frameworkHandle.SendMessage(TestMessageLevel.Error, $"Failed during test execution: {ex}"); + } + } + } + + private TestOutcome MapToOutcome(MultiNodeTestResult.TestStatus status) + { + switch (status) + { + case MultiNodeTestResult.TestStatus.Passed: + return TestOutcome.Passed; + case MultiNodeTestResult.TestStatus.Skipped: + return TestOutcome.Skipped; + case MultiNodeTestResult.TestStatus.Failed: + return TestOutcome.Failed; + default: + return TestOutcome.None; // Unknown result + } + } + } +} diff --git a/src/Akka.MultiNode.TestRunner.Shared/Akka.MultiNode.TestRunner.Shared.csproj b/src/Akka.MultiNode.TestRunner.Shared/Akka.MultiNode.TestRunner.Shared.csproj new file mode 100644 index 0000000..32d3f08 --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/Akka.MultiNode.TestRunner.Shared.csproj @@ -0,0 +1,40 @@ + + + + + Akka.MultiNode.TestRunner.Shared + $(NetFrameworkTestVersion);$(NetCoreTestVersion) + Akka.MultiNode.TestRunner.Shared + + + + + + + + + + + + + + + + + + + + + + + + + + $(DefineConstants);CORECLR + + + + $(DefineConstants);RELEASE + + + diff --git a/src/Akka.MultiNodeTestRunner/Discovery.cs b/src/Akka.MultiNode.TestRunner.Shared/Discovery.cs similarity index 94% rename from src/Akka.MultiNodeTestRunner/Discovery.cs rename to src/Akka.MultiNode.TestRunner.Shared/Discovery.cs index 6d1d1e3..6834d09 100644 --- a/src/Akka.MultiNodeTestRunner/Discovery.cs +++ b/src/Akka.MultiNode.TestRunner.Shared/Discovery.cs @@ -7,17 +7,15 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using System.Threading; -using Akka.MultiNodeTestRunner.Shared; +using Akka.MultiNode.Shared; using Akka.Remote.TestKit; using Xunit.Abstractions; using Xunit.Sdk; -namespace Akka.MultiNodeTestRunner +namespace Akka.MultiNode.TestRunner.Shared { #if CORECLR public class Discovery : IMessageSink, IDisposable @@ -80,6 +78,12 @@ private List LoadTestCaseDetails(ITestCaseDiscoveryMessage testCaseDis var testAssembly = Assembly.LoadFrom(testCaseDiscoveryMessage.TestAssembly.Assembly.AssemblyPath); var specType = testAssembly.GetType(testClass.Name); #endif + if (!typeof(Remote.TestKit.MultiNodeSpec).IsAssignableFrom(specType)) + { + Console.WriteLine($"Ignoring {testClass.Name} test class - MNTR spec should inherit from {typeof(Remote.TestKit.MultiNodeSpec).FullName}"); + return new List(); + } + var roles = RoleNames(specType); var details = roles.Select((r, i) => new NodeTest diff --git a/src/Akka.MultiNode.TestRunner.Shared/MultiNodeSpec.cs b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeSpec.cs new file mode 100644 index 0000000..d76282c --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeSpec.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; +using Akka.MultiNode.Shared; + +namespace Akka.MultiNode.TestRunner.Shared +{ + /// + /// Spec found by + /// + public class MultiNodeSpec + { + public MultiNodeSpec(string specName, List tests) + { + SpecName = specName; + Tests = tests; + } + + public string SpecName { get; } + public List Tests { get; } + + public NodeTest FirstTest => Tests.First(); + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestResult.cs b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestResult.cs new file mode 100644 index 0000000..9bec330 --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestResult.cs @@ -0,0 +1,42 @@ +namespace Akka.MultiNode.TestRunner.Shared +{ + /// + /// MultiNodeTestResult + /// + public class MultiNodeTestResult + { + /// + /// MultiNodeTestResult + /// + public MultiNodeTestResult(string testName, TestStatus status) + { + TestName = testName; + Status = status; + } + + /// + /// Full name of executed test + /// + public string TestName { get; } + /// + /// Test result + /// + public TestStatus Status { get; } + + public enum TestStatus + { + /// + /// Test passed + /// + Passed, + /// + /// Test skipped + /// + Skipped, + /// + /// Test failed + /// + Failed + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunner.cs b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunner.cs new file mode 100644 index 0000000..c4983c3 --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunner.cs @@ -0,0 +1,539 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2019 Lightbend Inc. +// Copyright (C) 2013-2019 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.IO; +using Akka.MultiNode.Shared; +using Akka.MultiNode.Shared.Environment; +using Akka.MultiNode.Shared.Persistence; +using Akka.MultiNode.Shared.Sinks; +using Akka.MultiNode.Shared.TrxReporter; +using Akka.Remote.TestKit; +using Akka.Util; +using Newtonsoft.Json; +using Xunit; +using ErrorMessage = Xunit.Sdk.ErrorMessage; + +#if CORECLR +using System.Runtime.Loader; +#endif + +namespace Akka.MultiNode.TestRunner.Shared +{ + /// + /// Entry point for the MultiNodeTestRunner + /// + public class MultiNodeTestRunner + { + private static readonly HashSet ValidNetCorePlatform = new HashSet + { + "net", + "netcore" + }; + + protected ActorSystem TestRunSystem; + protected IActorRef SinkCoordinator; + + public Action TestStarted; + public Action TestFinished; + + /// + /// Executes multi-node tests from given assembly + /// + public List Execute(string assemblyPath, MultiNodeTestRunnerOptions options) + { + ValidatePlatform(options); + + // Perform output cleanup before anything is logged + if (options.ClearOutputDirectory && Directory.Exists(options.OutputDirectory)) + Directory.Delete(options.OutputDirectory, true); + + TestRunSystem = ActorSystem.Create("TestRunnerLogging"); + + var suiteName = Path.GetFileNameWithoutExtension(Path.GetFullPath(assemblyPath)); + SinkCoordinator = CreateSinkCoordinator(options, suiteName); + + var tcpLogger = TestRunSystem.ActorOf(Props.Create(() => new TcpLoggingServer(SinkCoordinator)), "TcpLogger"); + var listenEndpoint = new IPEndPoint(IPAddress.Parse(options.ListenAddress), options.ListenPort); + TestRunSystem.Tcp().Tell(new Tcp.Bind(tcpLogger, listenEndpoint), sender: tcpLogger); + + EnableAllSinks(assemblyPath, options); + + // Set MNTR environment for correct tests discovert + MultiNodeEnvironment.Initialize(); + + // In NetCore, if the assembly file hasn't been touched, + // XunitFrontController would fail loading external assemblies and its dependencies. + PreLoadTestAssembly_WhenNetCore(assemblyPath); + + // Here is where main action goes + var results = DiscoverAndRunSpecs(assemblyPath, options, tcpLogger); + + AbortTcpLoggingServer(tcpLogger); + CloseAllSinks(); + + // Block until all Sinks have been terminated. + TestRunSystem.WhenTerminated.Wait(TimeSpan.FromMinutes(1)); + + // Return the proper exit code + return results; + } + + /// + /// Discovers all tests in given assembly + /// + public static (List Specs, List Errors) DiscoverSpecs(string assemblyPath) + { + MultiNodeEnvironment.Initialize(); + + using (var controller = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyPath)) + { + using (var discovery = new Discovery()) + { + controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery()); + discovery.Finished.WaitOne(); + + if (!discovery.WasSuccessful) + { + return (Specs: new List(), Errors: discovery.Errors); + } + + var specs = discovery.Tests.Reverse().Select(pair => new MultiNodeSpec(pair.Key, pair.Value)).ToList(); + return (specs, new List()); + } + } + } + + private List DiscoverAndRunSpecs(string assemblyPath, MultiNodeTestRunnerOptions options, IActorRef tcpLogger) + { + var testResults = new List(); + PublishRunnerMessage($"Running MultiNodeTests for {assemblyPath}"); + + var (discoveredSpecs, errors) = DiscoverSpecs(assemblyPath); + if (errors.Any()) + { + ReportDiscoveryErrors(errors); + return testResults; + } + + // If port was set random, request the actual port from TcpLoggingServer + var listenPort = options.ListenPort > 0 + ? options.ListenPort + : tcpLogger.Ask(TcpLoggingServer.GetBoundPort.Instance).Result; + + foreach (var spec in discoveredSpecs) + { + var testName = spec.Tests.First().MethodName; + TestStarted?.Invoke(spec.SpecName); + + if (!string.IsNullOrEmpty(spec.FirstTest.SkipReason)) + { + PublishRunnerMessage($"Skipping test {testName}. Reason - {spec.FirstTest.SkipReason}"); + var skippedResult = new MultiNodeTestResult(testName, MultiNodeTestResult.TestStatus.Skipped); + testResults.Add(skippedResult); + TestFinished?.Invoke(skippedResult); + continue; + } + + if (options.SpecNames != null && + !options.SpecNames.All(name => spec.FirstTest.TestName.IndexOf(name, StringComparison.InvariantCultureIgnoreCase) < 0)) + { + PublishRunnerMessage($"Skipping [{spec.FirstTest.MethodName}] (Filtering)"); + var skippedResult = new MultiNodeTestResult(testName, MultiNodeTestResult.TestStatus.Skipped); + testResults.Add(skippedResult); + TestFinished?.Invoke(skippedResult); + continue; + } + + // Run test on several nodes and report results + var result = RunSpec(assemblyPath, options, spec, listenPort); + TestFinished?.Invoke(result); + testResults.Add(result); + } + + return testResults; + } + + private MultiNodeTestResult RunSpec(string assemblyPath, MultiNodeTestRunnerOptions options, MultiNodeSpec spec, int listenPort) + { + PublishRunnerMessage($"Starting test {spec.FirstTest.MethodName}"); + Console.Out.WriteLine($"Starting test {spec.FirstTest.MethodName}"); + + StartNewSpec(spec.Tests); + + var timelineCollector = TestRunSystem.ActorOf(Props.Create(() => new TimelineLogCollectorActor())); + string testOutputDir = null; + string runningSpecName = null; + + var processes = new List(); + foreach (var nodeTest in spec.Tests) + { + //Loop through each test, work out number of nodes to run on and kick off process + var sbArguments = new StringBuilder() + .Append($@"-Dmultinode.test-class=""{nodeTest.TypeName}"" ") + .Append($@"-Dmultinode.test-method=""{nodeTest.MethodName}"" ") + .Append($@"-Dmultinode.max-nodes={spec.Tests.Count} ") + .Append($@"-Dmultinode.server-host=""{"localhost"}"" ") + .Append($@"-Dmultinode.host=""{"localhost"}"" ") + .Append($@"-Dmultinode.index={nodeTest.Node - 1} ") + .Append($@"-Dmultinode.role=""{nodeTest.Role}"" ") + .Append($@"-Dmultinode.listen-address={options.ListenAddress} ") + .Append($@"-Dmultinode.listen-port={listenPort} "); + + // Configure process for node + var process = BuildNodeProcess(assemblyPath, options, sbArguments); + processes.Add(process); + + runningSpecName = nodeTest.TestName; + + //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, nodeTest.TestName)); + testOutputDir = testOutputDir ?? folder.FullName; + + // Start process for node + StartNodeProcess(process, nodeTest, options, folder, timelineCollector); + } + + // Wait for all nodes to finish and collect results + var specFailed = WaitForNodeExit(processes); + + PublishRunnerMessage("Waiting 3 seconds for all messages from all processes to be collected."); + Thread.Sleep(TimeSpan.FromSeconds(3)); + + // Save timelined logs to file system + DumpAggregatedSpecLogs(options, testOutputDir, timelineCollector, specFailed, runningSpecName); + + FinishSpec(spec.Tests, timelineCollector); + + return new MultiNodeTestResult(runningSpecName, specFailed ? MultiNodeTestResult.TestStatus.Failed : MultiNodeTestResult.TestStatus.Passed); + } + + private void DumpAggregatedSpecLogs(MultiNodeTestRunnerOptions options, string testOutputDir, + IActorRef timelineCollector, bool specFailed, string runningSpecName) + { + if (testOutputDir == null) return; + var dumpTasks = new List() + { + // Dump aggregated timeline to file for this test + timelineCollector.Ask(new TimelineLogCollectorActor.DumpToFile(Path.Combine(testOutputDir, "aggregated.txt"))), + // Print aggregated timeline into the console + timelineCollector.Ask(new TimelineLogCollectorActor.PrintToConsole()) + }; + + if (specFailed) + { + var failedSpecPath = Path.Combine(Path.GetFullPath(options.OutputDirectory), options.FailedSpecsDirectory, $"{runningSpecName}.txt"); + var dumpFailureArtifactTask = timelineCollector.Ask(new TimelineLogCollectorActor.DumpToFile(failedSpecPath)); + dumpTasks.Add(dumpFailureArtifactTask); + } + + Task.WaitAll(dumpTasks.ToArray()); + } + + private bool WaitForNodeExit(List processes) + { + var specFailed = false; + foreach (var process in processes) + { + process.WaitForExit(); + specFailed = specFailed || process.ExitCode > 0; + process.Dispose(); + } + + return specFailed; + } + + private void StartNodeProcess(Process process, NodeTest nodeTest, MultiNodeTestRunnerOptions options, + DirectoryInfo specFolder, IActorRef timelineCollector) + { + var nodeIndex = nodeTest.Node; + var nodeRole = nodeTest.Role; + var logFilePath = Path.Combine(specFolder.FullName, $"node{nodeIndex}__{nodeRole}__{options.Platform}.txt"); + var nodeInfo = new TimelineLogCollectorActor.NodeInfo(nodeIndex, nodeRole, options.Platform, nodeTest.TestName); + var fileActor = TestRunSystem.ActorOf(Props.Create(() => new FileSystemAppenderActor(logFilePath))); + process.OutputDataReceived += (sender, eventArgs) => + { + if (eventArgs?.Data != null) + { + fileActor.Tell(eventArgs.Data); + timelineCollector.Tell(new TimelineLogCollectorActor.LogMessage(nodeInfo, eventArgs.Data)); + if (options.TeamCityFormattingOn) + { + // teamCityTest.WriteStdOutput(eventArgs.Data); TODO: open flood gates + } + } + }; + var closureTest = nodeTest; + process.Exited += (sender, eventArgs) => + { + if (process.ExitCode == 0) + { + ReportSpecPassFromExitCode(nodeIndex, nodeRole, closureTest.TestName); + } + }; + + process.Start(); + process.BeginOutputReadLine(); + PublishRunnerMessage($"Started node {nodeIndex} : {nodeRole} on pid {process.Id}"); + } + + private Process BuildNodeProcess(string assemblyPath, MultiNodeTestRunnerOptions options, StringBuilder sbArguments) + { +#if CORECLR + const string nrNetFileName = "Akka.MultiNode.NodeRunner.exe"; + const string nrNetCodeFileName = "Akka.MultiNode.NodeRunner.dll"; + var searchPaths = new [] + { + AppContext.BaseDirectory, + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + }; + + // Try to find node runner location (may be different under `dotnet test` on different platforms) + var ntrNetPath = FileToolInPaths(nrNetFileName, searchPaths); + var ntrNetCorePath = FileToolInPaths(nrNetCodeFileName, searchPaths); + + string fileName; + switch (options.Platform) + { + case "net": + if (!ntrNetPath.HasValue) + throw new Exception($"No {nrNetFileName} file is found at paths: {string.Join(", ", searchPaths)}"); + + fileName = ntrNetPath.Value; + sbArguments.Insert(0, $@" -Dmultinode.test-assembly=""{assemblyPath}"" "); + break; + case "netcore": + if (!ntrNetCorePath.HasValue) + throw new Exception($"No {nrNetCodeFileName} file is found at paths: {string.Join(", ", searchPaths)}"); + + fileName = "dotnet"; + sbArguments.Insert(0, $@" -Dmultinode.test-assembly=""{assemblyPath}"" "); + sbArguments.Insert(0, ntrNetCorePath.Value); + break; + default: throw new ArgumentOutOfRangeException(); + } + + return new Process + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + UseShellExecute = false, + RedirectStandardOutput = true, + Arguments = sbArguments.ToString(), + WorkingDirectory = Path.GetDirectoryName(assemblyPath) + } + }; +#else + const string nodeRunnerFileName = "Akka.MultiNode.NodeRunner.exe"; + var searchPaths = new [] + { + AppContext.BaseDirectory, + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + }; + + // Under 'dotnet test' or as standalone util location of node runner is different. + // The most robust way is to just scan possible locations and find it + var ntrNetPath = FileToolInPaths(nodeRunnerFileName, dirPaths: searchPaths); + if (!ntrNetPath.HasValue) + throw new Exception($"Failed to find node runner '{nodeRunnerFileName}' at paths: {string.Join(", ", searchPaths)}"); + + sbArguments.Insert(0, $@"-Dmultinode.test-assembly=""{assemblyPath}"" "); + return new Process + { + StartInfo = new ProcessStartInfo + { + FileName = ntrNetPath.Value, + UseShellExecute = false, + RedirectStandardOutput = true, + Arguments = sbArguments.ToString() + } + }; +#endif + } + + private void ReportDiscoveryErrors(List errors) + { + var sb = new StringBuilder(); + sb.AppendLine("One or more exception was thrown while discovering test cases. Test Aborted."); + foreach (var err in errors) + { + for (int i = 0; i < err.ExceptionTypes.Length; ++i) + { + sb.AppendLine(); + sb.Append($"{err.ExceptionTypes[i]}: {err.Messages[i]}"); + sb.Append(err.StackTraces[i]); + } + } + + PublishRunnerMessage(sb.ToString()); + Console.Out.WriteLine(sb.ToString()); + } + + private void PreLoadTestAssembly_WhenNetCore(string assemblyPath) + { +#if CORECLR + var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); + var asms = assembly.GetReferencedAssemblies(); + var basePath = Path.GetDirectoryName(assemblyPath); + foreach (var asm in asms) + { + try + { + Assembly.Load(new AssemblyName(asm.FullName)); + } + catch (Exception) + { + var path = Path.Combine(basePath, asm.Name + ".dll"); + try + { + AssemblyLoadContext.Default.LoadFromAssemblyPath(path); + } + catch (Exception e) + { + Console.Out.WriteLine($"Failed to load dll: {path}"); + } + } + } +#endif + } + + private void ValidatePlatform(MultiNodeTestRunnerOptions options) + { +#if CORECLR + if (!ValidNetCorePlatform.Contains(options.Platform)) + { + throw new Exception($"Target platform not supported: {options.Platform}. Supported platforms are net and netcore"); + } +#else + if (options.Platform != "net") + { + throw new Exception($"Target platform not supported: {options.Platform}. Supported platforms are net"); + } +#endif + } + + private IActorRef CreateSinkCoordinator(MultiNodeTestRunnerOptions options, string suiteName) + { + Props coordinatorProps; + switch (options.Reporter.ToLowerInvariant()) + { + case "trx": + coordinatorProps = Props.Create(() => new SinkCoordinator(new[] {new TrxMessageSink(suiteName)})); + break; + + case "teamcity": + coordinatorProps = Props.Create(() => + new SinkCoordinator(new[] {new TeamCityMessageSink(Console.WriteLine, suiteName)})); + break; + + case "console": + coordinatorProps = Props.Create(() => new SinkCoordinator(new[] {new ConsoleMessageSink()})); + break; + + default: + throw new ArgumentException( + $"Given reporter name '{options.Reporter}' is not understood, valid reporters are: trx and teamcity"); + } + + return TestRunSystem.ActorOf(coordinatorProps, "sinkCoordinator"); + } + + static string ChangeDllPathPlatform(string path, string targetPlatform) + { + return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), "..", targetPlatform, Path.GetFileName(path))); + } + + private void EnableAllSinks(string assemblyName, MultiNodeTestRunnerOptions options) + { + var now = DateTime.UtcNow; + + // if multinode.output-directory wasn't specified, the results files will be written + // to the same directory as the test assembly. + var outputDirectory = options.OutputDirectory; + + MessageSink CreateJsonFileSink() + { + var fileName = FileNameGenerator.GenerateFileName(outputDirectory, assemblyName, options.Platform, ".json", now); + var jsonStoreProps = Props.Create(() => new FileSystemMessageSinkActor(new JsonPersistentTestRunStore(), fileName, !options.TeamCityFormattingOn, true)); + return new FileSystemMessageSink(jsonStoreProps); + } + + MessageSink CreateVisualizerFileSink() + { + var fileName = FileNameGenerator.GenerateFileName(outputDirectory, assemblyName, options.Platform, ".html", now); + var visualizerProps = Props.Create(() => new FileSystemMessageSinkActor(new VisualizerPersistentTestRunStore(), fileName, !options.TeamCityFormattingOn, true)); + 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())); + } + } + + private void AbortTcpLoggingServer(IActorRef tcpLogger) + { + tcpLogger.Ask(new TcpLoggingServer.StopListener(), TimeSpan.FromMinutes(1)).Wait(); + } + + private void CloseAllSinks() + { + SinkCoordinator.Tell(new SinkCoordinator.CloseAllSinks()); + } + + private void StartNewSpec(IList tests) + { + SinkCoordinator.Tell(tests); + } + + private void ReportSpecPassFromExitCode(int nodeIndex, string nodeRole, string testName) + { + SinkCoordinator.Tell(new NodeCompletedSpecWithSuccess(nodeIndex, nodeRole, testName + " passed.")); + } + + private void FinishSpec(IList tests, IActorRef timelineCollector) + { + var spec = tests.First(); + var log = timelineCollector.Ask(new TimelineLogCollectorActor.GetSpecLog(), TimeSpan.FromMinutes(1)).Result; + SinkCoordinator.Tell(new EndSpec(spec.TestName, spec.MethodName, log)); + } + + private void PublishRunnerMessage(string message) + { + SinkCoordinator.Tell(new SinkCoordinator.RunnerMessage(message)); + } + + /// + /// Finds given tool in specified paths + /// + private Option FileToolInPaths(string toolFileName, params string[] dirPaths) + { + foreach (var dir in dirPaths) + { + var path = Path.Combine(dir, toolFileName); + if (File.Exists(path)) + return path; + } + + return Option.None; + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunnerOptions.cs b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunnerOptions.cs new file mode 100644 index 0000000..d020a8b --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/MultiNodeTestRunnerOptions.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.IO; +using Akka.Util; + +namespace Akka.MultiNode.TestRunner.Shared +{ + /// + /// MultiNodeTestRunnerOptions + /// + public class MultiNodeTestRunnerOptions + { + /// + /// Default options + /// + public static readonly MultiNodeTestRunnerOptions Default = new MultiNodeTestRunnerOptions(); + + public MultiNodeTestRunnerOptions(string outputDirectory = null, string failedSpecsDirectory = null, + string listenAddress = null, int listenPort = 0, List specNames = null, + string platform = null, string reporter = null, bool clearOutputDirectory = false, + bool teamCityFormattingOn = false) + { + ListenPort = listenPort; + SpecNames = specNames; + ClearOutputDirectory = clearOutputDirectory; + TeamCityFormattingOn = teamCityFormattingOn; + Reporter = reporter ?? "console"; + Platform = platform ?? (RuntimeDetector.IsWindows ? "net" : "netcore"); + FailedSpecsDirectory = failedSpecsDirectory ?? "FAILED_SPECS_LOGS"; + ListenAddress = listenAddress ?? "127.0.0.1"; + OutputDirectory = outputDirectory ?? "TestResults"; + } + + /// + /// File output directory + /// + public string OutputDirectory { get; } + /// + /// Subdirectory to store failed specs logs + /// + public string FailedSpecsDirectory { get; } + /// + /// MNTR controller listener address + /// + public string ListenAddress { get; } + /// + /// MNTR controller listener port. Set 0 to use random available port + /// + public int ListenPort { get; } + /// + /// List of spec names to be executed. Other specs are skipped + /// + public List SpecNames { get; } + /// + /// Current platform. "net" or "netcore" + /// + public string Platform { get; } + /// + /// Reporter. "trx"/"teamcity"/"console" + /// + public string Reporter { get; } + /// + /// If set, performs output directory cleanup before running tests + /// + public bool ClearOutputDirectory { get; } + /// + /// TeamCity formatting on/off + /// + public bool TeamCityFormattingOn { get; } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNodeTestRunner/Properties/AssemblyInfo.cs b/src/Akka.MultiNode.TestRunner.Shared/Properties/AssemblyInfo.cs similarity index 54% rename from src/Akka.MultiNodeTestRunner/Properties/AssemblyInfo.cs rename to src/Akka.MultiNode.TestRunner.Shared/Properties/AssemblyInfo.cs index 46a4a3d..1262693 100644 --- a/src/Akka.MultiNodeTestRunner/Properties/AssemblyInfo.cs +++ b/src/Akka.MultiNode.TestRunner.Shared/Properties/AssemblyInfo.cs @@ -5,19 +5,9 @@ // //----------------------------------------------------------------------- -using System.Reflection; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. - -[assembly: InternalsVisibleTo("Akka.MultiNodeTestRunner.Shared.Tests")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4442771d-674d-45ad-8e13-ffdc6b442716")] +[assembly: InternalsVisibleTo("Akka.MultiNode.Shared.Tests")] diff --git a/src/Akka.MultiNode.TestRunner.Shared/TcpLoggingServer.cs b/src/Akka.MultiNode.TestRunner.Shared/TcpLoggingServer.cs new file mode 100644 index 0000000..a5592ad --- /dev/null +++ b/src/Akka.MultiNode.TestRunner.Shared/TcpLoggingServer.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Immutable; +using System.Net; +using System.Threading; +using Akka.Actor; +using Akka.Event; +using Akka.IO; +using Akka.Util; +using Akka.Util.Internal; + +namespace Akka.MultiNode.TestRunner.Shared +{ + internal class TcpLoggingServer : ReceiveActor + { + private readonly ILoggingAdapter _log = Context.GetLogger(); + private IActorRef _tcpManager = Nobody.Instance; + private IActorRef _abortSender; + + private Option _boundPort; + private IImmutableSet _boundPortSubscribers = ImmutableHashSet.Empty; + + public TcpLoggingServer(IActorRef sinkCoordinator) + { + Receive(bound => + { + // When bound, save port and notify requestors if any + _boundPort = (bound.LocalAddress as IPEndPoint).Port; + _boundPortSubscribers.ForEach(s => s.Tell(_boundPort.Value)); + + _tcpManager = Sender; + }); + + Receive(_ => + { + // If bound port is not received yet, just save subscriber and send respose later + if (_boundPort.HasValue) + Sender.Tell(_boundPort.Value); + else + _boundPortSubscribers = _boundPortSubscribers.Add(Sender); + }); + + Receive(connected => + { + _log.Info($"Node connected on {Sender}"); + Sender.Tell(new Tcp.Register(Self)); + }); + + Receive( + closed => _log.Info($"Node disconnected on {Sender}{Environment.NewLine}")); + + Receive(received => + { + var message = received.Data.ToString(); + sinkCoordinator.Tell(message); + }); + + Receive(_ => + { + _abortSender = Sender; + _tcpManager.Tell(Tcp.Unbind.Instance); + }); + Receive(_ => _abortSender.Tell(new ListenerStopped())); + } + + public class StopListener { } + public class ListenerStopped { } + + public class GetBoundPort + { + private GetBoundPort() { } + public static readonly GetBoundPort Instance = new GetBoundPort(); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.csproj b/src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.csproj similarity index 58% rename from src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.csproj rename to src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.csproj index 0315c6d..56e17b2 100644 --- a/src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.csproj +++ b/src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.csproj @@ -2,25 +2,21 @@ - Akka.MultiNodeTestRunner + Akka.MultiNode.TestRunner Akka.NET Multi-node Test Runner; used for executing tests written with Akka.Remote.TestKit $(NetFrameworkTestVersion);$(NetCoreTestVersion) Exe $(AkkaPackageTags) - Akka.MultiNodeTestRunner.nuspec + Akka.MultiNode.TestRunner.nuspec + Akka.MultiNode.TestRunner - + - - - - - - + diff --git a/src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec.template b/src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec.template similarity index 100% rename from src/Akka.MultiNodeTestRunner/Akka.MultiNodeTestRunner.nuspec.template rename to src/Akka.MultiNode.TestRunner/Akka.MultiNode.TestRunner.nuspec.template diff --git a/src/Akka.MultiNode.TestRunner/Program.cs b/src/Akka.MultiNode.TestRunner/Program.cs new file mode 100644 index 0000000..cc782bf --- /dev/null +++ b/src/Akka.MultiNode.TestRunner/Program.cs @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2019 Lightbend Inc. +// Copyright (C) 2013-2019 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Event; +using Akka.IO; +using Akka.MultiNode.Shared; +using Akka.MultiNode.Shared.Persistence; +using Akka.MultiNode.Shared.Sinks; +using Akka.MultiNode.Shared.TrxReporter; +using Akka.MultiNode.TestRunner.Shared; +using Akka.Remote.TestKit; +using Xunit; + +#if CORECLR +using System.Runtime.Loader; +#endif + +namespace Akka.MultiNode.TestRunner +{ + /// + /// Entry point for the MultiNodeTestRunner + /// + class Program + { + /// + /// MultiNodeTestRunner takes the following : + /// + /// C:\> Akka.MultiNode.TestRunner.exe [assembly name] [-Dmultinode.enable-filesink=on] [-Dmultinode.output-directory={dir path}] [-Dmultinode.spec={spec name}] + /// + /// + /// + /// Argument + /// The name and possible value of a given Akka.MultiNode.TestRunner.exe argument. + /// + /// + /// AssemblyName + /// + /// The full path or name of an assembly containing as least one MultiNodeSpec in the current working directory. + /// + /// i.e. "Akka.Cluster.Tests.MultiNode.dll" + /// "C:\akka.net\src\Akka.Cluster.Tests\bin\Debug\Akka.Cluster.Tests.MultiNode.dll" + /// + /// + /// + /// -Dmultinode.enable-filesink + /// Having this flag set means that the contents of this test run will be saved in the + /// current working directory as a .JSON file. + /// + /// + /// + /// -Dmultinode.multinode.output-directory + /// Setting this flag means that any persistent multi-node test runner output files + /// will be written to this directory instead of the default, which is the same folder + /// as the test binary. + /// + /// + /// + /// -Dmultinode.listen-address={ip} + /// + /// Determines the address that this multi-node test runner will use to listen for log messages from + /// individual NodeTestRunner.exe processes. + /// + /// Defaults to 127.0.0.1 + /// + /// + /// + /// -Dmultinode.listen-port={port} + /// + /// Determines the port number that this multi-node test runner will use to listen for log messages from + /// individual NodeTestRunner.exe processes. + /// + /// Defaults to 6577 + /// + /// + /// + /// -Dmultinode.spec={spec name} + /// + /// Setting this flag means that only tests which contains the spec name will be executed + /// otherwise all tests will be executed + /// + /// + /// + /// + static void Main(string[] args) + { + var assemblyPath = Path.GetFullPath(args[0].Trim('"')); // unquote the string first + var outputDirectory = CommandLine.GetPropertyOrDefault("multinode.output-directory", string.Empty); + var failedSpecsDirectory = CommandLine.GetPropertyOrDefault("multinode.failed-specs-directory", "FAILED_SPECS_LOGS"); + var listenAddress = CommandLine.GetPropertyOrDefault("multinode.listen-address", "127.0.0.1"); + var listenPort = CommandLine.GetInt32OrDefault("multinode.listen-port", 6577); + var specName = CommandLine.GetPropertyOrDefault("multinode.spec", ""); + var platform = CommandLine.GetPropertyOrDefault("multinode.platform", "net"); + var reporter = CommandLine.GetPropertyOrDefault("multinode.reporter", "console"); + var clearOutputDirectory = CommandLine.GetInt32OrDefault("multinode.clear-output", 0) > 0; + var teamCityFormattingStr = CommandLine.GetPropertyOrDefault("multinode.teamcity", "false"); + if (!bool.TryParse(teamCityFormattingStr, out var teamCityFormattingOn)) + throw new ArgumentException("Invalid argument provided for -Dteamcity"); + + var runner = new MultiNodeTestRunner(); + var results = runner.Execute(assemblyPath, new MultiNodeTestRunnerOptions( + outputDirectory: outputDirectory, + failedSpecsDirectory: failedSpecsDirectory, + teamCityFormattingOn: teamCityFormattingOn, + listenAddress: listenAddress, + listenPort: listenPort, + specNames: !string.IsNullOrEmpty(specName) ? new List() { specName } : null, + platform: platform, + reporter: reporter, + clearOutputDirectory: clearOutputDirectory + )); + + if (Debugger.IsAttached) + Console.ReadLine(); // block when debugging + + // Return the proper exit code + var retCode = results.Any(r => r.Status == MultiNodeTestResult.TestStatus.Failed) ? 1 : 0; + Environment.Exit(retCode); + } + } +} \ No newline at end of file diff --git a/src/Akka.MultiNodeTestRunner/README.md b/src/Akka.MultiNode.TestRunner/README.md similarity index 100% rename from src/Akka.MultiNodeTestRunner/README.md rename to src/Akka.MultiNode.TestRunner/README.md diff --git a/src/Akka.MultiNodeTestRunner/Class1.cs b/src/Akka.MultiNodeTestRunner/Class1.cs deleted file mode 100644 index ab2768d..0000000 --- a/src/Akka.MultiNodeTestRunner/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Akka.MultiNodeTestRunner -{ - public class Class1 - { - } -} diff --git a/src/Akka.MultiNodeTestRunner/Program.cs b/src/Akka.MultiNodeTestRunner/Program.cs deleted file mode 100644 index 1b8906c..0000000 --- a/src/Akka.MultiNodeTestRunner/Program.cs +++ /dev/null @@ -1,530 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (C) 2009-2019 Lightbend Inc. -// Copyright (C) 2013-2019 .NET Foundation -// -//----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Akka.Actor; -using Akka.Event; -using Akka.IO; -using Akka.MultiNodeTestRunner.Shared; -using Akka.MultiNodeTestRunner.Shared.Persistence; -using Akka.MultiNodeTestRunner.Shared.Reporting; -using Akka.MultiNodeTestRunner.Shared.Sinks; -using Akka.Remote.TestKit; -using Akka.Util; -using JetBrains.TeamCity.ServiceMessages.Write.Special; -using JetBrains.TeamCity.ServiceMessages.Write.Special.Impl; -using Xunit; -#if CORECLR -using System.Runtime.Loader; -#endif - -namespace Akka.MultiNodeTestRunner -{ - using Shared.AzureDevOps; - - /// - /// Entry point for the MultiNodeTestRunner - /// - class Program - { - private static HashSet _validNetCorePlatform = new HashSet - { - "net", - "netcore" - }; - - protected static ActorSystem TestRunSystem; - protected static IActorRef SinkCoordinator; - - /// - /// file output directory - /// - protected static string OutputDirectory; - /// - /// Subdirectory to store failed specs logs - /// - protected static string FailedSpecsDirectory; - - protected static bool TeamCityFormattingOn; - protected static bool MultiPlatform; - - /// - /// MultiNodeTestRunner takes the following : - /// - /// C:\> Akka.MultiNodeTestRunner.exe [assembly name] [-Dmultinode.enable-filesink=on] [-Dmultinode.output-directory={dir path}] [-Dmultinode.spec={spec name}] - /// - /// - /// - /// Argument - /// The name and possible value of a given Akka.MultiNodeTestRunner.exe argument. - /// - /// - /// AssemblyName - /// - /// The full path or name of an assembly containing as least one MultiNodeSpec in the current working directory. - /// - /// i.e. "Akka.Cluster.Tests.MultiNode.dll" - /// "C:\akka.net\src\Akka.Cluster.Tests\bin\Debug\Akka.Cluster.Tests.MultiNode.dll" - /// - /// - /// - /// -Dmultinode.enable-filesink - /// Having this flag set means that the contents of this test run will be saved in the - /// current working directory as a .JSON file. - /// - /// - /// - /// -Dmultinode.multinode.output-directory - /// Setting this flag means that any persistent multi-node test runner output files - /// will be written to this directory instead of the default, which is the same folder - /// as the test binary. - /// - /// - /// - /// -Dmultinode.listen-address={ip} - /// - /// Determines the address that this multi-node test runner will use to listen for log messages from - /// individual NodeTestRunner.exe processes. - /// - /// Defaults to 127.0.0.1 - /// - /// - /// - /// -Dmultinode.listen-port={port} - /// - /// Determines the port number that this multi-node test runner will use to listen for log messages from - /// individual NodeTestRunner.exe processes. - /// - /// Defaults to 6577 - /// - /// - /// - /// -Dmultinode.spec={spec name} - /// - /// Setting this flag means that only tests which contains the spec name will be executed - /// otherwise all tests will be executed - /// - /// - /// - /// - static void Main(string[] args) - { - OutputDirectory = CommandLine.GetPropertyOrDefault("multinode.output-directory", string.Empty); - FailedSpecsDirectory = CommandLine.GetPropertyOrDefault("multinode.failed-specs-directory", "FAILED_SPECS_LOGS"); - TestRunSystem = ActorSystem.Create("TestRunnerLogging"); - - 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"); - - 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 platform = CommandLine.GetPropertyOrDefault("multinode.platform", "net"); - var reporter = CommandLine.GetPropertyOrDefault("multinode.reporter", "console"); - - var clearOutputDirectory = CommandLine.GetInt32OrDefault("multinode.clear-output", 0); - if (clearOutputDirectory > 0 && Directory.Exists(OutputDirectory)) - Directory.Delete(OutputDirectory, true); - - Props coordinatorProps; - switch (reporter.ToLowerInvariant()) - { - case "trx": - coordinatorProps = Props.Create(() => new SinkCoordinator(new[] { new TrxMessageSink(suiteName) })); - break; - - case "teamcity": - coordinatorProps = Props.Create(() => new SinkCoordinator(new[] { new TeamCityMessageSink(Console.WriteLine, suiteName) })); - break; - - case "console": - coordinatorProps = Props.Create(() => new SinkCoordinator(new[] { new ConsoleMessageSink() })); - break; - - default: - throw new ArgumentException($"Given reporter name '{reporter}' is not understood, valid reporters are: trx and teamcity"); - } - - SinkCoordinator = TestRunSystem.ActorOf(coordinatorProps, "sinkCoordinator"); - -#if CORECLR - if (!_validNetCorePlatform.Contains(platform)) - { - throw new Exception($"Target platform not supported: {platform}. Supported platforms are net and netcore"); - } -#else - if (platform != "net") - { - throw new Exception($"Target platform not supported: {platform}. Supported platforms are net"); - } -#endif - - var tcpLogger = TestRunSystem.ActorOf(Props.Create(() => new TcpLoggingServer(SinkCoordinator)), "TcpLogger"); - TestRunSystem.Tcp().Tell(new Tcp.Bind(tcpLogger, listenEndpoint), sender: tcpLogger); - - var assemblyPath = Path.GetFullPath(args[0].Trim('"')); //unquote the string first - - EnableAllSinks(assemblyPath, platform); - PublishRunnerMessage($"Running MultiNodeTests for {assemblyPath}"); -#if CORECLR - // In NetCore, if the assembly file hasn't been touched, - // XunitFrontController would fail loading external assemblies and its dependencies. - var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); - var asms = assembly.GetReferencedAssemblies(); - var basePath = Path.GetDirectoryName(assemblyPath); - foreach (var asm in asms) - { - try - { - Assembly.Load(new AssemblyName(asm.FullName)); - } - catch (Exception) - { - var path = Path.Combine(basePath, asm.Name + ".dll"); - try - { - AssemblyLoadContext.Default.LoadFromAssemblyPath(path); - } - catch (Exception e) - { - Console.Out.WriteLine($"Failed to load dll: {path}"); - } - } - } -#endif - - using (var controller = new XunitFrontController(AppDomainSupport.IfAvailable, assemblyPath)) - { - using (var discovery = new Discovery()) - { - controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery()); - discovery.Finished.WaitOne(); - - if (discovery.WasSuccessful) - { - foreach (var test in discovery.Tests.Reverse()) - { - if (!string.IsNullOrEmpty(test.Value.First().SkipReason)) - { - PublishRunnerMessage($"Skipping test {test.Value.First().MethodName}. Reason - {test.Value.First().SkipReason}"); - continue; - } - - if (!string.IsNullOrWhiteSpace(specName) && - CultureInfo.InvariantCulture.CompareInfo.IndexOf(test.Value.First().TestName, - specName, - CompareOptions.IgnoreCase) < 0) - { - PublishRunnerMessage($"Skipping [{test.Value.First().MethodName}] (Filtering)"); - continue; - } - - var processes = new List(); - - PublishRunnerMessage($"Starting test {test.Value.First().MethodName}"); - Console.Out.WriteLine($"Starting test {test.Value.First().MethodName}"); - - StartNewSpec(test.Value); -#if CORECLR - var ntrNetPath = Path.Combine(AppContext.BaseDirectory, "Akka.NodeTestRunner.exe"); - var ntrNetCorePath = Path.Combine(AppContext.BaseDirectory, "Akka.NodeTestRunner.dll"); - var alternateIndex = 0; -#endif - var timelineCollector = TestRunSystem.ActorOf(Props.Create(() => new TimelineLogCollectorActor())); - string testOutputDir = null; - string runningSpecName = null; - - foreach (var nodeTest in test.Value) - { - //Loop through each test, work out number of nodes to run on and kick off process - var sbArguments = new StringBuilder() - //.Append($@"-Dmultinode.test-assembly=""{assemblyPath}"" ") - .Append($@"-Dmultinode.test-class=""{nodeTest.TypeName}"" ") - .Append($@"-Dmultinode.test-method=""{nodeTest.MethodName}"" ") - .Append($@"-Dmultinode.max-nodes={test.Value.Count} ") - .Append($@"-Dmultinode.server-host=""{"localhost"}"" ") - .Append($@"-Dmultinode.host=""{"localhost"}"" ") - .Append($@"-Dmultinode.index={nodeTest.Node - 1} ") - .Append($@"-Dmultinode.role=""{nodeTest.Role}"" ") - .Append($@"-Dmultinode.listen-address={listenAddress} ") - .Append($@"-Dmultinode.listen-port={listenPort} "); - -#if CORECLR - string fileName = null; - switch (platform) - { - case "net": - fileName = ntrNetPath; - sbArguments.Insert(0, $@" -Dmultinode.test-assembly=""{assemblyPath}"" "); - break; - case "netcore": - fileName = "dotnet"; - sbArguments.Insert(0, $@" -Dmultinode.test-assembly=""{assemblyPath}"" "); - sbArguments.Insert(0, ntrNetCorePath); - break; - } - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = fileName, - UseShellExecute = false, - RedirectStandardOutput = true, - Arguments = sbArguments.ToString(), - WorkingDirectory = Path.GetDirectoryName(assemblyPath) - } - }; -#else - sbArguments.Insert(0, $@"-Dmultinode.test-assembly=""{assemblyPath}"" "); - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "Akka.NodeTestRunner.exe", - UseShellExecute = false, - RedirectStandardOutput = true, - Arguments = sbArguments.ToString() - } - }; -#endif - - processes.Add(process); - var nodeIndex = nodeTest.Node; - var nodeRole = nodeTest.Role; - -#if CORECLR - if (platform == "netcore") - { - process.StartInfo.FileName = "dotnet"; - process.StartInfo.Arguments = ntrNetCorePath + " " + process.StartInfo.Arguments; - process.StartInfo.WorkingDirectory = Path.GetDirectoryName(assemblyPath); - } -#endif - - //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.TestName)); - testOutputDir = testOutputDir ?? folder.FullName; - var logFilePath = Path.Combine(folder.FullName, $"node{nodeIndex}__{nodeRole}__{platform}.txt"); - runningSpecName = nodeTest.TestName; - var nodeInfo = new TimelineLogCollectorActor.NodeInfo(nodeIndex, nodeRole, platform, nodeTest.TestName); - var fileActor = TestRunSystem.ActorOf(Props.Create(() => new FileSystemAppenderActor(logFilePath))); - process.OutputDataReceived += (sender, eventArgs) => - { - if (eventArgs?.Data != null) - { - fileActor.Tell(eventArgs.Data); - timelineCollector.Tell(new TimelineLogCollectorActor.LogMessage(nodeInfo, eventArgs.Data)); - if (TeamCityFormattingOn) - { - // teamCityTest.WriteStdOutput(eventArgs.Data); TODO: open flood gates - } - } - }; - var closureTest = nodeTest; - process.Exited += (sender, eventArgs) => - { - if (process.ExitCode == 0) - { - ReportSpecPassFromExitCode(nodeIndex, nodeRole, closureTest.TestName); - } - }; - - process.Start(); - process.BeginOutputReadLine(); - PublishRunnerMessage($"Started node {nodeIndex} : {nodeRole} on pid {process.Id}"); - } - - var specFailed = false; - foreach (var process in processes) - { - process.WaitForExit(); - specFailed = specFailed || process.ExitCode > 0; - process.Dispose(); - } - - PublishRunnerMessage("Waiting 3 seconds for all messages from all processes to be collected."); - Thread.Sleep(TimeSpan.FromSeconds(3)); - - if (testOutputDir != null) - { - var dumpTasks = new List() - { - // Dump aggregated timeline to file for this test - timelineCollector.Ask(new TimelineLogCollectorActor.DumpToFile(Path.Combine(testOutputDir, "aggregated.txt"))), - // Print aggregated timeline into the console - timelineCollector.Ask(new TimelineLogCollectorActor.PrintToConsole()) - }; - - if (specFailed) - { - var dumpFailureArtifactTask = timelineCollector.Ask( - new TimelineLogCollectorActor.DumpToFile(Path.Combine(Path.GetFullPath(OutputDirectory), FailedSpecsDirectory, $"{runningSpecName}.txt"))); - dumpTasks.Add(dumpFailureArtifactTask); - } - Task.WaitAll(dumpTasks.ToArray()); - } - - FinishSpec(test.Value, timelineCollector); - } - Console.WriteLine("Complete"); - PublishRunnerMessage("Waiting 5 seconds for all messages from all processes to be collected."); - Thread.Sleep(TimeSpan.FromSeconds(5)); - } - else - { - var sb = new StringBuilder(); - sb.AppendLine("One or more exception was thrown while discovering test cases. Test Aborted."); - foreach (var err in discovery.Errors) - { - for (int i = 0; i < err.ExceptionTypes.Length; ++i) - { - sb.AppendLine(); - sb.Append($"{err.ExceptionTypes[i]}: {err.Messages[i]}"); - sb.Append(err.StackTraces[i]); - } - } - PublishRunnerMessage(sb.ToString()); - Console.Out.WriteLine(sb.ToString()); - } - } - } - - AbortTcpLoggingServer(tcpLogger); - 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); - } - - static string ChangeDllPathPlatform(string path, string targetPlatform) - { - return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), "..", targetPlatform, Path.GetFileName(path))); - } - - static void EnableAllSinks(string assemblyName, string platform) - { - var now = DateTime.UtcNow; - - // if multinode.output-directory wasn't specified, the results files will be written - // to the same directory as the test assembly. - var outputDirectory = OutputDirectory; - - MessageSink CreateJsonFileSink() - { - var fileName = FileNameGenerator.GenerateFileName(outputDirectory, assemblyName, platform, ".json", now); - var jsonStoreProps = Props.Create(() => new FileSystemMessageSinkActor(new JsonPersistentTestRunStore(), fileName, !TeamCityFormattingOn, true)); - return new FileSystemMessageSink(jsonStoreProps); - } - - MessageSink CreateVisualizerFileSink() - { - var fileName = FileNameGenerator.GenerateFileName(outputDirectory, assemblyName, platform, ".html", now); - var visualizerProps = Props.Create(() => new FileSystemMessageSinkActor(new VisualizerPersistentTestRunStore(), fileName, !TeamCityFormattingOn, true)); - 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())); - } - } - - private static void AbortTcpLoggingServer(IActorRef tcpLogger) - { - tcpLogger.Ask(new TcpLoggingServer.StopListener(), TimeSpan.FromMinutes(1)).Wait(); - } - - private static void CloseAllSinks() - { - SinkCoordinator.Tell(new SinkCoordinator.CloseAllSinks()); - } - - private static void StartNewSpec(IList tests) - { - SinkCoordinator.Tell(tests); - } - - private static void ReportSpecPassFromExitCode(int nodeIndex, string nodeRole, string testName) - { - SinkCoordinator.Tell(new NodeCompletedSpecWithSuccess(nodeIndex, nodeRole, testName + " passed.")); - } - - private static void FinishSpec(IList tests, IActorRef timelineCollector) - { - var spec = tests.First(); - var log = timelineCollector.Ask(new TimelineLogCollectorActor.GetSpecLog(), TimeSpan.FromMinutes(1)).Result; - SinkCoordinator.Tell(new EndSpec(spec.TestName, spec.MethodName, log)); - } - - private static void PublishRunnerMessage(string message) - { - SinkCoordinator.Tell(new SinkCoordinator.RunnerMessage(message)); - } - - private static void PublishToAllSinks(string message) - { - SinkCoordinator.Tell(message, ActorRefs.NoSender); - } - } - - internal class TcpLoggingServer : ReceiveActor - { - private readonly ILoggingAdapter _log = Context.GetLogger(); - private IActorRef _tcpManager = Nobody.Instance; - private IActorRef _abortSender; - - public TcpLoggingServer(IActorRef sinkCoordinator) - { - Receive(_ => _tcpManager = Sender); - Receive(connected => - { - _log.Info($"Node connected on {Sender}"); - Sender.Tell(new Tcp.Register(Self)); - }); - - Receive( - closed => _log.Info($"Node disconnected on {Sender}{Environment.NewLine}")); - - Receive(received => - { - var message = received.Data.ToString(); - sinkCoordinator.Tell(message); - }); - - Receive(_ => - { - _abortSender = Sender; - _tcpManager.Tell(Tcp.Unbind.Instance); - }); - Receive(_ => _abortSender.Tell(new ListenerStopped())); - } - - public class StopListener { } - public class ListenerStopped { } - } -} \ No newline at end of file diff --git a/src/common.props b/src/common.props index a4c47f4..297aa17 100644 --- a/src/common.props +++ b/src/common.props @@ -18,7 +18,7 @@ netcoreapp3.0 net472 netstandard2.0 - 4.14.0 + 5.10.0 2.9.0 1.4.0-beta4 akka;actors;actor model;Akka;concurrency diff --git a/update-sample-test-nuget.cmd b/update-sample-test-nuget.cmd new file mode 100644 index 0000000..5ccaed5 --- /dev/null +++ b/update-sample-test-nuget.cmd @@ -0,0 +1,22 @@ +git commit -a -m "Updated package version" + +cd C:\Projects\Upwork\Aaron\Akka.MultiNodeTestRunner\src\Akka.MultiNodeTestRunner.VisualStudio\bin\Debug +del /s *.nupkg +del /s *.snupkg + +cd C:\Projects\Upwork\Aaron\Akka.MultiNodeTestRunner +dotnet pack src\Akka.MultiNodeTestRunner.VisualStudio\Akka.MultiNodeTestRunner.VisualStudio.csproj + +cd C:\LocalNuget +del /s *.nupkg + +cd C:\Projects\Upwork\Aaron\Akka.MultiNodeTestRunner\src\Akka.MultiNodeTestRunner.VisualStudio\bin\Debug +dotnet nuget push *.nupkg --source c:\LocalNuget + +cd ..\..\..\Akka.MultiNodeTestRunner.SampleTests +dotnet remove package Akka.MultiNodeTestRunner.VisualStudio +dotnet add package Akka.MultiNodeTestRunner.VisualStudio -v 2.4.2-* + +cd C:\Projects\Upwork\Aaron\Akka.MultiNodeTestRunner + +pause \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 0000000..b899a86 --- /dev/null +++ b/version.json @@ -0,0 +1,11 @@ +{ + "version": "2.4.2-preview.{height}", + "publicReleaseRefSpec": [ + "^refs/heads/master$", // we release out of master + "^refs/tags/v\\d+\\.\\d+", // we also release tags starting with vN.N + "^refs/heads/rel/*" // we also release branches starting with rel/ + ], + "nugetPackageVersion": { + "semVer": 2 + } +} diff --git a/visualstudio.xunit.ChildProcessDbgSettings b/visualstudio.xunit.ChildProcessDbgSettings new file mode 100644 index 0000000..156463f --- /dev/null +++ b/visualstudio.xunit.ChildProcessDbgSettings @@ -0,0 +1,4 @@ + + + + \ No newline at end of file