<PackageReference Include="NUnit" Version="3.0.0-alpha" />

PairwiseStrategy

PairwiseStrategy creates test cases by combining the parameter data so that all possible pairs of data items are used.
using NUnit.Framework.Interfaces; using System.Collections; using System.Collections.Generic; namespace NUnit.Framework.Internal.Builders { public class PairwiseStrategy : ICombiningStrategy { internal class FleaRand { private const int FleaRandSize = 256; private uint b; private uint c; private uint d; private uint z; private uint[] m = new uint[256]; private uint[] r = new uint[256]; private uint q; public FleaRand(uint seed) { b = seed; c = seed; d = seed; z = seed; for (int i = 0; i < m.Length; i++) { m[i] = seed; } for (int j = 0; j < 10; j++) { Batch(); } q = 0; } public uint Next() { if (q == 0) { Batch(); q = (uint)(r.Length - 1); } else q--; return r[q]; } private void Batch() { uint num = b; uint num2 = c + ++z; uint num3 = d; for (int i = 0; i < r.Length; i++) { uint num4 = m[(long)num % (long)m.Length]; m[(long)num % (long)m.Length] = num3; num3 = (num2 << 19) + (num2 >> 13) + num; num2 = (num ^ m[i]); num = num4 + num3; r[i] = num2; } b = num; c = num2; d = num3; } } internal class FeatureInfo { public readonly int Dimension; public readonly int Feature; public FeatureInfo(int dimension, int feature) { Dimension = dimension; Feature = feature; } } internal class Tuple { private readonly List<FeatureInfo> features = new List<FeatureInfo>(); public int Count => features.Count; public FeatureInfo this[int index] { get { return features[index]; } } public void Add(FeatureInfo feature) { features.Add(feature); } } internal class TupleCollection { private readonly List<Tuple> tuples = new List<Tuple>(); public int Count => tuples.Count; public Tuple this[int index] { get { return tuples[index]; } } public void Add(Tuple tuple) { tuples.Add(tuple); } public void RemoveAt(int index) { tuples.RemoveAt(index); } } internal class TestCase { public readonly int[] Features; public TestCase(int numberOfDimensions) { Features = new int[numberOfDimensions]; } public bool IsTupleCovered(Tuple tuple) { for (int i = 0; i < tuple.Count; i++) { if (Features[tuple[i].Dimension] != tuple[i].Feature) return false; } return true; } } internal class TestCaseCollection : IEnumerable { private readonly List<TestCase> testCases = new List<TestCase>(); public void Add(TestCase testCase) { testCases.Add(testCase); } public IEnumerator GetEnumerator() { return testCases.GetEnumerator(); } public bool IsTupleCovered(Tuple tuple) { foreach (TestCase testCase in testCases) { if (testCase.IsTupleCovered(tuple)) return true; } return false; } } internal class PairwiseTestCaseGenerator { private const int MaxTupleLength = 2; private readonly FleaRand random = new FleaRand(0); private readonly int[] dimensions; private readonly TupleCollection[][] uncoveredTuples; private readonly int[][] currentTupleLength; private readonly TestCaseCollection testCases = new TestCaseCollection(); public PairwiseTestCaseGenerator(int[] dimensions) { this.dimensions = dimensions; uncoveredTuples = new TupleCollection[this.dimensions.Length][]; for (int i = 0; i < uncoveredTuples.Length; i++) { uncoveredTuples[i] = new TupleCollection[this.dimensions[i]]; for (int j = 0; j < this.dimensions[i]; j++) { uncoveredTuples[i][j] = new TupleCollection(); } } currentTupleLength = new int[this.dimensions.Length][]; for (int k = 0; k < this.dimensions.Length; k++) { currentTupleLength[k] = new int[this.dimensions[k]]; } } public IEnumerable GetTestCases() { CreateTestCases(); return testCases; } private void CreateTestCases() { while (true) { ExtendTupleSet(); Tuple tuple = FindTupleToCover(); if (tuple == null) break; TestCase testCase = FindGoodTestCase(tuple); RemoveTuplesCoveredBy(testCase); testCases.Add(testCase); } } private void ExtendTupleSet() { for (int i = 0; i < dimensions.Length; i++) { for (int j = 0; j < dimensions[i]; j++) { ExtendTupleSet(i, j); } } } private void ExtendTupleSet(int dimension, int feature) { if (uncoveredTuples[dimension][feature].Count <= 0 && currentTupleLength[dimension][feature] != 2) { currentTupleLength[dimension][feature]++; int num = currentTupleLength[dimension][feature]; if (num == 1) { Tuple tuple = new Tuple(); tuple.Add(new FeatureInfo(dimension, feature)); if (!testCases.IsTupleCovered(tuple)) uncoveredTuples[dimension][feature].Add(tuple); } else { for (int i = 0; i < dimensions.Length; i++) { for (int j = 0; j < dimensions[i]; j++) { Tuple tuple2 = new Tuple(); tuple2.Add(new FeatureInfo(i, j)); if (tuple2[0].Dimension != dimension) { tuple2.Add(new FeatureInfo(dimension, feature)); if (!testCases.IsTupleCovered(tuple2)) uncoveredTuples[dimension][feature].Add(tuple2); } } } } } } private Tuple FindTupleToCover() { int num = 2; int num2 = 0; Tuple result = null; for (int i = 0; i < dimensions.Length; i++) { for (int j = 0; j < dimensions[i]; j++) { if (currentTupleLength[i][j] < num) { num = currentTupleLength[i][j]; num2 = uncoveredTuples[i][j].Count; result = uncoveredTuples[i][j][0]; } else if (currentTupleLength[i][j] == num && uncoveredTuples[i][j].Count > num2) { num2 = uncoveredTuples[i][j].Count; result = uncoveredTuples[i][j][0]; } } } return result; } private TestCase FindGoodTestCase(Tuple tuple) { TestCase result = null; int num = -1; for (int i = 0; i < 5; i++) { TestCase testCase = new TestCase(dimensions.Length); int num2 = CreateTestCase(tuple, testCase); if (num2 > num) { result = testCase; num = num2; } } return result; } private int CreateTestCase(Tuple tuple, TestCase test) { for (int i = 0; i < test.Features.Length; i++) { test.Features[i] = (int)((long)random.Next() % (long)dimensions[i]); } for (int j = 0; j < tuple.Count; j++) { test.Features[tuple[j].Dimension] = tuple[j].Feature; } return MaximizeCoverage(test, tuple); } private int MaximizeCoverage(TestCase test, Tuple tuple) { int[] mutableDimensions = GetMutableDimensions(tuple); bool flag; int num; do { flag = false; num = 1; for (int num2 = mutableDimensions.Length; num2 > 1; num2--) { int num3 = (int)((long)random.Next() % (long)num2); int num4 = mutableDimensions[num2 - 1]; mutableDimensions[num2 - 1] = mutableDimensions[num3]; mutableDimensions[num3] = num4; } foreach (int num5 in mutableDimensions) { List<int> list = new List<int>(); int num6 = CountTuplesCovered(test, num5, test.Features[num5]); int num7 = currentTupleLength[num5][test.Features[num5]]; for (int j = 0; j < dimensions[num5]; j++) { test.Features[num5] = j; int num8 = CountTuplesCovered(test, num5, j); if (currentTupleLength[num5][j] < num7) { flag = true; num7 = currentTupleLength[num5][j]; num6 = num8; list.Clear(); list.Add(j); } else if (currentTupleLength[num5][j] == num7 && num8 >= num6) { if (num8 > num6) { flag = true; num6 = num8; list.Clear(); } list.Add(j); } } if (list.Count == 1) test.Features[num5] = list[0]; else test.Features[num5] = list[(int)((long)random.Next() % (long)list.Count)]; num += num6; } } while (flag); return num; } private int[] GetMutableDimensions(Tuple tuple) { bool[] array = new bool[dimensions.Length]; for (int i = 0; i < tuple.Count; i++) { array[tuple[i].Dimension] = true; } List<int> list = new List<int>(); for (int j = 0; j < dimensions.Length; j++) { if (!array[j]) list.Add(j); } return list.ToArray(); } private int CountTuplesCovered(TestCase test, int dimension, int feature) { int num = 0; TupleCollection tupleCollection = uncoveredTuples[dimension][feature]; for (int i = 0; i < tupleCollection.Count; i++) { if (test.IsTupleCovered(tupleCollection[i])) num++; } return num; } private void RemoveTuplesCoveredBy(TestCase testCase) { for (int i = 0; i < uncoveredTuples.Length; i++) { for (int j = 0; j < uncoveredTuples[i].Length; j++) { TupleCollection tupleCollection = uncoveredTuples[i][j]; for (int num = tupleCollection.Count - 1; num >= 0; num--) { if (testCase.IsTupleCovered(tupleCollection[num])) tupleCollection.RemoveAt(num); } } } } } public IEnumerable<ITestCaseData> GetTestCases(IEnumerable[] sources) { List<ITestCaseData> list = new List<ITestCaseData>(); List<object>[] array = CreateValueSet(sources); int[] dimensions = CreateDimensions(array); IEnumerable testCases = new PairwiseTestCaseGenerator(dimensions).GetTestCases(); foreach (TestCase item2 in testCases) { object[] array2 = new object[item2.Features.Length]; for (int i = 0; i < item2.Features.Length; i++) { array2[i] = array[i][item2.Features[i]]; } ParameterSet item = new ParameterSet(array2); list.Add(item); } return list; } private List<object>[] CreateValueSet(IEnumerable[] sources) { List<object>[] array = new List<object>[sources.Length]; for (int i = 0; i < array.Length; i++) { List<object> list = new List<object>(); foreach (object item in sources[i]) { list.Add(item); } array[i] = list; } return array; } private int[] CreateDimensions(List<object>[] valueSet) { int[] array = new int[valueSet.Length]; for (int i = 0; i < valueSet.Length; i++) { array[i] = valueSet[i].Count; } return array; } } }