Unity-jump-proj
This commit is contained in:
@ -0,0 +1,16 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyTitle("UnityEngine.TestRunner")]
|
||||
|
||||
[assembly: InternalsVisibleTo("UnityEditor.TestRunner")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Unity.PerformanceTesting")]
|
||||
[assembly: InternalsVisibleTo("Unity.PerformanceTesting.Editor")]
|
||||
[assembly: InternalsVisibleTo("Assembly-CSharp-testable")]
|
||||
[assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")]
|
||||
[assembly: InternalsVisibleTo("UnityEngine.TestRunner.Tests")]
|
||||
[assembly: InternalsVisibleTo("UnityEditor.TestRunner.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageManagerUI.Editor")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0")]
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc22cc13b69c1094c85e176c008b9ef8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ad55f5ad04d1d045a1f287409c650dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace UnityEngine.TestTools.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// An NUnit test constraint class to test whether a given block of code makes any GC allocations.
|
||||
///
|
||||
/// Use this class with NUnit's Assert.That() method to make assertions about the GC behaviour of your code. The constraint executes the delegate you provide, and checks if it has caused any GC memory to be allocated. If any GC memory was allocated, the constraint passes; otherwise, the constraint fails.
|
||||
///
|
||||
/// Usually you negate this constraint to make sure that your delegate does not allocate any GC memory. This is easy to do using the Is class:
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using NUnit.Framework;
|
||||
/// using UnityEngine.TestTools.Constraints;
|
||||
/// using Is = UnityEngine.TestTools.Constraints.Is;
|
||||
///
|
||||
/// public class MyTestClass
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void SettingAVariableDoesNotAllocate()
|
||||
/// {
|
||||
/// Assert.That(() => {
|
||||
/// int a = 0;
|
||||
/// a = 1;
|
||||
/// }, Is.Not.AllocatingGCMemory());
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class AllocatingGCMemoryConstraint : Constraint
|
||||
{
|
||||
private class AllocatingGCMemoryResult : ConstraintResult
|
||||
{
|
||||
private readonly int diff;
|
||||
public AllocatingGCMemoryResult(IConstraint constraint, object actualValue, int diff) : base(constraint, actualValue, diff > 0)
|
||||
{
|
||||
this.diff = diff;
|
||||
}
|
||||
|
||||
public override void WriteMessageTo(MessageWriter writer)
|
||||
{
|
||||
if (diff == 0)
|
||||
writer.WriteMessageLine("The provided delegate did not make any GC allocations.");
|
||||
else
|
||||
writer.WriteMessageLine("The provided delegate made {0} GC allocation(s).", diff);
|
||||
}
|
||||
}
|
||||
|
||||
private ConstraintResult ApplyTo(Action action, object original)
|
||||
{
|
||||
var recorder = Recorder.Get("GC.Alloc");
|
||||
|
||||
// The recorder was created enabled, which means it captured the creation of the Recorder object itself, etc.
|
||||
// Disabling it flushes its data, so that we can retrieve the sample block count and have it correctly account
|
||||
// for these initial allocations.
|
||||
recorder.enabled = false;
|
||||
|
||||
#if !UNITY_WEBGL
|
||||
recorder.FilterToCurrentThread();
|
||||
#endif
|
||||
|
||||
recorder.enabled = true;
|
||||
|
||||
try
|
||||
{
|
||||
action();
|
||||
}
|
||||
finally
|
||||
{
|
||||
recorder.enabled = false;
|
||||
#if !UNITY_WEBGL
|
||||
recorder.CollectFromAllThreads();
|
||||
#endif
|
||||
}
|
||||
|
||||
return new AllocatingGCMemoryResult(this, original, recorder.sampleBlockCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies GC memory constraint to the test.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object to apply the GC constraint to. Should be a <see cref="TestDelegate"/>.</param>
|
||||
/// <returns>A ConstraintResult</returns>
|
||||
/// <exception cref="ArgumentNullException">Throws a <see cref="ArgumentNullException"/> if the provided object is null.</exception>
|
||||
/// <exception cref="ArgumentException">Throws a <see cref="ArgumentException"/> if the provided object is not a <see cref="TestDelegate"/>.</exception>
|
||||
public override ConstraintResult ApplyTo(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
TestDelegate d = obj as TestDelegate;
|
||||
if (d == null)
|
||||
throw new ArgumentException(string.Format("The actual value must be a TestDelegate but was {0}",
|
||||
obj.GetType()));
|
||||
|
||||
return ApplyTo(() => d.Invoke(), obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the constraint is satisfied by a given reference.
|
||||
/// The default implementation simply dereferences the value but
|
||||
/// derived classes may override it to provide for delayed processing.
|
||||
/// </summary>
|
||||
/// <typeparam name="TActual">The type of the actual value delegate to be tested.</typeparam>
|
||||
/// <param name="del">A reference to the value delegate to be tested</param>
|
||||
/// <returns>A ConstraintResult</returns>
|
||||
/// <exception cref="ArgumentNullException">Throws a <see cref="ArgumentNullException"/> if the provided delegate is null.</exception>
|
||||
public override ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del)
|
||||
{
|
||||
if (del == null)
|
||||
throw new ArgumentNullException();
|
||||
|
||||
return ApplyTo(() => del.Invoke(), del);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Description of what this constraint tests, for to use in messages and in the ConstraintResult.
|
||||
/// </summary>
|
||||
public override string Description
|
||||
{
|
||||
get { return "allocates GC memory"; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d09858396dd7adb4bbdb22ea0c8c3a37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
||||
using NUnit.Framework.Constraints;
|
||||
|
||||
namespace UnityEngine.TestTools.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// An NUnit test constraint class to test whether a given block of code makes any GC allocations.
|
||||
/// </summary>
|
||||
public static class ConstraintExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this with NUnit's Assert.That() method to make assertions about the GC behaviour of your code. The constraint executes the delegate you provide, and checks if it caused any GC memory to be allocated. If any GC memory was allocated, the constraint passes; otherwise, the constraint fails.
|
||||
/// See https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/api/UnityEngine.TestTools.Constraints.AllocatingGCMemoryConstraint.html for an example.
|
||||
/// </summary>
|
||||
/// <param name="chain"></param>
|
||||
/// <returns></returns>
|
||||
public static AllocatingGCMemoryConstraint AllocatingGCMemory(this ConstraintExpression chain)
|
||||
{
|
||||
var constraint = new AllocatingGCMemoryConstraint();
|
||||
chain.Append(constraint);
|
||||
return constraint;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68a48d1900320ed458e118415857faf6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,18 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class InvalidSignatureException : ResultStateException
|
||||
{
|
||||
public InvalidSignatureException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public override ResultState ResultState
|
||||
{
|
||||
get { return ResultState.NotRunnable; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9650d910fcaefb34cb45f121c1993892
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,17 @@
|
||||
namespace UnityEngine.TestTools.Constraints
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension of the `Is` class in NUnit.Framework, see [Is](https://docs.microsoft.com/en-us/dotnet/api/nunit.framework.is?view=xamarin-ios-sdk-12).
|
||||
/// </summary>
|
||||
public class Is : NUnit.Framework.Is
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of `AllocatingGCMemoryConstraint`.
|
||||
/// </summary>
|
||||
/// <returns>A new AllocatingGCMemoryConstraint object.</returns>
|
||||
public static AllocatingGCMemoryConstraint AllocatingGCMemory()
|
||||
{
|
||||
return new AllocatingGCMemoryConstraint();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d5833966abeadb429de247e4316eef4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,79 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A test fails if Unity logs a message other than a regular log or warning message. Use `LogAssert` to check for an expected message in the log so that the test does not fail when Unity logs the message.
|
||||
/// Use `LogAssert.Expect` before running the code under test, as the check for expected logs runs at the end of each frame.
|
||||
/// A test also reports a failure, if an expected message does not appear, or if Unity does not log any regular log or warning messages.
|
||||
///
|
||||
/// `LogAssert` lets you expect Unity log messages that would otherwise cause the test to fail.
|
||||
/// </summary>
|
||||
public static class LogAssert
|
||||
{
|
||||
/// <summary>
|
||||
/// Verifies that a log message of a specified type appears in the log. A test won't fail from an expected error, assertion, or exception log message. It does fail if an expected message does not appear in the log.
|
||||
/// If multiple LogAssert.Expect are used to expect multiple messages, they are expected to be logged in that order.
|
||||
/// </summary>
|
||||
/// <param name="type">A type of log to expect. It can take one of the [LogType enum](https://docs.unity3d.com/ScriptReference/LogType.html) values.</param>
|
||||
/// <param name="message">A string value that should equate to the expected message.</param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [Test]
|
||||
/// public void LogAssertExample()
|
||||
/// {
|
||||
/// // Expect a regular log message
|
||||
/// LogAssert.Expect(LogType.Log, "Log message");
|
||||
///
|
||||
/// // The test fails without the following expected log message
|
||||
/// Debug.Log("Log message");
|
||||
///
|
||||
/// // An error log
|
||||
/// Debug.LogError("Error message");
|
||||
///
|
||||
/// // Without expecting an error log, the test would fail
|
||||
/// LogAssert.Expect(LogType.Error, "Error message");
|
||||
/// }
|
||||
///
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static void Expect(LogType type, string message)
|
||||
{
|
||||
LogScope.Current.ExpectedLogs.Enqueue(new LogMatch() { LogType = type, Message = message });
|
||||
}
|
||||
/// <summary>
|
||||
/// Verifies that a log message of a specified type appears in the log. A test won't fail from an expected error, assertion, or exception log message. It does fail if an expected message does not appear in the log.
|
||||
/// </summary>
|
||||
/// <param name="type">A type of log to expect. It can take one of the [LogType enum](https://docs.unity3d.com/ScriptReference/LogType.html) values.</param>
|
||||
/// <param name="message">A regular expression pattern to match the expected message.</param>
|
||||
public static void Expect(LogType type, Regex message)
|
||||
{
|
||||
LogScope.Current.ExpectedLogs.Enqueue(new LogMatch() { LogType = type, MessageRegex = message });
|
||||
}
|
||||
/// <summary>
|
||||
/// Triggers an assertion when receiving any log messages and fails the test if some are unexpected messages. If multiple tests need to check for no received unexpected logs, consider using the <see cref="TestMustExpectAllLogsAttribute"/> attribute instead.
|
||||
/// </summary>
|
||||
public static void NoUnexpectedReceived()
|
||||
{
|
||||
LogScope.Current.NoUnexpectedReceived();
|
||||
}
|
||||
/// <summary>Set this property to `true` to prevent unexpected error log messages from triggering an assertion. By default, it is `false`.</summary>
|
||||
/// <returns>The value of the ignoreFailingMessages boolean property.</returns>
|
||||
public static bool ignoreFailingMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return LogScope.Current.IgnoreFailingMessages;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != LogScope.Current.IgnoreFailingMessages)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "\nIgnoreFailingMessages:" + (value? "true":"false"));
|
||||
}
|
||||
LogScope.Current.IgnoreFailingMessages = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c97b794b51780d349a16826a4c7898d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1d8465ba1376b148bdab58965101f47
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Logging
|
||||
{
|
||||
internal interface ILogScope : IDisposable
|
||||
{
|
||||
Queue<LogMatch> ExpectedLogs { get; set; }
|
||||
List<LogEvent> AllLogs { get; }
|
||||
List<LogEvent> FailingLogs { get; }
|
||||
void EvaluateLogScope(bool endOfScopeCheck);
|
||||
bool IgnoreFailingMessages { get; set; }
|
||||
bool IsNUnitException { get; }
|
||||
bool IsNUnitSuccessException { get; }
|
||||
bool IsNUnitInconclusiveException { get; }
|
||||
bool IsNUnitIgnoreException { get; }
|
||||
string NUnitExceptionMessage { get; }
|
||||
void AddLog(string message, string stacktrace, LogType type);
|
||||
bool AnyFailingLogs();
|
||||
void ProcessExpectedLogs();
|
||||
void NoUnexpectedReceived();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3504aa04cda851b44a65973f9aead6f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,18 @@
|
||||
namespace UnityEngine.TestTools.Logging
|
||||
{
|
||||
internal class LogEvent
|
||||
{
|
||||
public string Message { get; set; }
|
||||
|
||||
public string StackTrace { get; set; }
|
||||
|
||||
public LogType LogType { get; set; }
|
||||
|
||||
public bool IsHandled { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}] {1}", LogType, Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c56471f08a0f6846afc792f0b4205b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace UnityEngine.TestTools.Logging
|
||||
{
|
||||
[Serializable]
|
||||
internal class LogMatch
|
||||
{
|
||||
[SerializeField]
|
||||
private bool m_UseRegex;
|
||||
[SerializeField]
|
||||
private string m_Message;
|
||||
[SerializeField]
|
||||
private string m_MessageRegex;
|
||||
[SerializeField]
|
||||
private string m_LogType;
|
||||
|
||||
public string Message
|
||||
{
|
||||
get { return m_Message; }
|
||||
set
|
||||
{
|
||||
m_Message = value;
|
||||
m_UseRegex = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Regex MessageRegex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!m_UseRegex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Regex(m_MessageRegex);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
m_MessageRegex = value.ToString();
|
||||
m_UseRegex = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MessageRegex = null;
|
||||
m_UseRegex = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LogType? LogType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(m_LogType))
|
||||
{
|
||||
return Enum.Parse(typeof(LogType), m_LogType) as LogType ? ;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
m_LogType = value.Value.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LogType = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Matches(LogEvent log)
|
||||
{
|
||||
if (LogType != null && LogType != log.LogType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_UseRegex)
|
||||
{
|
||||
return MessageRegex.IsMatch(log.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Message.Equals(log.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (m_UseRegex)
|
||||
return string.Format("[{0}] Regex: {1}", LogType, MessageRegex);
|
||||
else
|
||||
return string.Format("[{0}] {1}", LogType, Message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9945ffed4692c6044b6d3acf81efd694
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools.Logging
|
||||
{
|
||||
sealed class LogScope : ILogScope
|
||||
{
|
||||
static List<LogScope> s_ActiveScopes = new List<LogScope>();
|
||||
|
||||
readonly object m_Lock = new object();
|
||||
bool m_Disposed;
|
||||
bool m_NeedToProcessLogs;
|
||||
|
||||
public Queue<LogMatch> ExpectedLogs { get; set; }
|
||||
public List<LogEvent> AllLogs { get; }
|
||||
public List<LogEvent> FailingLogs { get; }
|
||||
public bool IgnoreFailingMessages { get; set; }
|
||||
public bool IsNUnitException { get; private set; }
|
||||
public bool IsNUnitSuccessException { get; private set; }
|
||||
public bool IsNUnitInconclusiveException { get; private set; }
|
||||
public bool IsNUnitIgnoreException { get; private set; }
|
||||
public string NUnitExceptionMessage { get; private set; }
|
||||
|
||||
public static LogScope Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_ActiveScopes.Count == 0)
|
||||
throw new InvalidOperationException("No log scope is available");
|
||||
return s_ActiveScopes[0];
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasCurrentLogScope()
|
||||
{
|
||||
return s_ActiveScopes.Count > 0;
|
||||
}
|
||||
|
||||
public LogScope()
|
||||
{
|
||||
AllLogs = new List<LogEvent>();
|
||||
FailingLogs = new List<LogEvent>();
|
||||
ExpectedLogs = new Queue<LogMatch>();
|
||||
IgnoreFailingMessages = false;
|
||||
Activate();
|
||||
}
|
||||
|
||||
void Activate()
|
||||
{
|
||||
s_ActiveScopes.Insert(0, this);
|
||||
RegisterScope(this);
|
||||
Application.logMessageReceivedThreaded -= AddLog;
|
||||
Application.logMessageReceivedThreaded += AddLog;
|
||||
}
|
||||
|
||||
void Deactivate()
|
||||
{
|
||||
Application.logMessageReceivedThreaded -= AddLog;
|
||||
s_ActiveScopes.Remove(this);
|
||||
UnregisterScope(this);
|
||||
}
|
||||
|
||||
static void RegisterScope(LogScope logScope)
|
||||
{
|
||||
Application.logMessageReceivedThreaded += logScope.AddLog;
|
||||
}
|
||||
|
||||
static void UnregisterScope(LogScope logScope)
|
||||
{
|
||||
Application.logMessageReceivedThreaded -= logScope.AddLog;
|
||||
}
|
||||
|
||||
public void AddLog(string message, string stacktrace, LogType type)
|
||||
{
|
||||
lock (m_Lock)
|
||||
{
|
||||
m_NeedToProcessLogs = true;
|
||||
var log = new LogEvent
|
||||
{
|
||||
LogType = type,
|
||||
Message = message,
|
||||
StackTrace = stacktrace,
|
||||
};
|
||||
|
||||
AllLogs.Add(log);
|
||||
|
||||
if (IsNUnitResultStateException(stacktrace, type))
|
||||
{
|
||||
if (message.StartsWith("SuccessException"))
|
||||
{
|
||||
IsNUnitException = true;
|
||||
IsNUnitSuccessException = true;
|
||||
if (message.StartsWith("SuccessException: "))
|
||||
{
|
||||
NUnitExceptionMessage = message.Substring("SuccessException: ".Length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (message.StartsWith("InconclusiveException"))
|
||||
{
|
||||
IsNUnitException = true;
|
||||
IsNUnitInconclusiveException = true;
|
||||
if (message.StartsWith("InconclusiveException: "))
|
||||
{
|
||||
NUnitExceptionMessage = message.Substring("InconclusiveException: ".Length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (message.StartsWith("IgnoreException"))
|
||||
{
|
||||
IsNUnitException = true;
|
||||
IsNUnitIgnoreException = true;
|
||||
if (message.StartsWith("IgnoreException: "))
|
||||
{
|
||||
NUnitExceptionMessage = message.Substring("IgnoreException: ".Length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsFailingLog(type) && !IgnoreFailingMessages)
|
||||
{
|
||||
FailingLogs.Add(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsNUnitResultStateException(string stacktrace, LogType logType)
|
||||
{
|
||||
if (logType != LogType.Exception)
|
||||
return false;
|
||||
|
||||
return string.IsNullOrEmpty(stacktrace) || stacktrace.StartsWith("NUnit.Framework.Assert.");
|
||||
}
|
||||
|
||||
static bool IsFailingLog(LogType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Assert:
|
||||
case LogType.Error:
|
||||
case LogType.Exception:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
void Dispose(bool disposing)
|
||||
{
|
||||
if (m_Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Disposed = true;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
public bool AnyFailingLogs()
|
||||
{
|
||||
ProcessExpectedLogs();
|
||||
return FailingLogs.Any();
|
||||
}
|
||||
|
||||
public void EvaluateLogScope(bool endOfScopeCheck)
|
||||
{
|
||||
ProcessExpectedLogs();
|
||||
if (FailingLogs.Any())
|
||||
{
|
||||
var failureInWrongOrder = FailingLogs.FirstOrDefault(log => ExpectedLogs.Any(expected => expected.Matches(log)));
|
||||
if (failureInWrongOrder != null)
|
||||
{
|
||||
var nextExpected = ExpectedLogs.Peek();
|
||||
throw new OutOfOrderExpectedLogMessageException(failureInWrongOrder, nextExpected);
|
||||
}
|
||||
|
||||
var failingLog = FailingLogs.First();
|
||||
throw new UnhandledLogMessageException(failingLog);
|
||||
}
|
||||
if (endOfScopeCheck && ExpectedLogs.Any())
|
||||
{
|
||||
throw new UnexpectedLogMessageException(ExpectedLogs.Peek());
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessExpectedLogs()
|
||||
{
|
||||
lock (m_Lock)
|
||||
{
|
||||
if (!m_NeedToProcessLogs || !ExpectedLogs.Any())
|
||||
return;
|
||||
|
||||
LogMatch expectedLog = null;
|
||||
foreach (var logEvent in AllLogs)
|
||||
{
|
||||
if (!ExpectedLogs.Any())
|
||||
break;
|
||||
if (logEvent.IsHandled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (expectedLog == null && ExpectedLogs.Any())
|
||||
expectedLog = ExpectedLogs.Peek();
|
||||
|
||||
if (expectedLog != null && expectedLog.Matches(logEvent))
|
||||
{
|
||||
ExpectedLogs.Dequeue();
|
||||
logEvent.IsHandled = true;
|
||||
if (FailingLogs.Any(expectedLog.Matches))
|
||||
{
|
||||
var failingLog = FailingLogs.First(expectedLog.Matches);
|
||||
FailingLogs.Remove(failingLog);
|
||||
}
|
||||
expectedLog = null;
|
||||
}
|
||||
}
|
||||
m_NeedToProcessLogs = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void NoUnexpectedReceived()
|
||||
{
|
||||
lock (m_Lock)
|
||||
{
|
||||
ProcessExpectedLogs();
|
||||
|
||||
var unhandledLog = AllLogs.FirstOrDefault(x => !x.IsHandled);
|
||||
if (unhandledLog != null)
|
||||
{
|
||||
throw new UnhandledLogMessageException(unhandledLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bbc17b35884fdf468e4b52ae4222882
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,26 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class OutOfOrderExpectedLogMessageException : ResultStateException
|
||||
{
|
||||
public OutOfOrderExpectedLogMessageException(LogEvent log, LogMatch nextExpected)
|
||||
: base(BuildMessage(log, nextExpected))
|
||||
{
|
||||
}
|
||||
|
||||
private static string BuildMessage(LogEvent log, LogMatch nextExpected)
|
||||
{
|
||||
return $"Expected {log.LogType} with '{log.Message}' arrived out of order. Expected {nextExpected.LogType} with '{nextExpected.Message}' next.";
|
||||
}
|
||||
|
||||
public override ResultState ResultState
|
||||
{
|
||||
get { return ResultState.Failure; }
|
||||
}
|
||||
|
||||
public override string StackTrace { get { return null; } }
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2fcdb9402e0a4427bd5bd01f71ecf6d5
|
||||
timeCreated: 1652951919
|
@ -0,0 +1,29 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class UnexpectedLogMessageException : ResultStateException
|
||||
{
|
||||
public LogMatch LogEvent;
|
||||
|
||||
public UnexpectedLogMessageException(LogMatch log)
|
||||
: base(BuildMessage(log))
|
||||
{
|
||||
LogEvent = log;
|
||||
}
|
||||
|
||||
private static string BuildMessage(LogMatch log)
|
||||
{
|
||||
return string.Format("Expected log did not appear: {0}", log);
|
||||
}
|
||||
|
||||
public override ResultState ResultState
|
||||
{
|
||||
get { return ResultState.Failure; }
|
||||
}
|
||||
|
||||
public override string StackTrace { get { return null; } }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b2eeca598284bd4abb4a15c30df1576
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,35 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class UnhandledLogMessageException : ResultStateException
|
||||
{
|
||||
public LogEvent LogEvent;
|
||||
private readonly string m_CustomStackTrace;
|
||||
|
||||
public UnhandledLogMessageException(LogEvent log)
|
||||
: base(BuildMessage(log))
|
||||
{
|
||||
LogEvent = log;
|
||||
m_CustomStackTrace = StackTraceFilter.Filter(log.StackTrace);
|
||||
}
|
||||
|
||||
private static string BuildMessage(LogEvent log)
|
||||
{
|
||||
return string.Format("Unhandled log message: '{0}'. Use UnityEngine.TestTools.LogAssert.Expect", log);
|
||||
}
|
||||
|
||||
public override ResultState ResultState
|
||||
{
|
||||
get { return ResultState.Failure; }
|
||||
}
|
||||
|
||||
public override string StackTrace
|
||||
{
|
||||
get { return m_CustomStackTrace; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8ed4063f2beecd41a234a582202f3c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,28 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class UnityTestTimeoutException : ResultStateException
|
||||
{
|
||||
public UnityTestTimeoutException(int timeout)
|
||||
: base(BuildMessage(timeout))
|
||||
{
|
||||
}
|
||||
|
||||
private static string BuildMessage(int timeout)
|
||||
{
|
||||
return string.Format("Timeout value of {0}ms was exceeded", timeout);
|
||||
}
|
||||
|
||||
public override ResultState ResultState
|
||||
{
|
||||
get { return ResultState.Failure; }
|
||||
}
|
||||
|
||||
public override string StackTrace
|
||||
{
|
||||
get { return ""; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffb335140c799c4408411d81789fb05c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e8d6af343b383544ba5743d119f4062
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
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,85 @@
|
||||
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
|
||||
{
|
||||
string m_ConditionKey;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
/// An example in which tests are ignored in the Mac editor only.
|
||||
/// <code>
|
||||
/// 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,94 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
|
||||
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,36 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// The presence of this attribute will cause the test runner to require that every single log is expected. By
|
||||
/// default, the runner will only automatically fail 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 (will affect every test in the assembly), fixtures (will
|
||||
/// affect 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 then whitelist failing fixtures and test methods
|
||||
/// with `[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,20 @@
|
||||
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,82 @@
|
||||
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>
|
||||
[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,37 @@
|
||||
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).
|
||||
/// <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>
|
||||
/// </summary>
|
||||
[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).
|
||||
/// <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>
|
||||
/// </summary>
|
||||
[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.Internal.Commands;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Builders;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
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).
|
||||
/// <example>
|
||||
/// ## 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:
|
||||
/// <code>
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator EditorUtility_WhenExecuted_ReturnsSuccess()
|
||||
/// {
|
||||
/// var utility = RunEditorUtilityInTheBackground();
|
||||
///
|
||||
/// while (utility.isRunning)
|
||||
/// {
|
||||
/// yield return null;
|
||||
/// }
|
||||
///
|
||||
/// Assert.IsTrue(utility.isSuccess);
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <example>
|
||||
/// ## 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`:
|
||||
/// <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>
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class UnityTestAttribute : CombiningStrategyAttribute, ISimpleTestBuilder, IImplyFixture, ITestBuilder, IApplyToTest
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TestMethod CreateTestMethod(IMethodInfo method, Test suite)
|
||||
{
|
||||
TestCaseParameters parms = new TestCaseParameters
|
||||
{
|
||||
ExpectedResult = new object(),
|
||||
HasExpectedResult = true
|
||||
};
|
||||
|
||||
var t = _builder.BuildTestMethod(method, suite, parms);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void AdaptToUnityTestMethod(TestMethod t)
|
||||
{
|
||||
if (t.parms != null)
|
||||
{
|
||||
t.parms.HasExpectedResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
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,296 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
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;
|
||||
private bool m_SkipYieldAfterActions;
|
||||
protected BeforeAfterTestCommandBase(TestCommand innerCommand, string beforeErrorPrefix, string afterErrorPrefix, bool skipYieldAfterActions = false)
|
||||
: base(innerCommand)
|
||||
{
|
||||
m_BeforeErrorPrefix = beforeErrorPrefix;
|
||||
m_AfterErrorPrefix = afterErrorPrefix;
|
||||
m_SkipYieldAfterActions = skipYieldAfterActions;
|
||||
}
|
||||
|
||||
internal Func<long> GetUtcNow = () => new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
|
||||
|
||||
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 returnType)
|
||||
{
|
||||
if (cacheStorage.TryGetValue(fixtureType, out var result))
|
||||
{
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
cacheStorage[fixtureType] = GetMethodsWithAttributeFromFixture(fixtureType, attributeType, returnType);
|
||||
|
||||
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 returnType)
|
||||
{
|
||||
MethodInfo[] methodsWithAttribute = Reflect.GetMethodsWithAttribute(fixtureType, setUpType, true);
|
||||
return methodsWithAttribute.Where(x => x.ReturnType == returnType).ToList();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
var unityContext = (UnityTestExecutionContext)context;
|
||||
var state = GetState(unityContext);
|
||||
|
||||
// When entering PlayMode state will incorrectly be seen as null. Looking at the hashcode to be certain that it is null.
|
||||
if (state?.GetHashCode() == null)
|
||||
{
|
||||
// We do not expect a state to exist in playmode
|
||||
state = ScriptableObject.CreateInstance<BeforeAfterTestCommandState>();
|
||||
}
|
||||
|
||||
state.ApplyTestResult(context.CurrentResult);
|
||||
|
||||
while (state.NextBeforeStepIndex < BeforeActions.Length)
|
||||
{
|
||||
state.Timestamp = GetUtcNow();
|
||||
var action = BeforeActions[state.NextBeforeStepIndex];
|
||||
IEnumerator enumerator;
|
||||
try
|
||||
{
|
||||
enumerator = InvokeBefore(action, Test, unityContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TestHasRun = true;
|
||||
context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex);
|
||||
break;
|
||||
}
|
||||
ActivePcHelper.SetEnumeratorPC(enumerator, state.NextBeforeStepPc);
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_SkipYieldAfterActions) // Evaluate the log scope right away for the commands where we do not yield
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
state.TestHasRun = true;
|
||||
context.CurrentResult.RecordPrefixedException(m_BeforeErrorPrefix, ex);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
break;
|
||||
}
|
||||
|
||||
state.NextBeforeStepPc = ActivePcHelper.GetEnumeratorPC(enumerator);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
if (m_SkipYieldAfterActions)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
|
||||
if (GetUtcNow() - state.Timestamp > unityContext.TestCaseTimeout || CoroutineTimedOut(unityContext))
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedError(m_BeforeErrorPrefix, new UnityTestTimeoutException(unityContext.TestCaseTimeout).Message);
|
||||
state.TestHasRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.NextBeforeStepIndex++;
|
||||
state.NextBeforeStepPc = 0;
|
||||
}
|
||||
|
||||
if (!state.TestHasRun)
|
||||
{
|
||||
if (innerCommand is IEnumerableTestMethodCommand)
|
||||
{
|
||||
var executeEnumerable = ((IEnumerableTestMethodCommand)innerCommand).ExecuteEnumerable(context);
|
||||
foreach (var iterator in executeEnumerable)
|
||||
{
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
yield return iterator;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.CurrentResult = innerCommand.Execute(context);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
}
|
||||
|
||||
state.TestHasRun = true;
|
||||
}
|
||||
|
||||
while (state.NextAfterStepIndex < AfterActions.Length)
|
||||
{
|
||||
state.Timestamp = GetUtcNow();
|
||||
state.TestAfterStarted = true;
|
||||
var action = AfterActions[state.NextAfterStepIndex];
|
||||
IEnumerator enumerator;
|
||||
try
|
||||
{
|
||||
enumerator = InvokeAfter(action, Test, unityContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
break;
|
||||
}
|
||||
ActivePcHelper.SetEnumeratorPC(enumerator, state.NextAfterStepPc);
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_SkipYieldAfterActions) // Evaluate the log scope right away for the commands where we do not yield
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedException(m_AfterErrorPrefix, ex);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
break;
|
||||
}
|
||||
|
||||
state.NextAfterStepPc = ActivePcHelper.GetEnumeratorPC(enumerator);
|
||||
state.StoreTestResult(context.CurrentResult);
|
||||
|
||||
|
||||
if (GetUtcNow() - state.Timestamp > unityContext.TestCaseTimeout || CoroutineTimedOut(unityContext))
|
||||
{
|
||||
context.CurrentResult.RecordPrefixedError(m_AfterErrorPrefix, new UnityTestTimeoutException(unityContext.TestCaseTimeout).Message);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (m_SkipYieldAfterActions)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.NextAfterStepIndex++;
|
||||
state.NextAfterStepPc = 0;
|
||||
}
|
||||
|
||||
state.Reset();
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
throw new NotImplementedException("Use ExecuteEnumerable");
|
||||
}
|
||||
|
||||
private static TestCommandPcHelper pcHelper;
|
||||
private static bool CoroutineTimedOut(ITestExecutionContext unityContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(unityContext.CurrentResult.Message))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return unityContext.CurrentResult.ResultState.Equals(ResultState.Failure) &&
|
||||
unityContext.CurrentResult.Message.Contains(new UnityTestTimeoutException(unityContext.TestCaseTimeout).Message);
|
||||
}
|
||||
|
||||
|
||||
internal static TestCommandPcHelper ActivePcHelper
|
||||
{
|
||||
get
|
||||
{
|
||||
if (pcHelper == null)
|
||||
{
|
||||
pcHelper = new TestCommandPcHelper();
|
||||
}
|
||||
|
||||
return pcHelper;
|
||||
}
|
||||
set
|
||||
{
|
||||
pcHelper = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbbca1d8a0434be4bbc7f165523763ac
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal class BeforeAfterTestCommandState : ScriptableObject
|
||||
{
|
||||
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 long Timestamp;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void StoreTestResult(TestResult result)
|
||||
{
|
||||
CurrentTestResultStatus = result.ResultState.Status;
|
||||
CurrentTestResultLabel = result.ResultState.Label;
|
||||
CurrentTestResultSite = result.ResultState.Site;
|
||||
CurrentTestMessage = result.Message;
|
||||
CurrentTestStrackTrace = result.StackTrace;
|
||||
}
|
||||
|
||||
public void ApplyTestResult(TestResult result)
|
||||
{
|
||||
result.SetResult(new ResultState(CurrentTestResultStatus, CurrentTestResultLabel, CurrentTestResultSite), CurrentTestMessage, CurrentTestStrackTrace);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.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,70 @@
|
||||
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;
|
||||
if (unityContext.EnumerableTestState?.GetHashCode() == null)
|
||||
{
|
||||
unityContext.EnumerableTestState = new EnumerableTestState();
|
||||
}
|
||||
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,71 @@
|
||||
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;
|
||||
if (unityContext.EnumerableTestState?.GetHashCode() == null)
|
||||
{
|
||||
unityContext.EnumerableTestState = new EnumerableTestState();
|
||||
}
|
||||
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>
|
||||
{
|
||||
static readonly Dictionary<Type, List<MethodInfo>> m_BeforeActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
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), typeof(IEnumerator));
|
||||
AfterActions = GetActions(m_AfterActionsCache, Test.TypeInfo.Type, typeof(UnityTearDownAttribute), 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,135 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
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)
|
||||
{
|
||||
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));
|
||||
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)
|
||||
{
|
||||
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 ExecuteEnumerableAndRecordExceptions(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,32 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
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,38 @@
|
||||
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>
|
||||
{
|
||||
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,51 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
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>
|
||||
{
|
||||
static readonly Dictionary<Type, List<MethodInfo>> m_BeforeActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
static readonly Dictionary<Type, List<MethodInfo>> m_AfterActionsCache = new Dictionary<Type, List<MethodInfo>>();
|
||||
|
||||
public SetUpTearDownCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "SetUp", "TearDown", true)
|
||||
{
|
||||
using (new ProfilerMarker(nameof(SetUpTearDownCommand)).Auto())
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetActions(m_BeforeActionsCache, Test.TypeInfo.Type, typeof(SetUpAttribute), typeof(void));
|
||||
AfterActions = GetActions(m_AfterActionsCache, Test.TypeInfo.Type, typeof(TearDownAttribute), typeof(void)).Reverse().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerator InvokeBefore(MethodInfo action, Test test, UnityTestExecutionContext context)
|
||||
{
|
||||
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)
|
||||
{
|
||||
using (new ProfilerMarker(test.Name + ".TearDown").Auto())
|
||||
Reflect.InvokeMethod(action, context.TestObject);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
protected override BeforeAfterTestCommandState GetState(UnityTestExecutionContext context)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0db3f3921670cd4ca2e925737c3fba4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,42 @@
|
||||
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>
|
||||
{
|
||||
static readonly Dictionary<MethodInfo, List<ITestAction>> m_TestActionsCache = new Dictionary<MethodInfo, List<ITestAction>>();
|
||||
|
||||
public TestActionCommand(TestCommand innerCommand)
|
||||
: base(innerCommand, "BeforeTest", "AfterTest", true)
|
||||
{
|
||||
if (Test.TypeInfo.Type != null)
|
||||
{
|
||||
BeforeActions = GetTestActions(m_TestActionsCache, Test.Method.MethodInfo);
|
||||
AfterActions = BeforeActions;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,19 @@
|
||||
using System;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using Unity.Profiling;
|
||||
|
||||
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())
|
||||
return base.Execute(context);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2ca9278c9dd49df877d622d0f657ecf
|
||||
timeCreated: 1614093428
|
@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
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;
|
||||
|
||||
private 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)
|
||||
return;
|
||||
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,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,36 @@
|
||||
using System.Collections;
|
||||
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)
|
||||
{
|
||||
IList testCategories = test.Properties[PropertyNames.Category].Cast<string>().ToList();
|
||||
|
||||
if (test is TestMethod)
|
||||
{
|
||||
// Do not count tests with no attribute as Uncategorized if test fixture class has at least one attribute
|
||||
// The test inherits the attribute from the test fixture
|
||||
IList fixtureCategories = test.Parent.Properties[PropertyNames.Category].Cast<string>().ToList();
|
||||
if (fixtureCategories.Count > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (testCategories.Count == 0 && ExpectedValue == k_DefaultCategory && test is TestMethod)
|
||||
return true;
|
||||
|
||||
return base.Match(test);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebeedaa04bb53e24ba2e7fb6745e3fd3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
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,12 @@
|
||||
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,134 @@
|
||||
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;
|
||||
|
||||
public OrderedTestSuiteModifier(string[] orderedTestNames)
|
||||
{
|
||||
m_OrderedTestNames = orderedTestNames;
|
||||
}
|
||||
|
||||
public TestSuite ModifySuite(TestSuite root)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
var ancestorList = GetAncestorList(test);
|
||||
|
||||
for (int i = 0; i < ancestorList.Count; i++)
|
||||
{
|
||||
if (i >= workingStack.Count || ancestorList[i].Name != workingStack[i].Name || !ancestorList[i].HasChildren)
|
||||
{
|
||||
// The ancestor list diverges from the current working set. We need to insert a new element
|
||||
var commonParent = workingStack[i - 1];
|
||||
var nodeToClone = ancestorList[i];
|
||||
|
||||
var newNode = CloneNode(nodeToClone);
|
||||
CloneProperties(newNode, nodeToClone);
|
||||
newNode.Properties.Set(suiteIsReorderedProperty, true);
|
||||
(commonParent as TestSuite).Add(newNode);
|
||||
if (i < workingStack.Count)
|
||||
{
|
||||
workingStack = workingStack.Take(i).ToList();
|
||||
}
|
||||
|
||||
workingStack.Add(newNode);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return suite;
|
||||
}
|
||||
|
||||
private static void CloneProperties(ITest target, ITest source)
|
||||
{
|
||||
if (target == source)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in source.Properties.Keys)
|
||||
{
|
||||
foreach (var value in source.Properties[key])
|
||||
{
|
||||
target.Properties.Set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Test CloneNode(ITest test)
|
||||
{
|
||||
var type = test.GetType();
|
||||
if (type == typeof(TestSuite))
|
||||
{
|
||||
return new TestSuite(test.Name);
|
||||
}
|
||||
|
||||
if (type == typeof(TestAssembly))
|
||||
{
|
||||
var testAssembly = test as TestAssembly;
|
||||
return new TestAssembly(testAssembly.Assembly, testAssembly.Name);
|
||||
}
|
||||
|
||||
if (type == typeof(TestFixture))
|
||||
{
|
||||
return new TestFixture(test.TypeInfo);
|
||||
}
|
||||
|
||||
if (type == typeof(TestMethod))
|
||||
{
|
||||
return test as Test;
|
||||
}
|
||||
|
||||
if (type == typeof(ParameterizedMethodSuite))
|
||||
{
|
||||
return new ParameterizedMethodSuite(test.Method);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(type.FullName);
|
||||
}
|
||||
|
||||
private static List<ITest> GetAncestorList(ITest test)
|
||||
{
|
||||
var list = new List<ITest>();
|
||||
while (test != null)
|
||||
{
|
||||
list.Insert(0, test);
|
||||
test = test.Parent;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private static ITest FindTest(ITest node, string fullName)
|
||||
{
|
||||
if (node.HasChildren)
|
||||
{
|
||||
return node.Tests
|
||||
.Select(test => FindTest(test, fullName))
|
||||
.FirstOrDefault(match => match != null);
|
||||
}
|
||||
|
||||
return TestExtensions.GetFullName(node.FullName, node.HasChildIndex() ? node.GetChildIndex() : -1) == 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,354 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
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;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
using CountdownEvent = System.Threading.CountdownEvent;
|
||||
|
||||
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), "OneTimeSetUp: " + message);
|
||||
}
|
||||
|
||||
private void SkipChildren(TestSuite suite, ResultState resultState, string message)
|
||||
{
|
||||
foreach (Test child in suite.Tests)
|
||||
{
|
||||
if (_childFilter.Pass(child))
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
this.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,76 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
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;
|
||||
}
|
||||
|
||||
if (m_Command is ApplyChangesToContextCommand)
|
||||
{
|
||||
var applyChangesToContextCommand = (ApplyChangesToContextCommand)m_Command;
|
||||
applyChangesToContextCommand.ApplyChanges(Context);
|
||||
m_Command = applyChangesToContextCommand.GetInnerCommand();
|
||||
}
|
||||
|
||||
var enumerableTestMethodCommand = (IEnumerableTestMethodCommand)m_Command;
|
||||
try
|
||||
{
|
||||
var executeEnumerable = enumerableTestMethodCommand.ExecuteEnumerable(Context).GetEnumerator();
|
||||
|
||||
var coroutineRunner = new CoroutineRunner(monoBehaviourCoroutineRunner, Context);
|
||||
yield return coroutineRunner.HandleEnumerableTest(executeEnumerable);
|
||||
|
||||
if (coroutineRunner.HasFailedWithTimeout())
|
||||
{
|
||||
Context.CurrentResult.SetResult(ResultState.Failure, new UnityTestTimeoutException(Context.TestCaseTimeout).Message);
|
||||
}
|
||||
|
||||
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:
|
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
using UnityEngine.TestTools;
|
||||
using SetUpTearDownCommand = NUnit.Framework.Internal.Commands.SetUpTearDownCommand;
|
||||
using TestActionCommand = NUnit.Framework.Internal.Commands.TestActionCommand;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class EditModeTestCallbacks
|
||||
{
|
||||
public static Action RestoringTestContext { get; set; }
|
||||
}
|
||||
|
||||
internal class DefaultTestWorkItem : UnityWorkItem
|
||||
{
|
||||
private TestCommand _command;
|
||||
public DefaultTestWorkItem(TestMethod test, ITestFilter filter)
|
||||
: base(test, null)
|
||||
{
|
||||
_command = TestCommandBuilder.BuildTestCommand(test, filter);
|
||||
}
|
||||
|
||||
protected override IEnumerable PerformWork()
|
||||
{
|
||||
if (m_DontRunRestoringResult && EditModeTestCallbacks.RestoringTestContext != null)
|
||||
{
|
||||
EditModeTestCallbacks.RestoringTestContext();
|
||||
Result = Context.CurrentResult;
|
||||
yield break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_command is SkipCommand || _command is FailCommand)
|
||||
{
|
||||
Result = _command.Execute(Context);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (!(_command is IEnumerableTestMethodCommand))
|
||||
{
|
||||
Debug.LogError("Cannot perform work on " + _command.GetType().Name);
|
||||
yield break;
|
||||
}
|
||||
if (Context.TestCaseTimeout == 0)
|
||||
{
|
||||
Context.TestCaseTimeout = k_DefaultTimeout;
|
||||
}
|
||||
foreach (var workItemStep in ((IEnumerableTestMethodCommand)_command).ExecuteEnumerable(Context))
|
||||
{
|
||||
ResultedInDomainReload = false;
|
||||
|
||||
if (workItemStep is IEditModeTestYieldInstruction)
|
||||
{
|
||||
var editModeTestYieldInstruction = (IEditModeTestYieldInstruction)workItemStep;
|
||||
yield return editModeTestYieldInstruction;
|
||||
var enumerator = editModeTestYieldInstruction.Perform();
|
||||
while (true)
|
||||
{
|
||||
bool moveNext;
|
||||
try
|
||||
{
|
||||
moveNext = enumerator.MoveNext();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.CurrentResult.RecordException(e);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!moveNext)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return workItemStep;
|
||||
}
|
||||
}
|
||||
|
||||
Result = Context.CurrentResult;
|
||||
}
|
||||
finally
|
||||
{
|
||||
WorkItemComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7cfda246e604b945b12b7afedb094ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,34 @@
|
||||
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class FailCommand : TestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
private ResultState m_ResultState;
|
||||
private string m_Message;
|
||||
|
||||
public FailCommand(Test test, ResultState resultState, string message)
|
||||
: base(test)
|
||||
{
|
||||
m_ResultState = resultState;
|
||||
m_Message = message;
|
||||
}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
context.CurrentResult.SetResult(m_ResultState, m_Message);
|
||||
return context.CurrentResult;
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
context.CurrentResult.SetResult(m_ResultState, m_Message);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68e5dc8bfd5d72647a93b7f2e1da831a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,10 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal interface IEnumerableTestMethodCommand
|
||||
{
|
||||
IEnumerable ExecuteEnumerable(ITestExecutionContext context);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbd43d8a3b8122d4e89b055f53382b11
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,13 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class PlaymodeWorkItemFactory : WorkItemFactory
|
||||
{
|
||||
protected override UnityWorkItem Create(TestMethod method, ITestFilter filter, ITest loadedTest)
|
||||
{
|
||||
return new CoroutineTestWorkItem(method, filter);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ef6801a8b664544aa9f2ab1bc1f8b60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,4 @@
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class RestoreTestContextAfterDomainReload {}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26721f9940339264fb14bdbfe1290e21
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Commands;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal static class TestCommandBuilder
|
||||
{
|
||||
public static TestCommand BuildTestCommand(TestMethod test, ITestFilter filter)
|
||||
{
|
||||
if (test.RunState != RunState.Runnable &&
|
||||
!(test.RunState == RunState.Explicit && filter.IsExplicitMatch(test)))
|
||||
{
|
||||
return new SkipCommand(test);
|
||||
}
|
||||
|
||||
var testReturnsIEnumerator = test.Method.ReturnType.Type == typeof(IEnumerator);
|
||||
|
||||
TestCommand command;
|
||||
if (!testReturnsIEnumerator)
|
||||
{
|
||||
command = new UnityTestMethodCommand(test);
|
||||
}
|
||||
else
|
||||
{
|
||||
command = new EnumerableTestMethodCommand(test);
|
||||
}
|
||||
|
||||
command = new UnityLogCheckDelegatingCommand(command);
|
||||
foreach (var wrapper in test.Method.GetCustomAttributes<IWrapTestMethod>(true))
|
||||
{
|
||||
command = wrapper.Wrap(command);
|
||||
if (command == null)
|
||||
{
|
||||
var message = String.Format("IWrapTestMethod implementation '{0}' returned null as command.",
|
||||
wrapper.GetType().FullName);
|
||||
return new FailCommand(test, ResultState.Failure, message);
|
||||
}
|
||||
|
||||
if (testReturnsIEnumerator && !(command is IEnumerableTestMethodCommand))
|
||||
{
|
||||
command = TryReplaceWithEnumerableCommand(command);
|
||||
if (command != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var message = String.Format("'{0}' is not supported on {1} as it does not handle returning IEnumerator.",
|
||||
wrapper.GetType().FullName,
|
||||
GetTestBuilderName(test));
|
||||
return new FailCommand(test, ResultState.Failure, message);
|
||||
}
|
||||
}
|
||||
|
||||
command = new UnityEngine.TestTools.TestActionCommand(command);
|
||||
command = new UnityEngine.TestTools.SetUpTearDownCommand(command);
|
||||
|
||||
if (!testReturnsIEnumerator)
|
||||
{
|
||||
command = new ImmediateEnumerableCommand(command);
|
||||
}
|
||||
|
||||
foreach (var wrapper in test.Method.GetCustomAttributes<IWrapSetUpTearDown>(true))
|
||||
{
|
||||
command = wrapper.Wrap(command);
|
||||
if (command == null)
|
||||
{
|
||||
var message = String.Format("IWrapSetUpTearDown implementation '{0}' returned null as command.",
|
||||
wrapper.GetType().FullName);
|
||||
return new FailCommand(test, ResultState.Failure, message);
|
||||
}
|
||||
|
||||
if (testReturnsIEnumerator && !(command is IEnumerableTestMethodCommand))
|
||||
{
|
||||
command = TryReplaceWithEnumerableCommand(command);
|
||||
if (command != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var message = String.Format("'{0}' is not supported on {1} as it does not handle returning IEnumerator.",
|
||||
wrapper.GetType().FullName,
|
||||
GetTestBuilderName(test));
|
||||
return new FailCommand(test, ResultState.Failure, message);
|
||||
}
|
||||
}
|
||||
|
||||
command = new EnumerableSetUpTearDownCommand(command);
|
||||
command = new OuterUnityTestActionCommand(command);
|
||||
|
||||
IApplyToContext[] changes = test.Method.GetCustomAttributes<IApplyToContext>(true);
|
||||
if (changes.Length > 0)
|
||||
{
|
||||
command = new EnumerableApplyChangesToContextCommand(command, changes);
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
private static string GetTestBuilderName(TestMethod testMethod)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
testMethod.Method.GetCustomAttributes<ITestBuilder>(true).Select(attribute => attribute.GetType().Name),
|
||||
testMethod.Method.GetCustomAttributes<ISimpleTestBuilder>(true).Select(attribute => attribute.GetType().Name)
|
||||
}.SelectMany(v => v).FirstOrDefault();
|
||||
}
|
||||
|
||||
private static TestCommand TryReplaceWithEnumerableCommand(TestCommand command)
|
||||
{
|
||||
switch (command.GetType().Name)
|
||||
{
|
||||
case nameof(RepeatAttribute.RepeatedTestCommand):
|
||||
return new EnumerableRepeatedTestCommand(command as RepeatAttribute.RepeatedTestCommand);
|
||||
case nameof(RetryAttribute.RetryCommand):
|
||||
return new EnumerableRetryTestCommand(command as RetryAttribute.RetryCommand);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f40df9c8cf926b241b093a37028d8815
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,150 @@
|
||||
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 UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
class UnityLogCheckDelegatingCommand : DelegatingTestCommand, IEnumerableTestMethodCommand
|
||||
{
|
||||
static Dictionary<object, bool?> s_AttributeCache = new Dictionary<object, bool?>();
|
||||
|
||||
public UnityLogCheckDelegatingCommand(TestCommand innerCommand)
|
||||
: base(innerCommand) {}
|
||||
|
||||
public override TestResult Execute(ITestExecutionContext context)
|
||||
{
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
if (ExecuteAndCheckLog(logScope, context.CurrentResult, () => innerCommand.Execute(context)))
|
||||
PostTestValidation(logScope, innerCommand, context.CurrentResult);
|
||||
}
|
||||
|
||||
return context.CurrentResult;
|
||||
}
|
||||
|
||||
public IEnumerable ExecuteEnumerable(ITestExecutionContext context)
|
||||
{
|
||||
if (!(innerCommand is IEnumerableTestMethodCommand enumerableTestMethodCommand))
|
||||
{
|
||||
Execute(context);
|
||||
yield break;
|
||||
}
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
IEnumerable executeEnumerable = null;
|
||||
|
||||
if (!ExecuteAndCheckLog(logScope, context.CurrentResult,
|
||||
() => executeEnumerable = enumerableTestMethodCommand.ExecuteEnumerable(context)))
|
||||
yield break;
|
||||
|
||||
foreach (var step in executeEnumerable)
|
||||
{
|
||||
// do not check expected logs here - we want to permit expecting and receiving messages to run
|
||||
// across frames. (but we do always want to catch a fail immediately.)
|
||||
if (!CheckFailingLogs(logScope, context.CurrentResult))
|
||||
yield break;
|
||||
|
||||
yield return step;
|
||||
}
|
||||
|
||||
if (!CheckLogs(context.CurrentResult, logScope))
|
||||
yield break;
|
||||
|
||||
PostTestValidation(logScope, innerCommand, context.CurrentResult);
|
||||
}
|
||||
}
|
||||
|
||||
static bool CaptureException(TestResult result, Action action)
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.RecordException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ExecuteAndCheckLog(LogScope logScope, TestResult result, Action action)
|
||||
=> CaptureException(result, action) && CheckLogs(result, logScope);
|
||||
|
||||
static void PostTestValidation(LogScope logScope, TestCommand command, TestResult result)
|
||||
{
|
||||
if (MustExpect(command.Test.Method.MethodInfo))
|
||||
CaptureException(result, logScope.NoUnexpectedReceived);
|
||||
}
|
||||
|
||||
static bool CheckLogs(TestResult result, LogScope logScope)
|
||||
{
|
||||
try
|
||||
{
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.RecordException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CheckFailingLogs(LogScope logScope, TestResult result)
|
||||
{
|
||||
try
|
||||
{
|
||||
logScope.EvaluateLogScope(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.RecordException(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MustExpect(MemberInfo method)
|
||||
{
|
||||
// method
|
||||
|
||||
var methodAttr = method.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault();
|
||||
if (methodAttr != null)
|
||||
return methodAttr.MustExpect;
|
||||
|
||||
// fixture
|
||||
|
||||
var fixture = method.DeclaringType;
|
||||
if (!s_AttributeCache.TryGetValue(fixture, out var mustExpect))
|
||||
{
|
||||
var fixtureAttr = fixture.GetCustomAttributes<TestMustExpectAllLogsAttribute>(true).FirstOrDefault();
|
||||
mustExpect = s_AttributeCache[fixture] = fixtureAttr?.MustExpect;
|
||||
}
|
||||
|
||||
if (mustExpect != null)
|
||||
return mustExpect.Value;
|
||||
|
||||
// assembly
|
||||
|
||||
var assembly = fixture.Assembly;
|
||||
if (!s_AttributeCache.TryGetValue(assembly, out mustExpect))
|
||||
{
|
||||
var assemblyAttr = assembly.GetCustomAttributes<TestMustExpectAllLogsAttribute>().FirstOrDefault();
|
||||
mustExpect = s_AttributeCache[assembly] = assemblyAttr?.MustExpect;
|
||||
}
|
||||
|
||||
return mustExpect == true;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48230e4e90fb4d14a9d56bddea898413
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal interface IUnityTestAssemblyRunner
|
||||
{
|
||||
ITest LoadedTest { get; }
|
||||
ITestResult Result { get; }
|
||||
bool IsTestLoaded { get; }
|
||||
bool IsTestRunning { get; }
|
||||
bool IsTestComplete { get; }
|
||||
UnityWorkItem TopLevelWorkItem { get; set; }
|
||||
UnityTestExecutionContext GetCurrentContext();
|
||||
ITest Load(Assembly[] assemblies, TestPlatform testPlatform, IDictionary<string, object> settings);
|
||||
IEnumerable Run(ITestListener listener, ITestFilter filter);
|
||||
void StopRun();
|
||||
}
|
||||
|
||||
internal class UnityTestAssemblyRunner : IUnityTestAssemblyRunner
|
||||
{
|
||||
private readonly UnityTestAssemblyBuilder unityBuilder;
|
||||
private readonly WorkItemFactory m_Factory;
|
||||
|
||||
protected UnityTestExecutionContext Context { get; set; }
|
||||
|
||||
public UnityTestExecutionContext GetCurrentContext()
|
||||
{
|
||||
return UnityTestExecutionContext.CurrentContext;
|
||||
}
|
||||
|
||||
protected IDictionary<string, object> Settings { get; set; }
|
||||
public ITest LoadedTest { get; protected set; }
|
||||
|
||||
public ITestResult Result
|
||||
{
|
||||
get { return TopLevelWorkItem == null ? null : TopLevelWorkItem.Result; }
|
||||
}
|
||||
|
||||
public bool IsTestLoaded
|
||||
{
|
||||
get { return LoadedTest != null; }
|
||||
}
|
||||
|
||||
public bool IsTestRunning
|
||||
{
|
||||
get { return TopLevelWorkItem != null && TopLevelWorkItem.State == NUnit.Framework.Internal.Execution.WorkItemState.Running; }
|
||||
}
|
||||
public bool IsTestComplete
|
||||
{
|
||||
get { return TopLevelWorkItem != null && TopLevelWorkItem.State == NUnit.Framework.Internal.Execution.WorkItemState.Complete; }
|
||||
}
|
||||
|
||||
public UnityTestAssemblyRunner(UnityTestAssemblyBuilder builder, WorkItemFactory factory)
|
||||
{
|
||||
unityBuilder = builder;
|
||||
m_Factory = factory;
|
||||
Context = new UnityTestExecutionContext();
|
||||
}
|
||||
|
||||
public ITest Load(Assembly[] assemblies, TestPlatform testPlatform, IDictionary<string, object> settings)
|
||||
{
|
||||
Settings = settings;
|
||||
|
||||
if (settings.ContainsKey(FrameworkPackageSettings.RandomSeed))
|
||||
Randomizer.InitialSeed = (int)settings[FrameworkPackageSettings.RandomSeed];
|
||||
|
||||
var tree = unityBuilder.Build(assemblies, Enumerable.Repeat(testPlatform, assemblies.Length).ToArray(), settings);
|
||||
|
||||
return LoadedTest = tree;
|
||||
}
|
||||
|
||||
public IEnumerable Run(ITestListener listener, ITestFilter filter)
|
||||
{
|
||||
TopLevelWorkItem = m_Factory.Create(LoadedTest, filter);
|
||||
TopLevelWorkItem.InitializeContext(Context);
|
||||
UnityTestExecutionContext.CurrentContext = Context;
|
||||
Context.Listener = listener;
|
||||
|
||||
return TopLevelWorkItem.Execute();
|
||||
}
|
||||
|
||||
public UnityWorkItem TopLevelWorkItem { get; set; }
|
||||
|
||||
public void StopRun()
|
||||
{
|
||||
if (IsTestRunning)
|
||||
{
|
||||
TopLevelWorkItem.Cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 874e40a588dbb1e48bc128d686337d4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Constraints;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class UnityTestExecutionContext : ITestExecutionContext
|
||||
{
|
||||
private readonly UnityTestExecutionContext _priorContext;
|
||||
private TestResult _currentResult;
|
||||
private int _assertCount;
|
||||
|
||||
public static UnityTestExecutionContext CurrentContext { get; set; }
|
||||
|
||||
public UnityTestExecutionContext Context { get; private set; }
|
||||
|
||||
public Test CurrentTest { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public long StartTicks { get; set; }
|
||||
public TestResult CurrentResult
|
||||
{
|
||||
get { return _currentResult; }
|
||||
set
|
||||
{
|
||||
_currentResult = value;
|
||||
if (value != null)
|
||||
OutWriter = value.OutWriter;
|
||||
}
|
||||
}
|
||||
|
||||
public object TestObject { get; set; }
|
||||
public string WorkDirectory { get; set; }
|
||||
|
||||
|
||||
private TestExecutionStatus _executionStatus;
|
||||
public TestExecutionStatus ExecutionStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
// ExecutionStatus may have been set to StopRequested or AbortRequested
|
||||
// in a prior context. If so, reflect the same setting in this context.
|
||||
if (_executionStatus == TestExecutionStatus.Running && _priorContext != null)
|
||||
_executionStatus = _priorContext.ExecutionStatus;
|
||||
|
||||
return _executionStatus;
|
||||
}
|
||||
set
|
||||
{
|
||||
_executionStatus = value;
|
||||
|
||||
// Push the same setting up to all prior contexts
|
||||
if (_priorContext != null)
|
||||
_priorContext.ExecutionStatus = value;
|
||||
}
|
||||
}
|
||||
|
||||
public List<ITestAction> UpstreamActions { get; private set; }
|
||||
public int TestCaseTimeout { get; set; }
|
||||
public CultureInfo CurrentCulture { get; set; }
|
||||
public CultureInfo CurrentUICulture { get; set; }
|
||||
public ITestListener Listener { get; set; }
|
||||
|
||||
public UnityTestExecutionContext()
|
||||
{
|
||||
UpstreamActions = new List<ITestAction>();
|
||||
CurrentContext = this;
|
||||
}
|
||||
|
||||
public UnityTestExecutionContext(UnityTestExecutionContext other)
|
||||
{
|
||||
_priorContext = other;
|
||||
|
||||
CurrentTest = other.CurrentTest;
|
||||
CurrentResult = other.CurrentResult;
|
||||
TestObject = other.TestObject;
|
||||
WorkDirectory = other.WorkDirectory;
|
||||
Listener = other.Listener;
|
||||
TestCaseTimeout = other.TestCaseTimeout;
|
||||
UpstreamActions = new List<ITestAction>(other.UpstreamActions);
|
||||
SetUpTearDownState = other.SetUpTearDownState;
|
||||
OuterUnityTestActionState = other.OuterUnityTestActionState;
|
||||
EnumerableTestState = other.EnumerableTestState;
|
||||
|
||||
TestContext.CurrentTestExecutionContext = this;
|
||||
|
||||
CurrentCulture = other.CurrentCulture;
|
||||
CurrentUICulture = other.CurrentUICulture;
|
||||
CurrentContext = this;
|
||||
}
|
||||
|
||||
public TextWriter OutWriter { get; private set; }
|
||||
public bool StopOnError { get; set; }
|
||||
|
||||
public IWorkItemDispatcher Dispatcher { get; set; }
|
||||
|
||||
public ParallelScope ParallelScope { get; set; }
|
||||
public string WorkerId { get; private set; }
|
||||
public Randomizer RandomGenerator { get; private set; }
|
||||
public ValueFormatter CurrentValueFormatter { get; private set; }
|
||||
public bool IsSingleThreaded { get; set; }
|
||||
public BeforeAfterTestCommandState SetUpTearDownState { get; set; }
|
||||
public BeforeAfterTestCommandState OuterUnityTestActionState { get; set; }
|
||||
public EnumerableTestState EnumerableTestState { get; set; }
|
||||
|
||||
internal int AssertCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _assertCount;
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementAssertCount()
|
||||
{
|
||||
_assertCount += 1;
|
||||
}
|
||||
|
||||
public void AddFormatter(ValueFormatterFactory formatterFactory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 59ff995fabb3bac45afa0f96f333e5dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Execution;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal abstract class UnityWorkItem
|
||||
{
|
||||
protected readonly WorkItemFactory m_Factory;
|
||||
protected bool m_ExecuteTestStartEvent;
|
||||
protected bool m_DontRunRestoringResult;
|
||||
protected const int k_DefaultTimeout = 1000 * 180;
|
||||
public event EventHandler Completed;
|
||||
|
||||
public bool ResultedInDomainReload { get; internal set; }
|
||||
|
||||
public UnityTestExecutionContext Context { get; private set; }
|
||||
|
||||
public Test Test { get; private set; }
|
||||
|
||||
public TestResult Result { get; protected set; }
|
||||
|
||||
public WorkItemState State { get; private set; }
|
||||
|
||||
public List<ITestAction> Actions { get; private set; }
|
||||
|
||||
protected UnityWorkItem(Test test, WorkItemFactory factory)
|
||||
{
|
||||
m_Factory = factory;
|
||||
Test = test;
|
||||
Actions = new List<ITestAction>();
|
||||
Result = test.MakeTestResult();
|
||||
State = WorkItemState.Ready;
|
||||
m_ExecuteTestStartEvent = ShouldExecuteStartEvent();
|
||||
m_DontRunRestoringResult = ShouldRestore(test);
|
||||
}
|
||||
|
||||
protected static bool ShouldRestore(ITest loadedTest)
|
||||
{
|
||||
return UnityWorkItemDataHolder.alreadyExecutedTests != null &&
|
||||
UnityWorkItemDataHolder.alreadyExecutedTests.Contains(loadedTest.GetUniqueName());
|
||||
}
|
||||
|
||||
protected bool ShouldExecuteStartEvent()
|
||||
{
|
||||
return UnityWorkItemDataHolder.alreadyStartedTests != null &&
|
||||
UnityWorkItemDataHolder.alreadyStartedTests.All(x => x != Test.GetUniqueName()) &&
|
||||
!ShouldRestore(Test);
|
||||
}
|
||||
|
||||
protected abstract IEnumerable PerformWork();
|
||||
|
||||
public void InitializeContext(UnityTestExecutionContext context)
|
||||
{
|
||||
Context = context;
|
||||
|
||||
if (Test is TestAssembly)
|
||||
Actions.AddRange(ActionsHelper.GetActionsFromTestAssembly((TestAssembly)Test));
|
||||
else if (Test is ParameterizedMethodSuite)
|
||||
Actions.AddRange(ActionsHelper.GetActionsFromTestMethodInfo(Test.Method));
|
||||
else if (Test.TypeInfo != null)
|
||||
Actions.AddRange(ActionsHelper.GetActionsFromTypesAttributes(Test.TypeInfo.Type));
|
||||
}
|
||||
|
||||
public virtual IEnumerable Execute()
|
||||
{
|
||||
Context.CurrentTest = this.Test;
|
||||
Context.CurrentResult = this.Result;
|
||||
|
||||
if (m_ExecuteTestStartEvent)
|
||||
{
|
||||
Context.Listener.TestStarted(Test);
|
||||
}
|
||||
|
||||
Context.StartTime = DateTime.UtcNow;
|
||||
Context.StartTicks = Stopwatch.GetTimestamp();
|
||||
|
||||
State = WorkItemState.Running;
|
||||
|
||||
return PerformWork();
|
||||
}
|
||||
|
||||
protected void WorkItemComplete()
|
||||
{
|
||||
State = WorkItemState.Complete;
|
||||
|
||||
Result.StartTime = Context.StartTime;
|
||||
Result.EndTime = DateTime.UtcNow;
|
||||
|
||||
long tickCount = Stopwatch.GetTimestamp() - Context.StartTicks;
|
||||
double seconds = (double)tickCount / Stopwatch.Frequency;
|
||||
Result.Duration = seconds;
|
||||
|
||||
//Result.AssertCount += Context.AssertCount;
|
||||
|
||||
Context.Listener.TestFinished(Result);
|
||||
|
||||
if (Completed != null)
|
||||
Completed(this, EventArgs.Empty);
|
||||
|
||||
Context.TestObject = null;
|
||||
Test.Fixture = null;
|
||||
}
|
||||
|
||||
public virtual void Cancel(bool force)
|
||||
{
|
||||
Result.SetResult(ResultState.Cancelled, "Cancelled by user");
|
||||
Context.Listener.TestFinished(Result);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79ced2556f0af814a840b86232613ff1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal class UnityWorkItemDataHolder
|
||||
{
|
||||
public static List<string> alreadyStartedTests = new List<string>();
|
||||
public static List<string> alreadyExecutedTests;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b3e90046c38f1d4dad2e0d5a79e871c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,28 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions.Runner
|
||||
{
|
||||
internal abstract class WorkItemFactory
|
||||
{
|
||||
public UnityWorkItem Create(ITest loadedTest, ITestFilter filter)
|
||||
{
|
||||
TestSuite suite = loadedTest as TestSuite;
|
||||
if (suite != null)
|
||||
{
|
||||
return new CompositeWorkItem(suite, filter, this);
|
||||
}
|
||||
|
||||
var testMethod = (TestMethod)loadedTest;
|
||||
if (testMethod.Method.ReturnType.Type != typeof(IEnumerator))
|
||||
{
|
||||
return new DefaultTestWorkItem(testMethod, filter);
|
||||
}
|
||||
|
||||
return Create(testMethod, filter, loadedTest);
|
||||
}
|
||||
|
||||
protected abstract UnityWorkItem Create(TestMethod method, ITestFilter filter, ITest loadedTest);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c15bf0966eb95847a4260d830a30d30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,193 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Filters;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions
|
||||
{
|
||||
internal static class TestExtensions
|
||||
{
|
||||
private static IEnumerable<string> GetTestCategories(this ITest test)
|
||||
{
|
||||
var categories = test.Properties[PropertyNames.Category].Cast<string>().ToList();
|
||||
if (categories.Count == 0 && test is TestMethod)
|
||||
{
|
||||
// only mark tests as Uncategorized if the test fixture doesn't have a category,
|
||||
// otherwise the test inherits the Fixture category
|
||||
var fixtureCategories = test.Parent.Properties[PropertyNames.Category].Cast<string>().ToList();
|
||||
if (fixtureCategories.Count == 0)
|
||||
categories.Add(CategoryFilterExtended.k_DefaultCategory);
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static bool HasCategory(this ITest test, string[] categoryFilter)
|
||||
{
|
||||
var categories = test.GetAllCategoriesFromTest().Distinct();
|
||||
return categoryFilter.Any(c => categories.Any(r => r == c));
|
||||
}
|
||||
|
||||
public static List<string> GetAllCategoriesFromTest(this ITest test)
|
||||
{
|
||||
if (test.Parent == null)
|
||||
return test.GetTestCategories().ToList();
|
||||
|
||||
var categories = GetAllCategoriesFromTest(test.Parent);
|
||||
categories.AddRange(test.GetTestCategories());
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static void ParseForNameDuplicates(this ITest test)
|
||||
{
|
||||
var duplicates = new Dictionary<string, int>();
|
||||
for (var i = 0; i < test.Tests.Count; i++)
|
||||
{
|
||||
var child = test.Tests[i];
|
||||
int count;
|
||||
if (duplicates.TryGetValue(child.FullName, out count))
|
||||
{
|
||||
count++;
|
||||
child.Properties.Add("childIndex", count);
|
||||
duplicates[child.FullName] = count;
|
||||
}
|
||||
else
|
||||
{
|
||||
duplicates.Add(child.FullName, 1);
|
||||
}
|
||||
ParseForNameDuplicates(child);
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetChildIndex(this ITest test)
|
||||
{
|
||||
var index = test.Properties["childIndex"];
|
||||
return (int)index[0];
|
||||
}
|
||||
|
||||
public static bool HasChildIndex(this ITest test)
|
||||
{
|
||||
var index = test.Properties["childIndex"];
|
||||
return index.Count > 0;
|
||||
}
|
||||
|
||||
static string GetAncestorPath(ITest test)
|
||||
{
|
||||
var path = "";
|
||||
var testParent = test.Parent;
|
||||
|
||||
while (testParent != null && testParent.Parent != null && !string.IsNullOrEmpty(testParent.Name))
|
||||
{
|
||||
path = testParent.Name + "/" + path;
|
||||
testParent = testParent.Parent;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static string GetUniqueName(this ITest test)
|
||||
{
|
||||
var id = GetAncestorPath(test) + GetFullName(test);
|
||||
if (test.HasChildIndex())
|
||||
{
|
||||
var index = test.GetChildIndex();
|
||||
if (index >= 0)
|
||||
id += index;
|
||||
}
|
||||
if (test.IsSuite)
|
||||
{
|
||||
id += "[suite]";
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public static string GetFullName(this ITest test)
|
||||
{
|
||||
var typeInfo = test.TypeInfo ?? test.Parent?.TypeInfo ?? test.Tests.FirstOrDefault()?.TypeInfo;
|
||||
if (typeInfo == null)
|
||||
{
|
||||
return "[" + test.Name + "]";
|
||||
}
|
||||
|
||||
var assemblyId = typeInfo.Assembly.GetName().Name;
|
||||
if (assemblyId == test.Name)
|
||||
{
|
||||
return $"[{test.Name}]";
|
||||
}
|
||||
|
||||
return string.Format("[{0}][{1}]", assemblyId, test.FullName);
|
||||
}
|
||||
|
||||
public static string GetFullNameWithoutDllPath(this ITest test)
|
||||
{
|
||||
if (test.Parent == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
var typeInfo = test.TypeInfo ?? test.Parent?.TypeInfo;
|
||||
if (typeInfo == null && IsAssembly(test))
|
||||
{
|
||||
return test.Name;
|
||||
}
|
||||
|
||||
return test.FullName;
|
||||
}
|
||||
|
||||
private static bool IsAssembly(this ITest test)
|
||||
{
|
||||
return test.Parent.Parent == null;
|
||||
}
|
||||
|
||||
public static string GetSkipReason(this ITest test)
|
||||
{
|
||||
if (test.Properties.ContainsKey(PropertyNames.SkipReason))
|
||||
return (string)test.Properties.Get(PropertyNames.SkipReason);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetParentId(this ITest test)
|
||||
{
|
||||
if (test.Parent != null)
|
||||
return test.Parent.Id;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetParentFullName(this ITest test)
|
||||
{
|
||||
if (test.Parent != null)
|
||||
return test.Parent.FullName;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetParentUniqueName(this ITest test)
|
||||
{
|
||||
if (test.Parent != null)
|
||||
return GetUniqueName(test.Parent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static string GetFullName(string testFullName, int childIndex)
|
||||
{
|
||||
return childIndex != -1 ? GetIndexedTestCaseName(testFullName, childIndex) : testFullName;
|
||||
}
|
||||
private static string GetIndexedTestCaseName(string fullName, int index)
|
||||
{
|
||||
var generatedTestSuffix = " GeneratedTestCase" + index;
|
||||
if (fullName.EndsWith(")"))
|
||||
{
|
||||
// Test names from generated TestCaseSource look like Test(TestCaseSourceType)
|
||||
// This inserts a unique test case index in the name, so that it becomes Test(TestCaseSourceType GeneratedTestCase0)
|
||||
return fullName.Substring(0, fullName.Length - 1) + generatedTestSuffix + fullName[fullName.Length - 1];
|
||||
}
|
||||
|
||||
// In some cases there can be tests with duplicate names generated in other ways and they won't have () in their name
|
||||
// We just append a suffix at the end of the name in that case
|
||||
return fullName + generatedTestSuffix;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bc74398aa3944646ade4ee78cd57484
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestRunner.NUnitExtensions
|
||||
{
|
||||
internal static class TestResultExtensions
|
||||
{
|
||||
public static void RecordPrefixedException(this TestResult testResult, string prefix, Exception ex, ResultState resultState = null)
|
||||
|
||||
{
|
||||
if (ex is NUnitException)
|
||||
{
|
||||
ex = ex.InnerException;
|
||||
}
|
||||
|
||||
if (resultState == null)
|
||||
{
|
||||
resultState = testResult.ResultState == ResultState.Cancelled
|
||||
? ResultState.Cancelled
|
||||
: ResultState.Error;
|
||||
}
|
||||
|
||||
var exceptionMessage = ExceptionHelper.BuildMessage(ex);
|
||||
string stackTrace = "--" + prefix + NUnit.Env.NewLine + ExceptionHelper.BuildStackTrace(ex);
|
||||
if (testResult.StackTrace != null)
|
||||
{
|
||||
stackTrace = testResult.StackTrace + NUnit.Env.NewLine + stackTrace;
|
||||
}
|
||||
|
||||
if (testResult.Test.IsSuite)
|
||||
{
|
||||
resultState = resultState.WithSite(FailureSite.TearDown);
|
||||
}
|
||||
|
||||
if (ex is ResultStateException)
|
||||
{
|
||||
exceptionMessage = ex.Message;
|
||||
resultState = ((ResultStateException)ex).ResultState;
|
||||
stackTrace = StackFilter.Filter(ex.StackTrace);
|
||||
}
|
||||
|
||||
string message = (string.IsNullOrEmpty(prefix) ? "" : (prefix + " : ")) + exceptionMessage;
|
||||
if (testResult.Message != null)
|
||||
{
|
||||
message = testResult.Message + NUnit.Env.NewLine + message;
|
||||
}
|
||||
|
||||
testResult.SetResult(resultState, message, stackTrace);
|
||||
}
|
||||
|
||||
public static void RecordPrefixedError(this TestResult testResult, string prefix, string error, ResultState resultState = null)
|
||||
|
||||
{
|
||||
if (resultState == null)
|
||||
{
|
||||
resultState = testResult.ResultState == ResultState.Cancelled
|
||||
? ResultState.Cancelled
|
||||
: ResultState.Error;
|
||||
}
|
||||
|
||||
if (testResult.Test.IsSuite)
|
||||
{
|
||||
resultState = resultState.WithSite(FailureSite.TearDown);
|
||||
}
|
||||
|
||||
string message = (string.IsNullOrEmpty(prefix) ? "" : (prefix + " : ")) + error;
|
||||
if (testResult.Message != null)
|
||||
{
|
||||
message = testResult.Message + NUnit.Env.NewLine + message;
|
||||
}
|
||||
|
||||
testResult.SetResult(resultState, message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65fb6da362a78334ab360a125cfafdaf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using NUnit;
|
||||
using NUnit.Framework.Api;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
|
||||
namespace UnityEngine.TestTools.NUnitExtensions
|
||||
{
|
||||
internal class UnityTestAssemblyBuilder : DefaultTestAssemblyBuilder, IAsyncTestAssemblyBuilder
|
||||
{
|
||||
private readonly string m_ProductName;
|
||||
private readonly ITestSuiteModifier[] m_TestSuiteModifiers;
|
||||
public UnityTestAssemblyBuilder(string[] orderedTestNames)
|
||||
{
|
||||
m_TestSuiteModifiers = orderedTestNames != null && orderedTestNames.Length > 0
|
||||
? new ITestSuiteModifier[] {new OrderedTestSuiteModifier(orderedTestNames)}
|
||||
: new ITestSuiteModifier[0];
|
||||
m_ProductName = Application.productName;
|
||||
}
|
||||
|
||||
public ITest Build(Assembly[] assemblies, TestPlatform[] testPlatforms, IDictionary<string, object> options)
|
||||
{
|
||||
var test = BuildAsync(assemblies, testPlatforms, options);
|
||||
while (test.MoveNext())
|
||||
{
|
||||
}
|
||||
|
||||
return test.Current;
|
||||
}
|
||||
|
||||
public IEnumerator<ITest> BuildAsync(Assembly[] assemblies, TestPlatform[] testPlatforms, IDictionary<string, object> options)
|
||||
{
|
||||
var productName = string.Join("_", m_ProductName.Split(Path.GetInvalidFileNameChars()));
|
||||
var suite = new TestSuite(productName);
|
||||
for (var index = 0; index < assemblies.Length; index++)
|
||||
{
|
||||
var assembly = assemblies[index];
|
||||
var platform = testPlatforms[index];
|
||||
|
||||
var assemblySuite = Build(assembly, options) as TestSuite;
|
||||
if (assemblySuite != null && assemblySuite.HasChildren)
|
||||
{
|
||||
assemblySuite.Properties.Set("platform", platform);
|
||||
suite.Add(assemblySuite);
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
suite.ParseForNameDuplicates();
|
||||
|
||||
foreach (var testSuiteModifier in m_TestSuiteModifiers)
|
||||
{
|
||||
suite = testSuiteModifier.ModifySuite(suite);
|
||||
}
|
||||
|
||||
yield return suite;
|
||||
}
|
||||
|
||||
public static Dictionary<string, object> GetNUnitTestBuilderSettings(TestPlatform testPlatform)
|
||||
{
|
||||
var emptySettings = new Dictionary<string, object>();
|
||||
emptySettings.Add(FrameworkPackageSettings.TestParameters, "platform=" + testPlatform);
|
||||
return emptySettings;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98ba0396e4b4ee8498a8f097affcfddf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ddb9e1c877ea80479d1eab4ddaa5d0d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 61e236e8570a95e4eb754fb291e102e0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,47 @@
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.Callbacks
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
internal class PlayModeRunnerCallback : MonoBehaviour, ITestRunnerListener
|
||||
{
|
||||
private TestResultRenderer m_ResultRenderer;
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
Application.logMessageReceivedThreaded -= LogRecieved;
|
||||
if (Camera.main == null)
|
||||
{
|
||||
gameObject.AddComponent<Camera>();
|
||||
}
|
||||
m_ResultRenderer = new TestResultRenderer(testResults);
|
||||
m_ResultRenderer.ShowResults();
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (m_ResultRenderer != null)
|
||||
m_ResultRenderer.Draw();
|
||||
}
|
||||
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
Application.logMessageReceivedThreaded += LogRecieved;
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
}
|
||||
|
||||
private void LogRecieved(string message, string stacktrace, LogType type)
|
||||
{
|
||||
if (TestContext.Out != null)
|
||||
TestContext.Out.WriteLine(message);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3cf5cb9e1ef590c48b1f919f2a7bd895
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,45 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.Networking.PlayerConnection;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.Callbacks
|
||||
{
|
||||
internal class PlayerQuitHandler : MonoBehaviour, ITestRunnerListener
|
||||
{
|
||||
public void Start()
|
||||
{
|
||||
PlayerConnection.instance.Register(PlayerConnectionMessageIds.quitPlayerMessageId, ProcessPlayerQuiteMessage);
|
||||
}
|
||||
|
||||
private void ProcessPlayerQuiteMessage(MessageEventArgs arg0)
|
||||
{
|
||||
//Some platforms don't quit, so we need to disconnect to make sure they will not connect to another editor instance automatically.
|
||||
PlayerConnection.instance.DisconnectAll();
|
||||
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
//XBOX has an error when quitting
|
||||
if (Application.platform == RuntimePlatform.XboxOne)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
Application.Quit();
|
||||
}
|
||||
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8ed0b11850145c4995dd76170bb2500
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,132 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.Networking.PlayerConnection;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.Callbacks
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
internal class RemoteTestResultSender : MonoBehaviour, ITestRunnerListener
|
||||
{
|
||||
private class QueueData
|
||||
{
|
||||
public Guid id { get; set; }
|
||||
public byte[] data { get; set; }
|
||||
}
|
||||
|
||||
private const int k_aliveMessageFrequency = 120;
|
||||
private float m_NextliveMessage = k_aliveMessageFrequency;
|
||||
private readonly Queue<QueueData> m_SendQueue = new Queue<QueueData>();
|
||||
private readonly object m_LockQueue = new object();
|
||||
private readonly IRemoteTestResultDataFactory m_TestResultDataFactory = new RemoteTestResultDataFactory();
|
||||
|
||||
public void Start()
|
||||
{
|
||||
StartCoroutine(SendDataRoutine());
|
||||
}
|
||||
|
||||
private byte[] SerializeObject(object objectToSerialize)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(JsonUtility.ToJson(objectToSerialize));
|
||||
}
|
||||
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
var data = SerializeObject(m_TestResultDataFactory.CreateFromTest(testsToRun));
|
||||
lock (m_LockQueue)
|
||||
{
|
||||
m_SendQueue.Enqueue(new QueueData
|
||||
{
|
||||
id = PlayerConnectionMessageIds.runStartedMessageId,
|
||||
data = data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
var data = SerializeObject(m_TestResultDataFactory.CreateFromTestResult(testResults));
|
||||
lock (m_LockQueue)
|
||||
{
|
||||
m_SendQueue.Enqueue(new QueueData { id = PlayerConnectionMessageIds.runFinishedMessageId, data = data, });
|
||||
}
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
var data = SerializeObject(m_TestResultDataFactory.CreateFromTest(test));
|
||||
lock (m_LockQueue)
|
||||
{
|
||||
m_SendQueue.Enqueue(new QueueData
|
||||
{
|
||||
id = PlayerConnectionMessageIds.testStartedMessageId,
|
||||
data = data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
var testRunnerResultForApi = m_TestResultDataFactory.CreateFromTestResult(result);
|
||||
var resultData = SerializeObject(testRunnerResultForApi);
|
||||
lock (m_LockQueue)
|
||||
{
|
||||
m_SendQueue.Enqueue(new QueueData
|
||||
{
|
||||
id = PlayerConnectionMessageIds.testFinishedMessageId,
|
||||
data = resultData,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator SendDataRoutine()
|
||||
{
|
||||
while (!PlayerConnection.instance.isConnected)
|
||||
{
|
||||
yield return new WaitForSeconds(1);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
lock (m_LockQueue)
|
||||
{
|
||||
if (PlayerConnection.instance.isConnected && m_SendQueue.Count > 0)
|
||||
{
|
||||
ResetNextPlayerAliveMessageTime();
|
||||
var queueData = m_SendQueue.Dequeue();
|
||||
PlayerConnection.instance.Send(queueData.id, queueData.data);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
//This is needed so we dont stall the player totally
|
||||
if (!m_SendQueue.Any())
|
||||
{
|
||||
SendAliveMessageIfNeeded();
|
||||
yield return new WaitForSeconds(0.02f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendAliveMessageIfNeeded()
|
||||
{
|
||||
if (Time.timeSinceLevelLoad < m_NextliveMessage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("Sending player alive message back to editor.");
|
||||
ResetNextPlayerAliveMessageTime();
|
||||
PlayerConnection.instance.Send(PlayerConnectionMessageIds.playerAliveHeartbeat, new byte[0]);
|
||||
}
|
||||
|
||||
private void ResetNextPlayerAliveMessageTime()
|
||||
{
|
||||
m_NextliveMessage = Time.timeSinceLevelLoad + k_aliveMessageFrequency;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20793418366caf14293b29c55df5e9ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,97 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.Callbacks
|
||||
{
|
||||
internal class TestResultRenderer
|
||||
{
|
||||
private static class Styles
|
||||
{
|
||||
public static readonly GUIStyle SucceedLabelStyle;
|
||||
public static readonly GUIStyle FailedLabelStyle;
|
||||
public static readonly GUIStyle FailedMessagesStyle;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
SucceedLabelStyle = new GUIStyle("label");
|
||||
SucceedLabelStyle.normal.textColor = Color.green;
|
||||
SucceedLabelStyle.fontSize = 48;
|
||||
|
||||
FailedLabelStyle = new GUIStyle("label");
|
||||
FailedLabelStyle.normal.textColor = Color.red;
|
||||
FailedLabelStyle.fontSize = 32;
|
||||
|
||||
FailedMessagesStyle = new GUIStyle("label");
|
||||
FailedMessagesStyle.wordWrap = false;
|
||||
FailedMessagesStyle.richText = true;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ITestResult> m_FailedTestCollection;
|
||||
|
||||
private bool m_ShowResults;
|
||||
private Vector2 m_ScrollPosition;
|
||||
|
||||
public TestResultRenderer(ITestResult testResults)
|
||||
{
|
||||
m_FailedTestCollection = new List<ITestResult>();
|
||||
GetFailedTests(testResults);
|
||||
}
|
||||
|
||||
private void GetFailedTests(ITestResult testResults)
|
||||
{
|
||||
if (testResults is TestCaseResult)
|
||||
{
|
||||
if (testResults.ResultState.Status == TestStatus.Failed)
|
||||
m_FailedTestCollection.Add(testResults);
|
||||
}
|
||||
else if (testResults.HasChildren)
|
||||
{
|
||||
foreach (var testResultsChild in testResults.Children)
|
||||
{
|
||||
GetFailedTests(testResultsChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const int k_MaxStringLength = 15000;
|
||||
|
||||
public void ShowResults()
|
||||
{
|
||||
m_ShowResults = true;
|
||||
Cursor.visible = true;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (!m_ShowResults) return;
|
||||
if (m_FailedTestCollection.Count == 0)
|
||||
{
|
||||
GUILayout.Label("All test(s) succeeded", Styles.SucceedLabelStyle, GUILayout.Width(600));
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = m_FailedTestCollection.Count;
|
||||
GUILayout.Label(count + " tests failed!", Styles.FailedLabelStyle);
|
||||
|
||||
m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUILayout.ExpandWidth(true));
|
||||
var text = "";
|
||||
|
||||
text += "<b><size=18>Code-based tests</size></b>\n";
|
||||
text += string.Join("\n", m_FailedTestCollection
|
||||
.Select(result => result.Name + " " + result.ResultState + "\n" + result.Message)
|
||||
.ToArray());
|
||||
|
||||
if (text.Length > k_MaxStringLength)
|
||||
text = text.Substring(0, k_MaxStringLength);
|
||||
|
||||
GUILayout.TextArea(text, Styles.FailedMessagesStyle);
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
if (GUILayout.Button("Close"))
|
||||
Application.Quit();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ebb87899ca30b743bb4274bc00c02b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,36 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.Callbacks
|
||||
{
|
||||
internal class TestResultRendererCallback : MonoBehaviour, ITestRunnerListener
|
||||
{
|
||||
private TestResultRenderer m_ResultRenderer;
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
if (Camera.main == null)
|
||||
{
|
||||
gameObject.AddComponent<Camera>();
|
||||
}
|
||||
m_ResultRenderer = new TestResultRenderer(testResults);
|
||||
m_ResultRenderer.ShowResults();
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
if (m_ResultRenderer != null)
|
||||
m_ResultRenderer.Draw();
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfc336f10b83bd74eaded16a658275c7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal interface ITestRunnerListener
|
||||
{
|
||||
void RunStarted(ITest testsToRun);
|
||||
void RunFinished(ITestResult testResults);
|
||||
void TestStarted(ITest test);
|
||||
void TestFinished(ITestResult result);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class TestFinishedEvent : UnityEvent<ITestResult> {}
|
||||
|
||||
[Serializable]
|
||||
internal class TestStartedEvent : UnityEvent<ITest> {}
|
||||
|
||||
[Serializable]
|
||||
internal class RunFinishedEvent : UnityEvent<ITestResult> {}
|
||||
|
||||
[Serializable]
|
||||
internal class RunStartedEvent : UnityEvent<ITest> {}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1b534518943030499685344fd1d476d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 256a0ca37fa972840bce7fca446e75e7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,28 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// In an Edit Mode test, you can use `IEditModeTestYieldInstruction` interface to implement your own instruction. There are also a couple of commonly used implementations available:
|
||||
/// - [EnterPlayMore](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/api/UnityEngine.TestTools.EnterPlayMode.html)
|
||||
/// - <see cref = "ExitPlayMode"/>
|
||||
/// - <see cref = "RecompileScripts"/>
|
||||
/// - <see cref = "WaitForDomainReload"/>
|
||||
/// </summary>
|
||||
public interface IEditModeTestYieldInstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not the instruction expects a domain reload to occur.
|
||||
/// </summary>
|
||||
bool ExpectDomainReload { get; }
|
||||
/// <summary>
|
||||
/// Whether or not the instruction expects the Unity Editor to be in **Play Mode**.
|
||||
/// </summary>
|
||||
bool ExpectedPlaymodeState { get; }
|
||||
/// <summary>
|
||||
/// Used to define multi-frame operations performed when instantiating a yield instruction.
|
||||
/// </summary>
|
||||
/// <returns>Enumerable collection of operations to perform.</returns>
|
||||
IEnumerator Perform();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 898bc38486fc899428fbe5bd6adfe473
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
[Serializable]
|
||||
[AddComponentMenu("")]
|
||||
internal class PlaymodeTestsController : MonoBehaviour
|
||||
{
|
||||
private IEnumerator m_TestSteps;
|
||||
|
||||
[SerializeField]
|
||||
private List<string> m_AssembliesWithTests;
|
||||
public List<string> AssembliesWithTests
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_AssembliesWithTests;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_AssembliesWithTests = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
internal TestStartedEvent testStartedEvent = new TestStartedEvent();
|
||||
[SerializeField]
|
||||
internal TestFinishedEvent testFinishedEvent = new TestFinishedEvent();
|
||||
[SerializeField]
|
||||
internal RunStartedEvent runStartedEvent = new RunStartedEvent();
|
||||
[SerializeField]
|
||||
internal RunFinishedEvent runFinishedEvent = new RunFinishedEvent();
|
||||
|
||||
internal const string kPlaymodeTestControllerName = "Code-based tests runner";
|
||||
|
||||
[SerializeField]
|
||||
public PlaymodeTestsControllerSettings settings = new PlaymodeTestsControllerSettings();
|
||||
|
||||
internal UnityTestAssemblyRunner m_Runner;
|
||||
|
||||
public IEnumerator Start()
|
||||
{
|
||||
//Skip 2 frame because Unity.
|
||||
yield return null;
|
||||
yield return null;
|
||||
StartCoroutine(Run());
|
||||
}
|
||||
|
||||
internal static bool IsControllerOnScene()
|
||||
{
|
||||
return GameObject.Find(kPlaymodeTestControllerName) != null;
|
||||
}
|
||||
|
||||
internal static PlaymodeTestsController GetController()
|
||||
{
|
||||
return GameObject.Find(kPlaymodeTestControllerName).GetComponent<PlaymodeTestsController>();
|
||||
}
|
||||
|
||||
public IEnumerator TestRunnerCoroutine()
|
||||
{
|
||||
while (m_TestSteps.MoveNext())
|
||||
{
|
||||
yield return m_TestSteps.Current;
|
||||
}
|
||||
|
||||
if (m_Runner.IsTestComplete)
|
||||
{
|
||||
runFinishedEvent.Invoke(m_Runner.Result);
|
||||
Cleanup();
|
||||
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator Run()
|
||||
{
|
||||
CoroutineTestWorkItem.monoBehaviourCoroutineRunner = this;
|
||||
gameObject.hideFlags |= HideFlags.DontSave;
|
||||
|
||||
if (settings.sceneBased)
|
||||
{
|
||||
SceneManager.LoadScene(1, LoadSceneMode.Additive);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
var testListUtil = new PlayerTestAssemblyProvider(new AssemblyLoadProxy(), m_AssembliesWithTests);
|
||||
m_Runner = new UnityTestAssemblyRunner(new UnityTestAssemblyBuilder(settings.orderedTestNames), new PlaymodeWorkItemFactory());
|
||||
|
||||
var loadedTests = m_Runner.Load(testListUtil.GetUserAssemblies().Select(a => a.Assembly).ToArray(), TestPlatform.PlayMode, UnityTestAssemblyBuilder.GetNUnitTestBuilderSettings(TestPlatform.PlayMode));
|
||||
loadedTests.ParseForNameDuplicates();
|
||||
runStartedEvent.Invoke(m_Runner.LoadedTest);
|
||||
|
||||
var testListenerWrapper = new TestListenerWrapper(testStartedEvent, testFinishedEvent);
|
||||
m_TestSteps = m_Runner.Run(testListenerWrapper, settings.BuildNUnitFilter()).GetEnumerator();
|
||||
|
||||
yield return TestRunnerCoroutine();
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
if (m_Runner != null)
|
||||
{
|
||||
m_Runner.StopRun();
|
||||
m_Runner = null;
|
||||
}
|
||||
if (Application.isEditor)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TryCleanup()
|
||||
{
|
||||
var controller = GetController();
|
||||
if (controller != null)
|
||||
{
|
||||
controller.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 102e512f651ee834f951a2516c1ea3b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestTools.TestRunner.GUI;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
[Serializable]
|
||||
internal class PlaymodeTestsControllerSettings
|
||||
{
|
||||
[SerializeField]
|
||||
public RuntimeTestRunnerFilter[] filters;
|
||||
public bool sceneBased;
|
||||
public string originalScene;
|
||||
public string bootstrapScene;
|
||||
public bool runInBackgroundValue;
|
||||
public bool consoleErrorPaused;
|
||||
public string[] orderedTestNames;
|
||||
|
||||
|
||||
public static PlaymodeTestsControllerSettings CreateRunnerSettings(RuntimeTestRunnerFilter[] filters, string[] orderedTestNames)
|
||||
{
|
||||
var settings = new PlaymodeTestsControllerSettings
|
||||
{
|
||||
filters = filters,
|
||||
sceneBased = false,
|
||||
originalScene = SceneManager.GetActiveScene().path,
|
||||
bootstrapScene = null,
|
||||
orderedTestNames = orderedTestNames
|
||||
};
|
||||
return settings;
|
||||
}
|
||||
|
||||
internal ITestFilter BuildNUnitFilter()
|
||||
{
|
||||
return new OrFilter(filters.Select(f => f.BuildNUnitFilter()).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2799eb4c84e72e54092a292cf626936b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91c20d2c22b8b3a4cb6c816bd225591a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
internal interface IRemoteTestResultDataFactory
|
||||
{
|
||||
RemoteTestResultDataWithTestData CreateFromTestResult(ITestResult result);
|
||||
RemoteTestResultDataWithTestData CreateFromTest(ITest test);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 874c0713cdc44f549b0161750b48d2c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
internal static class PlayerConnectionMessageIds
|
||||
{
|
||||
public static Guid runStartedMessageId { get { return new Guid("6a7f53dd-4672-461d-a7b5-9467e9393fd3"); } }
|
||||
public static Guid runFinishedMessageId { get { return new Guid("ffb622fc-34ad-4901-8d7b-47fb04b0bdd4"); } }
|
||||
public static Guid testStartedMessageId { get { return new Guid("b54d241e-d88d-4dba-8c8f-ee415d11c030"); } }
|
||||
public static Guid testFinishedMessageId { get { return new Guid("72f7b7f4-6829-4cd1-afde-78872b9d5adc"); } }
|
||||
public static Guid quitPlayerMessageId { get { return new Guid("ab44bfe0-bb50-4ee6-9977-69d2ea6bb3a0"); } }
|
||||
public static Guid playerAliveHeartbeat { get { return new Guid("8c0c307b-f7fd-4216-8623-35b4b3f55fb6"); } }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41d60936b62cc6d4ca7fe628b22b0e40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
[Serializable]
|
||||
internal class RemoteTestData
|
||||
{
|
||||
public string id;
|
||||
public string name;
|
||||
public string fullName;
|
||||
public int testCaseCount;
|
||||
public int ChildIndex;
|
||||
public bool hasChildren;
|
||||
public bool isSuite;
|
||||
public string[] childrenIds;
|
||||
public int testCaseTimeout;
|
||||
public string[] Categories;
|
||||
public bool IsTestAssembly;
|
||||
public RunState RunState;
|
||||
public string Description;
|
||||
public string SkipReason;
|
||||
public string ParentId;
|
||||
public string UniqueName;
|
||||
public string ParentUniqueName;
|
||||
public string ParentFullName;
|
||||
|
||||
internal RemoteTestData(ITest test)
|
||||
{
|
||||
id = test.Id;
|
||||
name = test.Name;
|
||||
fullName = test.FullName;
|
||||
testCaseCount = test.TestCaseCount;
|
||||
ChildIndex = -1;
|
||||
if (test.Properties["childIndex"].Count > 0)
|
||||
{
|
||||
ChildIndex = (int)test.Properties["childIndex"][0];
|
||||
}
|
||||
hasChildren = test.HasChildren;
|
||||
isSuite = test.IsSuite;
|
||||
childrenIds = test.Tests.Select(t => t.Id).ToArray();
|
||||
Categories = test.GetAllCategoriesFromTest().ToArray();
|
||||
IsTestAssembly = test is TestAssembly;
|
||||
RunState = (RunState)Enum.Parse(typeof(RunState), test.RunState.ToString());
|
||||
Description = (string)test.Properties.Get(PropertyNames.Description);
|
||||
SkipReason = test.GetSkipReason();
|
||||
ParentId = test.GetParentId();
|
||||
UniqueName = test.GetUniqueName();
|
||||
ParentUniqueName = test.GetParentUniqueName();
|
||||
ParentFullName = test.GetParentFullName();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b135ec222fdcd11468014c90d11d6821
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
[Serializable]
|
||||
internal class RemoteTestResultData
|
||||
{
|
||||
public string testId;
|
||||
public string name;
|
||||
public string fullName;
|
||||
public string resultState;
|
||||
public TestStatus testStatus;
|
||||
public double duration;
|
||||
public DateTime startTime;
|
||||
public DateTime endTime;
|
||||
public string message;
|
||||
public string stackTrace;
|
||||
public int assertCount;
|
||||
public int failCount;
|
||||
public int passCount;
|
||||
public int skipCount;
|
||||
public int inconclusiveCount;
|
||||
public bool hasChildren;
|
||||
public string output;
|
||||
public string xml;
|
||||
public string[] childrenIds;
|
||||
|
||||
internal RemoteTestResultData(ITestResult result, bool isTopLevel)
|
||||
{
|
||||
testId = result.Test.Id;
|
||||
name = result.Name;
|
||||
fullName = result.FullName;
|
||||
resultState = result.ResultState.ToString();
|
||||
testStatus = result.ResultState.Status;
|
||||
duration = result.Duration;
|
||||
startTime = result.StartTime;
|
||||
endTime = result.EndTime;
|
||||
message = result.Message;
|
||||
stackTrace = result.StackTrace;
|
||||
assertCount = result.AssertCount;
|
||||
failCount = result.FailCount;
|
||||
passCount = result.PassCount;
|
||||
skipCount = result.SkipCount;
|
||||
inconclusiveCount = result.InconclusiveCount;
|
||||
hasChildren = result.HasChildren;
|
||||
output = result.Output;
|
||||
if (isTopLevel)
|
||||
{
|
||||
xml = result.ToXml(true).OuterXml;
|
||||
}
|
||||
childrenIds = result.Children.Select(child => child.Test.Id).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03e4d63665d06f04c8a6cf68133c1592
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,51 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
internal class RemoteTestResultDataFactory : IRemoteTestResultDataFactory
|
||||
{
|
||||
public RemoteTestResultDataWithTestData CreateFromTestResult(ITestResult result)
|
||||
{
|
||||
var tests = CreateTestDataList(result.Test);
|
||||
tests.First().testCaseTimeout = UnityTestExecutionContext.CurrentContext.TestCaseTimeout;
|
||||
return new RemoteTestResultDataWithTestData()
|
||||
{
|
||||
results = CreateTestResultDataList(result),
|
||||
tests = tests
|
||||
};
|
||||
}
|
||||
|
||||
public RemoteTestResultDataWithTestData CreateFromTest(ITest test)
|
||||
{
|
||||
var tests = CreateTestDataList(test);
|
||||
if (UnityTestExecutionContext.CurrentContext != null)
|
||||
{
|
||||
tests.First().testCaseTimeout = UnityTestExecutionContext.CurrentContext.TestCaseTimeout;
|
||||
}
|
||||
|
||||
return new RemoteTestResultDataWithTestData()
|
||||
{
|
||||
tests = tests
|
||||
};
|
||||
}
|
||||
|
||||
private RemoteTestData[] CreateTestDataList(ITest test)
|
||||
{
|
||||
var list = new List<RemoteTestData>();
|
||||
list.Add(new RemoteTestData(test));
|
||||
list.AddRange(test.Tests.SelectMany(CreateTestDataList));
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
private static RemoteTestResultData[] CreateTestResultDataList(ITestResult result)
|
||||
{
|
||||
var list = new List<RemoteTestResultData>();
|
||||
list.Add(new RemoteTestResultData(result, result.Test.Parent == null));
|
||||
list.AddRange(result.Children.SelectMany(CreateTestResultDataList));
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 826b6becaef90fb458eedebe4c2f3664
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
|
||||
namespace UnityEngine.TestRunner.TestLaunchers
|
||||
{
|
||||
[Serializable]
|
||||
internal class RemoteTestResultDataWithTestData
|
||||
{
|
||||
public RemoteTestResultData[] results;
|
||||
public RemoteTestData[] tests;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 475e3699f219c854f8581a9838135002
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Filters;
|
||||
using FullNameFilter = UnityEngine.TestRunner.NUnitExtensions.Filters.FullNameFilter;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.GUI
|
||||
{
|
||||
[Serializable]
|
||||
internal class RuntimeTestRunnerFilter
|
||||
{
|
||||
public string[] assemblyNames;
|
||||
public string[] groupNames;
|
||||
public string[] categoryNames;
|
||||
public string[] testNames;
|
||||
public bool synchronousOnly = false;
|
||||
|
||||
public ITestFilter BuildNUnitFilter()
|
||||
{
|
||||
var filters = new List<ITestFilter>();
|
||||
|
||||
AddFilters(filters, testNames, (s) => new FullNameFilter(s));
|
||||
AddFilters(filters, groupNames, OptimizedGroupFilter);
|
||||
AddFilters(filters, assemblyNames, (s) => new AssemblyNameFilter(s));
|
||||
AddFilters(filters, categoryNames, (s) => new CategoryFilterExtended(s) {IsRegex = true});
|
||||
|
||||
if (synchronousOnly)
|
||||
{
|
||||
filters.Add(new SynchronousFilter());
|
||||
}
|
||||
|
||||
return filters.Count == 0 ? TestFilter.Empty : new AndFilter(filters.ToArray());
|
||||
}
|
||||
|
||||
static FullNameFilter OptimizedGroupFilter(string s)
|
||||
{
|
||||
if (s.Length >= 2)
|
||||
{
|
||||
// A common case is that the regex we are filtering by is of the form
|
||||
// ^JUST_A_NAME$
|
||||
// so we have a regex that matches _precisely_ one string. This can be done without a regex, which
|
||||
// is much much faster.
|
||||
if (s[0] == '^' && s[s.Length - 1] == '$')
|
||||
{
|
||||
var raw = s.Substring(1, s.Length - 2);
|
||||
var escaped = Regex.Escape(raw);
|
||||
// if a regex is the same when we escape it, it means that it doesn't contain any characters
|
||||
// with any meaning in the regex. Hence the regex is just a plain name.
|
||||
if (raw.Equals(escaped, StringComparison.Ordinal))
|
||||
return new FullNameFilter(raw);
|
||||
}
|
||||
}
|
||||
|
||||
return new FullNameFilter(s) { IsRegex = true };
|
||||
}
|
||||
|
||||
private static void AddFilters(List<ITestFilter> filters, string[] values, Func<string, TestFilter> builder)
|
||||
{
|
||||
if (values == null || values.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inclusionFilters = values.Where(v => !v.StartsWith("!")).Select(v => builder(v) as ITestFilter).ToArray();
|
||||
var exclusionFilters = values.Where(v => v.StartsWith("!"))
|
||||
.Select(v => new NotFilter(builder(v.Substring(1))) as ITestFilter)
|
||||
.ToArray();
|
||||
if (inclusionFilters.Length > 0 && exclusionFilters.Length > 0)
|
||||
{
|
||||
filters.Add(new AndFilter(new OrFilter(inclusionFilters), new AndFilter(exclusionFilters)));
|
||||
}
|
||||
else if (inclusionFilters.Length > 0)
|
||||
{
|
||||
filters.Add(new OrFilter(inclusionFilters));
|
||||
}
|
||||
else // Only exclusionFilters
|
||||
{
|
||||
filters.Add(new AndFilter(exclusionFilters));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a025ba7ee40d0104db8d08b1d9eabb0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,51 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner.GUI
|
||||
{
|
||||
class SynchronousFilter : ITestFilter
|
||||
{
|
||||
public TNode ToXml(bool recursive)
|
||||
{
|
||||
return new TNode("synchronousOnly");
|
||||
}
|
||||
|
||||
public TNode AddToXml(TNode parentNode, bool recursive)
|
||||
{
|
||||
return parentNode.AddElement("synchronousOnly");
|
||||
}
|
||||
|
||||
public bool Pass(ITest test)
|
||||
{
|
||||
if (test.Method == null)
|
||||
return true;
|
||||
|
||||
if (test.Method.ReturnType.Type == typeof(IEnumerator))
|
||||
return false;
|
||||
|
||||
if (test.Method.GetCustomAttributes<IOuterUnityTestAction>(true).Any())
|
||||
return false;
|
||||
|
||||
if (test.TypeInfo?.Type != null)
|
||||
{
|
||||
if (Reflect.GetMethodsWithAttribute(test.TypeInfo.Type, typeof(UnitySetUpAttribute), true)
|
||||
.Any(mi => mi.ReturnType == typeof(System.Collections.IEnumerator)))
|
||||
return false;
|
||||
|
||||
if (Reflect.GetMethodsWithAttribute(test.TypeInfo.Type, typeof(UnityTearDownAttribute), true)
|
||||
.Any(mi => mi.ReturnType == typeof(System.Collections.IEnumerator)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsExplicitMatch(ITest test)
|
||||
{
|
||||
return Pass(test);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9aec9d3b0a86466ab4647d01e8fc87d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class TestEnumeratorWrapper
|
||||
{
|
||||
private readonly TestMethod m_TestMethod;
|
||||
|
||||
public TestEnumeratorWrapper(TestMethod testMethod)
|
||||
{
|
||||
m_TestMethod = testMethod;
|
||||
}
|
||||
|
||||
public IEnumerator GetEnumerator(ITestExecutionContext context)
|
||||
{
|
||||
if (m_TestMethod.Method.ReturnType.Type == typeof(IEnumerator))
|
||||
{
|
||||
return HandleEnumerableTest(context);
|
||||
}
|
||||
var message = string.Format("Return type {0} of {1} in {2} is not supported.",
|
||||
m_TestMethod.Method.ReturnType, m_TestMethod.Method.Name, m_TestMethod.Method.TypeInfo.FullName);
|
||||
if (m_TestMethod.Method.ReturnType.Type == typeof(IEnumerable))
|
||||
{
|
||||
message += "\nDid you mean IEnumerator?";
|
||||
}
|
||||
throw new InvalidSignatureException(message);
|
||||
}
|
||||
|
||||
private IEnumerator HandleEnumerableTest(ITestExecutionContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
return m_TestMethod.Method.MethodInfo.Invoke(context.TestObject, m_TestMethod.parms != null ? m_TestMethod.parms.OriginalArguments : null) as IEnumerator;
|
||||
}
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
if (e.InnerException is IgnoreException)
|
||||
{
|
||||
context.CurrentResult.SetResult(ResultState.Ignored, e.InnerException.Message);
|
||||
return null;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ad0b0c865b01af4ca1b414689e71259
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,30 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools.TestRunner
|
||||
{
|
||||
internal class TestListenerWrapper : ITestListener
|
||||
{
|
||||
private readonly TestFinishedEvent m_TestFinishedEvent;
|
||||
private readonly TestStartedEvent m_TestStartedEvent;
|
||||
|
||||
public TestListenerWrapper(TestStartedEvent testStartedEvent, TestFinishedEvent testFinishedEvent)
|
||||
{
|
||||
m_TestStartedEvent = testStartedEvent;
|
||||
m_TestFinishedEvent = testFinishedEvent;
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
m_TestStartedEvent.Invoke(test);
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
m_TestFinishedEvent.Invoke(result);
|
||||
}
|
||||
|
||||
public void TestOutput(TestOutput output)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73deb9b8722aa284eab27c4dc90956c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A flag indicating the targeted test platforms.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
[Serializable]
|
||||
public enum TestPlatform : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Both platforms.
|
||||
/// </summary>
|
||||
All = 0xFF,
|
||||
/// <summary>
|
||||
/// The EditMode test platform.
|
||||
/// </summary>
|
||||
EditMode = 1 << 1,
|
||||
/// <summary>
|
||||
/// The PlayMode test platform.
|
||||
/// </summary>
|
||||
PlayMode = 1 << 2
|
||||
}
|
||||
|
||||
internal static class TestPlatformEnumExtensions
|
||||
{
|
||||
public static bool IsFlagIncluded(this TestPlatform flags, TestPlatform flag)
|
||||
{
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 743879b4db4bc1a4b829aae4386f4acf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "UnityEngine.TestRunner",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": []
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27619889b8ba8c24980f49ee34dbb44a
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb32bccaf32a6db448d1c0cc99c78688
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51557afa652635743b264a309f0a5c60
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal class AssemblyLoadProxy : IAssemblyLoadProxy
|
||||
{
|
||||
public IAssemblyWrapper Load(string assemblyString)
|
||||
{
|
||||
return new AssemblyWrapper(Assembly.Load(assemblyString));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb593906b7b6d824087dcaebf6c082e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal class AssemblyWrapper : IAssemblyWrapper
|
||||
{
|
||||
public AssemblyWrapper(Assembly assembly)
|
||||
{
|
||||
Assembly = assembly;
|
||||
Name = assembly.GetName();
|
||||
}
|
||||
|
||||
public Assembly Assembly { get; }
|
||||
|
||||
public AssemblyName Name { get; }
|
||||
|
||||
public virtual string Location
|
||||
{
|
||||
get
|
||||
{
|
||||
//Some platforms dont support this
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual AssemblyName[] GetReferencedAssemblies()
|
||||
{
|
||||
//Some platforms dont support this
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e3b9bbf2c1a3cd4f88883ca32882ec6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,7 @@
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal interface IAssemblyLoadProxy
|
||||
{
|
||||
IAssemblyWrapper Load(string assemblyString);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12dfd4bdbb5c8e6419432fbc54ef25d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,12 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal interface IAssemblyWrapper
|
||||
{
|
||||
Assembly Assembly { get; }
|
||||
AssemblyName Name { get; }
|
||||
string Location { get; }
|
||||
AssemblyName[] GetReferencedAssemblies();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c5afe945b715e149a70113a4be7b32a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,7 @@
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal interface IScriptingRuntimeProxy
|
||||
{
|
||||
string[] GetAllUserAssemblies();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe4aef60e4ace544c8430da8ef8acba2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal class PlayerTestAssemblyProvider
|
||||
{
|
||||
private IAssemblyLoadProxy m_AssemblyLoadProxy;
|
||||
private readonly List<string> m_AssembliesToLoad;
|
||||
|
||||
//Cached until domain reload
|
||||
private static List<IAssemblyWrapper> m_LoadedAssemblies;
|
||||
|
||||
internal PlayerTestAssemblyProvider(IAssemblyLoadProxy assemblyLoadProxy, List<string> assembliesToLoad)
|
||||
{
|
||||
m_AssemblyLoadProxy = assemblyLoadProxy;
|
||||
m_AssembliesToLoad = assembliesToLoad;
|
||||
LoadAssemblies();
|
||||
}
|
||||
|
||||
public List<IAssemblyWrapper> GetUserAssemblies()
|
||||
{
|
||||
return m_LoadedAssemblies;
|
||||
}
|
||||
|
||||
private void LoadAssemblies()
|
||||
{
|
||||
if (m_LoadedAssemblies != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_LoadedAssemblies = new List<IAssemblyWrapper>();
|
||||
|
||||
foreach (var userAssembly in m_AssembliesToLoad)
|
||||
{
|
||||
IAssemblyWrapper a;
|
||||
try
|
||||
{
|
||||
a = m_AssemblyLoadProxy.Load(userAssembly);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (a != null)
|
||||
m_LoadedAssemblies.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43a3aec217baa9644a7cf34b5f93fed9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,10 @@
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal class ScriptingRuntimeProxy : IScriptingRuntimeProxy
|
||||
{
|
||||
public string[] GetAllUserAssemblies()
|
||||
{
|
||||
return ScriptingRuntime.GetAllUserAssemblies();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3a361a6ad1aff14ba8f48976e94ad76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
internal static class AttributeHelper
|
||||
{
|
||||
internal static Type GetTargetClassFromName(string targetClassName, Type attributeInterface)
|
||||
{
|
||||
Type targetClass = null;
|
||||
foreach (var assemblyName in ScriptingRuntime.GetAllUserAssemblies())
|
||||
{
|
||||
// we need to pass the assembly name without the .dll extension, so removing that first
|
||||
var name = Path.GetFileNameWithoutExtension(assemblyName);
|
||||
targetClass = Type.GetType(targetClassName + "," + name);
|
||||
if (targetClass != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (targetClass == null)
|
||||
{
|
||||
Debug.LogWarningFormat("Class type not found: " + targetClassName);
|
||||
return null;
|
||||
}
|
||||
|
||||
ValidateTargetClass(targetClass, attributeInterface);
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
private static void ValidateTargetClass(Type targetClass, Type attributeInterface)
|
||||
{
|
||||
var constructorInfos = targetClass.GetConstructors();
|
||||
if (constructorInfos.All(constructor => constructor.GetParameters().Length != 0))
|
||||
{
|
||||
Debug.LogWarningFormat("{0} does not implement default constructor", targetClass.Name);
|
||||
}
|
||||
|
||||
if (!attributeInterface.IsAssignableFrom(targetClass))
|
||||
{
|
||||
Debug.LogWarningFormat("{0} does not implement {1}", targetClass.Name, attributeInterface.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae8ce3ffe04ac2c42945fd27e0291fc3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compare two Color objects. ColorEqualityComparer.Instance has default calculation error value set to 0.01f. To set a test specific error value instantiate a comparer instance using the one argument constructor.
|
||||
/// </summary>
|
||||
public class ColorEqualityComparer : IEqualityComparer<Color>
|
||||
{
|
||||
private const float k_DefaultError = 0.01f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
|
||||
private static readonly ColorEqualityComparer m_Instance = new ColorEqualityComparer();
|
||||
/// <summary>
|
||||
///A singleton instance of the comparer with a default error value set to 0.01f.
|
||||
/// </summary>
|
||||
public static ColorEqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
private ColorEqualityComparer() : this(k_DefaultError)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates an instance of the comparer with a custom error value.
|
||||
/// </summary>
|
||||
/// <param name="error">The custom error value.</param>
|
||||
public ColorEqualityComparer(float error)
|
||||
{
|
||||
this.AllowedError = error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the actual and expected Color objects for equality using <see cref="Utils.AreFloatsEqualAbsoluteError"/> to compare the RGB and Alpha attributes of Color. Returns true if expected and actual objects are equal otherwise, it returns false.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected Color value used to compare.</param>
|
||||
/// <param name="actual">The actual Color value to test.</param>
|
||||
/// <returns>True if actual and expected are equal, false otherwise</returns>
|
||||
/// <example>
|
||||
///<code>
|
||||
/// [TestFixture]
|
||||
/// public class ColorEqualityTest
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void GivenColorsAreEqual_WithAllowedCalculationError()
|
||||
/// {
|
||||
/// // Using default error
|
||||
/// var firstColor = new Color(0f, 0f, 0f, 0f);
|
||||
/// var secondColor = new Color(0f, 0f, 0f, 0f);
|
||||
///
|
||||
/// Assert.That(firstColor, Is.EqualTo(secondColor).Using(ColorEqualityComparer.Instance));
|
||||
///
|
||||
/// // Allowed error 10e-5f
|
||||
/// var comparer = new ColorEqualityComparer(10e-5f);
|
||||
/// firstColor = new Color(0f, 0f, 0f, 1f);
|
||||
/// secondColor = new Color(10e-6f, 0f, 0f, 1f);
|
||||
///
|
||||
/// Assert.That(firstColor, Is.EqualTo(secondColor).Using(comparer));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Color expected, Color actual)
|
||||
{
|
||||
return Utils.AreFloatsEqualAbsoluteError(expected.r, actual.r, AllowedError) &&
|
||||
Utils.AreFloatsEqualAbsoluteError(expected.g, actual.g, AllowedError) &&
|
||||
Utils.AreFloatsEqualAbsoluteError(expected.b, actual.b, AllowedError) &&
|
||||
Utils.AreFloatsEqualAbsoluteError(expected.a, actual.a, AllowedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="color">A not null Color object.</param>
|
||||
/// <returns>Returns 0.</returns>
|
||||
public int GetHashCode(Color color)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6105bc8cf5ce544487daca4cbc62583
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal class CoroutineRunner
|
||||
{
|
||||
private bool m_Running;
|
||||
private bool m_Timeout;
|
||||
private readonly MonoBehaviour m_Controller;
|
||||
private readonly UnityTestExecutionContext m_Context;
|
||||
private Coroutine m_TimeOutCoroutine;
|
||||
private IEnumerator m_TestCoroutine;
|
||||
|
||||
internal const int k_DefaultTimeout = 1000 * 180;
|
||||
|
||||
public CoroutineRunner(MonoBehaviour playmodeTestsController, UnityTestExecutionContext context)
|
||||
{
|
||||
m_Controller = playmodeTestsController;
|
||||
m_Context = context;
|
||||
}
|
||||
|
||||
public IEnumerator HandleEnumerableTest(IEnumerator testEnumerator)
|
||||
{
|
||||
if (m_Context.TestCaseTimeout == 0)
|
||||
{
|
||||
m_Context.TestCaseTimeout = k_DefaultTimeout;
|
||||
}
|
||||
do
|
||||
{
|
||||
if (!m_Running)
|
||||
{
|
||||
m_Running = true;
|
||||
m_TestCoroutine = ExMethod(testEnumerator, m_Context.TestCaseTimeout);
|
||||
m_Controller.StartCoroutine(m_TestCoroutine);
|
||||
}
|
||||
if (m_Context.ExecutionStatus == TestExecutionStatus.StopRequested || m_Context.ExecutionStatus == TestExecutionStatus.AbortRequested)
|
||||
{
|
||||
StopAllRunningCoroutines();
|
||||
yield break;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
while (m_Running);
|
||||
}
|
||||
|
||||
private void StopAllRunningCoroutines()
|
||||
{
|
||||
if (m_TimeOutCoroutine != null)
|
||||
{
|
||||
m_Controller.StopCoroutine(m_TimeOutCoroutine);
|
||||
}
|
||||
|
||||
if (m_TestCoroutine != null)
|
||||
{
|
||||
m_Controller.StopCoroutine(m_TestCoroutine);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator ExMethod(IEnumerator e, int timeout)
|
||||
{
|
||||
m_TimeOutCoroutine = m_Controller.StartCoroutine(StartTimer(e, timeout,
|
||||
() =>
|
||||
{
|
||||
m_Timeout = true;
|
||||
m_Running = false;
|
||||
}));
|
||||
|
||||
yield return m_Controller.StartCoroutine(e);
|
||||
m_Controller.StopCoroutine(m_TimeOutCoroutine);
|
||||
m_Running = false;
|
||||
}
|
||||
|
||||
private IEnumerator StartTimer(IEnumerator coroutineToBeKilled, int timeout, Action onTimeout)
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(timeout / 1000f);
|
||||
if (coroutineToBeKilled != null)
|
||||
m_Controller.StopCoroutine(coroutineToBeKilled);
|
||||
if (onTimeout != null)
|
||||
{
|
||||
onTimeout();
|
||||
StopAllRunningCoroutines();
|
||||
m_Context.CurrentResult.RecordException(new UnityTestTimeoutException(m_Context.TestCaseTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasFailedWithTimeout()
|
||||
{
|
||||
return m_Timeout;
|
||||
}
|
||||
|
||||
public int GetDefaultTimeout()
|
||||
{
|
||||
return k_DefaultTimeout;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24a158219395ebf44a60547b97784ddc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compare two float values for equality with NUnit constraints. Use FloatEqualityComparer.Instance comparer to have the default error value set to 0.0001f. For any other error, use the one argument constructor to create a comparer.
|
||||
/// </summary>
|
||||
public class FloatEqualityComparer : IEqualityComparer<float>
|
||||
{
|
||||
private const float k_DefaultError = 0.0001f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
private static readonly FloatEqualityComparer m_Instance = new FloatEqualityComparer();
|
||||
/// <summary>
|
||||
///A singleton instance of the comparer with a default error value set to 0.0001f.
|
||||
/// </summary>
|
||||
public static FloatEqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
private FloatEqualityComparer() : this(k_DefaultError) {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of a FloatEqualityComparer with a custom error value instead of the default 0.0001f.
|
||||
/// </summary>
|
||||
/// <param name="allowedError">The custom error value</param>
|
||||
public FloatEqualityComparer(float allowedError)
|
||||
{
|
||||
this.AllowedError = allowedError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the actual and expected float values for equality using <see cref="Utils.AreFloatsEqual"/>.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected float value used to compare.</param>
|
||||
/// <param name="actual">The actual float value to test.</param>
|
||||
/// <returns>True if the values are equals, false otherwise.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class FloatsTest
|
||||
///{
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoFloatsAreEqual()
|
||||
/// {
|
||||
/// var comparer = new FloatEqualityComparer(10e-6f);
|
||||
/// var actual = -0.00009f;
|
||||
/// var expected = 0.00009f;
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(comparer));
|
||||
///
|
||||
/// // Default relative error 0.0001f
|
||||
/// actual = 10e-8f;
|
||||
/// expected = 0f;
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(FloatEqualityComparer.Instance));
|
||||
/// }
|
||||
///}
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(float expected, float actual)
|
||||
{
|
||||
return Utils.AreFloatsEqual(expected, actual, AllowedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="value">A not null float number.</param>
|
||||
/// <returns>Returns 0.</returns>
|
||||
public int GetHashCode(float value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af5042802f06c804c8abddd544b77a4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,21 @@
|
||||
using System.Collections;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// When implemented by an attribute, this interface implemented to provide actions to execute before setup and after teardown of tests.
|
||||
/// </summary>
|
||||
public interface IOuterUnityTestAction
|
||||
{
|
||||
/// <summary>Executed before each test is run</summary>
|
||||
/// <param name="test">The test that is going to be run.</param>
|
||||
/// <returns>Enumerable collection of actions to perform before test setup.</returns>
|
||||
IEnumerator BeforeTest(ITest test);
|
||||
|
||||
/// <summary>Executed after each test is run</summary>
|
||||
/// <param name="test">The test that has just been run.</param>
|
||||
/// <returns>Enumerable collection of actions to perform after test teardown.</returns>
|
||||
IEnumerator AfterTest(ITest test);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9c2a6302985d3846b7b9f6fd9e2da9a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,13 @@
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Implement this interface if you want to define a set of actions to execute as a post-build step. Cleanup runs right away for a standalone test run, but only after all the tests run within the Editor.
|
||||
/// </summary>
|
||||
public interface IPostBuildCleanup
|
||||
{
|
||||
/// <summary>
|
||||
/// Implement this method to specify actions that should run as a post-build cleanup step.
|
||||
/// </summary>
|
||||
void Cleanup();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff67c526455160f4690a44f74dee4cbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,59 @@
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Implement this interface if you want to define a set of actions to run as a pre-build step.
|
||||
/// </summary>
|
||||
public interface IPrebuildSetup
|
||||
{
|
||||
/// <summary>
|
||||
/// Implement this method to call actions automatically before the build process.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class CreateSpriteTest : IPrebuildSetup
|
||||
/// {
|
||||
/// Texture2D m_Texture;
|
||||
/// Sprite m_Sprite;
|
||||
///
|
||||
/// public void Setup()
|
||||
/// {
|
||||
/// #if UNITY_EDITOR
|
||||
/// var spritePath = "Assets/Resources/Circle.png";
|
||||
///
|
||||
/// var ti = UnityEditor.AssetImporter.GetAtPath(spritePath) as UnityEditor.TextureImporter;
|
||||
///
|
||||
/// ti.textureCompression = UnityEditor.TextureImporterCompression.Uncompressed;
|
||||
///
|
||||
/// ti.SaveAndReimport();
|
||||
/// #endif
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public void SetUpTest()
|
||||
/// {
|
||||
/// m_Texture = Resources.Load<Texture2D>("Circle");
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void WhenNullTextureIsPassed_CreateShouldReturnNullSprite()
|
||||
/// {
|
||||
/// // Check with Valid Texture.
|
||||
///
|
||||
/// LogAssert.Expect(LogType.Log, "Circle Sprite Created");
|
||||
///
|
||||
/// Sprite.Create(m_Texture, new Rect(0, 0, m_Texture.width, m_Texture.height), new Vector2(0.5f, 0.5f));
|
||||
///
|
||||
/// Debug.Log("Circle Sprite Created");
|
||||
///
|
||||
/// // Check with NULL Texture. Should return NULL Sprite.
|
||||
/// m_Sprite = Sprite.Create(null, new Rect(0, 0, m_Texture.width, m_Texture.height), new Vector2(0.5f, 0.5f));
|
||||
///
|
||||
/// Assert.That(m_Sprite, Is.Null, "Sprite created with null texture should be null");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
void Setup();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acc16f0c684508f44813662a300c574b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,31 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEngine.TestRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for getting callsbacks on test progress directly from NUnit. This is available both in the editor and directly in the runtime. It is registered by using <see cref="TestRunCallbackAttribute"/>.
|
||||
/// </summary>
|
||||
public interface ITestRunCallback
|
||||
{
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is started.
|
||||
/// </summary>
|
||||
/// <param name="testsToRun">The full loaded test tree.</param>
|
||||
void RunStarted(ITest testsToRun);
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is finished.
|
||||
/// </summary>
|
||||
/// <param name="testResults">The result of the test run.</param>
|
||||
void RunFinished(ITestResult testResults);
|
||||
/// <summary>
|
||||
/// A callback invoked when each individual node of the test tree has started executing.
|
||||
/// </summary>
|
||||
/// <param name="test">The test node currently executed.</param>
|
||||
void TestStarted(ITest test);
|
||||
/// <summary>
|
||||
/// A callback invoked when each individual node of the test tree has finished executing.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the test tree node after it had been executed.</param>
|
||||
void TestFinished(ITestResult result);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38d0b8a87b967304da08a2ae9b955066
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce8da628f68c7594b8b9a597fa52db7b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface implemented by a MonoBehaviour test.
|
||||
/// </summary>
|
||||
public interface IMonoBehaviourTest
|
||||
{
|
||||
/// <summary>True when the test is considered finished.</summary>
|
||||
bool IsTestFinished {get; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a002d3737b873954395b7cf862873ab8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,60 @@
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a wrapper that allows running tests on MonoBehaviour scripts. Inherits from <see cref="CustomYieldInstruction"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A MonoBehaviour component created for the test and attached to the tests [GameObject](https://docs.unity3d.com/ScriptReference/GameObject.html).</typeparam>
|
||||
public class MonoBehaviourTest<T> : CustomYieldInstruction where T : MonoBehaviour, IMonoBehaviourTest
|
||||
{
|
||||
/// <summary>A MonoBehaviour component created for the test and attached to the tests [GameObject](https://docs.unity3d.com/ScriptReference/GameObject.html).</summary>
|
||||
public T component { get; }
|
||||
/// <summary>
|
||||
/// A `GameObject` created as a container for the test component.
|
||||
/// </summary>
|
||||
public GameObject gameObject { get { return component.gameObject; } }
|
||||
/// <summary>
|
||||
/// `MonoBehaviourTest` is a [coroutine](https://docs.unity3d.com/ScriptReference/Coroutine.html) and a helper for writing MonoBehaviour tests.
|
||||
/// Yield a `MonoBehaviour`Test when using the `UnityTest` attribute to instantiate the `MonoBehaviour` you wish to test and wait for it to finish running. Implement the `IMonoBehaviourTest` interface on the `MonoBehaviour` to state when the test completes.
|
||||
/// </summary>
|
||||
/// <param name="dontDestroyOnLoad"></param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [UnityTest]
|
||||
/// public IEnumerator MonoBehaviourTest_Works()
|
||||
/// {
|
||||
/// yield return new MonoBehaviourTest<MyMonoBehaviourTest>();
|
||||
/// }
|
||||
///
|
||||
/// public class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest
|
||||
/// {
|
||||
/// private int frameCount;
|
||||
/// public bool IsTestFinished
|
||||
/// {
|
||||
/// get { return frameCount > 10; }
|
||||
/// }
|
||||
///
|
||||
/// void Update()
|
||||
/// {
|
||||
/// frameCount++;
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public MonoBehaviourTest(bool dontDestroyOnLoad = true)
|
||||
{
|
||||
var go = new GameObject("MonoBehaviourTest: " + typeof(T).FullName);
|
||||
component = go.AddComponent<T>();
|
||||
if (dontDestroyOnLoad)
|
||||
{
|
||||
Object.DontDestroyOnLoad(go);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// (Inherited) Returns `true`` if the test is not finished yet, which keeps the coroutine suspended
|
||||
/// </summary>
|
||||
public override bool keepWaiting
|
||||
{
|
||||
get { return !component.IsTestFinished; }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 164c9b1458eaab743a4b45c37a4d720d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// PostBuildCleanup attributes run if the respective test or test class is in the current test run. The test is included either by running all tests or setting a [filter](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/workflow-create-test.html#filters) that includes the test. If multiple tests reference the same pre-built setup or post-build cleanup, then it only runs once.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class PostBuildCleanupAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of PostBuildCleanupAttribute by type.
|
||||
/// </summary>
|
||||
/// <param name="targetClass">The type of the target class.</param>
|
||||
public PostBuildCleanupAttribute(Type targetClass)
|
||||
{
|
||||
TargetClass = targetClass;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of PostBuildCleanupAttribute by class name.
|
||||
/// </summary>
|
||||
/// <param name="targetClassName">The name of the target class.</param>
|
||||
public PostBuildCleanupAttribute(string targetClassName)
|
||||
{
|
||||
TargetClass = AttributeHelper.GetTargetClassFromName(targetClassName, typeof(IPostBuildCleanup));
|
||||
}
|
||||
|
||||
internal Type TargetClass { get; private set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 621fd19bcb071b64aa1d68f0271aa780
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
/// <summary>
|
||||
/// PrebuildSetup attribute run if the test or test class is in the current test run. The test is included either by running all tests or setting a filter that includes the test. If multiple tests reference the same pre-built setup or post-build cleanup, then it only runs once.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class PrebuildSetupAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes and returns an instance of PrebuildSetupAttribute by type.
|
||||
/// </summary>
|
||||
/// <param name="targetClass">The type of the target class.</param>
|
||||
public PrebuildSetupAttribute(Type targetClass)
|
||||
{
|
||||
TargetClass = targetClass;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="targetClassName"></param>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class CreateSpriteTest : IPrebuildSetup
|
||||
/// {
|
||||
/// Texture2D m_Texture;
|
||||
/// Sprite m_Sprite;
|
||||
///
|
||||
/// public void Setup()
|
||||
/// {
|
||||
///
|
||||
/// #if UNITY_EDITOR
|
||||
///
|
||||
/// var spritePath = "Assets/Resources/Circle.png";
|
||||
/// var ti = UnityEditor.AssetImporter.GetAtPath(spritePath) as UnityEditor.TextureImporter;
|
||||
/// ti.textureCompression = UnityEditor.TextureImporterCompression.Uncompressed;
|
||||
/// ti.SaveAndReimport();
|
||||
///
|
||||
/// #endif
|
||||
/// }
|
||||
///
|
||||
/// [SetUp]
|
||||
/// public void SetUpTest()
|
||||
/// {
|
||||
/// m_Texture = Resources.Load<Texture2D>("Circle");
|
||||
/// }
|
||||
///
|
||||
/// [Test]
|
||||
/// public void WhenNullTextureIsPassed_CreateShouldReturnNullSprite()
|
||||
/// {
|
||||
///
|
||||
/// // Check with Valid Texture.
|
||||
/// LogAssert.Expect(LogType.Log, "Circle Sprite Created");
|
||||
/// Sprite.Create(m_Texture, new Rect(0, 0, m_Texture.width, m_Texture.height), new Vector2(0.5f, 0.5f));
|
||||
/// Debug.Log("Circle Sprite Created");
|
||||
///
|
||||
/// // Check with NULL Texture. Should return NULL Sprite.
|
||||
/// m_Sprite = Sprite.Create(null, new Rect(0, 0, m_Texture.width, m_Texture.heig`t), new Vector2(0.5f, 0.5f));
|
||||
/// Assert.That(m_Sprite, Is.Null, "Sprite created with null texture should be null");
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// Tip: Use `#if UNITY_EDITOR` if you want to access Editor only APIs, but the setup/cleanup is inside a **Play Mode** assembly.
|
||||
/// </example>
|
||||
public PrebuildSetupAttribute(string targetClassName)
|
||||
{
|
||||
TargetClass = AttributeHelper.GetTargetClassFromName(targetClassName, typeof(IPrebuildSetup));
|
||||
}
|
||||
|
||||
internal Type TargetClass { get; private set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d1b7ce919aa8864409412e809073cf96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,80 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this utility to compare two Quaternion objects for equality
|
||||
/// with NUnit assertion constraints.
|
||||
/// Use the static instance QuaternionEqualityComparer.Instance
|
||||
/// to have the default calculation error value set to 0.00001f.
|
||||
/// For any other custom error value, use the one argument constructor.
|
||||
/// </summary>
|
||||
public class QuaternionEqualityComparer : IEqualityComparer<Quaternion>
|
||||
{
|
||||
private const float k_DefaultError = 0.00001f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
private static readonly QuaternionEqualityComparer m_Instance = new QuaternionEqualityComparer();
|
||||
/// <summary>
|
||||
///A comparer instance with the default error value 0.00001f.
|
||||
/// </summary>
|
||||
public static QuaternionEqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
|
||||
private QuaternionEqualityComparer() : this(k_DefaultError) {}
|
||||
/// <summary>
|
||||
/// Creates an instance of the comparer with a custom allowed error value.
|
||||
/// </summary>
|
||||
/// <param name="allowedError">Describes the custom allowed error value</param>
|
||||
public QuaternionEqualityComparer(float allowedError)
|
||||
{
|
||||
AllowedError = allowedError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the actual and expected Quaternion objects
|
||||
/// for equality using the <see cref="Quaternion.Dot "/> method.
|
||||
/// </summary>
|
||||
/// <param name="expected">Expected Quaternion value used for comparison</param>
|
||||
/// <param name="actual">Actual Quaternion value to test</param>
|
||||
/// <returns>True if the quaternion are equals, false otherwise.</returns>
|
||||
/// <example>
|
||||
/// The following example shows how to verify if two Quaternion are equals
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class QuaternionTest
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoQuaternionsAreEqual()
|
||||
/// {
|
||||
/// var actual = new Quaternion(10f, 0f, 0f, 0f);
|
||||
/// var expected = new Quaternion(1f, 10f, 0f, 0f);
|
||||
/// var comparer = new QuaternionEqualityComparer(10e-6f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(comparer));
|
||||
///
|
||||
/// //Using default error 0.00001f
|
||||
/// actual = new Quaternion(10f, 0f, 0.1f, 0f);
|
||||
/// expected = new Quaternion(1f, 10f, 0.1f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(QuaternionEqualityComparer.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Quaternion expected, Quaternion actual)
|
||||
{
|
||||
return Mathf.Abs(Quaternion.Dot(expected, actual)) > (1.0f - AllowedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="quaternion">A not null Quaternion</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Quaternion quaternion)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b28913f21577de429da928d6d05219f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,43 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
internal static class StackTraceFilter
|
||||
{
|
||||
private static readonly string[] s_FilteredLogMessages =
|
||||
{
|
||||
@"UnityEngine.DebugLogHandler:Internal_Log",
|
||||
@"UnityEngine.DebugLogHandler:Log",
|
||||
@"UnityEngine.Logger:Log",
|
||||
@"UnityEngine.Debug"
|
||||
};
|
||||
|
||||
private static readonly string[] s_LastMessages =
|
||||
{
|
||||
@"System.Reflection.MonoMethod:InternalInvoke(Object, Object[], Exception&)",
|
||||
@"UnityEditor.TestTools.TestRunner.EditModeRunner:InvokeDelegator"
|
||||
};
|
||||
|
||||
public static string Filter(string inputStackTrace)
|
||||
{
|
||||
int idx;
|
||||
foreach (var lastMessage in s_LastMessages)
|
||||
{
|
||||
idx = inputStackTrace.IndexOf(lastMessage);
|
||||
if (idx != -1)
|
||||
inputStackTrace = inputStackTrace.Substring(0, idx);
|
||||
}
|
||||
|
||||
var inputStackTraceLines = inputStackTrace.Split('\n');
|
||||
var result = new StringBuilder();
|
||||
foreach (var line in inputStackTraceLines)
|
||||
{
|
||||
if (s_FilteredLogMessages.Any(s => line.StartsWith(s)))
|
||||
continue;
|
||||
result.AppendLine(line);
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc748d99f1f0d484a811a566fc7915ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using UnityEngine.Scripting;
|
||||
|
||||
namespace UnityEngine.TestRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// An assembly level attribute that indicates that a given type should be subscribed for receiving updates on the test progress.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// using NUnit.Framework.Interfaces;
|
||||
/// using UnityEngine;
|
||||
/// using UnityEngine.TestRunner;
|
||||
///
|
||||
/// [assembly:TestRunCallback(typeof(TestListener))]
|
||||
///
|
||||
/// public class TestListener : ITestRunCallback
|
||||
/// {
|
||||
/// public void RunStarted(ITest testsToRun)
|
||||
/// {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// public void RunFinished(ITestResult testResults)
|
||||
/// {
|
||||
/// Debug.Log($"Run finished with result {testResults.ResultState}.");
|
||||
/// }
|
||||
///
|
||||
/// public void TestStarted(ITest test)
|
||||
/// {
|
||||
///
|
||||
/// }
|
||||
///
|
||||
/// public void TestFinished(ITestResult result)
|
||||
/// {
|
||||
///
|
||||
/// }
|
||||
///}
|
||||
/// </code>
|
||||
/// </example>
|
||||
[AttributeUsage(AttributeTargets.Assembly)]
|
||||
public class TestRunCallbackAttribute : Attribute
|
||||
{
|
||||
private Type m_Type;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="TestRunCallbackAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">A target type that implements <see cref="ITestRunCallback"/>.</param>
|
||||
/// <exception cref="ArgumentException">Throws an ArgumentException if the provided type does not implement <see cref="ITestRunCallback"/>.</exception>
|
||||
public TestRunCallbackAttribute(Type type)
|
||||
{
|
||||
var interfaceType = typeof(ITestRunCallback);
|
||||
if (!interfaceType.IsAssignableFrom(type))
|
||||
{
|
||||
throw new ArgumentException(string.Format(
|
||||
"Type {2} provided to {0} does not implement {1}. If the stripping level is set to high, the implementing class should have the {3}.",
|
||||
this.GetType().Name, interfaceType.Name, type.Name, typeof(PreserveAttribute).Name));
|
||||
}
|
||||
m_Type = type;
|
||||
}
|
||||
|
||||
internal ITestRunCallback ConstructCallback()
|
||||
{
|
||||
return Activator.CreateInstance(m_Type) as ITestRunCallback;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76b3a3296de548f48b0c3d088fb4b490
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEngine.TestRunner.Utils
|
||||
{
|
||||
internal class TestRunCallbackListener : ScriptableObject, ITestRunnerListener
|
||||
{
|
||||
private ITestRunCallback[] m_Callbacks;
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
InvokeAllCallbacks(callback => callback.RunStarted(testsToRun));
|
||||
}
|
||||
|
||||
private static ITestRunCallback[] GetAllCallbacks()
|
||||
{
|
||||
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
allAssemblies = allAssemblies.Where(x => x.GetReferencedAssemblies().Any(z => z.Name == "UnityEngine.TestRunner")).ToArray();
|
||||
var attributes = allAssemblies.SelectMany(assembly => assembly.GetCustomAttributes(typeof(TestRunCallbackAttribute), true).OfType<TestRunCallbackAttribute>()).ToArray();
|
||||
return attributes.Select(attribute => attribute.ConstructCallback()).ToArray();
|
||||
}
|
||||
|
||||
private void InvokeAllCallbacks(Action<ITestRunCallback> invoker)
|
||||
{
|
||||
if (m_Callbacks == null)
|
||||
{
|
||||
m_Callbacks = GetAllCallbacks();
|
||||
}
|
||||
|
||||
foreach (var testRunCallback in m_Callbacks)
|
||||
{
|
||||
try
|
||||
{
|
||||
invoker(testRunCallback);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
InvokeAllCallbacks(callback => callback.RunFinished(testResults));
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
InvokeAllCallbacks(callback => callback.TestStarted(test));
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
InvokeAllCallbacks(callback => callback.TestFinished(result));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68f09f0f82599b5448579854e622a4c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// This contains test utility functions for float value comparison and creating primitives.
|
||||
/// </summary>
|
||||
public static class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Relative epsilon comparison of two float values for equality.
|
||||
/// The relative error is the absolute error divided by the magnitude of the exact value.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected float value used to compare.</param>
|
||||
/// <param name="actual">The actual float value to test.</param>
|
||||
/// <param name="epsilon"> Epsilon is the relative error to be used in relative epsilon comparison.</param>
|
||||
/// <returns>Returns true if the actual value is equivalent to the expected value.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// class UtilsTests
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void CheckThat_FloatsAreEqual()
|
||||
/// {
|
||||
/// float expected = 10e-8f;
|
||||
/// float actual = 0f;
|
||||
/// float allowedRelativeError = 10e-6f;
|
||||
///
|
||||
/// Assert.That(Utils.AreFloatsEqual(expected, actual, allowedRelativeError), Is.True);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static bool AreFloatsEqual(float expected, float actual, float epsilon)
|
||||
{
|
||||
// special case for infinity
|
||||
if (expected == Mathf.Infinity || actual == Mathf.Infinity || expected == Mathf.NegativeInfinity || actual == Mathf.NegativeInfinity)
|
||||
return expected == actual;
|
||||
|
||||
// we cover both relative and absolute tolerance with this check
|
||||
// which is better than just relative in case of small (in abs value) args
|
||||
// please note that "usually" approximation is used [i.e. abs(x)+abs(y)+1]
|
||||
// but we speak about test code so we dont care that much about performance
|
||||
// but we do care about checks being more precise
|
||||
return Math.Abs(actual - expected) <= epsilon * Mathf.Max(Mathf.Max(Mathf.Abs(actual), Mathf.Abs(expected)), 1.0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two floating point numbers for equality under the given absolute tolerance.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected float value used to compare.</param>
|
||||
/// <param name="actual">The actual float value to test.</param>
|
||||
/// <param name="allowedAbsoluteError">AllowedAbsoluteError is the permitted error tolerance.</param>
|
||||
/// <returns> Returns true if the actual value is equivalent to the expected value under the given tolerance.
|
||||
/// </returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// class UtilsTests
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void ChechThat_FloatsAreAbsoluteEqual()
|
||||
/// {
|
||||
/// float expected = 0f;
|
||||
/// float actual = 10e-6f;
|
||||
/// float error = 10e-5f;
|
||||
///
|
||||
/// Assert.That(Utils.AreFloatsEqualAbsoluteError(expected, actual, error), Is.True);
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static bool AreFloatsEqualAbsoluteError(float expected, float actual, float allowedAbsoluteError)
|
||||
{
|
||||
return Math.Abs(actual - expected) <= allowedAbsoluteError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analogous to GameObject.CreatePrimitive, but creates a primitive mesh renderer with fast shader instead of a default builtin shader.
|
||||
/// Optimized for testing performance.
|
||||
/// </summary>
|
||||
/// <returns>A GameObject with primitive mesh renderer and collider.</returns>
|
||||
/// <param name="type">The type of primitive object to create.</param>
|
||||
public static GameObject CreatePrimitive(PrimitiveType type)
|
||||
{
|
||||
var prim = GameObject.CreatePrimitive(type);
|
||||
var renderer = prim.GetComponent<Renderer>();
|
||||
if (renderer)
|
||||
renderer.sharedMaterial = new Material(Shader.Find("VertexLit"));
|
||||
return prim;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9502550ba4785e3499d6c9251fa2114b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use these classes to compare two objects of the same type for equality within the range of a given tolerance using NUnit or custom constraints . Call Instance to apply the default calculation error value to the comparison.
|
||||
/// </summary>
|
||||
public class Vector2ComparerWithEqualsOperator : IEqualityComparer<Vector2>
|
||||
{
|
||||
private static readonly Vector2ComparerWithEqualsOperator m_Instance = new Vector2ComparerWithEqualsOperator();
|
||||
/// <summary>
|
||||
/// A singleton instance of the comparer with a predefined default error value.
|
||||
/// </summary>
|
||||
public static Vector2ComparerWithEqualsOperator Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector2ComparerWithEqualsOperator() {}
|
||||
/// <summary>
|
||||
/// Compares the actual and expected objects for equality using a custom comparison mechanism.
|
||||
/// </summary>
|
||||
/// <param name="expected">Expected Vector2 used to compare</param>
|
||||
/// <param name="actual">Actual Vector2 value to test.</param>
|
||||
/// <returns>Returns true if expected and actual objects are equal, otherwise it returns false.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class Vector2Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector2ObjectsAreEqual()
|
||||
/// {
|
||||
/// var actual = new Vector2(10e-7f, 10e-7f);
|
||||
/// var expected = new Vector2(0f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector2ComparerWithEqualsOperator.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Vector2 expected, Vector2 actual)
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec2"> A not null Vector2 object</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector2 vec2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 65701ebe8bada6b4785e9c7afe7f5bee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compare two Vector2 objects for
|
||||
/// equality with NUnit constraints. Use the static
|
||||
/// <see cref="Vector2EqualityComparer.Instance"/>
|
||||
/// to have the calculation error value set to default 0.0001f.
|
||||
/// For any other error value, instantiate a new comparer
|
||||
/// object with the one argument constructor.
|
||||
/// </summary>
|
||||
public class Vector2EqualityComparer : IEqualityComparer<Vector2>
|
||||
{
|
||||
private const float k_DefaultError = 0.0001f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
private static readonly Vector2EqualityComparer m_Instance = new Vector2EqualityComparer();
|
||||
|
||||
/// <summary>
|
||||
/// A comparer instance with the default error value set to 0.0001f.
|
||||
///</summary>
|
||||
public static Vector2EqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector2EqualityComparer() : this(k_DefaultError)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes an instance of Vector2Equality comparer with custom allowed calculation error.
|
||||
/// </summary>
|
||||
/// <param name="error">This value identifies the calculation error allowed.</param>
|
||||
public Vector2EqualityComparer(float error)
|
||||
{
|
||||
this.AllowedError = error;
|
||||
}
|
||||
/// <summary>
|
||||
/// Compares the actual and expected Vector2 objects for equality using the <see cref="Utils.AreFloatsEqual"/> method.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected Vector2 used for comparison</param>
|
||||
/// <param name="actual">The actual Vector2 to test</param>
|
||||
/// <returns>True if the vectors are equals, false otherwise.</returns>
|
||||
/// <example>
|
||||
/// The following example shows how to verify if two Vector2 are equals
|
||||
///<code>
|
||||
///[TestFixture]
|
||||
/// public class Vector2Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector2ObjectsAreEqual()
|
||||
/// {
|
||||
/// // Custom calculation error
|
||||
/// var actual = new Vector2(10e-7f, 10e-7f);
|
||||
/// var expected = new Vector2(0f, 0f);
|
||||
/// var comparer = new Vector2EqualityComparer(10e-6f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(comparer));
|
||||
///
|
||||
/// //Default error 0.0001f
|
||||
/// actual = new Vector2(0.01f, 0.01f);
|
||||
/// expected = new Vector2(0.01f, 0.01f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector2EqualityComparer.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Vector2 expected, Vector2 actual)
|
||||
{
|
||||
return Utils.AreFloatsEqual(expected.x, actual.x, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.y, actual.y, AllowedError);
|
||||
}
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec2">A not null Vector2</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector2 vec2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58ad09607a0d62d458a78d7174665566
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use these classes to compare two objects of the same type for equality within the range of a given tolerance using NUnit or custom constraints . Call Instance to apply the default calculation error value to the comparison.
|
||||
/// </summary>
|
||||
public class Vector3ComparerWithEqualsOperator : IEqualityComparer<Vector3>
|
||||
{
|
||||
private static readonly Vector3ComparerWithEqualsOperator m_Instance = new Vector3ComparerWithEqualsOperator();
|
||||
/// <summary>
|
||||
/// A singleton instance of the comparer with a predefined default error value.
|
||||
/// </summary>
|
||||
public static Vector3ComparerWithEqualsOperator Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector3ComparerWithEqualsOperator() {}
|
||||
/// <summary>
|
||||
/// Compares the actual and expected objects for equality using a custom comparison mechanism.
|
||||
/// </summary>
|
||||
/// <param name="expected">Expected Vector3 used to compare</param>
|
||||
/// <param name="actual">Actual Vector3 value to test.</param>
|
||||
/// <returns>Returns true if expected and actual objects are equal, otherwise it returns false.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class Vector3Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector3ObjectsAreEqual()
|
||||
/// {
|
||||
/// var actual = new Vector2(10e-7f, 10e-7f, 10e-7f);
|
||||
/// var expected = new Vector2(0f, 0f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector3ComparerWithEqualsOperator.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Vector3 expected, Vector3 actual)
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec3"> A not null Vector3 object</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector3 vec3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b994928117e3db418da69c821da7e19
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,82 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compare two Vector3 objects for equality with NUnit constraints. Call Vector3EqualityComparer.Instance comparer to perform a comparison with the default calculation error value 0.0001f. To specify a different error value, use the one argument constructor to instantiate a new comparer.
|
||||
/// </summary>
|
||||
public class Vector3EqualityComparer : IEqualityComparer<Vector3>
|
||||
{
|
||||
private const float k_DefaultError = 0.0001f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
private static readonly Vector3EqualityComparer m_Instance = new Vector3EqualityComparer();
|
||||
|
||||
/// <summary>
|
||||
/// A comparer instance with the default calculation error value equal to 0.0001f.
|
||||
///</summary>
|
||||
public static Vector3EqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector3EqualityComparer() : this(k_DefaultError) {}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of Vector3Equality comparer with custom allowed calculation error.
|
||||
/// </summary>
|
||||
/// <param name="allowedError">This value identifies the calculation error allowed.</param>
|
||||
public Vector3EqualityComparer(float allowedError)
|
||||
{
|
||||
this.AllowedError = allowedError;
|
||||
}
|
||||
///<summary>
|
||||
/// Compares the actual and expected Vector3 objects
|
||||
/// for equality using <see cref="Utils.AreFloatsEqual"/> to compare the x, y, and z attributes of Vector3.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected Vector3 used for comparison</param>
|
||||
/// <param name="actual">The actual Vector3 to test</param>
|
||||
/// <returns>True if the vectors are equals, false otherwise.</returns>
|
||||
/// <example>
|
||||
/// The following example shows how to verify if two Vector3 are equals
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class Vector3Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector3ObjectsAreEqual()
|
||||
/// {
|
||||
/// //Custom error 10e-6f
|
||||
/// var actual = new Vector3(10e-8f, 10e-8f, 10e-8f);
|
||||
/// var expected = new Vector3(0f, 0f, 0f);
|
||||
/// var comparer = new Vector3EqualityComparer(10e-6f);
|
||||
///
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(comparer));
|
||||
///
|
||||
/// //Default error 0.0001f
|
||||
/// actual = new Vector3(0.01f, 0.01f, 0f);
|
||||
/// expected = new Vector3(0.01f, 0.01f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector3EqualityComparer.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
///</example>
|
||||
|
||||
public bool Equals(Vector3 expected, Vector3 actual)
|
||||
{
|
||||
return Utils.AreFloatsEqual(expected.x, actual.x, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.y, actual.y, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.z, actual.z, AllowedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec3">A not null Vector3</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector3 vec3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bd2bc28ff24d5c488844851cb785db0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use these classes to compare two objects of the same type for equality within the range of a given tolerance using NUnit or custom constraints . Call Instance to apply the default calculation error value to the comparison.
|
||||
/// </summary>
|
||||
public class Vector4ComparerWithEqualsOperator : IEqualityComparer<Vector4>
|
||||
{
|
||||
private static readonly Vector4ComparerWithEqualsOperator m_Instance = new Vector4ComparerWithEqualsOperator();
|
||||
/// <summary>
|
||||
/// A singleton instance of the comparer with a predefined default error value.
|
||||
/// </summary>
|
||||
public static Vector4ComparerWithEqualsOperator Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector4ComparerWithEqualsOperator() {}
|
||||
/// <summary>
|
||||
/// Compares the actual and expected objects for equality using a custom comparison mechanism.
|
||||
/// </summary>
|
||||
/// <param name="expected">Expected Vector4 used to compare</param>
|
||||
/// <param name="actual">Actual Vector4 value to test.</param>
|
||||
/// <returns>Returns true if expected and actual objects are equal, otherwise it returns false.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// [TestFixture]
|
||||
/// public class Vector4Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector4ObjectsAreEqual()
|
||||
/// {
|
||||
/// var actual = new Vector4(10e-7f, 10e-7f, 10e-7f, 10e-7f);
|
||||
/// var expected = new Vector4(0f, 0f, 0f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector4ComparerWithEqualsOperator.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Vector4 expected, Vector4 actual)
|
||||
{
|
||||
return expected == actual;
|
||||
}
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec4"> A not null Vector4 object</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector4 vec4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44100f5f60f351348b9719b46d46cebe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEngine.TestTools.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Use this class to compare two Vector4 objects for equality with NUnit constraints. Call Vector4EqualityComparer.Instance to perform comparisons using default calculation error value 0.0001f. To set a custom test value, instantiate a new comparer using the one argument constructor.
|
||||
/// </summary>
|
||||
public class Vector4EqualityComparer : IEqualityComparer<Vector4>
|
||||
{
|
||||
private const float k_DefaultError = 0.0001f;
|
||||
private readonly float AllowedError;
|
||||
|
||||
private static readonly Vector4EqualityComparer m_Instance = new Vector4EqualityComparer();
|
||||
/// <summary>
|
||||
/// A comparer instance with the default calculation error value set to 0.0001f.
|
||||
/// </summary>
|
||||
public static Vector4EqualityComparer Instance { get { return m_Instance; } }
|
||||
|
||||
private Vector4EqualityComparer() : this(k_DefaultError) {}
|
||||
/// <summary>
|
||||
/// Initializes an instance of Vector4Equality comparer with custom allowed calculation error.
|
||||
/// </summary>
|
||||
/// <param name="allowedError">This value identifies the calculation error allowed.</param>
|
||||
public Vector4EqualityComparer(float allowedError)
|
||||
{
|
||||
this.AllowedError = allowedError;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the actual and expected Vector4 objects for equality using <see cref="Utils.AreFloatsEqual"/> to compare the x, y, z, and w attributes of Vector4.
|
||||
/// </summary>
|
||||
/// <param name="expected">The expected Vector4 used for comparison</param>
|
||||
/// <param name="actual">The actual Vector4 to test</param>
|
||||
/// <returns>True if the vectors are equals, false otherwise.</returns>
|
||||
/// <example>
|
||||
/// <code>
|
||||
///[TestFixture]
|
||||
/// public class Vector4Test
|
||||
/// {
|
||||
/// [Test]
|
||||
/// public void VerifyThat_TwoVector4ObjectsAreEqual()
|
||||
/// {
|
||||
/// // Custom error 10e-6f
|
||||
/// var actual = new Vector4(0, 0, 1e-6f, 1e-6f);
|
||||
/// var expected = new Vector4(1e-6f, 0f, 0f, 0f);
|
||||
/// var comparer = new Vector4EqualityComparer(10e-6f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(comparer));
|
||||
///
|
||||
/// // Default error 0.0001f
|
||||
/// actual = new Vector4(0.01f, 0.01f, 0f, 0f);
|
||||
/// expected = new Vector4(0.01f, 0.01f, 0f, 0f);
|
||||
///
|
||||
/// Assert.That(actual, Is.EqualTo(expected).Using(Vector4EqualityComparer.Instance));
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public bool Equals(Vector4 expected, Vector4 actual)
|
||||
{
|
||||
return Utils.AreFloatsEqual(expected.x, actual.x, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.y, actual.y, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.z, actual.z, AllowedError) &&
|
||||
Utils.AreFloatsEqual(expected.w, actual.w, AllowedError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serves as the default hash function.
|
||||
/// </summary>
|
||||
/// <param name="vec4"> A not null Vector4 object</param>
|
||||
/// <returns>Returns 0</returns>
|
||||
public int GetHashCode(Vector4 vec4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32da81683c22faf458026716a2b821aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user