Skip to content

Commit

Permalink
update dynamic skippable code (from SE.Redis tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
mgravell committed Aug 28, 2019
1 parent 7ebd9db commit 23ad634
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 71 deletions.
2 changes: 1 addition & 1 deletion Dapper.Tests.Contrib/TestSuites.cs
Expand Up @@ -68,7 +68,7 @@ public class MySqlServerTestSuite : TestSuite

public override IDbConnection GetConnection()
{
if (_skip) throw new SkipTestException("Skipping MySQL Tests - no server.");
if (_skip) Skip.Inconclusive("Skipping MySQL Tests - no server.");
return new MySqlConnection(ConnectionString);
}

Expand Down
30 changes: 29 additions & 1 deletion Dapper.Tests/Helpers/Attributes.cs
@@ -1,8 +1,36 @@
using System;
using Xunit;
using Xunit.Sdk;

namespace Dapper.Tests
{
/// <summary>
/// <para>Override for <see cref="Xunit.FactAttribute"/> that truncates our DisplayName down.</para>
/// <para>
/// Attribute that is applied to a method to indicate that it is a fact that should
/// be run by the test runner. It can also be extended to support a customized definition
/// of a test method.
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
[XunitTestCaseDiscoverer("Dapper.Tests.FactDiscoverer", "Dapper.Tests")]
public class FactAttribute : Xunit.FactAttribute
{
}

/// <summary>
/// <para>Override for <see cref="Xunit.TheoryAttribute"/> that truncates our DisplayName down.</para>
/// <para>
/// Marks a test method as being a data theory. Data theories are tests which are
/// fed various bits of data from a data source, mapping to parameters on the test
/// method. If the data source contains multiple rows, then the test method is executed
/// multiple times (once with each data row). Data is provided by attributes which
/// derive from Xunit.Sdk.DataAttribute (notably, Xunit.InlineDataAttribute and Xunit.MemberDataAttribute).
/// </para>
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
[XunitTestCaseDiscoverer("Dapper.Tests.TheoryDiscoverer", "Dapper.Tests")]
public class TheoryAttribute : Xunit.TheoryAttribute { }

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class FactLongRunningAttribute : FactAttribute
{
Expand Down
136 changes: 96 additions & 40 deletions Dapper.Tests/Helpers/XunitSkippable.cs
Expand Up @@ -9,38 +9,58 @@

namespace Dapper.Tests
{
public static class Skip
{
public static void Inconclusive(string reason = "inconclusive")
=> throw new SkipTestException(reason);

public static void If<T>(object obj, string reason = null)
where T : class
{
if (obj is T) Skip.Inconclusive(reason ?? $"not valid for {typeof(T).FullName}");
}
}
public class SkipTestException : Exception
{
public SkipTestException(string reason) : base(reason)
{
}
}

// Most of the below is a direct copy & port from the wonderful examples by Brad Wilson at
// https://github.com/xunit/samples.xunit/tree/master/DynamicSkipExample
public class SkippableFactDiscoverer : IXunitTestCaseDiscoverer
public class FactDiscoverer : Xunit.Sdk.FactDiscoverer
{
private readonly IMessageSink _diagnosticMessageSink;
public FactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { }

public SkippableFactDiscoverer(IMessageSink diagnosticMessageSink)
{
_diagnosticMessageSink = diagnosticMessageSink;
}
protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
=> new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod);
}

public IEnumerable<IXunitTestCase> Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute)
{
yield return new SkippableFactTestCase(_diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod);
}
public class TheoryDiscoverer : Xunit.Sdk.TheoryDiscoverer
{
public TheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { }

protected override IEnumerable<IXunitTestCase> CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow)
=> new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, dataRow) };

protected override IEnumerable<IXunitTestCase> CreateTestCasesForSkip(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, string skipReason)
=> new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) };

protected override IEnumerable<IXunitTestCase> CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute)
=> new[] { new SkippableTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) };

protected override IEnumerable<IXunitTestCase> CreateTestCasesForSkippedDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow, string skipReason)
=> new[] { new NamedSkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, skipReason, dataRow) };
}

public class SkippableFactTestCase : XunitTestCase
public class SkippableTestCase : XunitTestCase
{
protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>
base.GetDisplayName(factAttribute, displayName).StripName();

[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public SkippableFactTestCase()
{
}
public SkippableTestCase() { }

public SkippableFactTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, object[] testMethodArguments = null)
public SkippableTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, object[] testMethodArguments = null)
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments)
{
}
Expand All @@ -52,36 +72,56 @@ public SkippableFactTestCase(IMessageSink diagnosticMessageSink, TestMethodDispl
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource)
{
var skipMessageBus = new SkippableFactMessageBus(messageBus);
var result = await base.RunAsync(
diagnosticMessageSink,
skipMessageBus,
constructorArguments,
aggregator,
cancellationTokenSource).ConfigureAwait(false);
if (skipMessageBus.DynamicallySkippedTestCount > 0)
{
result.Failed -= skipMessageBus.DynamicallySkippedTestCount;
result.Skipped += skipMessageBus.DynamicallySkippedTestCount;
}

return result;
var skipMessageBus = new SkippableMessageBus(messageBus);
var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false);
return result.Update(skipMessageBus);
}
}

public class SkippableFactMessageBus : IMessageBus
public class SkippableTheoryTestCase : XunitTheoryTestCase
{
private readonly IMessageBus _innerBus;
public SkippableFactMessageBus(IMessageBus innerBus)
protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>
base.GetDisplayName(factAttribute, displayName).StripName();

[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public SkippableTheoryTestCase() { }

public SkippableTheoryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod)
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) { }

