Skip to content

Commit

Permalink
Add capability to failfast when a test takes longer than expected
Browse files Browse the repository at this point in the history
  • Loading branch information
mconnew committed Aug 15, 2022
1 parent aeb57b3 commit 140ed27
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;


namespace Infrastructure.Common
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly, AllowMultiple = false)]
public class FailFastAfterAttribute : Attribute
{
public FailFastAfterAttribute(string durationString)
{
FailTime = TimeSpan.Parse(durationString);
}

public TimeSpan FailTime { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ internal class WcfTestCase : XunitTestCase, IXunitTestCase
{
private string _skippedReason;
private bool _isTheory;
private readonly TimeSpan _failFastDuration;
private readonly IMessageSink _diagnosticMessageSink;

static TestEventListener s_testListener = new TestEventListener(new List<string>() { "Microsoft-Windows-Application Server-Applications" }, EventLevel.Verbose);
Expand All @@ -31,13 +32,15 @@ public WcfTestCase()

internal WcfTestCase(XunitTestCase testCase,
TestMethodDisplay defaultMethodDisplay,
TimeSpan failFastDuration,
string skippedReason = null,
bool isTheory = false,
IMessageSink diagnosticMessageSink = null)
: base(diagnosticMessageSink, defaultMethodDisplay, TestMethodDisplayOptions.None, testCase.TestMethod, testCase.TestMethodArguments)
{
_skippedReason = skippedReason;
_isTheory = isTheory;
_failFastDuration = failFastDuration;
_diagnosticMessageSink = diagnosticMessageSink;
}

Expand All @@ -47,9 +50,21 @@ public override async Task<RunSummary> RunAsync(
{
ConcurrentQueue<EventWrittenEventArgs> events = new ConcurrentQueue<EventWrittenEventArgs>();
s_testListener.EventWritten = events.Enqueue;
Timer timer = null;
if (_failFastDuration != System.Threading.Timeout.InfiniteTimeSpan)
{
timer = new Timer((s) => Environment.FailFast("Test timed out"),
null,
(int)_failFastDuration.TotalMilliseconds,
System.Threading.Timeout.Infinite);
}

RunSummary runsummary = await (_isTheory ? new XunitTheoryTestCaseRunner(this, DisplayName, _skippedReason, constructorArguments, _diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource).RunAsync()
: new XunitTestCaseRunner(this, DisplayName, _skippedReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource).RunAsync());
RunSummary runsummary;
using (timer)
{
runsummary = await (_isTheory ? new XunitTheoryTestCaseRunner(this, DisplayName, _skippedReason, constructorArguments, _diagnosticMessageSink, messageBus, aggregator, cancellationTokenSource).RunAsync()
: new XunitTestCaseRunner(this, DisplayName, _skippedReason, constructorArguments, TestMethodArguments, messageBus, aggregator, cancellationTokenSource).RunAsync());
}

s_testListener.EventWritten = null;
if (runsummary.Failed > 0 && events.Count > 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using Xunit.Abstractions;
using Xunit.Sdk;

Expand Down Expand Up @@ -47,13 +48,14 @@ internal static IEnumerable<IXunitTestCase> Discover(
string skippedReason = string.Format("Active issue(s): {0}", string.Join(", ", issueSkipList));
return testCases.Select(tc => new WcfTestCase((XunitTestCase)tc,
discoveryOptions.MethodDisplayOrDefault(),
Timeout.InfiniteTimeSpan,
skippedReason,
isTheory,
diagnosticMessageSink));
}
}

// Finally evaluate all the [Condition] attributes. These will execute code
// Evaluate all the [Condition] attributes. These will execute code
// that determines whether this test should be run or skipped.
ConditionAttribute[] conditions = testMethodInfo.GetCustomAttributes<ConditionAttribute>().ToArray();
if (conditions.Length > 0)
Expand All @@ -75,17 +77,39 @@ internal static IEnumerable<IXunitTestCase> Discover(
string skippedReason = string.Format("Condition(s) not met: {0}", string.Join(", ", skipReasons));
return testCases.Select(tc => new WcfTestCase((XunitTestCase)tc,
discoveryOptions.MethodDisplayOrDefault(),
Timeout.InfiniteTimeSpan,
skippedReason,
isTheory,
diagnosticMessageSink));
}
}

// Look for FailFastAfterAttribute applied to the test method
FailFastAfterAttribute failFastAttribute = testMethodInfo.GetCustomAttribute<FailFastAfterAttribute>();
if (failFastAttribute == null)
{
// If it's not found on the method, check the class
failFastAttribute = testMethodInfo.DeclaringType.GetCustomAttribute<FailFastAfterAttribute>();
}

if (failFastAttribute == null)
{
// If it's not found on the method or class, check the assembly
failFastAttribute = testMethodInfo.DeclaringType.Assembly.GetCustomAttribute<FailFastAfterAttribute>();
}

TimeSpan failFastDuration = Timeout.InfiniteTimeSpan;
if (failFastAttribute != null)
{
failFastDuration = failFastAttribute.FailTime;
}

// If we get this far, we have decided to run the test.
// Still wrap it in a WcfTestCase with a null skip message
// so that other WcfTestCase customizations are used.
return testCases.Select(tc => new WcfTestCase(testCase: (XunitTestCase)tc,
defaultMethodDisplay: discoveryOptions.MethodDisplayOrDefault(),
failFastDuration: failFastDuration,
skippedReason: null,
isTheory: isTheory,
diagnosticMessageSink: diagnosticMessageSink));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using Infrastructure.Common;
using Xunit;

[assembly: FailFastAfter("00:01:00")]

public static partial class ChannelBaseTests_4_0_0
{
[WcfFact]
Expand Down

0 comments on commit 140ed27

Please sign in to comment.