Skip to content

Commit c43455c

Browse files
authored
Version 1.1.0-beta2 release
Version 1.1.0-beta2 release
2 parents 529608f + 4578657 commit c43455c

File tree

13 files changed

+197
-48
lines changed

13 files changed

+197
-48
lines changed

RELEASE_NOTES.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
#### 1.1.0-beta2 December 23 2019 ####
2+
- [Add support for Xunit TestFrameworkAttribute attribute](https://github.com/akkadotnet/Akka.MultiNodeTestRunner/pull/116)
3+
4+
In this release we added `MultiNodeTestFramework` to simplify non-parallel test setup. This test
5+
framework is a simple override of the built-in `XunitTestFramework` that disables/ignores the
6+
Xunit `CollectionBehaviorAttribute`, put all test classes from a single assembly into a single test
7+
collection, and disables the test collection parallelization.
8+
9+
To use this test framework, you will need to add an assembly level attribute that tells Xunit to
10+
use this custom test framework instead:
11+
12+
```c#
13+
[assembly: TestFramework("Akka.MultiNode.TestAdapter.MultiNodeTestFramework", "Akka.MultiNode.TestAdapter")]
14+
```
15+
16+
Note that you can also use this assembly level attribute to achieve more or less the same effect:
17+
```c#
18+
[assembly: CollectionBehavior(DisableTestParallelization = true)]
19+
```
20+
121
#### 1.1.0-beta1 October 20 2019 ####
222

323
- [Switch to pure Xunit implementation](https://github.com/akkadotnet/Akka.MultiNodeTestRunner/pull/105)

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"msbuild-sdks": {
3-
"MSBuild.Sdk.Extras": "3.0.38"
3+
"MSBuild.Sdk.Extras": "3.0.44"
44
}
55
}

src/Akka.MultiNode.SampleMultiNodeTests/Metadata/SampleTestsMetadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Xunit;
22

3-
[assembly: CollectionBehavior(DisableTestParallelization = true)]
3+
[assembly: TestFramework("Akka.MultiNode.TestAdapter.MultiNodeTestFramework", "Akka.MultiNode.TestAdapter")]
44
namespace Akka.MultiNode.TestAdapter.SampleTests.Metadata
55
{
66
/// <summary>

src/Akka.MultiNode.TestAdapter.Tests/Internal/MultiNodeTestRunnerDiscovery/DiscoveryCases.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// </copyright>
66
//-----------------------------------------------------------------------
77

8+
using System.Linq;
89
using Akka.Remote.TestKit;
910

1011
namespace Akka.MultiNode.TestAdapter.Tests.Internal.MultiNodeTestRunnerDiscovery
@@ -139,6 +140,31 @@ public FloodyChildSpec3(FloodyConfig config) : base(config)
139140
}
140141
}
141142

143+
public class NoReflectionConfig : MultiNodeConfig
144+
{
145+
public NoReflectionConfig()
146+
{
147+
foreach(var i in Enumerable.Range(1, 10))
148+
{
149+
Role("node-" + i);
150+
}
151+
}
152+
}
153+
154+
public class NoReflectionSpec : MultiNodeSpec
155+
{
156+
public NoReflectionSpec(NoReflectionConfig config): base(config, typeof(NoReflectionSpec))
157+
{
158+
}
159+
160+
[MultiNodeFact(Skip = "Only for discovery tests")]
161+
public void Dummy()
162+
{
163+
}
164+
165+
protected override int InitialParticipantsValueFactory { get; }
166+
}
167+
142168
public class DiverseConfig : MultiNodeConfig
143169
{
144170
public RoleName RoleProp { get; set; }

src/Akka.MultiNode.TestAdapter.Tests/Internal/MultiNodeTestRunnerDiscovery/DiscoverySpec.cs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class DiscoverySpec
2121
public void No_abstract_classes()
2222
{
2323
var discoveredSpecs = DiscoverSpecs();
24-
Assert.DoesNotContain(discoveredSpecs, s => s.TypeName == nameof(DiscoveryCases.NoAbstractClassesSpec));
24+
Assert.False(discoveredSpecs.ContainsKey(KeyFromSpecName(nameof(DiscoveryCases.NoAbstractClassesSpec))));
2525
}
2626

2727
[Fact(DisplayName = "Deeply inherited classes are discoverable")]
@@ -30,9 +30,7 @@ public void Deeply_inherited_are_ok()
3030
var discoveredSpecs = DiscoverSpecs();
3131
Assert.Equal(
3232
"DeeplyInheritedChildRole",
33-
discoveredSpecs
34-
.FirstOrDefault(s => s.TypeName == KeyFromSpecName(nameof(DiscoveryCases.DeeplyInheritedChildSpec)))?.Nodes
35-
.First().Role);
33+
discoveredSpecs[KeyFromSpecName(nameof(DiscoveryCases.DeeplyInheritedChildSpec))].First().Role);
3634
}
3735

