#nullable enable using System.Reflection; namespace Bit.Test.Common.AutoFixture.Attributes; /// <summary> /// This attribute helps to generate all possible combinations of the provided pattern values for a given number of parameters. /// <remarks> /// <para> /// The repeating pattern values should be provided as an array for each parameter. Currently supports up to 3 parameters. /// </para> /// <para> /// The attribute is a variation of the <see cref="BitAutoDataAttribute"/> attribute and can be used in the same way, except that all fixed value parameters needs to be provided as an array. /// </para> /// <para> /// Note: Use it with caution. While this attribute is useful for handling repeating parameters, having too many parameters should be avoided as it is considered a code smell in most of the cases. /// If your test requires more than 2 repeating parameters, or the test have too many conditions that change the behavior of the test, consider refactoring the test by splitting it into multiple smaller ones. /// </para> /// </remarks> /// <example> /// 1st example: /// <code> /// [RepeatingPatternBitAutoData([false], [1,2,3])] /// public void TestMethod(bool first, int second, SomeOtherData third, ...) /// </code> /// Would generate the following test cases: /// <list type="bullet"> /// <item>false, 1</item> /// <item>false, 2</item> /// <item>false, 3</item> /// </list> /// 2nd example: /// <code> /// [RepeatingPatternBitAutoData([false, true], [false, true], [false, true])] /// public void TestMethod(bool first, bool second, bool third) /// </code> /// Would generate the following test cases: /// <list type="bullet"> /// <item>false, false, false</item> /// <item>false, false, true</item> /// <item>false, true, false</item> /// <item>false, true, true</item> /// <item>true, false, false</item> /// <item>true, false, true</item> /// <item>true, true, false</item> /// <item>true, true, true</item> /// </list> /// </example> /// </summary> public class RepeatingPatternBitAutoDataAttribute : BitAutoDataAttribute { private readonly List<object?[]> _repeatingDataList; public RepeatingPatternBitAutoDataAttribute(object?[] first) { _repeatingDataList = AllValues([first]); } public RepeatingPatternBitAutoDataAttribute(object?[] first, object?[] second) { _repeatingDataList = AllValues([first, second]); } public RepeatingPatternBitAutoDataAttribute(object?[] first, object?[] second, object?[] third) { _repeatingDataList = AllValues([first, second, third]); } public override IEnumerable<object?[]> GetData(MethodInfo testMethod) { if (_repeatingDataList.Count == 0) { yield return base.GetData(testMethod).First(); } foreach (var repeatingData in _repeatingDataList) { var bitData = base.GetData(testMethod).First(); for (var i = 0; i < repeatingData.Length; i++) { bitData[i] = repeatingData[i]; } yield return bitData; } } private static List<object?[]> AllValues(object?[][] parameterToPatternValues) { var result = new List<object?[]>(); GenerateCombinations(parameterToPatternValues, new object[parameterToPatternValues.Length], 0, result); return result; } private static void GenerateCombinations(object?[][] parameterToPatternValues, object?[] current, int index, List<object?[]> result) { if (index == current.Length) { result.Add((object[])current.Clone()); return; } var patternValues = parameterToPatternValues[index]; foreach (var value in patternValues) { current[index] = value; GenerateCombinations(parameterToPatternValues, current, index + 1, result); } } }