blob: 087400ffab0f85dd487dc5c5972c4252588c8611 [file] [log] [blame]
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using NUnit.Framework.Internal.Commands;
using System;
#nullable enable
namespace Lucene.Net.Util
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public abstract partial class LuceneTestCase
{
/// <summary>
/// Specifies that a test should be run multiple times. If any repetition fails,
/// the remaining ones are not run and a failure is reported.
/// </summary>
/// <remarks>
/// This attribute differs from <see cref="NUnit.Framework.RepeatAttribute"/> in that
/// it is aware of <see cref="RandomizedContext"/> and will reset the test seed on each
/// iteration. As a result, if there is a test failure, the seed that is reported will
/// duplicate the exact test conditions on the first try.
/// </remarks>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RepeatAttribute : System.Attribute, IRepeatTest
{
private readonly int repeatCount;
/// <summary>
/// Initializes a new instance of <see cref="RepeatAttribute"/>.
/// </summary>
/// <param name="repeatCount">The number of times to run the test.</param>
public RepeatAttribute(int repeatCount)
{
this.repeatCount = repeatCount;
}
/// <summary>
/// Wrap a command and return the result.
/// </summary>
/// <param name="command">The command to be wrapped.</param>
/// <returns>The wrapped command.</returns>
public TestCommand Wrap(TestCommand command)
{
return new RepeatTestCommand(command, repeatCount);
}
/// <summary>
/// The test command for the <see cref="RepeatAttribute"/>.
/// </summary>
private class RepeatTestCommand : BeforeAndAfterTestCommand
{
private readonly int repeatCount;
/// <summary>
/// Initializes a new instance of the <see cref="RepeatTestCommand"/> class.
/// </summary>
/// <param name="innerCommand">The inner command.</param>
/// <param name="repeatCount">The number of repetitions</param>
public RepeatTestCommand(TestCommand innerCommand, int repeatCount)
: base(innerCommand)
{
this.repeatCount = repeatCount;
}
/// <summary>
/// Runs the test, saving a <see cref="TestResult"/> in the supplied <see cref="TestExecutionContext"/>.
/// </summary>
/// <param name="context">The context in which the test should run.</param>
/// <returns>A <see cref="TestResult"/>.</returns>
public override TestResult Execute(TestExecutionContext context)
{
if (context.CurrentTest.TypeInfo is null || !context.CurrentTest.TypeInfo.Type.IsSubclassOf(typeof(LuceneTestCase)))
{
return SetResultErrorNonLuceneTestCaseSubclass(context);
}
RandomizedContext? randomizedContext = context.CurrentTest.GetRandomizedContext();
if (randomizedContext is null)
{
return SetResultErrorNonLuceneTestCaseSubclass(context);
}
var random = new J2N.Randomizer(randomizedContext.RandomSeed);
for (int i = 0; i < repeatCount; i++)
{
// Regenerate the test seed for this iteration
randomizedContext.ResetSeed(testSeed: random.NextInt64());
try
{
// Execute the SetUp, Test, and TearDown with the seed
context.CurrentResult = innerCommand.Execute(context);
}
catch (Exception ex)
{
if (context.CurrentResult is null) context.CurrentResult = context.CurrentTest.MakeTestResult();
context.CurrentResult.RecordException(ex);
}
if (context.CurrentResult.ResultState != ResultState.Success)
{
if (context.CurrentResult.ResultState == ResultState.Failure || context.CurrentResult.ResultState == ResultState.Error)
{
string message = $"Repeat failed on iteration '{i}'.{Environment.NewLine}{Environment.NewLine}{context.CurrentResult.Message}";
context.CurrentResult.SetResult(context.CurrentResult.ResultState, message, context.CurrentResult.StackTrace);
}
break; // Only repeat for successuful test runs
}
context.CurrentRepeatCount++;
}
return context.CurrentResult;
}
private static TestResult SetResultErrorNonLuceneTestCaseSubclass(TestExecutionContext context)
{
if (context.CurrentResult is null) context.CurrentResult = context.CurrentTest.MakeTestResult();
// We only want this attribute to be used on subclasses of LuceneTestCase. This is an error.
context.CurrentResult.SetResult(ResultState.Error,
$"{typeof(RepeatAttribute).FullName} may only be used on a test in a subclass of {nameof(LuceneTestCase)}.");
return context.CurrentResult;
}
}
}
}
}