3836
[Fact(DisplayName = "Child test class with default constructors are ok")]
@@ -52,25 +50,19 @@ public void Child_class_with_default_constructor_are_ok()
5250
public void Discovered_count_equals_number_of_roles_mult_specs()
5351
{
5452
var discoveredSpecs = DiscoverSpecs();
55-
Assert.Equal(5, discoveredSpecs
56-
.FirstOrDefault(s => s.TypeName == KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec1)))?.Nodes.Count);
57-
Assert.Equal(5, discoveredSpecs
58-
.FirstOrDefault(s => s.TypeName == KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec2)))?.Nodes.Count);
59-
Assert.Equal(5, discoveredSpecs
60-
.FirstOrDefault(s => s.TypeName == KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec3)))?.Nodes.Count);
53+
Assert.Equal(5, discoveredSpecs[KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec1))].Count);
54+
Assert.Equal(5, discoveredSpecs[KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec2))].Count);
55+
Assert.Equal(5, discoveredSpecs[KeyFromSpecName(nameof(DiscoveryCases.FloodyChildSpec3))].Count);
6156
}
6257

63-
[Fact(DisplayName = "Only public props and fields are considered when looking for RoleNames")]
64-
public void Public_props_and_fields_are_considered()
58+
[Fact(DisplayName = "Only the MultiNodeConfig.Roles property is used to compute the number of Roles in MultiNodeFact")]
59+
public void Only_MultiNodeConfig_role_count_used()
6560
{
6661
var discoveredSpecs = DiscoverSpecs();
67-
Assert.Equal(
68-
discoveredSpecs
69-
.FirstOrDefault(test => test.TypeName == KeyFromSpecName(nameof(DiscoveryCases.DiverseSpec)))?.Nodes
70-
.Select(n => n.Role), new[] {"RoleProp", "RoleField"});
62+
Assert.Equal(10, discoveredSpecs[KeyFromSpecName(nameof(DiscoveryCases.NoReflectionSpec))].Select(c => c.Role).Count());
7163
}
72-
73-
private static List<MultiNodeTestCase> DiscoverSpecs()
64+
65+
private static Dictionary<string, List<NodeTest>> DiscoverSpecs()
7466
{
7567
var assemblyPath = new Uri(typeof(DiscoveryCases).GetTypeInfo().Assembly.Location).LocalPath;
7668

@@ -80,7 +72,9 @@ private static List<MultiNodeTestCase> DiscoverSpecs()
8072
{
8173
controller.Find(false, discovery, TestFrameworkOptions.ForDiscovery());
8274
discovery.Finished.WaitOne();
83-
return discovery.TestCases;
75+
return discovery
76+
.TestCases
77+
.ToDictionary(t => t.TypeName, t => t.Nodes);
8478
}
8579
}
8680
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Xunit.Abstractions;
5+
using Xunit.Sdk;
6+
7+
namespace Akka.MultiNode.TestAdapter.Internal
8+
{
9+
internal class CollectionPerSessionTestCollectionFactory : IXunitTestCollectionFactory
10+
{
11+
private readonly Dictionary<IAssemblyInfo, TestCollection> _collectionCache =
12+
new Dictionary<IAssemblyInfo, TestCollection>();
13+
14+
public ITestCollection Get(ITypeInfo testClass)
15+
{
16+
if (_collectionCache.TryGetValue(testClass.Assembly, out var collection))
17+
return collection;
18+
19+
collection = new TestCollection(
20+
new TestAssembly(testClass.Assembly),
21+
null,
22+
$"MultiNode test collection for {Path.GetFileName(testClass.Assembly.AssemblyPath)}");
23+
_collectionCache[testClass.Assembly] = collection;
24+
return collection;
25+
}
26+
27+
public string DisplayName => "collection-per-session";
28+
}
29+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
using Xunit.Abstractions;
6+
using Xunit.Sdk;
7+
8+
namespace Akka.MultiNode.TestAdapter.Internal
9+
{
10+
internal class MultiNodeTestAssemblyRunner : XunitTestAssemblyRunner
11+
{
12+
public MultiNodeTestAssemblyRunner(
13+
ITestAssembly testAssembly,
14+
IEnumerable<IXunitTestCase> testCases,
15+
IMessageSink diagnosticMessageSink,
16+
IMessageSink executionMessageSink,
17+
ITestFrameworkExecutionOptions executionOptions)
18+
: base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions)
19+
{
20+
}
21+
22+
protected override async Task<RunSummary> RunTestCollectionsAsync(IMessageBus messageBus, CancellationTokenSource cancellationTokenSource)
23+
{
24+
var summary = new RunSummary();
25+
26+
foreach (var (testCollection, testCases) in OrderTestCollections())
27+
{
28+
summary.Aggregate(await RunTestCollectionAsync(messageBus, testCollection, testCases, cancellationTokenSource));
29+
if (cancellationTokenSource.IsCancellationRequested)
30+
break;
31+
}
32+
33+
return summary;
34+
}
35+
}
36+
}