public override async Task<RunSummary> RunAsync(
IMessageSink diagnosticMessageSink,
IMessageBus messageBus,
object[] constructorArguments,
ExceptionAggregator aggregator,
CancellationTokenSource cancellationTokenSource)
{
_innerBus = innerBus;
var skipMessageBus = new SkippableMessageBus(messageBus);
var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false);
return result.Update(skipMessageBus);
}
}

public class NamedSkippedDataRowTestCase : XunitSkippedDataRowTestCase
{
protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) =>
base.GetDisplayName(factAttribute, displayName).StripName();

[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public NamedSkippedDataRowTestCase() { }

public NamedSkippedDataRowTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, string skipReason, object[] testMethodArguments = null)
: base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, skipReason, testMethodArguments) { }
}

public class SkippableMessageBus : IMessageBus
{
private readonly IMessageBus InnerBus;
public SkippableMessageBus(IMessageBus innerBus) => InnerBus = innerBus;

public int DynamicallySkippedTestCount { get; private set; }

public void Dispose()
{
}
public void Dispose() { }

public bool QueueMessage(IMessageSinkMessage message)
{
Expand All @@ -91,10 +131,26 @@ public bool QueueMessage(IMessageSinkMessage message)
if (exceptionType == typeof(SkipTestException).FullName)
{
DynamicallySkippedTestCount++;
return _innerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages.FirstOrDefault()));
return InnerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages.FirstOrDefault()));
}
}
return _innerBus.QueueMessage(message);
return InnerBus.QueueMessage(message);
}
}

internal static class XUnitExtensions
{
internal static string StripName(this string name) =>
name.Replace("Dapper.Tests.", "");

public static RunSummary Update(this RunSummary summary, SkippableMessageBus bus)
{
if (bus.DynamicallySkippedTestCount > 0)
{
summary.Failed -= bus.DynamicallySkippedTestCount;
summary.Skipped += bus.DynamicallySkippedTestCount;
}
return summary;
}
}
}
18 changes: 3 additions & 15 deletions Dapper.Tests/ParameterTests.cs
Expand Up @@ -718,11 +718,7 @@ private class HazSqlGeo
[Fact]
public void DBGeography_SO24405645_SO24402424()
{
try
{
SkipIfMsDataClient();
}
catch (SkipTestException) { return; } // just while we figure out why that isn't skipping
SkipIfMsDataClient();

EntityFramework.Handlers.Register();

Expand All @@ -745,11 +741,7 @@ public void DBGeography_SO24405645_SO24402424()
[Fact]
public void SqlGeography_SO25538154()
{
try
{
SkipIfMsDataClient();
}
catch (SkipTestException) { return; } // just while we figure out why that isn't skipping
SkipIfMsDataClient();

SqlMapper.ResetTypeHandlers();
connection.Execute("create table #SqlGeo (id int, geo geography, geometry geometry)");
Expand Down Expand Up @@ -789,11 +781,7 @@ public void NullableSqlGeometry()
[Fact]
public void SqlHierarchyId_SO18888911()
{
try
{
SkipIfMsDataClient();
}
catch (SkipTestException) { return; } // just while we figure out why that isn't skipping
SkipIfMsDataClient();

SqlMapper.ResetTypeHandlers();
var row = connection.Query<HazSqlHierarchy>("select 3 as [Id], hierarchyid::Parse('/1/2/3/') as [Path]").Single();
Expand Down
12 changes: 2 additions & 10 deletions Dapper.Tests/Providers/EntityFrameworkTests.cs
Expand Up @@ -22,11 +22,7 @@ public EntityFrameworkTests()
[Fact]
public void Issue570_DbGeo_HasValues()
{
try
{
SkipIfMsDataClient();
}
catch (SkipTestException) { return; } // just while we figure out why that isn't skipping
SkipIfMsDataClient();

EntityFramework.Handlers.Register();
const string redmond = "POINT (-122.1215 47.6740)";
Expand All @@ -43,11 +39,7 @@ public void Issue570_DbGeo_HasValues()
[Fact]
public void Issue22_ExecuteScalar_EntityFramework()
{
try
{
SkipIfMsDataClient();
}
catch (SkipTestException) { return; } // just while we figure out why that isn't skipping
SkipIfMsDataClient();

var geo = DbGeography.LineFromText("LINESTRING(-122.360 47.656, -122.343 47.656 )", 4326);
var geo2 = connection.ExecuteScalar<DbGeography>("select @geo", new { geo });
Expand Down
5 changes: 1 addition & 4 deletions Dapper.Tests/TestBase.cs
Expand Up @@ -82,10 +82,7 @@ public sealed class MicrosoftSqlClientProvider : SqlServerDatabaseProvider
public abstract class TestBase<TProvider> : IDisposable where TProvider : DatabaseProvider
{
protected void SkipIfMsDataClient()
{
if (connection is Microsoft.Data.SqlClient.SqlConnection)
throw new SkipTestException("Not supported on Microsoft.Data.SqlClient");
}
=> Skip.If<Microsoft.Data.SqlClient.SqlConnection>(connection);

protected DbConnection GetOpenConnection() => Provider.GetOpenConnection();
protected DbConnection GetClosedConnection() => Provider.GetClosedConnection();
Expand Down

0 comments on commit 23ad634

Please sign in to comment.