File: xunit\WcfTestRunner.cs
Web Access
Project: ..\..\..\src\System.Private.ServiceModel\tests\Common\Infrastructure\Infrastructure.Common.csproj (Infrastructure.Common)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Xunit.Abstractions;
using Xunit.Sdk;
 
namespace Infrastructure.Common
{
    internal sealed class WcfTestRunner : XunitTestRunner
    {
        private readonly IMessageSink _diagnosticMessageSink;
        private readonly int _maxAttempts;
        private readonly int _retryDelayMilliseconds;
 
        internal WcfTestRunner(ITest test, IMessageBus messageBus, Type testClass, object[] constructorArguments,
            MethodInfo testMethod, object[] testMethodArguments, string skipReason,
            IReadOnlyList<BeforeAfterTestAttribute> beforeAfterAttributes, ExceptionAggregator aggregator,
            CancellationTokenSource cancellationTokenSource, IMessageSink diagnosticMessageSink,
            int maxAttempts, int retryDelayMilliseconds)
            : base(test, messageBus, testClass, constructorArguments, testMethod, testMethodArguments, skipReason,
                beforeAfterAttributes, aggregator, cancellationTokenSource)
        {
            _diagnosticMessageSink = diagnosticMessageSink;
            _maxAttempts = maxAttempts;
            _retryDelayMilliseconds = retryDelayMilliseconds;
        }
 
        protected override async Task<decimal> InvokeTestMethodAsync(ExceptionAggregator aggregator)
        {
            if (_maxAttempts == 1)
            {
                return await base.InvokeTestMethodAsync(aggregator);
            }
 
            Stopwatch stopwatch = Stopwatch.StartNew();
            ExceptionAggregator attemptAggregator = new ExceptionAggregator(aggregator);
 
            for (int attempt = 1; ; attempt++)
            {
                // Each invocation creates and disposes its own test class. Only the final
                // attempt's exceptions reach xUnit's result-reporting layer.
                await base.InvokeTestMethodAsync(attemptAggregator);
 
                if (!attemptAggregator.HasExceptions || attempt >= _maxAttempts || CancellationTokenSource.IsCancellationRequested)
                {
                    break;
                }
 
                _diagnosticMessageSink.OnMessage(new DiagnosticMessage(
                    "Test '{0}' failed on attempt {1} of {2}. Retrying in {3} ms.{4}{5}",
                    DisplayName, attempt, _maxAttempts, _retryDelayMilliseconds,
                    Environment.NewLine, attemptAggregator.ToException()));
 
                if (_retryDelayMilliseconds > 0)
                {
                    try
                    {
                        await Task.Delay(_retryDelayMilliseconds, CancellationTokenSource.Token);
                    }
                    catch (OperationCanceledException) when (CancellationTokenSource.IsCancellationRequested)
                    {
                        break;
                    }
                }
 
                if (CancellationTokenSource.IsCancellationRequested)
                {
                    break;
                }
 
                attemptAggregator = new ExceptionAggregator(aggregator);
            }
 
            aggregator.Aggregate(attemptAggregator);
            return (decimal)stopwatch.Elapsed.TotalSeconds;
        }
    }
}