123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
-
-
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using NUnit.Framework.Interfaces;
- using NUnit.Framework.Internal;
-
- namespace UnityEngine.TestRunner.NUnitExtensions
- {
- internal class OrderedTestSuiteModifier : ITestSuiteModifier
- {
- internal const string suiteIsReorderedProperty = "suiteIsReordered";
- private string[] m_OrderedTestNames;
- private readonly int m_randomOrderSeed;
-
- public OrderedTestSuiteModifier(string[] orderedTestNames, int randomOrderSeed)
- {
- m_OrderedTestNames = orderedTestNames;
- m_randomOrderSeed = randomOrderSeed;
- }
-
- public TestSuite ModifySuite(TestSuite root)
- {
- if((m_OrderedTestNames == null || m_OrderedTestNames?.Length == 0) && m_randomOrderSeed == 0)
- {
- return root;
- }
- // If we don't have a orderList but we do have a random seed, we need to generate a random order list
- if ((m_OrderedTestNames == null || m_OrderedTestNames.Length == 0) && m_randomOrderSeed != 0)
- {
- var testlist = GetAllTestList(root);
- var rand = new System.Random(m_randomOrderSeed);
- var randomNumberFromSeed = rand.Next();
- var shuffledList = testlist.OrderBy(fullName => GetHash(fullName, randomNumberFromSeed)).ToList();
-
- m_OrderedTestNames = shuffledList.ToArray();
- }
-
- var suite = new TestSuite(root.Name);
- suite.Properties.Set(suiteIsReorderedProperty, true);
- var workingStack = new List<ITest> { suite };
-
- foreach (var fullName in m_OrderedTestNames)
- {
- var test = FindTest(root, fullName);
- if (test == null)
- {
- continue;
- }
-
- workingStack = InsertTestInCurrentStackIncludingAllMissingAncestors(test, workingStack);
- }
-
- return suite;
- }
-
- private static List<ITest> InsertTestInCurrentStackIncludingAllMissingAncestors(ITest test, List<ITest> newAncestorStack)
- {
- var originalAncestorStack = GetAncestorStack(test);
-
- // We can start looking at index 1 in the stack, as all elements are assumed to share the same top root.
- for (int i = 1; i < originalAncestorStack.Count; i++)
- {
- if (DoAncestorsDiverge(newAncestorStack, originalAncestorStack, i))
- {
- // The ancestor list diverges from the current working stack so insert a new element
- var commonParent = newAncestorStack[i - 1];
- var nodeToClone = originalAncestorStack[i];
-
- var newNode = CloneNode(nodeToClone);
- (commonParent as TestSuite).Add(newNode);
- if (i < newAncestorStack.Count)
- {
- // Remove the diverging element and all its children.
- newAncestorStack = newAncestorStack.Take(i).ToList();
- }
-
- newAncestorStack.Add(newNode);
- }
- }
-
- return newAncestorStack;
- }
-
- private static bool DoAncestorsDiverge(List<ITest> newAncestorStack, List<ITest> originalAncestorStack, int i)
- {
- return i >= newAncestorStack.Count || originalAncestorStack[i].Name != newAncestorStack[i].Name || !originalAncestorStack[i].HasChildren;
- }
-
- private static Test CloneNode(ITest test)
- {
- var type = test.GetType();
- Test newTest;
- if (type == typeof(TestSuite))
- {
- newTest = new TestSuite(test.Name);
- }
- else if (type == typeof(TestAssembly))
- {
- var testAssembly = (TestAssembly)test;
- newTest = new TestAssembly(testAssembly.Assembly, testAssembly.Name);;
- }
- else if (type == typeof(TestFixture))
- {
- var existingFixture = (TestFixture)test;
- newTest = new TestFixture(test.TypeInfo);
- if (existingFixture.Arguments?.Length > 0)
- {
- // Newer versions of NUnit has a constructor that allows for setting this argument. Our custom NUnit version only allows for setting it through reflection at the moment.
- typeof(TestFixture).GetProperty(nameof(existingFixture.Arguments)).SetValue(newTest, existingFixture.Arguments);
- }
- }
- else if (type == typeof(TestMethod))
- {
- // On the testMethod level, it is safe to reuse the node.
- newTest = test as Test;
- }
- else if (type == typeof(ParameterizedMethodSuite))
- {
- newTest = new ParameterizedMethodSuite(test.Method);
- }
- else if (type == typeof(ParameterizedFixtureSuite))
- {
- newTest = new ParameterizedFixtureSuite(test.Tests[0].TypeInfo);
- }
- else if (type == typeof(SetUpFixture))
- {
- newTest = new SetUpFixture(test.TypeInfo);
- }
- else
- {
- // If there are any node types that we do not know how to handle, then we should fail hard, so they can be added.
- throw new NotImplementedException(type.FullName);
- }
-
- CloneProperties(newTest, test);
- newTest.RunState = test.RunState;
- newTest.Properties.Set(suiteIsReorderedProperty, true);
- return newTest;
- }
-
- private static void CloneProperties(ITest target, ITest source)
- {
- if (target == source)
- {
- // On the TestMethod level, the node is reused, so do not clone the node properties.
- return;
- }
-
- foreach (var key in source.Properties.Keys)
- {
- foreach (var value in source.Properties[key])
- {
- target.Properties.Set(key, value);
- }
- }
- }
-
- private static List<ITest> GetAncestorStack(ITest test)
- {
- var list = new List<ITest>();
- while (test != null)
- {
- list.Insert(0, test);
- test = test.Parent;
- }
-
- return list;
- }
-
- private static List<string> GetAllTestList(ITest test)
- {
- var listOfTests = new List<string>();
-
- if (test.IsSuite)
- {
- listOfTests.AddRange(test.Tests.SelectMany(GetAllTestList));
- }
- else
- {
- listOfTests.Add(test.FullName);
- }
- return listOfTests;
- }
-
- private static int GetHash(string fullName, int randomNumber)
- {
- var hash = 0;
- foreach (var c in fullName)
- {
- hash = hash * 31 + c;
- }
-
- return hash ^ randomNumber;
- }
-
- private static ITest FindTest(ITest node, string fullName)
- {
- if (node.HasChildren)
- {
- return node.Tests
- .Select(test => FindTest(test, fullName))
- .FirstOrDefault(match => match != null);
- }
-
- return node.FullName == fullName ? node : null;
- }
- }
- }
|