// This method is called by the xUnit test framework class es to run the test case. We will do the // loop here, forwarding on to the implementation in XunitTestCase to do the heavy lifting. We will // continue to re-run the test until the aggregator has an error (meaning that some internal error // condition happened), or the test runs without failure, or we've hit the maximum number of tries. /// <inheritdoc /> public override async Task <RunSummary> RunAsync( IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) { int runCount = 0; while (true) { // This is really the only tricky bit: we need to capture and delay messages (since those will // contain run status) until we know we've decided to accept the final result; var delayedMessageBus = new DelayedMessageBus(messageBus); var summary = await base.RunAsync( diagnosticMessageSink, delayedMessageBus, constructorArguments, aggregator, cancellationTokenSource); if (aggregator.HasExceptions || summary.Failed == 0 || ++runCount >= _maxRetries) { delayedMessageBus.Dispose(); // Sends all the delayed messages return(summary); } diagnosticMessageSink.OnMessage( new DiagnosticMessage("Execution of '{0}' failed (attempt #{1}), retrying...", DisplayName, runCount)); GC.Collect(); await Task.Delay(100); } }
// This method is called by the xUnit test framework class es to run the test case. We will do the // loop here, forwarding on to the implementation in XunitTestCase to do the heavy lifting. We will // continue to re-run the test until the aggregator has an error (meaning that some internal error // condition happened), or the test runs without failure, or we've hit the maximum number of tries. /// <inheritdoc /> public override async Task <RunSummary> RunAsync( IMessageSink diagnosticMessageSink, IMessageBus messageBus, object[] constructorArguments, ExceptionAggregator aggregator, CancellationTokenSource cancellationTokenSource) { int runCount = 0; while (true) { // This is really the only tricky bit: we need to capture and delay messages (since those will // contain run status) until we know we've decided to accept the final result; var delayedMessageBus = new DelayedMessageBus(messageBus); try { var summary = await base.RunAsync( diagnosticMessageSink, delayedMessageBus, constructorArguments, aggregator, cancellationTokenSource).WithTimeout(30_000); if (aggregator.HasExceptions || summary.Failed == 0 || ++runCount >= _maxRetries) { delayedMessageBus.Dispose(); // Sends all the delayed messages return(summary); } } catch (TimeoutException ex) { if (++runCount >= _maxRetries) { var runSummary = new RunSummary { Total = 1, Failed = 1 }; aggregator.Add(ex); if (!delayedMessageBus.QueueMessage(new TestCleanupFailure( new XunitTest(this, DisplayName), aggregator.ToException() !))) { cancellationTokenSource.Cancel(); } delayedMessageBus.Dispose(); // Sends all the delayed messages return(runSummary); } } diagnosticMessageSink.OnMessage( new DiagnosticMessage("Execution of '{0}' failed (attempt #{1}), retrying...", DisplayName, runCount)); GC.Collect(); await Task.Delay(100); } }