// Copyright (c) Charlie Poole, Rob Prouse and Contributors. MIT License - see LICENSE.txt using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; using NUnit.Framework.Internal.Commands; namespace NUnit.Framework { /// /// Specifies that a test method should be rerun on failure up to the specified /// maximum number of times. /// [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class RetryAttribute : NUnitAttribute, IRepeatTest { private readonly int _tryCount; /// /// Construct a /// /// The maximum number of times the test should be run if it fails public RetryAttribute(int tryCount) { _tryCount = tryCount; } #region IRepeatTest Members /// /// Wrap a command and return the result. /// /// The command to be wrapped /// The wrapped command public TestCommand Wrap(TestCommand command) { return new RetryCommand(command, _tryCount); } #endregion #region Nested RetryCommand Class /// /// The test command for the /// public class RetryCommand : DelegatingTestCommand { private readonly int _tryCount; /// /// Initializes a new instance of the class. /// /// The inner command. /// The maximum number of repetitions public RetryCommand(TestCommand innerCommand, int tryCount) : base(innerCommand) { _tryCount = tryCount; } /// /// Runs the test, saving a TestResult in the supplied TestExecutionContext. /// /// The context in which the test should run. /// A TestResult public override TestResult Execute(TestExecutionContext context) { int count = _tryCount; while (count-- > 0) { try { context.CurrentResult = innerCommand.Execute(context); } // Commands are supposed to catch exceptions, but some don't // and we want to look at restructuring the API in the future. catch (Exception ex) { if (context.CurrentResult is null) context.CurrentResult = context.CurrentTest.MakeTestResult(); context.CurrentResult.RecordException(ex); } if (context.CurrentResult.ResultState != ResultState.Failure) break; // Clear result for retry if (count > 0) { context.CurrentResult = context.CurrentTest.MakeTestResult(); context.CurrentRepeatCount++; // increment Retry count for next iteration. will only happen if we are guaranteed another iteration } } return context.CurrentResult; } } #endregion } }