first commit
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:
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user