Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Repeating pattern values for BitAutoData attribute #5167

Merged
merged 5 commits into from
Jan 9, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
๏ปฟ#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)
{
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);
}
}
}
Loading