Initial commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// This class delegates actions from the NUnit thread that should be executed on the main thread.
|
||||
/// NUnit thread calls Delegate which blocks the execution on the thread until the action is executed.
|
||||
/// The main thread will poll for awaiting actions (HasAction) and invoke them (Execute).
|
||||
/// Once the action is executed, the main thread releases the lock and executino on the NUnit thread is continued.
|
||||
/// </summary>
|
||||
internal class ActionDelegator : BaseDelegator
|
||||
{
|
||||
private Func<object> m_Action;
|
||||
public object Delegate(Action action)
|
||||
{
|
||||
return Delegate(() => { action(); return null; });
|
||||
}
|
||||
|
||||
public object Delegate(Func<object> action)
|
||||
{
|
||||
if (m_Aborted)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
AssertState();
|
||||
m_Context = UnityTestExecutionContext.CurrentContext;
|
||||
|
||||
m_Signal.Reset();
|
||||
m_Action = action;
|
||||
|
||||
WaitForSignal();
|
||||
|
||||
return HandleResult();
|
||||
}
|
||||
|
||||
private void AssertState()
|
||||
{
|
||||
if (m_Action != null)
|
||||
{
|
||||
throw new Exception("Action not executed yet");
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAction()
|
||||
{
|
||||
return m_Action != null;
|
||||
}
|
||||
|
||||
public void Execute(LogScope logScope)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetCurrentTestContext();
|
||||
m_Result = m_Action();
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_Exception = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_Action = null;
|
||||
m_Signal.Set();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f939b9e23a0946439b812551e07ac81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cb14878543cf3d4f8472b15f7ecf0e3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is an alternative to the standard `Ignore` attribute in [NUnit](https://nunit.org/). It allows for ignoring tests only under a specified condition. The condition evaluates during `OnLoad`, referenced by ID.
|
||||
/// </summary>
|
||||
public class ConditionalIgnoreAttribute : NUnitAttribute, IApplyToTest
|
||||
{
|
||||
private string m_ConditionKey;
|
||||
private string m_IgnoreReason;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConditionalIgnoreAttribute"/> class with a condition key.
|
||||
/// </summary>
|
||||
/// <param name="conditionKey">The key to check for enabling the conditional ignore. The condition is set with the static <see cref="AddConditionalIgnoreMapping"/> method.</param>
|
||||
/// <param name="ignoreReason">The reason for the ignore.</param>
|
||||
public ConditionalIgnoreAttribute(string conditionKey, string ignoreReason)
|
||||
{
|
||||
m_ConditionKey = conditionKey;
|
||||
m_IgnoreReason = ignoreReason;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a test as defined for the specific attribute.
|
||||
/// </summary>
|
||||
/// <param name="test">The test to modify</param>
|
||||
public void ApplyToTest(Test test)
|
||||
{
|
||||
var key = m_ConditionKey.ToLowerInvariant();
|
||||
if (m_ConditionMap.ContainsKey(key) && m_ConditionMap[key])
|
||||
{
|
||||
test.RunState = RunState.Ignored;
|
||||
string skipReason = string.Format(m_IgnoreReason);
|
||||
test.Properties.Add(PropertyNames.SkipReason, skipReason);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, bool> m_ConditionMap = new Dictionary<string, bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a flag indicating whether tests with the same key should be ignored.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to ignore tests for.</param>
|
||||
/// <param name="value">A boolean value indicating whether the tests should be ignored.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // An example in which tests are ignored in the Mac editor only.
|
||||
/// using UnityEditor;
|
||||
/// using NUnit.Framework;
|
||||
/// using UnityEngine.TestTools;
|
||||
///
|
||||
/// [InitializeOnLoad]
|
||||
/// public class OnLoad
|
||||
/// {
|
||||
/// static OnLoad()
|
||||
/// {
|
||||
/// var editorIsOSX = false;
|
||||
/// #if UNITY_EDITOR_OSX
|
||||
/// editorIsOSX = true;
|
||||
/// #endif
|
||||
///
|
||||
/// ConditionalIgnoreAttribute.AddConditionalIgnoreMapping("IgnoreInMacEditor", editorIsOSX);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// public class MyTestClass
|
||||
/// {
|
||||
/// [Test, ConditionalIgnore("IgnoreInMacEditor", "Ignored on Mac editor.")]
|
||||
/// public void TestNeverRunningInMacEditor()
|
||||
/// {
|
||||
/// Assert.Pass();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static void AddConditionalIgnoreMapping(string key, bool value)
|
||||
{
|
||||
m_ConditionMap.Add(key.ToLowerInvariant(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c82a8473f4a8f7b42a004c91e06d2f2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumeratorHelper
|
||||
{
|
||||
public static bool IsRunningNestedEnumerator => enumeratorStack.Count > 0;
|
||||
|
||||
private static IEnumerator currentEnumerator;
|
||||
private static Stack<IEnumerator> enumeratorStack = new Stack<IEnumerator>();
|
||||
|
||||
/// <summary>
|
||||
/// This method executes a given enumerator and all nested enumerators.
|
||||
/// If any resuming (setting of pc) is needed, it needs to be done before being passed to this method.
|
||||
/// </summary>
|
||||
public static IEnumerator UnpackNestedEnumerators(IEnumerator testEnumerator)
|
||||
{
|
||||
if (testEnumerator == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testEnumerator));
|
||||
}
|
||||
|
||||
currentEnumerator = testEnumerator;
|
||||
enumeratorStack.Clear();
|
||||
|
||||
return ProgressOnEnumerator();
|
||||
}
|
||||
|
||||
private static IEnumerator ProgressOnEnumerator()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!currentEnumerator.MoveNext())
|
||||
{
|
||||
if (enumeratorStack.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
currentEnumerator = enumeratorStack.Pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentEnumerator.Current is IEnumerator nestedEnumerator)
|
||||
{
|
||||
enumeratorStack.Push(currentEnumerator);
|
||||
currentEnumerator = nestedEnumerator;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return currentEnumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetEnumeratorPC(int pc)
|
||||
{
|
||||
if (currentEnumerator == null)
|
||||
{
|
||||
throw new Exception("No enumerator is currently running.");
|
||||
}
|
||||
|
||||
if (IsRunningNestedEnumerator)
|
||||
{
|
||||
throw new Exception("Cannot set the enumerator PC while running nested enumerators.");
|
||||
}
|
||||
|
||||
ActivePcHelper.SetEnumeratorPC(currentEnumerator, pc);
|
||||
}
|
||||
|
||||
public static int GetEnumeratorPC()
|
||||
{
|
||||
if (currentEnumerator == null)
|
||||
{
|
||||
throw new Exception("No enumerator is currently running.");
|
||||
}
|
||||
|
||||
if (IsRunningNestedEnumerator)
|
||||
{
|
||||
// Restrict the getting of PC, as it will not reflect what is currently running;
|
||||
throw new Exception("Cannot get the enumerator PC while running nested enumerators.");
|
||||
}
|
||||
|
||||
return ActivePcHelper.GetEnumeratorPC(currentEnumerator);
|
||||
}
|
||||
|
||||
private static TestCommandPcHelper pcHelper;
|
||||
internal static TestCommandPcHelper ActivePcHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pcHelper == null)
|
||||
{
|
||||
pcHelper = new TestCommandPcHelper();
|
||||
}
|
||||
|
||||
return pcHelper;
|
||||
}
|
||||
set
|
||||
{
|
||||
pcHelper = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff0aad009559b814ffc27001341fc1c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,82 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is an alternative to the standard `Ignore` attribute in [NUnit](http://www.nunit.org/). It allows for ignoring tests based on arguments which were passed to the test method.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para>The following example shows a method to use the `ParametrizedIgnore` attribute to ignore only one test with specific combination of arguments, where someString is `b` and someInt is `10`.</para>
|
||||
/// <code>using NUnit.Framework;
|
||||
/// using System.Collections.Generic;
|
||||
/// using UnityEngine.TestTools;
|
||||
///
|
||||
/// public class MyTestsClass
|
||||
/// {
|
||||
/// public static IEnumerable<TestCaseData> MyTestCaseSource()
|
||||
/// {
|
||||
/// for (int i = 0; i < 5; i++)
|
||||
/// {
|
||||
/// yield return new TestCaseData("a", i);
|
||||
/// yield return new TestCaseData("b", i);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// [Test, TestCaseSource("MyTestCaseSource")]
|
||||
/// [ParametrizedIgnore("b", 3)]
|
||||
/// public void Test(string someString, int someInt)
|
||||
/// {
|
||||
/// Assert.Pass();
|
||||
/// }
|
||||
/// }</code>
|
||||
/// <para>It could also be used together with `Values` attribute in [NUnit](http://www.nunit.org/).</para>
|
||||
/// <code>using NUnit.Framework;
|
||||
/// using UnityEngine.TestTools;
|
||||
///
|
||||
/// public class MyTestsClass
|
||||
/// {
|
||||
/// [Test]
|
||||
/// [ParametrizedIgnore("b", 10)]
|
||||
/// public void Test(
|
||||
/// [Values("a", "b")] string someString,
|
||||
/// [Values(5, 10)] int someInt)
|
||||
/// {
|
||||
/// Assert.Pass();
|
||||
/// }
|
||||
/// }</code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class ParametrizedIgnoreAttribute : NUnitAttribute, IWrapTestMethod
|
||||
{
|
||||
/// <summary>
|
||||
/// Argument combination for the test case to ignore.
|
||||
/// </summary>
|
||||
public object[] Arguments { get; }
|
||||
/// <summary>
|
||||
/// Reason for the ignore.
|
||||
/// </summary>
|
||||
public string Reason { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ParametrizedIgnoreAttribute"/> class with a argument combination that is ignored.
|
||||
/// </summary>
|
||||
/// <param name="arguments">The argument combination to ignore</param>
|
||||
public ParametrizedIgnoreAttribute(params object[] arguments)
|
||||
{
|
||||
this.Arguments = arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a test command with the command to handled parametrized ignore.
|
||||
/// </summary>
|
||||
/// <param name="command">The command to wrap.</param>
|
||||
/// <returns>A command handling parametrized ignore, with respect to the inner command.</returns>
|
||||
public TestCommand Wrap(TestCommand command)
|
||||
{
|
||||
return new ParametrizedIgnoreCommand(command, Arguments, Reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9c53c5109a7498ca4408e53f2593b1a
|
||||
timeCreated: 1699350131
|
||||
@@ -0,0 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Like the ValuesAttribute it is used to provide literal arguments for
|
||||
/// an individual parameter of a test. It has the Preserve attribute added,
|
||||
/// allowing it to persist in players build at a high stripping level.
|
||||
/// </summary>
|
||||
[Preserve]
|
||||
public class PreservedValuesAttribute : ValuesAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Construct the values attribute with a set of arguments.
|
||||
/// </summary>
|
||||
/// <param name="args">The set of arguments for the test parameter.</param>
|
||||
public PreservedValuesAttribute(params object[] args) : base(args)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 528c8790cf844716aead80624be893aa
|
||||
timeCreated: 1699350924
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class TestEnumerator
|
||||
{
|
||||
private readonly ITestExecutionContext m_Context;
|
||||
private static IEnumerator m_TestEnumerator;
|
||||
|
||||
public static IEnumerator Enumerator { get { return m_TestEnumerator; } }
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
m_TestEnumerator = null;
|
||||
}
|
||||
|
||||
public TestEnumerator(ITestExecutionContext context, IEnumerator testEnumerator)
|
||||
{
|
||||
m_Context = context;
|
||||
m_TestEnumerator = testEnumerator;
|
||||
}
|
||||
|
||||
public IEnumerator Execute()
|
||||
{
|
||||
m_Context.CurrentResult.SetResult(ResultState.Success);
|
||||
|
||||
return Execute(m_TestEnumerator, new EnumeratorContext(m_Context));
|
||||
}
|
||||
|
||||
private IEnumerator Execute(IEnumerator enumerator, EnumeratorContext context)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (context.ExceptionWasRecorded)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.RecordExceptionWithHint(ex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (enumerator.Current is IEnumerator nestedEnumerator)
|
||||
{
|
||||
yield return Execute(nestedEnumerator, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EnumeratorContext
|
||||
{
|
||||
private readonly ITestExecutionContext m_Context;
|
||||
|
||||
public EnumeratorContext(ITestExecutionContext context)
|
||||
{
|
||||
m_Context = context;
|
||||
}
|
||||
|
||||
public bool ExceptionWasRecorded
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public void RecordExceptionWithHint(Exception ex)
|
||||
{
|
||||
if (ExceptionWasRecorded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Context.CurrentResult.RecordException(ex);
|
||||
ExceptionWasRecorded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 750aad009559b814dbc27001341fc1c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// The presence of this attribute makes the Test Runner expect every single log. By
|
||||
/// default, the runner only fails automatically on any error logs, so this adds warnings and infos as well.
|
||||
/// It is the same as calling `LogAssert.NoUnexpectedReceived()` at the bottom of every affected test.
|
||||
///
|
||||
/// This attribute can be applied to test assemblies (affects every test in the assembly), fixtures (affects every test in the fixture), or on individual test methods. It is also automatically inherited from base
|
||||
/// fixtures.
|
||||
///
|
||||
/// The `MustExpect` property (on by default) lets you selectively enable or disable the higher level value. For
|
||||
/// example when migrating an assembly to this more strict checking method, you might attach
|
||||
/// `[assembly:TestMustExpectAllLogs]` to the assembly itself, but whitelist individual failing fixtures and test methods
|
||||
/// by attaching `[TestMustExpectAllLogs(MustExpect=false)]` until they can be migrated. This also means new tests in that
|
||||
/// assembly would be required to have the more strict checking.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class TestMustExpectAllLogsAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of TestMustExpectAllLogsAttribute.
|
||||
/// </summary>
|
||||
/// <param name="mustExpect">
|
||||
/// A value indicating whether the test must expect all logs.
|
||||
/// </param>
|
||||
public TestMustExpectAllLogsAttribute(bool mustExpect = true)
|
||||
=> MustExpect = mustExpect;
|
||||
/// <summary>
|
||||
/// Returns the flag of whether the test must expect all logs.
|
||||
/// </summary>
|
||||
public bool MustExpect { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3803f736886e77842995ddbc3531afaa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Builders;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class UnityCombinatorialStrategy : CombinatorialStrategy, ICombiningStrategy
|
||||
{
|
||||
public new IEnumerable<ITestCaseData> GetTestCases(IEnumerable[] sources)
|
||||
{
|
||||
var testCases = base.GetTestCases(sources);
|
||||
foreach (var testCase in testCases)
|
||||
{
|
||||
testCase.GetType().GetProperty("ExpectedResult").SetValue(testCase, new object(), null);
|
||||
}
|
||||
return testCases;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7af6ac3e6b51b8d4aab04adc85b8de2f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this attribute to define a specific set of platforms you want or do not want your test(s) to run on.
|
||||
///
|
||||
/// You can use this attribute on the test method, test class, or test assembly level. Use the supported <see cref="RuntimePlatform"/> enumeration values to specify the platforms. You can also specify which platforms to test by passing one or more `RuntimePlatform` values along with or without the include or exclude properties as parameters to the [Platform](https://github.com/nunit/docs/wiki/Platform-Attribute) attribute constructor.
|
||||
///
|
||||
/// The test(s) skips if the current target platform is:
|
||||
/// - Not explicitly specified in the included platforms list
|
||||
/// - In the excluded platforms list
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.TestTools;
|
||||
/// using NUnit.Framework;
|
||||
///
|
||||
/// [TestFixture]
|
||||
/// public class TestClass
|
||||
/// {
|
||||
/// [Test]
|
||||
/// [UnityPlatform(RuntimePlatform.WindowsPlayer)]
|
||||
/// public void TestMethod()
|
||||
/// {
|
||||
/// Assert.AreEqual(Application.platform, RuntimePlatform.WindowsPlayer);
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public class UnityPlatformAttribute : NUnitAttribute, IApplyToTest
|
||||
{
|
||||
/// <summary>
|
||||
/// A subset of platforms you need to have your tests run on.
|
||||
/// </summary>
|
||||
public RuntimePlatform[] include { get; set; }
|
||||
/// <summary>
|
||||
/// List the platforms you do not want to have your tests run on.
|
||||
/// </summary>
|
||||
public RuntimePlatform[] exclude { get; set; }
|
||||
|
||||
private string m_skippedReason;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="UnityPlatformAttribute"/> class.
|
||||
/// </summary>
|
||||
public UnityPlatformAttribute()
|
||||
{
|
||||
include = new List<RuntimePlatform>().ToArray();
|
||||
exclude = new List<RuntimePlatform>().ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="UnityPlatformAttribute"/> class with a list of platforms to include.
|
||||
/// </summary>
|
||||
/// <param name="include">The different <see cref="RuntimePlatform"/> to run the test on.</param>
|
||||
public UnityPlatformAttribute(params RuntimePlatform[] include)
|
||||
: this()
|
||||
{
|
||||
this.include = include;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a test as defined for the specific attribute.
|
||||
/// </summary>
|
||||
/// <param name="test">The test to modify</param>
|
||||
public void ApplyToTest(Test test)
|
||||
{
|
||||
if (test.RunState == RunState.NotRunnable || test.RunState == RunState.Ignored || IsPlatformSupported(Application.platform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
test.RunState = RunState.Skipped;
|
||||
test.Properties.Add(PropertyNames.SkipReason, m_skippedReason);
|
||||
}
|
||||
|
||||
internal bool IsPlatformSupported(RuntimePlatform testTargetPlatform)
|
||||
{
|
||||
if (include.Any() && !include.Any(x => x == testTargetPlatform))
|
||||
{
|
||||
m_skippedReason = string.Format("Only supported on {0}", string.Join(", ", include.Select(x => x.ToString()).ToArray()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (exclude.Any(x => x == testTargetPlatform))
|
||||
{
|
||||
m_skippedReason = string.Format("Not supported on {0}", string.Join(", ", exclude.Select(x => x.ToString()).ToArray()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5440c1153b397e14c9c7b1d6eb83b9f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// The `UnitySetUp` and <see cref="UnityTearDownAttribute"/> attributes are identical to the standard `SetUp` and `TearDown` attributes, with the exception that they allow for <see cref="IEditModeTestYieldInstruction"/>. The `UnitySetUp` and `UnityTearDown` attributes expect a return type of [IEnumerator](https://docs.microsoft.com/en-us/dotnet/api/system.collections.ienumerator?view=netframework-4.8).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// public class SetUpTearDownExample
|
||||
/// {
|
||||
/// [UnitySetUp]
|
||||
/// public IEnumerator SetUp()
|
||||
/// {
|
||||
/// yield return new EnterPlayMode();
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void MyTest()
|
||||
/// {
|
||||
/// Debug.Log("This runs inside playmode");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public IEnumerator TearDown()
|
||||
/// {
|
||||
/// yield return new ExitPlayMode();
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>## Base and Derived class example</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// public class BaseClass
|
||||
/// {
|
||||
/// [OneTimeSetUp]
|
||||
/// public void OneTimeSetUp()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeSetUp Base");
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public void SetUp()
|
||||
/// {
|
||||
/// Debug.Log("SetUp Base");
|
||||
/// }
|
||||
///
|
||||
/// [UnitySetUp]
|
||||
/// public IEnumerator UnitySetUp()
|
||||
/// {
|
||||
/// Debug.Log("UnitySetup Base");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [TearDown]
|
||||
/// public void TearDown()
|
||||
/// {
|
||||
/// Debug.Log("TearDown Base");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public IEnumerator UnityTearDown()
|
||||
/// {
|
||||
/// Debug.Log("UnityTearDown Base");
|
||||
/// yield return null;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// public class DerivedClass : BaseClass
|
||||
/// {
|
||||
/// [OneTimeSetUp]
|
||||
/// public new void OneTimeSetUp()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeSetUp");
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public new void SetUp()
|
||||
/// {
|
||||
/// Debug.Log("SetUp");
|
||||
/// }
|
||||
///
|
||||
/// [UnitySetUp]
|
||||
/// public new IEnumerator UnitySetUp()
|
||||
/// {
|
||||
/// Debug.Log("UnitySetup");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void UnitTest()
|
||||
/// {
|
||||
/// Debug.Log("Test");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator UnityTest()
|
||||
/// {
|
||||
/// Debug.Log("UnityTest before yield");
|
||||
/// yield return null;
|
||||
/// Debug.Log("UnityTest after yield");
|
||||
/// }
|
||||
///
|
||||
/// [TearDown]
|
||||
/// public new void TearDown()
|
||||
/// {
|
||||
/// Debug.Log("TearDown");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public new IEnumerator UnityTearDown()
|
||||
/// {
|
||||
/// Debug.Log("UnityTearDown");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [OneTimeTearDown]
|
||||
/// public void OneTimeTearDown()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeTearDown");
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// <para>## Domain reload example</para>
|
||||
/// <code>
|
||||
/// <![CDATA[
|
||||
/// public class BaseClass
|
||||
/// {
|
||||
/// [OneTimeSetUp]
|
||||
/// public void OneTimeSetUp()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeSetUp Base");
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public void SetUp()
|
||||
/// {
|
||||
/// Debug.Log("SetUp Base");
|
||||
/// }
|
||||
///
|
||||
/// [UnitySetUp]
|
||||
/// public IEnumerator UnitySetUp()
|
||||
/// {
|
||||
/// Debug.Log("UnitySetup Base");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [TearDown]
|
||||
/// public void TearDown()
|
||||
/// {
|
||||
/// Debug.Log("TearDown Base");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public IEnumerator UnityTearDown()
|
||||
/// {
|
||||
/// Debug.Log("UnityTearDown Base");
|
||||
/// yield return null;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// public class DerivedClass : BaseClass
|
||||
/// {
|
||||
/// [OneTimeSetUp]
|
||||
/// public new void OneTimeSetUp()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeSetUp");
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public new void SetUp()
|
||||
/// {
|
||||
/// Debug.Log("SetUp");
|
||||
/// }
|
||||
///
|
||||
/// [UnitySetUp]
|
||||
/// public new IEnumerator UnitySetUp()
|
||||
/// {
|
||||
/// Debug.Log("UnitySetup");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void UnitTest()
|
||||
/// {
|
||||
/// Debug.Log("Test");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator UnityTest()
|
||||
/// {
|
||||
/// Debug.Log("UnityTest before yield");
|
||||
/// yield return new EnterPlayMode();
|
||||
/// //Domain reload happening
|
||||
/// yield return new ExitPlayMode();
|
||||
/// Debug.Log("UnityTest after yield");
|
||||
/// }
|
||||
///
|
||||
/// [TearDown]
|
||||
/// public new void TearDown()
|
||||
/// {
|
||||
/// Debug.Log("TearDown");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public new IEnumerator UnityTearDown()
|
||||
/// {
|
||||
/// Debug.Log("UnityTearDown");
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// [OneTimeTearDown]
|
||||
/// public void OneTimeTearDown()
|
||||
/// {
|
||||
/// Debug.Log("OneTimeTearDown");
|
||||
/// }
|
||||
/// }
|
||||
/// ]]>
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UnitySetUpAttribute : NUnitAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc6401f13df54ba44bfd7cdc93c7d64d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="UnitySetUpAttribute"/> and `UnityTearDown` attributes are identical to the standard `SetUp` and `TearDown` attributes, with the exception that they allow for <see cref="IEditModeTestYieldInstruction"/>. The `UnitySetUp` and `UnityTearDown` attributes expect a return type of [IEnumerator](https://docs.microsoft.com/en-us/dotnet/api/system.collections.ienumerator?view=netframework-4.8).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
///public class SetUpTearDownExample
|
||||
/// {
|
||||
/// [UnitySetUp]
|
||||
/// public IEnumerator SetUp()
|
||||
/// {
|
||||
/// yield return new EnterPlayMode();
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void MyTest()
|
||||
/// {
|
||||
/// Debug.Log("This runs inside playmode");
|
||||
/// }
|
||||
///
|
||||
/// [UnityTearDown]
|
||||
/// public IEnumerator TearDown()
|
||||
/// {
|
||||
/// yield return new ExitPlayMode();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UnityTearDownAttribute : NUnitAttribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 600f4b74746dbf944901257f81a8af6d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Builders;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// `UnityTest` attribute is the main addition to the standard [NUnit](http://www.nunit.org/) library for the Unity Test Framework. This type of unit test allows you to skip a frame from within a test (so background tasks can finish) or give certain commands to the Unity **Editor**, such as performing a domain reload or entering **Play Mode** from an **Edit Mode** test.
|
||||
/// In Play Mode, the `UnityTest` attribute runs as a [coroutine](https://docs.unity3d.com/Manual/Coroutines.html). Whereas Edit Mode tests run in the [EditorApplication.update](https://docs.unity3d.com/ScriptReference/EditorApplication-update.html) callback loop.
|
||||
/// The `UnityTest` attribute is, in fact, an alternative to the `NUnit` [Test attribute](https://github.com/nunit/docs/wiki/Test-Attribute), which allows yielding instructions back to the framework. Once the instruction is complete, the test run continues. If you `yield return null`, you skip a frame. That might be necessary to ensure that some changes do happen on the next iteration of either the `EditorApplication.update` loop or the [game loop](https://docs.unity3d.com/Manual/ExecutionOrder.html).
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <para>
|
||||
/// ## Edit Mode example
|
||||
/// The most simple example of an Edit Mode test could be the one that yields `null` to skip the current frame and then continues to run:
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator EditorUtility_WhenExecuted_ReturnsSuccess()
|
||||
/// {
|
||||
/// var utility = RunEditorUtilityInTheBackground();
|
||||
///
|
||||
/// while (utility.isRunning)
|
||||
/// {
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// Assert.IsTrue(utility.isSuccess);
|
||||
/// }
|
||||
/// </code>
|
||||
/// <para>
|
||||
/// ## Play Mode example
|
||||
///
|
||||
/// In Play Mode, a test runs as a coroutine attached to a [MonoBehaviour](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html). So all the yield instructions available in coroutines, are also available in your test.
|
||||
///
|
||||
/// From a Play Mode test you can use one of Unity’s [Yield Instructions](https://docs.unity3d.com/ScriptReference/YieldInstruction.html):
|
||||
///
|
||||
/// - [WaitForFixedUpdate](https://docs.unity3d.com/ScriptReference/WaitForFixedUpdate.html): to ensure changes expected within the next cycle of physics calculations.
|
||||
/// - [WaitForSeconds](https://docs.unity3d.com/ScriptReference/WaitForSeconds.html): if you want to pause your test coroutine for a fixed amount of time. Be careful about creating long-running tests.
|
||||
///
|
||||
/// The simplest example is to yield to `WaitForFixedUpdate`:
|
||||
/// </para>
|
||||
/// <code>
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator GameObject_WithRigidBody_WillBeAffectedByPhysics()
|
||||
/// {
|
||||
/// var go = new GameObject();
|
||||
/// go.AddComponent<Rigidbody>();
|
||||
/// var originalPosition = go.transform.position.y;
|
||||
///
|
||||
/// yield return new WaitForFixedUpdate();
|
||||
///
|
||||
/// Assert.AreNotEqual(originalPosition, go.transform.position.y);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UnityTestAttribute : CombiningStrategyAttribute, IImplyFixture, ISimpleTestBuilder, ITestBuilder, IApplyToTest
|
||||
{
|
||||
private const string k_MethodMarkedWithUnitytestMustReturnIenumerator = "Method marked with UnityTest must return IEnumerator.";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of UnityTestAttribute.
|
||||
/// </summary>
|
||||
public UnityTestAttribute() : base(new UnityCombinatorialStrategy(), new ParameterDataSourceProvider()) {}
|
||||
|
||||
private readonly NUnitTestCaseBuilder _builder = new NUnitTestCaseBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// This method builds the TestMethod from the Test and the method info. In addition it removes the expected result of the test.
|
||||
/// </summary>
|
||||
/// <param name="method">The method info.</param>
|
||||
/// <param name="suite">The test.</param>
|
||||
/// <returns>A TestMethod object</returns>
|
||||
TestMethod ISimpleTestBuilder.BuildFrom(IMethodInfo method, Test suite)
|
||||
{
|
||||
var t = CreateTestMethod(method, suite);
|
||||
|
||||
AdaptToUnityTestMethod(t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method hides the base method from CombiningStrategyAttribute.
|
||||
/// It builds a TestMethod from a Parameterized Test and the method info.
|
||||
/// In addition it removes the expected result of the test.
|
||||
/// </summary>
|
||||
/// <param name="method">The method info.</param>
|
||||
/// <param name="suite">The test.</param>
|
||||
/// <returns>A TestMethod object</returns>
|
||||
IEnumerable<TestMethod> ITestBuilder.BuildFrom(IMethodInfo method, Test suite)
|
||||
{
|
||||
var testMethods = base.BuildFrom(method, suite);
|
||||
|
||||
foreach (var t in testMethods)
|
||||
{
|
||||
AdaptToUnityTestMethod(t);
|
||||
}
|
||||
|
||||
return testMethods;
|
||||
}
|
||||
|
||||
private TestMethod CreateTestMethod(IMethodInfo method, Test suite)
|
||||
{
|
||||
TestCaseParameters parms = new TestCaseParameters
|
||||
{
|
||||
ExpectedResult = new object(),
|
||||
HasExpectedResult = true
|
||||
};
|
||||
|
||||
var t = _builder.BuildTestMethod(method, suite, parms);
|
||||
return t;
|
||||
}
|
||||
|
||||
private static void AdaptToUnityTestMethod(TestMethod t)
|
||||
{
|
||||
if (t.parms != null)
|
||||
{
|
||||
t.parms.HasExpectedResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsMethodReturnTypeIEnumerator(IMethodInfo method)
|
||||
{
|
||||
return !method.ReturnType.IsType(typeof(IEnumerator));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method hides the base method ApplyToTest from CombiningStrategyAttribute.
|
||||
/// In addition it ensures that the test with the `UnityTestAttribute` has an IEnumerator as return type.
|
||||
/// </summary>
|
||||
/// <param name="test">The test.</param>
|
||||
public new void ApplyToTest(Test test)
|
||||
{
|
||||
if (IsMethodReturnTypeIEnumerator(test.Method))
|
||||
{
|
||||
test.RunState = RunState.NotRunnable;
|
||||
test.Properties.Set(PropertyNames.SkipReason, k_MethodMarkedWithUnitytestMustReturnIenumerator);
|
||||
}
|
||||
|
||||
base.ApplyToTest(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fedb0f9e5006b1943abae52f52f08a1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
internal abstract class BaseDelegator
|
||||
{
|
||||
protected ManualResetEvent m_Signal = new ManualResetEvent(false);
|
||||
|
||||
protected object m_Result;
|
||||
protected Exception m_Exception;
|
||||
protected ITestExecutionContext m_Context;
|
||||
|
||||
protected bool m_Aborted;
|
||||
|
||||
protected object HandleResult()
|
||||
{
|
||||
SetCurrentTestContext();
|
||||
if (m_Exception != null)
|
||||
{
|
||||
var temp = m_Exception;
|
||||
m_Exception = null;
|
||||
throw temp;
|
||||
}
|
||||
var tempResult = m_Result;
|
||||
m_Result = null;
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
protected void WaitForSignal()
|
||||
{
|
||||
while (!m_Signal.WaitOne(100))
|
||||
{
|
||||
if (m_Aborted)
|
||||
{
|
||||
m_Aborted = false;
|
||||
Reflect.MethodCallWrapper = null;
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Abort()
|
||||
{
|
||||
m_Aborted = true;
|
||||
}
|
||||
|
||||
protected void SetCurrentTestContext()
|
||||
{
|
||||
var prop = typeof(TestExecutionContext).GetProperty("CurrentContext");
|
||||
if (prop != null)
|
||||
{
|
||||
prop.SetValue(null, m_Context, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37cea569bfefafe49a1513c4d7f0e9eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b72875690e0f7343911e06af3145bd5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal abstract class BeforeAfterTestCommandBase<T> : DelegatingTestCommand, IEnumerableTestMethodCommand where T : class
|
||||
{
|
||||
private string m_BeforeErrorPrefix;
|
||||
private string m_AfterErrorPrefix;
|
||||
protected BeforeAfterTestCommandBase(TestCommand innerCommand, string beforeErrorPrefix, string afterErrorPrefix)
|
||||
: base(innerCommand)
|
||||
{
|
||||
m_BeforeErrorPrefix = beforeErrorPrefix;
|
||||
m_AfterErrorPrefix = afterErrorPrefix;
|
||||
}
|
||||
|
||||
protected T[] BeforeActions = new T[0];
|
||||
|
||||
protected T[] AfterActions = new T[0];
|
||||
|
||||
protected static MethodInfo[] GetActions(IDictionary<Type, List<MethodInfo>> cacheStorage, Type fixtureType, Type attributeType, Type[] returnTypes)
|
||||
{
|
||||
if (cacheStorage.TryGetValue(fixtureType, out var result))
|
||||
{
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
cacheStorage[fixtureType] = GetMethodsWithAttributeFromFixture(fixtureType, attributeType, returnTypes);
|
||||
|
||||
return cacheStorage[fixtureType].ToArray();
|
||||
}
|
||||
|
||||
protected static T[] GetTestActions(IDictionary<MethodInfo, List<T>> cacheStorage, MethodInfo methodInfo)
|
||||
{
|
||||
if (cacheStorage.TryGetValue(methodInfo, out var result))
|
||||
{
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
var attributesForMethodInfo = new List<T>();
|
||||
var attributes = methodInfo.GetCustomAttributes(false);
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
if (attribute is T attribute1)
|
||||
{
|
||||
attributesForMethodInfo.Add(attribute1);
|
||||
}
|
||||
}
|
||||
|
||||
cacheStorage[methodInfo] = attributesForMethodInfo;
|
||||
|
||||
return cacheStorage[methodInfo].ToArray();
|
||||
}
|
||||
|
||||
private static List<MethodInfo> GetMethodsWithAttributeFromFixture(Type fixtureType, Type setUpType, Type[] returnTypes)
|
||||
{
|
||||
MethodInfo[] methodsWithAttribute = Reflect.GetMethodsWithAttribute(fixtureType, setUpType, true);
|
||||
var methodsInfo = new List<MethodInfo>();
|
||||
methodsInfo.AddRange(methodsWithAttribute.Where(method => returnTypes.Any(type => type == method.ReturnType)));
|
||||
return methodsInfo;
|
||||
}
|
||||
|
||||
protected abstract IEnumerator InvokeBefore(T action, Test test, UnityTestExecutionContext context);
|
||||
|
||||
protected abstract IEnumerator InvokeAfter(T action, Test test, UnityTestExecutionContext context);
|
||||
|
||||
protected virtual bool MoveBeforeEnumerator(IEnumerator enumerator, Test test)
|
||||
{
|
||||
return enumerator.MoveNext();
|
||||
}
|
||||
|
||||
protected virtual bool MoveAfterEnumerator(IEnumerator enumerator, Test test)
|
||||
{
|
||||
return enumerator.MoveNext();
|
||||
}
|
||||
|
||||
protected abstract BeforeAfterTestCommandState GetState(UnityTestExecutionContext context);
|
||||
|
||||
protected virtual bool AllowFrameSkipAfterAction(T action)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
var state = GetState(unityContext);
|
||||
if (state == null)
|
||||
{
|
||||
throw new Exception($"No state in context for {GetType().Name}.");
|
||||
}
|
||||
|
||||
if(state.ShouldRestore)
|
||||
{
|
||||
state.ApplyContext(unityContext);
|
||||
}
|
||||
|
||||
while (state.NextBeforeStepIndex < BeforeActions.Length)
|
||||
{
|
||||
var action = BeforeActions[state.NextBeforeStepIndex];
|
||||
IEnumerator enumerator;
|
||||
try
|
||||
{
|
||||
enumerator = EnumeratorHelper.UnpackNestedEnumerators(InvokeBefore(action, Test, unityContext));
|
||||
EnumeratorHelper.SetEnumeratorPC(state.NextBeforeStepPc);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TestHasRun = true;
|
||||
context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex);
|
||||
break;
|
||||
}
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!AllowFrameSkipAfterAction(action)) // Evaluate the log scope right away for the commands where we do not yield
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
if (enumerator.Current is IEditModeTestYieldInstruction)
|
||||
{
|
||||
if (unityContext.TestMode == TestPlatform.PlayMode)
|
||||
{
|
||||
throw new Exception($"PlayMode test are not allowed to yield {enumerator.Current.GetType().Name}");
|
||||
}
|
||||
|
||||
if (EnumeratorHelper.IsRunningNestedEnumerator)
|
||||
{
|
||||
throw new Exception($"Nested enumerators are not allowed to yield {enumerator.Current.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TestHasRun = true;
|
||||
context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex);
|
||||
state.StoreContext(unityContext);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!EnumeratorHelper.IsRunningNestedEnumerator)
|
||||
{
|
||||
// Only store the state in the main enumerator. Domain reloads are not supported from nested enumerators.
|
||||
state.NextBeforeStepPc = EnumeratorHelper.GetEnumeratorPC();
|
||||
state.StoreContext(unityContext);
|
||||
}
|
||||
|
||||
if (!AllowFrameSkipAfterAction(action))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
state.NextBeforeStepIndex++;
|
||||
state.NextBeforeStepPc = 0;
|
||||
}
|
||||
|
||||
if (!state.TestHasRun)
|
||||
{
|
||||
state.ShouldRestore = false; // Any inner commands that can perform domain reloads are responsible for restoring the context
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
state.TestHasRun = true;
|
||||
}
|
||||
|
||||
while (state.NextAfterStepIndex < AfterActions.Length)
|
||||
{
|
||||
state.TestAfterStarted = true;
|
||||
var action = AfterActions[state.NextAfterStepIndex];
|
||||
IEnumerator enumerator;
|
||||
try
|
||||
{
|
||||
enumerator = EnumeratorHelper.UnpackNestedEnumerators(InvokeAfter(action, Test, unityContext));
|
||||
EnumeratorHelper.SetEnumeratorPC(state.NextAfterStepPc);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex);
|
||||
state.StoreContext(unityContext);
|
||||
break;
|
||||
}
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!AllowFrameSkipAfterAction(action)) // Evaluate the log scope right away for the commands where we do not yield
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
if (enumerator.Current is IEditModeTestYieldInstruction)
|
||||
{
|
||||
if (unityContext.TestMode == TestPlatform.PlayMode)
|
||||
{
|
||||
throw new Exception($"PlayMode test are not allowed to yield {enumerator.Current.GetType().Name}");
|
||||
}
|
||||
|
||||
if (EnumeratorHelper.IsRunningNestedEnumerator)
|
||||
{
|
||||
throw new Exception($"Nested enumerators are not allowed to yield {enumerator.Current.GetType().Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex);
|
||||
state.StoreContext(unityContext);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!EnumeratorHelper.IsRunningNestedEnumerator)
|
||||
{
|
||||
// Only store the state in the main enumerator. Domain reloads are not supported from nested enumerators.
|
||||
state.NextAfterStepPc = EnumeratorHelper.GetEnumeratorPC();
|
||||
state.StoreContext(unityContext);
|
||||
}
|
||||
|
||||
if (!AllowFrameSkipAfterAction(action))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
|
||||
state.NextAfterStepIndex++;
|
||||
state.NextAfterStepPc = 0;
|
||||
}
|
||||
|
||||
state.Reset();
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbbca1d8a0434be4bbc7f165523763ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
[Serializable]
|
||||
internal class BeforeAfterTestCommandState
|
||||
{
|
||||
private const BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
|
||||
public int NextBeforeStepIndex;
|
||||
public int NextBeforeStepPc;
|
||||
public int NextAfterStepIndex;
|
||||
public int NextAfterStepPc;
|
||||
public bool TestHasRun;
|
||||
public TestStatus CurrentTestResultStatus;
|
||||
public string CurrentTestResultLabel;
|
||||
public FailureSite CurrentTestResultSite;
|
||||
public string CurrentTestMessage;
|
||||
public string CurrentTestStrackTrace;
|
||||
public bool TestAfterStarted;
|
||||
public string Output;
|
||||
public long StartTicks;
|
||||
public double StartTimeOA;
|
||||
public bool ShouldRestore;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
NextBeforeStepIndex = 0;
|
||||
NextBeforeStepPc = 0;
|
||||
NextAfterStepIndex = 0;
|
||||
NextAfterStepPc = 0;
|
||||
TestHasRun = false;
|
||||
CurrentTestResultStatus = TestStatus.Inconclusive;
|
||||
CurrentTestResultLabel = null;
|
||||
CurrentTestResultSite = default(FailureSite);
|
||||
CurrentTestMessage = null;
|
||||
CurrentTestStrackTrace = null;
|
||||
TestAfterStarted = false;
|
||||
Output = null;
|
||||
StartTicks = 0;
|
||||
StartTimeOA = 0;
|
||||
ShouldRestore = false;
|
||||
}
|
||||
|
||||
public void StoreContext(UnityTestExecutionContext context)
|
||||
{
|
||||
var result = context.CurrentResult;
|
||||
CurrentTestResultStatus = result.ResultState.Status;
|
||||
CurrentTestResultLabel = result.ResultState.Label;
|
||||
CurrentTestResultSite = result.ResultState.Site;
|
||||
CurrentTestMessage = result.Message;
|
||||
CurrentTestStrackTrace = result.StackTrace;
|
||||
Output = result.Output;
|
||||
StartTicks = context.StartTicks;
|
||||
StartTimeOA = context.StartTime.ToOADate();
|
||||
ShouldRestore = true;
|
||||
}
|
||||
|
||||
public void ApplyContext(UnityTestExecutionContext context)
|
||||
{
|
||||
var outputProp = context.CurrentResult.GetType().BaseType.GetField("_output", Flags);
|
||||
var stringBuilder = (outputProp.GetValue(context.CurrentResult) as StringBuilder);
|
||||
stringBuilder.Clear();
|
||||
stringBuilder.Append(Output);
|
||||
context.StartTicks = StartTicks;
|
||||
context.StartTime = DateTime.FromOADate(StartTimeOA);
|
||||
context.CurrentResult.SetResult(new ResultState(CurrentTestResultStatus, CurrentTestResultLabel, CurrentTestResultSite), CurrentTestMessage, CurrentTestStrackTrace);
|
||||
ShouldRestore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f65567c9026afb4db5de3355accc636
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableApplyChangesToContextCommand : ApplyChangesToContextCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
public EnumerableApplyChangesToContextCommand(TestCommand innerCommand, IEnumerable<IApplyToContext> changes)
|
||||
: base(innerCommand, changes) {}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
ApplyChanges(context);
|
||||
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b4429eff9fcffb48b006e8edcc90338
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableMaxTimeCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private int maxTime;
|
||||
public EnumerableMaxTimeCommand(MaxTimeCommand commandToReplace) : base(commandToReplace.GetInnerCommand())
|
||||
{
|
||||
maxTime = (int)typeof(MaxTimeCommand)
|
||||
.GetField("maxTime", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue(commandToReplace);
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new System.NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
long timestamp = Stopwatch.GetTimestamp();
|
||||
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
var duration = (Stopwatch.GetTimestamp() - timestamp) / (double) Stopwatch.Frequency;
|
||||
var testResult = context.CurrentResult;
|
||||
testResult.Duration = duration;
|
||||
if (testResult.ResultState == ResultState.Success)
|
||||
{
|
||||
var durationInMilliseconds = duration * 1000.0;
|
||||
if (durationInMilliseconds > maxTime)
|
||||
testResult.SetResult(ResultState.Failure, string.Format("Elapsed time of {0}ms exceeds maximum of {1}ms", (object) durationInMilliseconds, (object) this.maxTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa5dd1a9432747fd89501213ba621de6
|
||||
timeCreated: 1713961779
|
||||
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableRepeatedTestCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private int repeatCount;
|
||||
|
||||
public EnumerableRepeatedTestCommand(RepeatAttribute.RepeatedTestCommand commandToReplace) : base(commandToReplace.GetInnerCommand())
|
||||
{
|
||||
repeatCount = (int)typeof(RepeatAttribute.RepeatedTestCommand)
|
||||
.GetField("repeatCount", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue(commandToReplace);
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
int count = unityContext.EnumerableTestState.Repeat;
|
||||
var firstCycleAfterResume = count > 0;
|
||||
|
||||
while (count < repeatCount || (firstCycleAfterResume && count <= repeatCount))
|
||||
{
|
||||
if (!firstCycleAfterResume)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
firstCycleAfterResume = false;
|
||||
unityContext.EnumerableTestState.Repeat = count;
|
||||
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
if (context.CurrentResult.ResultState != ResultState.Success)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unityContext.EnumerableTestState.Repeat = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e273462feb9a65948826739f683cc9a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableRetryTestCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private int retryCount;
|
||||
|
||||
public EnumerableRetryTestCommand(RetryAttribute.RetryCommand commandToReplace) : base(commandToReplace.GetInnerCommand())
|
||||
{
|
||||
retryCount = (int)typeof(RetryAttribute.RetryCommand)
|
||||
.GetField("_retryCount", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
.GetValue(commandToReplace);
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
int count = unityContext.EnumerableTestState.Retry;
|
||||
var firstCycleAfterResume = count > 0;
|
||||
|
||||
while (count < retryCount || (firstCycleAfterResume && count <= retryCount))
|
||||
{
|
||||
if (!firstCycleAfterResume)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
|
||||
firstCycleAfterResume = false;
|
||||
|
||||
unityContext.EnumerableTestState.Retry = count;
|
||||
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
if (context.CurrentResult.ResultState != ResultState.Failure)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unityContext.EnumerableTestState.Retry = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6de2f178a24cd2e48a0816cacd9a0583
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableSetUpTearDownCommand : BeforeAfterTestCommandBase<MethodInfo>
|
||||
{
|
||||
private static readonly Dictionary<Type, List<MethodInfo>> m_BeforeActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
private static readonly Dictionary<Type, List<MethodInfo>> m_AfterActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
|
||||
public EnumerableSetUpTearDownCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "SetUp", "TearDown")
|
||||
{
|
||||
using (new ProfilerMarker(nameof(EnumerableSetUpTearDownCommand)).Auto())
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetActions(m_BeforeActionsCache, Test.TypeInfo.Type, typeof(UnitySetUpAttribute), new[] {typeof(IEnumerator)});
|
||||
AfterActions = GetActions(m_AfterActionsCache, Test.TypeInfo.Type, typeof(UnityTearDownAttribute), new[] {typeof(IEnumerator)}).Reverse().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MoveAfterEnumerator(IEnumerator enumerator, Test test)
|
||||
{
|
||||
using (new ProfilerMarker(test.Name + ".TearDown").Auto())
|
||||
return base.MoveAfterEnumerator(enumerator, test);
|
||||
}
|
||||
|
||||
protected override bool MoveBeforeEnumerator(IEnumerator enumerator, Test test)
|
||||
{
|
||||
using (new ProfilerMarker(test.Name + ".Setup").Auto())
|
||||
return base.MoveBeforeEnumerator(enumerator, test);
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeBefore(MethodInfo action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
return (IEnumerator)Reflect.InvokeMethod(action, context.TestObject);
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeAfter(MethodInfo action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
return (IEnumerator)Reflect.InvokeMethod(action, context.TestObject);
|
||||
}
|
||||
|
||||
protected override BeforeAfterTestCommandState GetState(UnityTestExecutionContext context)
|
||||
{
|
||||
return context.SetUpTearDownState;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd85a35169d313840a0874aea1a28629
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class EnumerableTestMethodCommand : TestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private readonly TestMethod testMethod;
|
||||
|
||||
public EnumerableTestMethodCommand(TestMethod testMethod)
|
||||
: base(testMethod)
|
||||
{
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext) context;
|
||||
yield return null;
|
||||
|
||||
IEnumerator currentExecutingTestEnumerator;
|
||||
try
|
||||
{
|
||||
currentExecutingTestEnumerator = new TestEnumeratorWrapper(testMethod).GetEnumerator(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordException(ex);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (currentExecutingTestEnumerator != null)
|
||||
{
|
||||
var testEnumeraterYieldInstruction = new TestEnumerator(context, currentExecutingTestEnumerator);
|
||||
|
||||
yield return testEnumeraterYieldInstruction;
|
||||
|
||||
var enumerator = testEnumeraterYieldInstruction.Execute();
|
||||
|
||||
var executingEnumerator = ExecuteEnumerableAndRecordExceptions(enumerator, new EnumeratorContext(context), unityContext);
|
||||
while (AdvanceEnumerator(executingEnumerator))
|
||||
{
|
||||
yield return executingEnumerator.Current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.CurrentResult.ResultState != ResultState.Ignored)
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AdvanceEnumerator(IEnumerator enumerator)
|
||||
{
|
||||
using (new ProfilerMarker(testMethod.MethodName).Auto())
|
||||
return enumerator.MoveNext();
|
||||
}
|
||||
|
||||
private IEnumerator ExecuteEnumerableAndRecordExceptions(IEnumerator enumerator, EnumeratorContext context, UnityTestExecutionContext unityContext)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (context.ExceptionWasRecorded)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bool executionDone = false;
|
||||
try
|
||||
{
|
||||
executionDone = !enumerator.MoveNext();
|
||||
if (unityContext.TestMode == TestPlatform.PlayMode && enumerator.Current is IEditModeTestYieldInstruction)
|
||||
{
|
||||
throw new Exception($"PlayMode test are not allowed to yield {enumerator.Current.GetType().Name}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.RecordExceptionWithHint(ex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (unityContext.HasTimedOut())
|
||||
{
|
||||
unityContext.CurrentResult.RecordException(new UnityTestTimeoutException(unityContext.TestCaseTimeout));
|
||||
yield return new RestoreTestContextAfterDomainReload(); // If this is right after a domain reload, give the editor a chance to restore.
|
||||
yield break;
|
||||
}
|
||||
if (executionDone)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (enumerator.Current is IEnumerator nestedEnumerator)
|
||||
{
|
||||
yield return ExecuteEnumerableAndRecordExceptions(nestedEnumerator, context, unityContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EnumeratorContext
|
||||
{
|
||||
private readonly ITestExecutionContext m_Context;
|
||||
|
||||
public EnumeratorContext(ITestExecutionContext context)
|
||||
{
|
||||
m_Context = context;
|
||||
}
|
||||
|
||||
public bool ExceptionWasRecorded
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public void RecordExceptionWithHint(Exception ex)
|
||||
{
|
||||
if (ExceptionWasRecorded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_Context.CurrentResult.RecordException(ex);
|
||||
ExceptionWasRecorded = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19a6f000f81e24c4a826c1abd43e77c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
[Serializable]
|
||||
internal class EnumerableTestState
|
||||
{
|
||||
public int Repeat;
|
||||
public int Retry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 356852e4738840b4b1ab533d3a66f0e1
|
||||
timeCreated: 1606321047
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class IgnoreTest
|
||||
{
|
||||
public string test { get; set; }
|
||||
public string ignoreComment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bbdc426fc0a48e1b66deaac7ea8f64c
|
||||
timeCreated: 1675764691
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class IgnoreTestCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private ITest _test;
|
||||
public IgnoreTestCommand(TestCommand innerCommand, ITest test) : base(innerCommand)
|
||||
{
|
||||
_test = test;
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var ignoreTests = ((UnityTestExecutionContext) context).IgnoreTests;
|
||||
if (ignoreTests != null && ignoreTests.Length > 0)
|
||||
{
|
||||
var fullName = _test.GetFullNameWithoutDllPath();
|
||||
foreach (var ignoreTest in ignoreTests)
|
||||
{
|
||||
if (ignoreTest.test.Equals(fullName))
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Ignored,ignoreTest.ignoreComment);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e55eddf2afda488db58fb17f985fee99
|
||||
timeCreated: 1675765192
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class ImmediateEnumerableCommand : DelegatingTestCommand
|
||||
{
|
||||
public ImmediateEnumerableCommand(TestCommand innerCommand)
|
||||
: base(innerCommand) {}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
if (iterator != null)
|
||||
{
|
||||
throw new Exception("Only null can be yielded at this point.");
|
||||
}
|
||||
}
|
||||
return context.CurrentResult;
|
||||
}
|
||||
|
||||
return innerCommand.Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8349e42a2b30c7a4abd8678c203428ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class OuterUnityTestActionCommand : BeforeAfterTestCommandBase<IOuterUnityTestAction>
|
||||
{
|
||||
private static readonly Dictionary<MethodInfo, List<IOuterUnityTestAction>> m_TestActionsCache = new Dictionary<MethodInfo, List<IOuterUnityTestAction>>();
|
||||
public OuterUnityTestActionCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "BeforeTest", "AfterTest")
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetTestActions(m_TestActionsCache, Test.Method.MethodInfo);
|
||||
AfterActions = BeforeActions;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeBefore(IOuterUnityTestAction action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
return action.BeforeTest(test);
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeAfter(IOuterUnityTestAction action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
return action.AfterTest(test);
|
||||
}
|
||||
|
||||
protected override BeforeAfterTestCommandState GetState(UnityTestExecutionContext context)
|
||||
{
|
||||
return context.OuterUnityTestActionState;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d4fc309a0784294c8ab658b53b12320
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using System.Linq;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class ParametrizedIgnoreCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private readonly TestCommand m_Command;
|
||||
|
||||
public object[] Arguments { get; }
|
||||
public string Reason { get; }
|
||||
|
||||
public ParametrizedIgnoreCommand(TestCommand command, object[] arguments, string reason = "") : base(command)
|
||||
{
|
||||
m_Command = command;
|
||||
Arguments = arguments;
|
||||
Reason = reason;
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
if (context.CurrentTest is TestMethod testMethod)
|
||||
{
|
||||
var isIgnored = testMethod.parms.Arguments.SequenceEqual(Arguments);
|
||||
if (isIgnored)
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Ignored, Reason);
|
||||
return context.CurrentResult;
|
||||
}
|
||||
}
|
||||
|
||||
return m_Command.Execute(context);
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
if (context.CurrentTest is TestMethod testMethod)
|
||||
{
|
||||
var isIgnored = testMethod.parms.Arguments.SequenceEqual(Arguments);
|
||||
if (isIgnored)
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Ignored, Reason);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74987b94d22047d8b286a727906b7019
|
||||
timeCreated: 1699350178
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class RepeatCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
public RepeatCommand(TestCommand innerCommand)
|
||||
: base(innerCommand)
|
||||
{
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
if (unityContext.RetryRepeatState?.GetHashCode() == null)
|
||||
{
|
||||
unityContext.RetryRepeatState = new EnumerableTestState();
|
||||
}
|
||||
|
||||
while(unityContext.RetryRepeatState.Repeat < unityContext.RepeatCount + 1)
|
||||
{
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
if (context.CurrentResult.ResultState != ResultState.Success)
|
||||
{
|
||||
unityContext.RetryRepeatState.Repeat++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (unityContext.RetryRepeatState.Repeat < unityContext.RepeatCount)
|
||||
{
|
||||
ReportTestFinishStartPair(unityContext);
|
||||
}
|
||||
unityContext.RetryRepeatState.Repeat++;
|
||||
}
|
||||
|
||||
SetIterationProperty(unityContext, unityContext.RetryRepeatState.Repeat-1);
|
||||
unityContext.RetryRepeatState.Repeat = 0;
|
||||
}
|
||||
|
||||
private static void ReportTestFinishStartPair(UnityTestExecutionContext unityContext)
|
||||
{
|
||||
unityContext.CurrentResult.StartTime = unityContext.StartTime;
|
||||
unityContext.CurrentResult.EndTime = DateTime.UtcNow;
|
||||
long tickCount = Stopwatch.GetTimestamp() - unityContext.StartTicks;
|
||||
double seconds = (double) tickCount / Stopwatch.Frequency;
|
||||
unityContext.CurrentResult.Duration = seconds;
|
||||
SetIterationProperty(unityContext, unityContext.RetryRepeatState.Repeat);
|
||||
unityContext.Listener.TestFinished(unityContext.CurrentResult);
|
||||
|
||||
// Start new test iteration
|
||||
unityContext.CurrentResult = unityContext.CurrentTest.MakeTestResult();
|
||||
unityContext.StartTime = DateTime.UtcNow;
|
||||
unityContext.StartTicks = Stopwatch.GetTimestamp();
|
||||
unityContext.Listener.TestStarted(unityContext.CurrentTest);
|
||||
}
|
||||
|
||||
private static void SetIterationProperty(UnityTestExecutionContext unityContext, int iteration)
|
||||
{
|
||||
unityContext.CurrentResult.Test.Properties.Set("repeatIteration", iteration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 851f94140ec64b7db846948429a87f01
|
||||
timeCreated: 1680611750
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestRunner.TestProtocol;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class RetryCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
public RetryCommand(TestCommand innerCommand)
|
||||
: base(innerCommand)
|
||||
{
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
if (unityContext.RetryRepeatState?.GetHashCode() == null)
|
||||
{
|
||||
unityContext.RetryRepeatState = new EnumerableTestState();
|
||||
}
|
||||
|
||||
while(unityContext.RetryRepeatState.Retry < unityContext.RetryCount + 1)
|
||||
{
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
}
|
||||
|
||||
if (context.CurrentResult.ResultState != ResultState.Failure || context.CurrentResult.ResultState == ResultState.Error)
|
||||
{
|
||||
unityContext.RetryRepeatState.Retry++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (unityContext.Automated && unityContext.RetryRepeatState.Retry < unityContext.RetryCount)
|
||||
{
|
||||
ReportTestFinishStartPair(unityContext);
|
||||
}
|
||||
unityContext.RetryRepeatState.Retry++;
|
||||
}
|
||||
|
||||
SetIterationProperty(unityContext, unityContext.RetryRepeatState.Retry - 1);
|
||||
unityContext.RetryRepeatState.Retry = 0;
|
||||
}
|
||||
|
||||
private static void ReportTestFinishStartPair(UnityTestExecutionContext unityContext)
|
||||
{
|
||||
unityContext.CurrentResult.StartTime = unityContext.StartTime;
|
||||
unityContext.CurrentResult.EndTime = DateTime.UtcNow;
|
||||
long tickCount = Stopwatch.GetTimestamp() - unityContext.StartTicks;
|
||||
double seconds = (double) tickCount / Stopwatch.Frequency;
|
||||
unityContext.CurrentResult.Duration = seconds;
|
||||
SetIterationProperty(unityContext, unityContext.RetryRepeatState.Retry);
|
||||
unityContext.Listener.TestFinished(unityContext.CurrentResult);
|
||||
|
||||
// Start new test iteration
|
||||
unityContext.CurrentResult = unityContext.CurrentTest.MakeTestResult();
|
||||
unityContext.StartTime = DateTime.UtcNow;
|
||||
unityContext.StartTicks = Stopwatch.GetTimestamp();
|
||||
unityContext.Listener.TestStarted(unityContext.CurrentTest);
|
||||
}
|
||||
|
||||
private static void SetIterationProperty(UnityTestExecutionContext unityContext, int iteration)
|
||||
{
|
||||
unityContext.CurrentResult.Test.Properties.Set("retryIteration", iteration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f180b12c4cff494c91056caa03f9c109
|
||||
timeCreated: 1677662056
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class SetUpTearDownCommand : BeforeAfterTestCommandBase<MethodInfo>
|
||||
{
|
||||
private static readonly Dictionary<Type, List<MethodInfo>> m_BeforeActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
private static readonly Dictionary<Type, List<MethodInfo>> m_AfterActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
|
||||
public SetUpTearDownCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "SetUp", "TearDown")
|
||||
{
|
||||
using (new ProfilerMarker(nameof(SetUpTearDownCommand)).Auto())
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetActions(m_BeforeActionsCache, Test.TypeInfo.Type, typeof(SetUpAttribute), new[] {typeof(void), typeof(Task)});
|
||||
AfterActions = GetActions(m_AfterActionsCache, Test.TypeInfo.Type, typeof(TearDownAttribute), new[] {typeof(void), typeof(Task)}).Reverse().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool AllowFrameSkipAfterAction(MethodInfo action)
|
||||
{
|
||||
return action.ReturnType == typeof(Task);
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeBefore(MethodInfo action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
if (action.ReturnType == typeof(Task))
|
||||
{
|
||||
Task task;
|
||||
using (new ProfilerMarker(test.Name + ".Setup").Auto())
|
||||
task = HandleTaskCommand(action, context);
|
||||
while (!task.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(task.Exception.InnerExceptions.Count == 1 ? task.Exception.InnerException : task.Exception).Throw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (new ProfilerMarker(test.Name + ".Setup").Auto())
|
||||
Reflect.InvokeMethod(action, context.TestObject);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeAfter(MethodInfo action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
if (action.ReturnType == typeof(Task))
|
||||
{
|
||||
Task task;
|
||||
using (new ProfilerMarker(test.Name + ".TearDown").Auto())
|
||||
task = HandleTaskCommand(action, context);
|
||||
while (!task.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(task.Exception.InnerExceptions.Count == 1 ? task.Exception.InnerException : task.Exception).Throw();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (new ProfilerMarker(test.Name + ".TearDown").Auto())
|
||||
Reflect.InvokeMethod(action, context.TestObject);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Task HandleTaskCommand(MethodInfo action, UnityTestExecutionContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Reflect.InvokeMethod(action, context.TestObject) as Task;
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected override BeforeAfterTestCommandState GetState(UnityTestExecutionContext context)
|
||||
{
|
||||
// Normal Setup/Teardown does not support domain reloads and will not need a persisted state.
|
||||
return new BeforeAfterTestCommandState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0db3f3921670cd4ca2e925737c3fba4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEditor;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditorInternal;
|
||||
#endif
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class StrictCheckCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
public StrictCheckCommand(TestCommand innerCommand) : base(innerCommand)
|
||||
{
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = UnityTestExecutionContext.CurrentContext;
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
yield return iterator;
|
||||
}
|
||||
|
||||
if (!unityContext.FeatureFlags.strictDomainReload)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
// Refreshing the asset database before the check, to ensure that
|
||||
// no potential pending domain reloads propagate to the following test
|
||||
// (due to ex. creation or deletion of asset files in the TearDown of a test).
|
||||
#if UNITY_EDITOR
|
||||
AssetDatabase.Refresh();
|
||||
#endif
|
||||
if (isDomainReloadPending())
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Failure, "A pending domain reload was detected.");
|
||||
}
|
||||
}
|
||||
private static bool isDomainReloadPending()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return (InternalEditorUtility.IsScriptReloadRequested() || EditorApplication.isCompiling);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc294d4e597b4ba5acf52ad834cce210
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class TaskTestMethodCommand : TestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private readonly TestMethod testMethod;
|
||||
|
||||
public TaskTestMethodCommand(TestMethod testMethod)
|
||||
: base(testMethod)
|
||||
{
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
yield return null;
|
||||
|
||||
IEnumerator currentExecutingTestEnumerator;
|
||||
try
|
||||
{
|
||||
currentExecutingTestEnumerator = new TestTaskWrapper(testMethod).Execute(context);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordException(ex);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (currentExecutingTestEnumerator != null)
|
||||
{
|
||||
var testEnumeratorYieldInstruction = new TestEnumerator(context, currentExecutingTestEnumerator);
|
||||
|
||||
yield return testEnumeratorYieldInstruction;
|
||||
|
||||
var enumerator = testEnumeratorYieldInstruction.Execute();
|
||||
|
||||
var executingEnumerator = ExecuteEnumerableAndRecordExceptions(enumerator, context);
|
||||
bool enumeratorDone = false;
|
||||
while (enumeratorDone == false)
|
||||
{
|
||||
enumeratorDone = !AdvanceEnumerator(executingEnumerator);
|
||||
if (((UnityTestExecutionContext) context).HasTimedOut())
|
||||
{
|
||||
context.CurrentResult.RecordException(new UnityTestTimeoutException(context.TestCaseTimeout));
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return executingEnumerator.Current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.CurrentResult.ResultState != ResultState.Ignored)
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool AdvanceEnumerator(IEnumerator enumerator)
|
||||
{
|
||||
using (new ProfilerMarker(testMethod.MethodName).Auto())
|
||||
{
|
||||
return enumerator.MoveNext();
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerator ExecuteEnumerableAndRecordExceptions(IEnumerator enumerator, ITestExecutionContext context)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordException(ex);
|
||||
break;
|
||||
}
|
||||
|
||||
if (enumerator.Current is IEnumerator)
|
||||
{
|
||||
var current = (IEnumerator)enumerator.Current;
|
||||
yield return ExecuteEnumerableAndRecordExceptions(current, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad85854b14d4140d88447bdce2fac91f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class TestActionCommand : BeforeAfterTestCommandBase<ITestAction>
|
||||
{
|
||||
private static readonly Dictionary<MethodInfo, List<ITestAction>> m_TestActionsCache = new Dictionary<MethodInfo, List<ITestAction>>();
|
||||
|
||||
public TestActionCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "BeforeTest", "AfterTest")
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetTestActions(m_TestActionsCache, Test.Method.MethodInfo);
|
||||
AfterActions = BeforeActions;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool AllowFrameSkipAfterAction(ITestAction action)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeBefore(ITestAction action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
action.BeforeTest(test);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeAfter(ITestAction action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
action.AfterTest(test);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
protected override BeforeAfterTestCommandState GetState(UnityTestExecutionContext context)
|
||||
{
|
||||
// TestActionCommand does not support domain reloads and will not need a persisted state.
|
||||
return new BeforeAfterTestCommandState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2de8ba3b840049641897e0da7ce1d5cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class TestCommandPcHelper
|
||||
{
|
||||
public virtual void SetEnumeratorPC(IEnumerator enumerator, int pc)
|
||||
{
|
||||
// Noop implementation used in playmode.
|
||||
}
|
||||
|
||||
public virtual int GetEnumeratorPC(IEnumerator enumerator)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33e6b78c96bb0694e96383e3c56b7b54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class UnityTestMethodCommand : TestMethodCommand
|
||||
{
|
||||
public UnityTestMethodCommand(TestMethod testMethod)
|
||||
: base(testMethod) {}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
using (new ProfilerMarker(Test.FullName).Auto())
|
||||
{
|
||||
var result = base.Execute(context);
|
||||
|
||||
if (((UnityTestExecutionContext) context).HasTimedOut())
|
||||
{
|
||||
context.CurrentResult.RecordException(new UnityTestTimeoutException(context.TestCaseTimeout));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2ca9278c9dd49df877d622d0f657ecf
|
||||
timeCreated: 1614093428
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialization of BaseDelegator that makes sure objects are created on the MainThread.
|
||||
/// It also deals with ScriptableObjects so that tests can survive assembly reload.
|
||||
/// </summary>
|
||||
internal class ConstructDelegator
|
||||
{
|
||||
private Type m_RequestedType;
|
||||
private object[] m_Arguments;
|
||||
|
||||
protected ScriptableObject m_CurrentRunningTest;
|
||||
private readonly IStateSerializer m_StateSerializer;
|
||||
|
||||
protected Exception m_Exception;
|
||||
protected object m_Result;
|
||||
protected ITestExecutionContext m_Context;
|
||||
|
||||
public ConstructDelegator(IStateSerializer stateSerializer)
|
||||
{
|
||||
m_StateSerializer = stateSerializer;
|
||||
}
|
||||
|
||||
protected object HandleResult()
|
||||
{
|
||||
SetCurrentTestContext();
|
||||
if (m_Exception != null)
|
||||
{
|
||||
var temp = m_Exception;
|
||||
m_Exception = null;
|
||||
throw temp;
|
||||
}
|
||||
var tempResult = m_Result;
|
||||
m_Result = null;
|
||||
return tempResult;
|
||||
}
|
||||
|
||||
protected void SetCurrentTestContext()
|
||||
{
|
||||
var prop = typeof(UnityTestExecutionContext).GetProperty("CurrentContext");
|
||||
if (prop != null)
|
||||
{
|
||||
prop.SetValue(null, m_Context, null);
|
||||
}
|
||||
}
|
||||
|
||||
public object Delegate(Type type, object[] arguments)
|
||||
{
|
||||
AssertState();
|
||||
m_Context = UnityTestExecutionContext.CurrentContext;
|
||||
|
||||
m_RequestedType = type;
|
||||
m_Arguments = arguments;
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
Execute(logScope);
|
||||
}
|
||||
|
||||
return HandleResult();
|
||||
}
|
||||
|
||||
private void AssertState()
|
||||
{
|
||||
if (m_RequestedType != null)
|
||||
{
|
||||
throw new Exception("Constructor not executed yet");
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAction()
|
||||
{
|
||||
return m_RequestedType != null;
|
||||
}
|
||||
|
||||
public void Execute(LogScope logScope)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (typeof(ScriptableObject).IsAssignableFrom(m_RequestedType))
|
||||
{
|
||||
if (m_CurrentRunningTest != null && m_RequestedType != m_CurrentRunningTest.GetType())
|
||||
{
|
||||
DestroyCurrentTestObjectIfExists();
|
||||
}
|
||||
if (m_CurrentRunningTest == null)
|
||||
{
|
||||
if (m_StateSerializer.CanRestoreFromScriptableObject(m_RequestedType))
|
||||
{
|
||||
m_CurrentRunningTest = m_StateSerializer.RestoreScriptableObjectInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CurrentRunningTest = ScriptableObject.CreateInstance(m_RequestedType);
|
||||
}
|
||||
}
|
||||
m_Result = m_CurrentRunningTest;
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyCurrentTestObjectIfExists();
|
||||
m_Result = Activator.CreateInstance(m_RequestedType, m_Arguments);
|
||||
if (m_StateSerializer.CanRestoreFromJson(m_RequestedType))
|
||||
{
|
||||
m_StateSerializer.RestoreClassFromJson(ref m_Result);
|
||||
}
|
||||
}
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_Exception = e;
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_RequestedType = null;
|
||||
m_Arguments = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void DestroyCurrentTestObjectIfExists()
|
||||
{
|
||||
if (m_CurrentRunningTest != null)
|
||||
{
|
||||
Object.DestroyImmediate(m_CurrentRunningTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b42e1db66fe9c634798674cb9e1df2ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3de99f9efc582a48995bc8e8c2df418
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal class AndFilterExtended : AndFilter
|
||||
{
|
||||
public AndFilterExtended(params ITestFilter[] filters) : base(filters) {}
|
||||
|
||||
public override bool IsExplicitMatch(ITest test)
|
||||
{
|
||||
var explicitFilters = Filters.Where(filter => !(filter is NonExplicitFilter)).ToArray();
|
||||
if (explicitFilters.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (TestFilter filter in explicitFilters)
|
||||
{
|
||||
if (!filter.IsExplicitMatch(test))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94cf1990d9304aff8063ceaa4bc68f6d
|
||||
timeCreated: 1600868070
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal class AssemblyNameFilter : ValueMatchFilter
|
||||
{
|
||||
public AssemblyNameFilter(string assemblyName) : base(assemblyName) {}
|
||||
|
||||
public override bool Match(ITest test)
|
||||
{
|
||||
string assemblyName = string.Empty;
|
||||
//Assembly fullname is in the format "Assembly-name, meta data ...", so extract the name by looking for the comma
|
||||
if (test.TypeInfo != null && test.TypeInfo.Assembly != null && test.TypeInfo.FullName != null)
|
||||
assemblyName = test.TypeInfo.Assembly.FullName.Substring(0, test.TypeInfo.Assembly.FullName.IndexOf(',')).TrimEnd(',');
|
||||
return ExpectedValue.Equals(assemblyName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
protected override string ElementName
|
||||
{
|
||||
get { return "id"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91319408591cec1478efd3c62f9f418a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal class CategoryFilterExtended : CategoryFilter
|
||||
{
|
||||
public static string k_DefaultCategory = "Uncategorized";
|
||||
|
||||
public CategoryFilterExtended(string name) : base(name)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Match(ITest test)
|
||||
{
|
||||
var categories = test.GetAllCategoriesFromTest();
|
||||
|
||||
foreach (string category in categories)
|
||||
{
|
||||
if (Match(category))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebeedaa04bb53e24ba2e7fb6745e3fd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal class EditorOnlyFilter : NonExplicitFilter
|
||||
{
|
||||
private const string k_Name = "EditorOnly";
|
||||
private bool m_EditorOnly;
|
||||
|
||||
public EditorOnlyFilter(bool editorOnly)
|
||||
{
|
||||
m_EditorOnly = editorOnly;
|
||||
}
|
||||
|
||||
public override bool Match(ITest test)
|
||||
{
|
||||
if (test.Properties.ContainsKey(k_Name))
|
||||
{
|
||||
var isEditorOnly = (bool)test.Properties.Get(k_Name);
|
||||
return isEditorOnly == m_EditorOnly;
|
||||
}
|
||||
|
||||
if (test.Parent != null)
|
||||
{
|
||||
return Match(test.Parent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override TNode AddToXml(TNode parentNode, bool recursive)
|
||||
{
|
||||
return parentNode.AddElement(k_Name, m_EditorOnly.ToString());
|
||||
}
|
||||
|
||||
public static void ApplyPropertyToTest(ITest test, bool isEditorOnly)
|
||||
{
|
||||
test.Properties.Set(k_Name, isEditorOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e42d5fdba604f0786aac7fcaaa84cf7
|
||||
timeCreated: 1590742347
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal class FullNameFilter : NUnit.Framework.Internal.Filters.FullNameFilter
|
||||
{
|
||||
public FullNameFilter(string expectedValue) : base(expectedValue)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Match(ITest test)
|
||||
{
|
||||
return Match(test.GetFullNameWithoutDllPath());
|
||||
}
|
||||
|
||||
protected override string ElementName => "test";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f07fb40f4e944ebeb84eb8f8a6bb5b13
|
||||
timeCreated: 1616483498
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Filters
|
||||
{
|
||||
internal abstract class NonExplicitFilter : TestFilter
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e948790d8b7e42e2abef6c45c5bc4c76
|
||||
timeCreated: 1600868183
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Api;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
internal interface IAsyncTestAssemblyBuilder : ITestAssemblyBuilder
|
||||
{
|
||||
IEnumerator<ITest> BuildAsync(Assembly[] assemblies, TestPlatform[] testPlatforms, IDictionary<string, object> options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3aa5c3d59b94854e843f10b75b3ad63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
internal interface IStateSerializer
|
||||
{
|
||||
ScriptableObject RestoreScriptableObjectInstance();
|
||||
void RestoreClassFromJson(ref object instance);
|
||||
bool CanRestoreFromJson(Type requestedType);
|
||||
bool CanRestoreFromScriptableObject(Type requestedType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f875a14565308a40a5262d2504da705
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions
|
||||
{
|
||||
internal interface ITestSuiteModifier
|
||||
{
|
||||
TestSuite ModifySuite(TestSuite suite);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c006cc4132f4e2d8b3e7208af3e208c
|
||||
timeCreated: 1654764592
|
||||
@@ -0,0 +1,210 @@
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a3af79041a1431ab3a2a9395a7ed2ae
|
||||
timeCreated: 1654765620
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37888acc09d9ee848bf9559f06645c45
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class CompositeWorkItem : UnityWorkItem
|
||||
{
|
||||
private readonly TestSuite _suite;
|
||||
private readonly TestSuiteResult _suiteResult;
|
||||
private readonly ITestFilter _childFilter;
|
||||
private TestCommand _setupCommand;
|
||||
private TestCommand _teardownCommand;
|
||||
|
||||
public List<UnityWorkItem> Children { get; private set; }
|
||||
|
||||
private int _countOrder;
|
||||
|
||||
private CountdownEvent _childTestCountdown;
|
||||
|
||||
public CompositeWorkItem(TestSuite suite, ITestFilter childFilter, WorkItemFactory factory)
|
||||
: base(suite, factory)
|
||||
{
|
||||
_suite = suite;
|
||||
_suiteResult = Result as TestSuiteResult;
|
||||
_childFilter = childFilter;
|
||||
_countOrder = 0;
|
||||
}
|
||||
|
||||
protected override IEnumerable PerformWork()
|
||||
{
|
||||
InitializeSetUpAndTearDownCommands();
|
||||
|
||||
if (UnityTestExecutionContext.CurrentContext != null && m_DontRunRestoringResult && EditModeTestCallbacks.RestoringTestContext != null)
|
||||
{
|
||||
EditModeTestCallbacks.RestoringTestContext();
|
||||
}
|
||||
|
||||
if (!CheckForCancellation())
|
||||
if (Test.RunState == RunState.Explicit && !_childFilter.IsExplicitMatch(Test))
|
||||
SkipFixture(ResultState.Explicit, GetSkipReason(), null);
|
||||
else
|
||||
switch (Test.RunState)
|
||||
{
|
||||
default:
|
||||
case RunState.Runnable:
|
||||
case RunState.Explicit:
|
||||
Result.SetResult(ResultState.Success);
|
||||
|
||||
CreateChildWorkItems();
|
||||
|
||||
if (Children.Count > 0)
|
||||
{
|
||||
if (!m_DontRunRestoringResult)
|
||||
{
|
||||
//This is needed to give the editor a chance to go out of playmode if needed before creating objects.
|
||||
//If we do not, the objects could be automatically destroyed when exiting playmode and could result in errors later on
|
||||
yield return null;
|
||||
PerformOneTimeSetUp();
|
||||
}
|
||||
|
||||
if (!CheckForCancellation())
|
||||
{
|
||||
switch (Result.ResultState.Status)
|
||||
{
|
||||
case TestStatus.Passed:
|
||||
foreach (var child in RunChildren())
|
||||
{
|
||||
if (CheckForCancellation())
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return child;
|
||||
}
|
||||
break;
|
||||
case TestStatus.Skipped:
|
||||
case TestStatus.Inconclusive:
|
||||
case TestStatus.Failed:
|
||||
SkipChildren(_suite, Result.ResultState.WithSite(FailureSite.Parent), "OneTimeSetUp: " + Result.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested && !m_DontRunRestoringResult)
|
||||
{
|
||||
PerformOneTimeTearDown();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RunState.Skipped:
|
||||
SkipFixture(ResultState.Skipped, GetSkipReason(), null);
|
||||
break;
|
||||
|
||||
case RunState.Ignored:
|
||||
SkipFixture(ResultState.Ignored, GetSkipReason(), null);
|
||||
break;
|
||||
|
||||
case RunState.NotRunnable:
|
||||
SkipFixture(ResultState.NotRunnable, GetSkipReason(), GetProviderStackTrace());
|
||||
break;
|
||||
}
|
||||
if (!ResultedInDomainReload)
|
||||
{
|
||||
WorkItemComplete();
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckForCancellation()
|
||||
{
|
||||
if (Context.ExecutionStatus != TestExecutionStatus.Running)
|
||||
{
|
||||
Result.SetResult(ResultState.Cancelled, "Test cancelled by user");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void InitializeSetUpAndTearDownCommands()
|
||||
{
|
||||
List<SetUpTearDownItem> setUpTearDownItems = _suite.TypeInfo != null
|
||||
? CommandBuilder.BuildSetUpTearDownList(_suite.TypeInfo.Type, typeof(OneTimeSetUpAttribute), typeof(OneTimeTearDownAttribute))
|
||||
: new List<SetUpTearDownItem>();
|
||||
|
||||
var actionItems = new List<TestActionItem>();
|
||||
foreach (ITestAction action in Actions)
|
||||
{
|
||||
bool applyToSuite = (action.Targets & ActionTargets.Suite) == ActionTargets.Suite
|
||||
|| action.Targets == ActionTargets.Default && !(Test is ParameterizedMethodSuite);
|
||||
|
||||
bool applyToTest = (action.Targets & ActionTargets.Test) == ActionTargets.Test
|
||||
&& !(Test is ParameterizedMethodSuite);
|
||||
|
||||
if (applyToSuite)
|
||||
actionItems.Add(new TestActionItem(action));
|
||||
|
||||
if (applyToTest)
|
||||
Context.UpstreamActions.Add(action);
|
||||
}
|
||||
|
||||
_setupCommand = CommandBuilder.MakeOneTimeSetUpCommand(_suite, setUpTearDownItems, actionItems);
|
||||
_teardownCommand = CommandBuilder.MakeOneTimeTearDownCommand(_suite, setUpTearDownItems, actionItems);
|
||||
}
|
||||
|
||||
private void PerformOneTimeSetUp()
|
||||
{
|
||||
var logScope = new LogScope();
|
||||
try
|
||||
{
|
||||
_setupCommand.Execute(Context);
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is NUnitException || ex is TargetInvocationException)
|
||||
ex = ex.InnerException;
|
||||
|
||||
Result.RecordException(ex, FailureSite.SetUp);
|
||||
}
|
||||
|
||||
logScope.Dispose();
|
||||
}
|
||||
|
||||
private IEnumerable RunChildren()
|
||||
{
|
||||
int childCount = Children.Count;
|
||||
if (childCount == 0)
|
||||
throw new InvalidOperationException("RunChildren called but item has no children");
|
||||
|
||||
_childTestCountdown = new CountdownEvent(childCount);
|
||||
|
||||
foreach (UnityWorkItem child in Children)
|
||||
{
|
||||
if (CheckForCancellation())
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var unityTestExecutionContext = new UnityTestExecutionContext(Context);
|
||||
child.InitializeContext(unityTestExecutionContext);
|
||||
|
||||
var enumerable = child.Execute().GetEnumerator();
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!enumerable.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
ResultedInDomainReload |= child.ResultedInDomainReload;
|
||||
yield return enumerable.Current;
|
||||
}
|
||||
|
||||
_suiteResult.AddResult(child.Result);
|
||||
childCount--;
|
||||
}
|
||||
|
||||
if (childCount > 0)
|
||||
{
|
||||
while (childCount-- > 0)
|
||||
CountDownChildTest();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateChildWorkItems()
|
||||
{
|
||||
Children = new List<UnityWorkItem>();
|
||||
var testSuite = _suite;
|
||||
|
||||
foreach (ITest test in testSuite.Tests)
|
||||
{
|
||||
if (_childFilter.Pass(test))
|
||||
{
|
||||
var child = m_Factory.Create(test, _childFilter);
|
||||
|
||||
if (test.Properties.ContainsKey(PropertyNames.Order))
|
||||
{
|
||||
Children.Insert(0, child);
|
||||
_countOrder++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Children.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_countOrder != 0) SortChildren();
|
||||
}
|
||||
|
||||
private class UnityWorkItemOrderComparer : IComparer<UnityWorkItem>
|
||||
{
|
||||
public int Compare(UnityWorkItem x, UnityWorkItem y)
|
||||
{
|
||||
var xKey = int.MaxValue;
|
||||
var yKey = int.MaxValue;
|
||||
|
||||
if (x.Test.Properties.ContainsKey(PropertyNames.Order))
|
||||
xKey = (int)x.Test.Properties[PropertyNames.Order][0];
|
||||
|
||||
if (y.Test.Properties.ContainsKey(PropertyNames.Order))
|
||||
yKey = (int)y.Test.Properties[PropertyNames.Order][0];
|
||||
|
||||
return xKey.CompareTo(yKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void SortChildren()
|
||||
{
|
||||
Children.Sort(0, _countOrder, new UnityWorkItemOrderComparer());
|
||||
}
|
||||
|
||||
private void SkipFixture(ResultState resultState, string message, string stackTrace)
|
||||
{
|
||||
Result.SetResult(resultState.WithSite(FailureSite.SetUp), message, StackFilter.Filter(stackTrace));
|
||||
SkipChildren(_suite, resultState.WithSite(FailureSite.Parent), message);
|
||||
}
|
||||
|
||||
private void SkipChildren(TestSuite suite, ResultState resultState, string message)
|
||||
{
|
||||
foreach (Test child in suite.Tests)
|
||||
{
|
||||
if (_childFilter.Pass(child))
|
||||
{
|
||||
if (!ShouldExecuteEvents(child))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Context.Listener.TestStarted(child);
|
||||
TestResult childResult = child.MakeTestResult();
|
||||
childResult.SetResult(resultState, message);
|
||||
_suiteResult.AddResult(childResult);
|
||||
|
||||
if (child.IsSuite)
|
||||
SkipChildren((TestSuite)child, resultState, message);
|
||||
|
||||
Context.Listener.TestFinished(childResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldExecuteEvents(Test test)
|
||||
{
|
||||
return UnityWorkItemDataHolder.alreadyExecutedTests == null || UnityWorkItemDataHolder.alreadyExecutedTests.All(x => x != test.GetUniqueName());
|
||||
}
|
||||
|
||||
private void PerformOneTimeTearDown()
|
||||
{
|
||||
var logScope = new LogScope();
|
||||
try
|
||||
{
|
||||
_teardownCommand.Execute(Context);
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is NUnitException || ex is TargetInvocationException)
|
||||
ex = ex.InnerException;
|
||||
|
||||
Result.RecordException(ex, FailureSite.SetUp);
|
||||
}
|
||||
|
||||
logScope.Dispose();
|
||||
}
|
||||
|
||||
private string GetSkipReason()
|
||||
{
|
||||
return (string)Test.Properties.Get(PropertyNames.SkipReason);
|
||||
}
|
||||
|
||||
private string GetProviderStackTrace()
|
||||
{
|
||||
return (string)Test.Properties.Get(PropertyNames.ProviderStackTrace);
|
||||
}
|
||||
|
||||
private void CountDownChildTest()
|
||||
{
|
||||
_childTestCountdown.Signal();
|
||||
if (_childTestCountdown.CurrentCount == 0)
|
||||
{
|
||||
if (Context.ExecutionStatus != TestExecutionStatus.AbortRequested)
|
||||
PerformOneTimeTearDown();
|
||||
|
||||
foreach (var childResult in _suiteResult.Children)
|
||||
if (childResult.ResultState == ResultState.Cancelled)
|
||||
{
|
||||
Result.SetResult(ResultState.Cancelled, "Cancelled by user");
|
||||
break;
|
||||
}
|
||||
|
||||
WorkItemComplete();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Cancel(bool force)
|
||||
{
|
||||
if (Children == null)
|
||||
return;
|
||||
|
||||
foreach (var child in Children)
|
||||
{
|
||||
var ctx = child.Context;
|
||||
if (ctx != null)
|
||||
ctx.ExecutionStatus = force ? TestExecutionStatus.AbortRequested : TestExecutionStatus.StopRequested;
|
||||
|
||||
if (child.State == WorkItemState.Running)
|
||||
child.Cancel(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 110d5035a36a6a34580fb65bb40cd78f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class CoroutineTestWorkItem : UnityWorkItem
|
||||
{
|
||||
private static MonoBehaviour m_MonoBehaviourCoroutineRunner;
|
||||
private TestCommand m_Command;
|
||||
|
||||
public static MonoBehaviour monoBehaviourCoroutineRunner
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_MonoBehaviourCoroutineRunner == null)
|
||||
{
|
||||
throw new NullReferenceException("MonoBehaviour coroutine runner not set");
|
||||
}
|
||||
return m_MonoBehaviourCoroutineRunner;
|
||||
}
|
||||
set { m_MonoBehaviourCoroutineRunner = value; }
|
||||
}
|
||||
|
||||
public CoroutineTestWorkItem(TestMethod test, ITestFilter filter)
|
||||
: base(test, null)
|
||||
{
|
||||
m_Command = m_Command = TestCommandBuilder.BuildTestCommand(test, filter);
|
||||
}
|
||||
|
||||
protected override IEnumerable PerformWork()
|
||||
{
|
||||
if (m_Command is SkipCommand)
|
||||
{
|
||||
m_Command.Execute(Context);
|
||||
Result = Context.CurrentResult;
|
||||
WorkItemComplete();
|
||||
yield break;
|
||||
}
|
||||
|
||||
var enumerableTestMethodCommand = (IEnumerableTestMethodCommand)m_Command;
|
||||
try
|
||||
{
|
||||
var executeEnumerable = enumerableTestMethodCommand.ExecuteEnumerable(Context).GetEnumerator();
|
||||
|
||||
var coroutineRunner = new CoroutineRunner(monoBehaviourCoroutineRunner, Context);
|
||||
yield return coroutineRunner.HandleEnumerableTest(executeEnumerable);
|
||||
|
||||
while (executeEnumerable.MoveNext()) {}
|
||||
|
||||
Result = Context.CurrentResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
WorkItemComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b557515fff172984e8c4400b43f1c631
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user