diff --git a/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj b/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj index bc13e1f..e612ad6 100644 --- a/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj +++ b/examples/Elastic.Ephemeral.Example/Elastic.Ephemeral.Example.csproj @@ -13,7 +13,7 @@ - + diff --git a/examples/Elastic.Ephemeral.Example/Program.cs b/examples/Elastic.Ephemeral.Example/Program.cs index efd2487..ffe89e1 100644 --- a/examples/Elastic.Ephemeral.Example/Program.cs +++ b/examples/Elastic.Ephemeral.Example/Program.cs @@ -29,7 +29,7 @@ if (cluster.DetectedProxy != DetectedProxySoftware.None) transportConfig = transportConfig.Proxy(new Uri("http://localhost:8080/"), null!, null!); -var transport = new DefaultHttpTransport(transportConfig); +var transport = new DistributedTransport(transportConfig); var response = await transport.RequestAsync(HttpMethod.GET, "/"); Console.WriteLine(response); diff --git a/examples/Elastic.Xunit.ExampleComplex/Setup.cs b/examples/Elastic.Xunit.ExampleComplex/Setup.cs index 31266be..905289a 100644 --- a/examples/Elastic.Xunit.ExampleComplex/Setup.cs +++ b/examples/Elastic.Xunit.ExampleComplex/Setup.cs @@ -19,13 +19,12 @@ public class MyRunOptions : ElasticXunitRunOptions { public MyRunOptions() { - ClusterFilter = ""; - RunUnitTests = false; + RunUnitTests = true; RunIntegrationTests = true; IntegrationTestsMayUseAlreadyRunningNode = true; Version = TestVersion; } - public static ElasticVersion TestVersion { get; } = "8.0.0-SNAPSHOT"; + public static ElasticVersion TestVersion { get; } = "latest-8"; } } diff --git a/examples/Elastic.Xunit.ExampleComplex/TestWithoutClusterFixture.cs b/examples/Elastic.Xunit.ExampleComplex/TestWithoutClusterFixture.cs index 9594304..3fab22a 100644 --- a/examples/Elastic.Xunit.ExampleComplex/TestWithoutClusterFixture.cs +++ b/examples/Elastic.Xunit.ExampleComplex/TestWithoutClusterFixture.cs @@ -8,7 +8,6 @@ namespace Elastic.Xunit.ExampleComplex { - [IntegrationTestCluster(typeof(TestCluster))] [SkipVersion("<6.3.0", "")] public class TestWithoutClusterFixture { diff --git a/examples/Elastic.Xunit.ExampleComplex/Tests.cs b/examples/Elastic.Xunit.ExampleComplex/Tests.cs index 48a565e..8a97d8d 100644 --- a/examples/Elastic.Xunit.ExampleComplex/Tests.cs +++ b/examples/Elastic.Xunit.ExampleComplex/Tests.cs @@ -2,17 +2,17 @@ // Elasticsearch B.V licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information +using Elastic.Elasticsearch.Managed; using Elastic.Elasticsearch.Xunit.XunitPlumbing; using Elasticsearch.Net; using FluentAssertions; +using Xunit; namespace Elastic.Xunit.ExampleComplex { public class MyTestClass : ClusterTestClassBase { - public MyTestClass(TestCluster cluster) : base(cluster) - { - } + public MyTestClass(TestCluster cluster) : base(cluster) { } [I] public void SomeTest() @@ -22,34 +22,70 @@ public void SomeTest() info.IsValid.Should().BeTrue(); Client.CreateIndex("INASda"); + Client.LowLevel.Search(PostData.Serializable(new {query = new {query_string = 1}})); + } + } + public class Tests1 : ClusterTestClassBase + { + public Tests1(TestCluster cluster) : base(cluster) { } - Client.LowLevel.Search(PostData.Serializable(new {query = new {query_string = 1}})); + [U] public void Unit1Test() => (1 + 1).Should().Be(2); + [U] public void Unit1Test1() => (1 + 1).Should().Be(2); + [U] public void Unit1Test2() => (1 + 1).Should().Be(2); + [U] public void Unit1Test3() => (1 + 1).Should().Be(2); + [U] public void Unit1Test4() => (1 + 1).Should().Be(2); + [U] public void Unit1Test5() => (1 + 1).Should().Be(2); + [U] public void Unit1Test6() => (1 + 1).Should().Be(2); + } + + public class Tests3 + { + [U] public void Unit3Test() => (1 + 1).Should().Be(2); + [U] public void Unit3Test1() => (1 + 1).Should().Be(2); + [U] public void Unit3Test2() => (1 + 1).Should().Be(2); + [U] public void Unit3Test3() => (1 + 1).Should().Be(2); + [U] public void Unit3Test4() => (1 + 1).Should().Be(2); + [U] public void Unit3Test5() => (1 + 1).Should().Be(2); + [U] public void Unit3Test6() => (1 + 1).Should().Be(2); + } + + public class Tests2 : ClusterTestClassBase + { + public Tests2(TestCluster cluster) : base(cluster) { } + + [U] public void Unit2Test() => (1 + 1).Should().Be(2); + [U] public void Unit2Test1() => (1 + 1).Should().Be(2); + [U] public void Unit2Test2() => (1 + 1).Should().Be(2); + [U] public void Unit2Test3() => (1 + 1).Should().Be(2); + [U] public void Unit2Test4() => (1 + 1).Should().Be(2); + [U] public void Unit2Test5() => (1 + 1).Should().Be(2); + [U] public void Unit2Test6() => (1 + 1).Should().Be(2); + } + + public class MyGenericTestClass : ClusterTestClassBase + { + public MyGenericTestClass(TestGenericCluster cluster) : base(cluster) { } + + [I] public void SomeTest() + { + var info = Client.RootNodeInfo(); + + info.IsValid.Should().BeTrue(); } + [U] public void MyGenericUnitTest() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest1() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest2() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest3() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest4() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest5() => (1 + 1).Should().Be(2); + [U] public void MyGenericUnitTest6() => (1 + 1).Should().Be(2); } -// -// public class MyGenericTestClass : ClusterTestClassBase -// { -// public MyGenericTestClass(TestGenericCluster cluster) : base(cluster) { } -// -// [I] public void SomeTest() -// { -// var info = this.Client.RootNodeInfo(); -// -// info.IsValid.Should().BeTrue(); -// } -// [U] public void UnitTest() -// { -// (1 + 1).Should().Be(2); -// } -// } [SkipVersion("<6.2.0", "")] public class SkipTestClass : ClusterTestClassBase { - public SkipTestClass(TestGenericCluster cluster) : base(cluster) - { - } + public SkipTestClass(TestGenericCluster cluster) : base(cluster) { } [I] public void SomeTest() @@ -62,4 +98,12 @@ public void SomeTest() [U] public void UnitTest() => (1 + 1).Should().Be(2); } + + public class DirectInterfaceTests : IClusterFixture + { + public DirectInterfaceTests(TestGenericCluster cluster) { } + + [U] + public void DirectUnitTest() => (1 + 1).Should().Be(2); + } } diff --git a/examples/Elastic.Xunit.ExampleMinimal/ExampleTest.cs b/examples/Elastic.Xunit.ExampleMinimal/ExampleTest.cs index 0b44131..493522c 100644 --- a/examples/Elastic.Xunit.ExampleMinimal/ExampleTest.cs +++ b/examples/Elastic.Xunit.ExampleMinimal/ExampleTest.cs @@ -21,7 +21,7 @@ public class MyTestCluster : XunitClusterBase /// We pass our configuration instance to the base class. /// We only configure it to run version 6.2.3 here but lots of additional options are available. /// - public MyTestCluster() : base(new XunitClusterConfiguration("8.0.0-SNAPSHOT") { }) + public MyTestCluster() : base(new XunitClusterConfiguration("latest-8") { }) { } } diff --git a/examples/ScratchPad/Program.cs b/examples/ScratchPad/Program.cs index 5ff29df..dabd8eb 100644 --- a/examples/ScratchPad/Program.cs +++ b/examples/ScratchPad/Program.cs @@ -24,8 +24,8 @@ public static class Program public static int Main() { - //ResolveVersions(); - ManualConfigRun(); + ResolveVersions(); + //ManualConfigRun(); //ValidateCombinations.Run(); return 0; } @@ -87,8 +87,8 @@ private static void ResolveVersions() { var versions = new[] { - "8.0.0-SNAPSHOT", "7.0.0-beta1", "6.6.1", "latest-7", "latest", "7.0.0", "7.4.0-SNAPSHOT", - "957e3089:7.2.0", "latest-6" + "latest-8", "7.0.0-beta1", "6.6.1", "latest-7", "latest", "7.0.0", "7.4.0-SNAPSHOT", + "957e3089:7.2.0" }; //versions = new[] {"latest-7"}; var products = new Product[] diff --git a/src/Elastic.Elasticsearch.Xunit/Elastic.Elasticsearch.Xunit.csproj b/src/Elastic.Elasticsearch.Xunit/Elastic.Elasticsearch.Xunit.csproj index 9877920..6015f45 100644 --- a/src/Elastic.Elasticsearch.Xunit/Elastic.Elasticsearch.Xunit.csproj +++ b/src/Elastic.Elasticsearch.Xunit/Elastic.Elasticsearch.Xunit.csproj @@ -2,10 +2,12 @@ netstandard2.0;netstandard2.1;net462 True + False Provides an Xunit test framework allowing you to run integration tests against local ephemeral Elasticsearch clusters elastic,elasticsearch,xunit,cluster,integration,test,ephemeral + diff --git a/src/Elastic.Elasticsearch.Xunit/ElasticXunitConfigurationAttribute.cs b/src/Elastic.Elasticsearch.Xunit/ElasticXunitConfigurationAttribute.cs index 328cee6..f7f280e 100644 --- a/src/Elastic.Elasticsearch.Xunit/ElasticXunitConfigurationAttribute.cs +++ b/src/Elastic.Elasticsearch.Xunit/ElasticXunitConfigurationAttribute.cs @@ -3,31 +3,20 @@ // See the LICENSE file in the project root for more information using System; +using Nullean.Xunit.Partitions; -namespace Elastic.Elasticsearch.Xunit -{ - /// - /// An assembly attribute that specifies the - /// for Xunit tests within the assembly. - /// - [AttributeUsage(AttributeTargets.Assembly)] - public class ElasticXunitConfigurationAttribute : Attribute - { - /// - /// Creates a new instance of - /// - /// - /// A type deriving from that specifies the run options - /// - public ElasticXunitConfigurationAttribute(Type type) - { - var options = Activator.CreateInstance(type) as ElasticXunitRunOptions; - Options = options ?? new ElasticXunitRunOptions(); - } +namespace Elastic.Elasticsearch.Xunit; - /// - /// The run options - /// - public ElasticXunitRunOptions Options { get; } - } +/// +/// An assembly attribute that specifies the +/// for Xunit tests within the assembly. +/// +[AttributeUsage(AttributeTargets.Assembly)] +public class ElasticXunitConfigurationAttribute : PartitionOptionsAttribute +{ + /// Creates a new instance of . + /// + /// A type deriving from that specifies the run options + /// + public ElasticXunitConfigurationAttribute(Type type) : base(type) { } } diff --git a/src/Elastic.Elasticsearch.Xunit/ElasticXunitRunOptions.cs b/src/Elastic.Elasticsearch.Xunit/ElasticXunitRunOptions.cs index 26fb4db..5215d3b 100644 --- a/src/Elastic.Elasticsearch.Xunit/ElasticXunitRunOptions.cs +++ b/src/Elastic.Elasticsearch.Xunit/ElasticXunitRunOptions.cs @@ -3,18 +3,20 @@ // See the LICENSE file in the project root for more information using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; +using System.Linq; +using System.Runtime.Serialization; using Elastic.Elasticsearch.Xunit.XunitPlumbing; using Elastic.Stack.ArtifactsApi; +using Nullean.Xunit.Partitions; +using Xunit.Abstractions; +using static System.StringSplitOptions; namespace Elastic.Elasticsearch.Xunit { /// /// The Xunit test runner options /// - public class ElasticXunitRunOptions + public class ElasticXunitRunOptions : PartitionOptions { /// /// Informs the runner whether we expect to run integration tests. Defaults to true @@ -35,16 +37,46 @@ public class ElasticXunitRunOptions public bool RunUnitTests { get; set; } /// - /// A global test filter that can be used to only run certain tests. + /// A global cluster filter that can be used to only run certain cluster's tests. /// Accepts a comma separated list of filters /// - public string TestFilter { get; set; } + [Obsolete("Use PartitionFilterRegex instead", false)] + [IgnoreDataMember] + public string ClusterFilter + { + get => PartitionFilterRegex; + set + { + if (string.IsNullOrWhiteSpace(value)) PartitionFilterRegex = value; + else + { + //attempt at being backwards compatible with old way of filtering + var re = string.Join("|", value.Split(new[] { ','}, RemoveEmptyEntries).Select(s => s.Trim())); + PartitionFilterRegex = re; + } + } + } /// - /// A global cluster filter that can be used to only run certain cluster's tests. + /// A global test filter that can be used to only run certain cluster's tests. /// Accepts a comma separated list of filters /// - public string ClusterFilter { get; set; } + [Obsolete("Use ParitionFilterRegex instead", false)] + [IgnoreDataMember] + public string TestFilter + { + get => TestFilterRegex; + set + { + if (string.IsNullOrWhiteSpace(value)) TestFilterRegex = value; + else + { + //attempt at being backwards compatible with old way of filtering + var re = string.Join("|", value.Split(new[] { ','}, RemoveEmptyEntries).Select(s => s.Trim())); + TestFilterRegex = re; + } + } + } /// /// Informs the runner what version of Elasticsearch is under test. Required for @@ -52,22 +84,38 @@ public class ElasticXunitRunOptions /// public ElasticVersion Version { get; set; } - /// - /// Called when the tests have finished running successfully - /// - /// Per cluster timings of the total test time, including starting Elasticsearch - /// All collection of failed cluster, failed tests tuples - public virtual void OnTestsFinished(Dictionary runnerClusterTotals, - ConcurrentBag> runnerFailedCollections) + public override void SetOptions(ITestFrameworkDiscoveryOptions discoveryOptions) { + base.SetOptions(discoveryOptions); + discoveryOptions.SetValue(nameof(Version), Version); + discoveryOptions.SetValue(nameof(RunIntegrationTests), RunIntegrationTests); + discoveryOptions.SetValue( + nameof(IntegrationTestsMayUseAlreadyRunningNode), + IntegrationTestsMayUseAlreadyRunningNode + ); + discoveryOptions.SetValue(nameof(RunUnitTests), RunUnitTests); +#pragma warning disable CS0618 // Type or member is obsolete + discoveryOptions.SetValue(nameof(TestFilter), TestFilter); + discoveryOptions.SetValue(nameof(ClusterFilter), ClusterFilter); +#pragma warning restore CS0618 // Type or member is obsolete } - /// - /// Called before tests run. An ideal place to perform actions such as writing information to - /// . - /// - public virtual void OnBeforeTestsRun() + public override void SetOptions(ITestFrameworkExecutionOptions executionOptions) { + + base.SetOptions(executionOptions); + executionOptions.SetValue(nameof(Version), Version); + executionOptions.SetValue(nameof(RunIntegrationTests), RunIntegrationTests); + executionOptions.SetValue( + nameof(IntegrationTestsMayUseAlreadyRunningNode), + IntegrationTestsMayUseAlreadyRunningNode + ); + executionOptions.SetValue(nameof(RunUnitTests), RunUnitTests); +#pragma warning disable CS0618 // Type or member is obsolete + executionOptions.SetValue(nameof(TestFilter), TestFilter); + executionOptions.SetValue(nameof(ClusterFilter), ClusterFilter); +#pragma warning restore CS0618 // Type or member is obsolete + } } } diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFramework.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFramework.cs index c329c03..baf0588 100644 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFramework.cs +++ b/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFramework.cs @@ -3,30 +3,25 @@ // See the LICENSE file in the project root for more information using System.Reflection; +using Elastic.Elasticsearch.Xunit.XunitPlumbing; using Xunit.Abstractions; using Xunit.Sdk; +using Nullean.Xunit.Partitions; +using Nullean.Xunit.Partitions.Sdk; -namespace Elastic.Elasticsearch.Xunit.Sdk -{ - public class ElasticTestFramework : XunitTestFramework - { - public ElasticTestFramework(IMessageSink messageSink) : base(messageSink) - { - } - - protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo) => - new ElasticTestFrameworkDiscoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink); +namespace Elastic.Elasticsearch.Xunit.Sdk; - protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName) - { - var assembly = Assembly.Load(assemblyName); - var options = assembly.GetCustomAttribute()?.Options ?? - new ElasticXunitRunOptions(); +// ReSharper disable once UnusedType.Global +public class ElasticTestFramework : PartitionTestFramework +{ + public ElasticTestFramework(IMessageSink messageSink) : base(messageSink) { } +} - return new TestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink) - { - Options = options - }; - } - } +public class ElasticTestFrameworkDiscovererFactory : ITestFrameworkDiscovererFactory +{ + public XunitTestFrameworkDiscoverer Create( + IAssemblyInfo assemblyInfo, ISourceInformationProvider sourceProvider, IMessageSink diagnosticMessageSink + ) + where TOptions : PartitionOptions, new() => + new PartitionTestFrameworkDiscoverer(assemblyInfo, sourceProvider, diagnosticMessageSink, typeof(IClusterFixture<>)); } diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFrameworkDiscoverer.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFrameworkDiscoverer.cs deleted file mode 100644 index 9edc1e9..0000000 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/ElasticTestFrameworkDiscoverer.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System.Reflection; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Elastic.Elasticsearch.Xunit.Sdk -{ - public class ElasticTestFrameworkDiscoverer : XunitTestFrameworkDiscoverer - { - public ElasticTestFrameworkDiscoverer(IAssemblyInfo assemblyInfo, ISourceInformationProvider sourceProvider, - IMessageSink diagnosticMessageSink, IXunitTestCollectionFactory collectionFactory = null) : base( - assemblyInfo, sourceProvider, diagnosticMessageSink, collectionFactory) - { - var a = Assembly.Load(new AssemblyName(assemblyInfo.Name)); - var options = a.GetCustomAttribute()?.Options ?? - new ElasticXunitRunOptions(); - Options = options; - } - - /// - /// The options for - /// - public ElasticXunitRunOptions Options { get; } - - protected override bool FindTestsForType(ITestClass testClass, bool includeSourceInformation, - IMessageBus messageBus, ITestFrameworkDiscoveryOptions discoveryOptions) - { - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.Version), Options.Version); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests), Options.RunIntegrationTests); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.IntegrationTestsMayUseAlreadyRunningNode), - Options.IntegrationTestsMayUseAlreadyRunningNode); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.RunUnitTests), Options.RunUnitTests); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.TestFilter), Options.TestFilter); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.ClusterFilter), Options.ClusterFilter); - return base.FindTestsForType(testClass, includeSourceInformation, messageBus, discoveryOptions); - } - } -} diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/ForEachAsyncExtensions.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/ForEachAsyncExtensions.cs deleted file mode 100644 index 87bf715..0000000 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/ForEachAsyncExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Elastic.Elasticsearch.Xunit.Sdk -{ - internal static class ForEachAsyncExtensions - { - internal static Task ForEachAsync(this IEnumerable source, int dop, Func body) => - Task.WhenAll( - from partition in Partitioner.Create(source).GetPartitions(dop) - select Task.Run(async delegate - { - using (partition) - while (partition.MoveNext()) - await body(partition.Current).ConfigureAwait(false); - })); - } -} diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/TestAssemblyRunner.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/TestAssemblyRunner.cs index 8bd70da..798fe16 100644 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/TestAssemblyRunner.cs +++ b/src/Elastic.Elasticsearch.Xunit/Sdk/TestAssemblyRunner.cs @@ -13,271 +13,99 @@ using Elastic.Elasticsearch.Ephemeral; using Elastic.Elasticsearch.Ephemeral.Tasks.ValidationTasks; using Elastic.Elasticsearch.Xunit.XunitPlumbing; +using Nullean.Xunit.Partitions.Sdk; using Xunit.Abstractions; using Xunit.Sdk; namespace Elastic.Elasticsearch.Xunit.Sdk { - internal class TestAssemblyRunner : XunitTestAssemblyRunner - { - private readonly Dictionary> _assemblyFixtureMappings = - new Dictionary>(); - - private readonly List, GroupedByCluster>> _grouped; + public class TestAssemblyRunnerFactory : ITestAssemblyRunnerFactory + { + public XunitTestAssemblyRunner Create(ITestAssembly testAssembly, IEnumerable testCases, + IMessageSink diagnosticMessageSink, + IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions) => + new TestAssemblyRunner(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, + executionOptions); + } + internal class TestAssemblyRunner : PartitionTestAssemblyRunner> + { public TestAssemblyRunner(ITestAssembly testAssembly, IEnumerable testCases, IMessageSink diagnosticMessageSink, IMessageSink executionMessageSink, ITestFrameworkExecutionOptions executionOptions) - : base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions) + : base(testAssembly, testCases, diagnosticMessageSink, executionMessageSink, executionOptions, typeof(IClusterFixture<>)) { - var tests = OrderTestCollections(); RunIntegrationTests = executionOptions.GetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests)); + RunUnitTests = executionOptions.GetValue(nameof(ElasticXunitRunOptions.RunUnitTests)); IntegrationTestsMayUseAlreadyRunningNode = executionOptions.GetValue(nameof(ElasticXunitRunOptions .IntegrationTestsMayUseAlreadyRunningNode)); - RunUnitTests = executionOptions.GetValue(nameof(ElasticXunitRunOptions.RunUnitTests)); - TestFilter = executionOptions.GetValue(nameof(ElasticXunitRunOptions.TestFilter)); - ClusterFilter = executionOptions.GetValue(nameof(ElasticXunitRunOptions.ClusterFilter)); - - //bit side effecty, sets up _assemblyFixtureMappings before possibly letting xunit do its regular concurrency thing - _grouped = (from c in tests - let cluster = ClusterFixture(c.Item2.First().TestMethod.TestClass) - let testcase = new GroupedByCluster {Collection = c.Item1, TestCases = c.Item2, Cluster = cluster} - group testcase by testcase.Cluster - into g - orderby g.Count() descending - select g).ToList(); } - public ConcurrentBag Summaries { get; } = new ConcurrentBag(); - - public ConcurrentBag> FailedCollections { get; } = - new ConcurrentBag>(); - - public Dictionary ClusterTotals { get; } = new Dictionary(); - private bool RunIntegrationTests { get; } private bool IntegrationTestsMayUseAlreadyRunningNode { get; } private bool RunUnitTests { get; } - private string TestFilter { get; } - private string ClusterFilter { get; } - protected override Task RunTestCollectionAsync(IMessageBus b, ITestCollection c, - IEnumerable t, CancellationTokenSource s) - { - var aggregator = new ExceptionAggregator(Aggregator); - var fixtureObjects = new Dictionary(); - foreach (var kv in _assemblyFixtureMappings) fixtureObjects.Add(kv.Key, kv.Value); - return new TestCollectionRunner(fixtureObjects, c, t, DiagnosticMessageSink, b, TestCaseOrderer, aggregator, - s) - .RunAsync(); - } - - protected override async Task RunTestCollectionsAsync(IMessageBus messageBus, + protected override async Task RunTestCollectionsAsync(IMessageBus bus, CancellationTokenSource cancellationTokenSource) { - //threading guess - var defaultMaxConcurrency = Environment.ProcessorCount * 4; - if (RunUnitTests && !RunIntegrationTests) - return await UnitTestPipeline(defaultMaxConcurrency, messageBus, cancellationTokenSource) + return await RunAllWithoutPartitionFixture(bus, cancellationTokenSource) .ConfigureAwait(false); - return await IntegrationPipeline(defaultMaxConcurrency, messageBus, cancellationTokenSource) + return await RunAllTests(bus, cancellationTokenSource) .ConfigureAwait(false); } - - private async Task UnitTestPipeline(int defaultMaxConcurrency, IMessageBus messageBus, - CancellationTokenSource ctx) + protected override async Task UseStateAndRun( + IEphemeralCluster cluster, + Func runGroup, + Func failAll + ) { - //make sure all clusters go in started state (won't actually start clusters in unit test mode) - //foreach (var g in this._grouped) g.Key?.Start(); - - var testFilters = CreateTestFilters(TestFilter); - await _grouped.SelectMany(g => g) - .ForEachAsync(defaultMaxConcurrency, - async g => { await RunTestCollections(messageBus, ctx, g, testFilters).ConfigureAwait(false); }) - .ConfigureAwait(false); - //foreach (var g in this._grouped) g.Key?.Dispose(); - - return new RunSummary + using (cluster) { - Total = Summaries.Sum(s => s.Total), - Failed = Summaries.Sum(s => s.Failed), - Skipped = Summaries.Sum(s => s.Skipped) - }; - } + ElasticXunitRunner.CurrentCluster = cluster; + var clusterConfiguration = cluster.ClusterConfiguration; + var timeout = clusterConfiguration?.Timeout ?? TimeSpan.FromMinutes(2); - private async Task IntegrationPipeline(int defaultMaxConcurrency, IMessageBus messageBus, - CancellationTokenSource ctx) - { - var testFilters = CreateTestFilters(TestFilter); - foreach (var group in _grouped) - { - ElasticXunitRunner.CurrentCluster = @group.Key; - if (@group.Key == null) + var started = false; + try { - var testCount = @group.SelectMany(q => q.TestCases).Count(); - Console.WriteLine($" -> Several tests skipped because they have no cluster associated"); - Summaries.Add(new RunSummary {Total = testCount, Skipped = testCount}); - continue; - } - - var type = @group.Key.GetType(); - var clusterName = type.Name.Replace("Cluster", string.Empty) ?? "UNKNOWN"; - if (!MatchesClusterFilter(clusterName)) continue; - - var dop = @group.Key.ClusterConfiguration?.MaxConcurrency ?? defaultMaxConcurrency; - dop = dop <= 0 ? defaultMaxConcurrency : dop; - - var timeout = @group.Key.ClusterConfiguration?.Timeout ?? TimeSpan.FromMinutes(2); - - var skipReasons = @group.SelectMany(g => g.TestCases.Select(t => t.SkipReason)).ToList(); - var allSkipped = skipReasons.All(r => !string.IsNullOrWhiteSpace(r)); - if (allSkipped) - { - Console.WriteLine($" -> All tests from {clusterName} are skipped under the current configuration"); - Summaries.Add(new RunSummary {Total = skipReasons.Count, Skipped = skipReasons.Count}); - continue; - } - - ClusterTotals.Add(clusterName, Stopwatch.StartNew()); + if (!IntegrationTestsMayUseAlreadyRunningNode || !ValidateRunningVersion(cluster)) + cluster.Start(timeout); - bool ValidateRunningVersion() - { - try - { - var t = new ValidateRunningVersion(); - t.Run(@group.Key); - return true; - } - catch (Exception) - { - return false; - } + started = true; } - - using (@group.Key) + catch (Exception e) { - if (!IntegrationTestsMayUseAlreadyRunningNode || !ValidateRunningVersion()) - @group.Key?.Start(timeout); - - await @group.ForEachAsync(dop, - async g => - { - await RunTestCollections(messageBus, ctx, g, testFilters).ConfigureAwait(false); - }) + await failAll(e, $"Further logs might be available at: {cluster.ClusterConfiguration?.FileSystem?.LogsPath}") .ConfigureAwait(false); } - - ClusterTotals[clusterName].Stop(); + if (started) + await runGroup(clusterConfiguration?.MaxConcurrency).ConfigureAwait(false); } - - return new RunSummary - { - Total = Summaries.Sum(s => s.Total), - Failed = Summaries.Sum(s => s.Failed), - Skipped = Summaries.Sum(s => s.Skipped) - }; } - private async Task RunTestCollections(IMessageBus messageBus, CancellationTokenSource ctx, GroupedByCluster g, - string[] testFilters) + private static bool ValidateRunningVersion(IEphemeralCluster cluster) { - var test = g.Collection.DisplayName.Replace("Test collection for", string.Empty).Trim(); - if (!MatchesATestFilter(test, testFilters)) return; - if (testFilters.Length > 0) Console.WriteLine(" -> " + test); - try { - var summary = await RunTestCollectionAsync(messageBus, g.Collection, g.TestCases, ctx) - .ConfigureAwait(false); - var type = g.Cluster?.GetType(); - var clusterName = type?.Name.Replace("Cluster", "") ?? "UNKNOWN"; - if (summary.Failed > 0) - FailedCollections.Add(Tuple.Create(clusterName, test)); - Summaries.Add(summary); + var t = new ValidateRunningVersion(); + t.Run(cluster); + return true; } - catch (TaskCanceledException) + catch (Exception) { - // TODO: What should happen here? + return false; } } - private static string[] CreateTestFilters(string testFilters) => - testFilters?.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray() - ?? new string[] { }; + public static Type GetClusterFixtureType(ITypeInfo testClass) => + GetPartitionFixtureType(testClass, typeof(IClusterFixture<>)); - private static bool MatchesATestFilter(string test, IReadOnlyCollection testFilters) - { - if (testFilters.Count == 0 || string.IsNullOrWhiteSpace(test)) return true; - return testFilters - .Any(filter => test.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0); - } - - private bool MatchesClusterFilter(string cluster) - { - if (string.IsNullOrWhiteSpace(cluster) || string.IsNullOrWhiteSpace(ClusterFilter)) return true; - return ClusterFilter - .Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries) - .Select(c => c.Trim()) - .Any(c => cluster.IndexOf(c, StringComparison.OrdinalIgnoreCase) >= 0); - } - - private IEphemeralCluster ClusterFixture(ITestClass testMethodTestClass) - { - var clusterType = GetClusterForClass(testMethodTestClass.Class); - if (clusterType == null) return null; - if (_assemblyFixtureMappings.TryGetValue(clusterType, out var cluster)) return cluster; - Aggregator.Run(() => - { - var o = Activator.CreateInstance(clusterType); - cluster = o as IEphemeralCluster; - }); - _assemblyFixtureMappings.Add(clusterType, cluster); - return cluster; - } - - public static bool IsAnIntegrationTestClusterType(Type type) => - typeof(XunitClusterBase).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) - || IsSubclassOfRawGeneric(typeof(XunitClusterBase<>), type); - - public static Type GetClusterForClass(ITypeInfo testClass) => - GetClusterFromClassClusterFixture(testClass) ?? GetClusterFromIntegrationAttribute(testClass); - - private static Type GetClusterFromClassClusterFixture(ITypeInfo testClass) => ( - from i in testClass.Interfaces - where i.IsGenericType - from a in i.GetGenericArguments() - select a.ToRuntimeType() - ).FirstOrDefault(IsAnIntegrationTestClusterType); - - private static Type GetClusterFromIntegrationAttribute(ITypeInfo testClass) => - testClass.GetCustomAttributes(typeof(IntegrationTestClusterAttribute)) - .FirstOrDefault()?.GetNamedArgument(nameof(IntegrationTestClusterAttribute.ClusterType)); - - private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) - { - while (toCheck != null && toCheck != typeof(object)) - { - var cur = toCheck.GetTypeInfo().IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; - if (generic == cur) return true; - - toCheck = toCheck.GetTypeInfo().BaseType; - } - - return false; - } - - private class GroupedByCluster - { - public IEphemeralCluster Cluster { get; set; } - public ITestCollection Collection { get; set; } - public List TestCases { get; set; } - } } } diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/TestCollectionRunner.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/TestCollectionRunner.cs deleted file mode 100644 index 94083cb..0000000 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/TestCollectionRunner.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Elastic.Elasticsearch.Xunit.Sdk -{ - internal class TestCollectionRunner : XunitTestCollectionRunner - { - private readonly Dictionary _assemblyFixtureMappings; - private readonly IMessageSink _diagnosticMessageSink; - - public TestCollectionRunner(Dictionary assemblyFixtureMappings, - ITestCollection testCollection, - IEnumerable testCases, - IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - ITestCaseOrderer testCaseOrderer, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - : base(testCollection, testCases, diagnosticMessageSink, messageBus, testCaseOrderer, aggregator, - cancellationTokenSource) - { - _assemblyFixtureMappings = assemblyFixtureMappings; - _diagnosticMessageSink = diagnosticMessageSink; - } - - protected override Task RunTestClassAsync(ITestClass testClass, IReflectionTypeInfo @class, - IEnumerable testCases) - { - // whats this doing exactly?? - var combinedFixtures = new Dictionary(_assemblyFixtureMappings); - foreach (var kvp in CollectionFixtureMappings) - combinedFixtures[kvp.Key] = kvp.Value; - - // We've done everything we need, so hand back off to default Xunit implementation for class runner - return new XunitTestClassRunner(testClass, @class, testCases, _diagnosticMessageSink, MessageBus, - TestCaseOrderer, new ExceptionAggregator(Aggregator), CancellationTokenSource, combinedFixtures) - .RunAsync(); - } - } -} diff --git a/src/Elastic.Elasticsearch.Xunit/Sdk/TestFrameworkExecutor.cs b/src/Elastic.Elasticsearch.Xunit/Sdk/TestFrameworkExecutor.cs deleted file mode 100644 index ab2fcea..0000000 --- a/src/Elastic.Elasticsearch.Xunit/Sdk/TestFrameworkExecutor.cs +++ /dev/null @@ -1,91 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Elastic.Elasticsearch.Managed; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Elastic.Elasticsearch.Xunit.Sdk -{ - internal class TestFrameworkExecutor : XunitTestFrameworkExecutor - { - public TestFrameworkExecutor(AssemblyName a, ISourceInformationProvider sip, IMessageSink d) : base(a, sip, d) - { - } - - public ElasticXunitRunOptions Options { get; set; } - - public override void RunAll(IMessageSink executionMessageSink, ITestFrameworkDiscoveryOptions discoveryOptions, - ITestFrameworkExecutionOptions executionOptions) - { - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.Version), Options.Version); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests), Options.RunIntegrationTests); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.IntegrationTestsMayUseAlreadyRunningNode), - Options.IntegrationTestsMayUseAlreadyRunningNode); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.RunUnitTests), Options.RunUnitTests); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.TestFilter), Options.TestFilter); - discoveryOptions.SetValue(nameof(ElasticXunitRunOptions.ClusterFilter), Options.ClusterFilter); - - executionOptions.SetValue(nameof(ElasticXunitRunOptions.Version), Options.Version); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests), Options.RunIntegrationTests); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.IntegrationTestsMayUseAlreadyRunningNode), - Options.IntegrationTestsMayUseAlreadyRunningNode); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.RunUnitTests), Options.RunUnitTests); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.TestFilter), Options.TestFilter); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.ClusterFilter), Options.ClusterFilter); - - base.RunAll(executionMessageSink, discoveryOptions, executionOptions); - } - - - public override void RunTests(IEnumerable testCases, IMessageSink executionMessageSink, - ITestFrameworkExecutionOptions executionOptions) - { - executionOptions.SetValue(nameof(ElasticXunitRunOptions.Version), Options.Version); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests), Options.RunIntegrationTests); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.IntegrationTestsMayUseAlreadyRunningNode), - Options.IntegrationTestsMayUseAlreadyRunningNode); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.RunUnitTests), Options.RunUnitTests); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.TestFilter), Options.TestFilter); - executionOptions.SetValue(nameof(ElasticXunitRunOptions.ClusterFilter), Options.ClusterFilter); - base.RunTests(testCases, executionMessageSink, executionOptions); - } - - protected override async void RunTestCases(IEnumerable testCases, IMessageSink sink, - ITestFrameworkExecutionOptions options) - { - options.SetValue(nameof(ElasticXunitRunOptions.Version), Options.Version); - options.SetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests), Options.RunIntegrationTests); - options.SetValue(nameof(ElasticXunitRunOptions.IntegrationTestsMayUseAlreadyRunningNode), - Options.IntegrationTestsMayUseAlreadyRunningNode); - options.SetValue(nameof(ElasticXunitRunOptions.RunUnitTests), Options.RunUnitTests); - options.SetValue(nameof(ElasticXunitRunOptions.TestFilter), Options.TestFilter); - options.SetValue(nameof(ElasticXunitRunOptions.ClusterFilter), Options.ClusterFilter); - try - { - using (var runner = - new TestAssemblyRunner(TestAssembly, testCases, DiagnosticMessageSink, sink, options)) - { - Options.OnBeforeTestsRun(); - await runner.RunAsync().ConfigureAwait(false); - Options.OnTestsFinished(runner.ClusterTotals, runner.FailedCollections); - } - } - catch (Exception e) - { - if (e is ElasticsearchCleanExitException || e is AggregateException ae && - ae.Flatten().InnerException is ElasticsearchCleanExitException) - sink.OnMessage(new TestAssemblyCleanupFailure(Enumerable.Empty(), TestAssembly, - new ElasticsearchCleanExitException("Node failed to start", e))); - else - sink.OnMessage(new TestAssemblyCleanupFailure(Enumerable.Empty(), TestAssembly, e)); - throw; - } - } - } -} diff --git a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/ElasticTestCaseDiscoverer.cs b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/ElasticTestCaseDiscoverer.cs index d259000..406795b 100644 --- a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/ElasticTestCaseDiscoverer.cs +++ b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/ElasticTestCaseDiscoverer.cs @@ -22,8 +22,11 @@ protected ElasticTestCaseDiscoverer(IMessageSink diagnosticMessageSink) => DiagnosticMessageSink = diagnosticMessageSink; /// - public IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, - ITestMethod testMethod, IAttributeInfo factAttribute) => + public IEnumerable Discover( + ITestFrameworkDiscoveryOptions discoveryOptions, + ITestMethod testMethod, + IAttributeInfo factAttribute + ) => SkipMethod(discoveryOptions, testMethod, out var skipReason) ? string.IsNullOrEmpty(skipReason) ? new IXunitTestCase[] { } @@ -47,17 +50,6 @@ protected virtual bool SkipMethod(ITestFrameworkDiscoveryOptions discoveryOption return false; } - protected static TValue GetAttribute(ITestMethod testMethod, string propertyName) - where TAttribute : Attribute - { - var classAttributes = testMethod.TestClass.Class.GetCustomAttributes(typeof(TAttribute)) ?? - Enumerable.Empty(); - var methodAttributes = testMethod.Method.GetCustomAttributes(typeof(TAttribute)) ?? - Enumerable.Empty(); - var attribute = classAttributes.Concat(methodAttributes).FirstOrDefault(); - return attribute == null ? default(TValue) : attribute.GetNamedArgument(propertyName); - } - protected static IList GetAttributes(ITestMethod testMethod) where TAttribute : Attribute { @@ -67,16 +59,5 @@ protected static IList GetAttributes(ITestMethod tes Enumerable.Empty(); return classAttributes.Concat(methodAttributes).ToList(); } - - protected static IEnumerable GetAttributes(ITestMethod testMethod, - string propertyName) - where TAttribute : Attribute - { - var classAttributes = testMethod.TestClass.Class.GetCustomAttributes(typeof(TAttribute)); - var methodAttributes = testMethod.Method.GetCustomAttributes(typeof(TAttribute)); - return classAttributes - .Concat(methodAttributes) - .Select(a => a.GetNamedArgument(propertyName)); - } } } diff --git a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestClusterAttribute.cs b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestClusterAttribute.cs deleted file mode 100644 index 1ff468a..0000000 --- a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestClusterAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to Elasticsearch B.V under one or more agreements. -// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. -// See the LICENSE file in the project root for more information - -using System; -using Elastic.Elasticsearch.Xunit.Sdk; - -namespace Elastic.Elasticsearch.Xunit.XunitPlumbing -{ - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class IntegrationTestClusterAttribute : Attribute - { - public IntegrationTestClusterAttribute(Type clusterType) - { - if (!TestAssemblyRunner.IsAnIntegrationTestClusterType(clusterType)) - throw new ArgumentException( - $"Cluster must be subclass of {nameof(XunitClusterBase)} or {nameof(XunitClusterBase)}<>"); - ClusterType = clusterType; - } - - public Type ClusterType { get; } - } -} diff --git a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestDiscoverer.cs b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestDiscoverer.cs index 69cbad4..e347be2 100644 --- a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestDiscoverer.cs +++ b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/IntegrationTestDiscoverer.cs @@ -34,11 +34,11 @@ public abstract class SkipTestAttributeBase : Attribute /// /// An Xunit integration test /// - [XunitTestCaseDiscoverer("Elastic.Elasticsearch.Xunit.XunitPlumbing.IntegrationTestDiscoverer", - "Elastic.Elasticsearch.Xunit")] - public class I : FactAttribute - { - } + [XunitTestCaseDiscoverer( + "Elastic.Elasticsearch.Xunit.XunitPlumbing.IntegrationTestDiscoverer", + "Elastic.Elasticsearch.Xunit" + )] + public class I : FactAttribute { } /// /// A test discoverer used to discover integration tests cases attached @@ -51,19 +51,22 @@ public IntegrationTestDiscoverer(IMessageSink diagnosticMessageSink) : base(diag } /// - protected override bool SkipMethod(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, - out string skipReason) + protected override bool SkipMethod( + ITestFrameworkDiscoveryOptions discoveryOptions, + ITestMethod testMethod, + out string skipReason + ) { skipReason = null; var runIntegrationTests = discoveryOptions.GetValue(nameof(ElasticXunitRunOptions.RunIntegrationTests)); if (!runIntegrationTests) return true; - var cluster = TestAssemblyRunner.GetClusterForClass(testMethod.TestClass.Class); + var cluster = TestAssemblyRunner.GetClusterFixtureType(testMethod.TestClass.Class); if (cluster == null) { skipReason += - $"{testMethod.TestClass.Class.Name} does not define a cluster through IClusterFixture or {nameof(IntegrationTestClusterAttribute)}"; + $"{testMethod.TestClass.Class.Name} does not define a cluster through IClusterFixture"; return true; } @@ -71,7 +74,7 @@ protected override bool SkipMethod(ITestFrameworkDiscoveryOptions discoveryOptio discoveryOptions.GetValue(nameof(ElasticXunitRunOptions.Version)); // Skip if the version we are testing against is attributed to be skipped do not run the test nameof(SkipVersionAttribute.Ranges) - var skipVersionAttribute = Enumerable.FirstOrDefault(GetAttributes(testMethod)); + var skipVersionAttribute = GetAttributes(testMethod).FirstOrDefault(); if (skipVersionAttribute != null) { var skipVersionRanges = diff --git a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/SkippingTestCase.cs b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/SkippingTestCase.cs index 2cd54a6..9cb7d21 100644 --- a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/SkippingTestCase.cs +++ b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/SkippingTestCase.cs @@ -12,9 +12,8 @@ namespace Elastic.Elasticsearch.Xunit.XunitPlumbing public class SkippingTestCase : TestMethodTestCase, IXunitTestCase { /// Used for de-serialization. - public SkippingTestCase() - { - } + // ReSharper disable once UnusedMember.Global + public SkippingTestCase() { } /// /// Initializes a new instance of the class. diff --git a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/UnitTestDiscoverer.cs b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/UnitTestDiscoverer.cs index 1f27ae0..954db69 100644 --- a/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/UnitTestDiscoverer.cs +++ b/src/Elastic.Elasticsearch.Xunit/XunitPlumbing/UnitTestDiscoverer.cs @@ -12,8 +12,10 @@ namespace Elastic.Elasticsearch.Xunit.XunitPlumbing /// /// An Xunit unit test /// - [XunitTestCaseDiscoverer("Elastic.Elasticsearch.Xunit.XunitPlumbing.UnitTestDiscoverer", - "Elastic.Elasticsearch.Xunit")] + [XunitTestCaseDiscoverer( + "Elastic.Elasticsearch.Xunit.XunitPlumbing.UnitTestDiscoverer", + "Elastic.Elasticsearch.Xunit" + )] public class U : FactAttribute { } @@ -29,7 +31,9 @@ public UnitTestDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticM } /// - protected override bool SkipMethod(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, + protected override bool SkipMethod( + ITestFrameworkDiscoveryOptions discoveryOptions, + ITestMethod testMethod, out string skipReason) { skipReason = null;