src/Akka.MultiNode.TestAdapter/Internal/MultiNodeTestCase.cs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,8 @@ private IEnumerable<RoleName> RoleNames(Type specType)
143143
{
144144
var configType = ctorWithConfig.GetParameters().First().ParameterType;
145145
var args = ConfigConstructorParamValues(configType);
146-
var configInstance = Activator.CreateInstance(configType, args);
147-
var roleType = typeof(RoleName);
148-
var configProps = configType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
149-
var roleProps = configProps.Where(p => p.PropertyType == roleType && p.Name != "Myself")
150-
.Select(p => (RoleName) p.GetValue(configInstance));
151-
var configFields = configType.GetFields(BindingFlags.Instance | BindingFlags.Public);
152-
var roleFields = configFields.Where(f => f.FieldType == roleType && f.Name != "Myself")
153-
.Select(f => (RoleName) f.GetValue(configInstance));
154-
var roles = roleProps.Concat(roleFields).Distinct();
155-
return roles;
146+
var configInstance = (MultiNodeConfig) Activator.CreateInstance(configType, args);
147+
return configInstance.Roles;
156148
}
157149
catch (Exception e)
158150
{

src/Akka.MultiNode.TestAdapter/Internal/MultiNodeTestCaseRunner.cs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,12 @@ protected override async Task<RunSummary> RunTestAsync()
225225

226226
private async Task DumpAggregatedSpecLogs(RunSummary summary, IActorRef timelineCollector)
227227
{
228-
var dumpPath = Path.GetFullPath(Path.Combine(Path.Combine(Options.OutputDirectory, TestCase.DisplayName), "aggregated.txt"));
229-
var failedSpecPath = Path.GetFullPath(Path.Combine(Options.OutputDirectory, Options.FailedSpecsDirectory, $"{TestCase.DisplayName}.txt"));
230-
231-
if (!Options.AppendLogOutput)
232-
{
233-
if(File.Exists(dumpPath))
234-
File.Delete(dumpPath);
235-
if(File.Exists(failedSpecPath))
236-
File.Delete(failedSpecPath);
237-
}
228+
var dumpFolder = Path.GetFullPath(Path.Combine(Options.OutputDirectory, TestCase.DisplayName));
229+
var dumpPath = Path.Combine(dumpFolder, "aggregated.txt");
230+
231+
Directory.CreateDirectory(dumpFolder);
232+
if (!Options.AppendLogOutput && File.Exists(dumpPath))
233+
File.Delete(dumpPath);
238234

239235
var logLines = await timelineCollector.Ask<string[]>(new TimelineLogCollectorActor.GetLog());
240236

@@ -243,8 +239,10 @@ private async Task DumpAggregatedSpecLogs(RunSummary summary, IActorRef timeline
243239

244240
if (summary.Failed > 0)
245241
{
246-
Directory.CreateDirectory(Path.GetDirectoryName(failedSpecPath));
242+
var failedSpecFolder = Path.GetFullPath(Path.Combine(Options.OutputDirectory, Options.FailedSpecsDirectory));
243+
var failedSpecPath = Path.Combine(failedSpecFolder, $"{TestCase.DisplayName}.txt");
247244

245+
Directory.CreateDirectory(failedSpecFolder);
248246
if(!Options.AppendLogOutput && File.Exists(failedSpecPath))
249247
File.Delete(failedSpecPath);
250248

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections.Generic;
2+
using System.Reflection;
3+
using Xunit.Abstractions;
4+
using Xunit.Sdk;
5+
6+
namespace Akka.MultiNode.TestAdapter.Internal
7+
{
8+
internal class MultiNodeTestFrameworkExecutor : XunitTestFrameworkExecutor
9+
{
10+
public MultiNodeTestFrameworkExecutor(
11+
AssemblyName assemblyName,
12+
ISourceInformationProvider sourceInformationProvider,
13+
IMessageSink diagnosticMessageSink)
14+
: base(assemblyName, sourceInformationProvider, diagnosticMessageSink)
15+
{
16+
}
17+
18+
protected override async void RunTestCases(IEnumerable<IXunitTestCase> testCases, IMessageSink executionMessageSink,
19+
ITestFrameworkExecutionOptions executionOptions)
20+
{
21+
using (var assemblyRunner = new MultiNodeTestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, executionMessageSink, executionOptions))
22+
await assemblyRunner.RunAsync();
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)