Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fa423365b1ce06a4dbdc6fb4a8597bfa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class CallbacksDelegator : ICallbacksDelegator
|
||||
{
|
||||
private static CallbacksDelegator s_instance;
|
||||
public static CallbacksDelegator instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance == null)
|
||||
{
|
||||
s_instance = new CallbacksDelegator(CallbacksHolder.instance.GetAll, new TestAdaptorFactory());
|
||||
}
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Func<ICallbacks[]> m_CallbacksProvider;
|
||||
private readonly ITestAdaptorFactory m_AdaptorFactory;
|
||||
|
||||
public CallbacksDelegator(Func<ICallbacks[]> callbacksProvider, ITestAdaptorFactory adaptorFactory)
|
||||
{
|
||||
m_CallbacksProvider = callbacksProvider;
|
||||
m_AdaptorFactory = adaptorFactory;
|
||||
}
|
||||
|
||||
public void RunStarted(ITest testsToRun)
|
||||
{
|
||||
m_AdaptorFactory.ClearResultsCache();
|
||||
var testRunnerTestsToRun = m_AdaptorFactory.Create(testsToRun);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunStarted(testRunnerTestsToRun));
|
||||
}
|
||||
|
||||
public void RunStartedRemotely(byte[] testsToRunData)
|
||||
{
|
||||
var testData = Deserialize<RemoteTestResultDataWithTestData>(testsToRunData);
|
||||
var testsToRun = m_AdaptorFactory.BuildTree(testData);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunStarted(testsToRun));
|
||||
}
|
||||
|
||||
public void RunFinished(ITestResult testResults)
|
||||
{
|
||||
var testResult = m_AdaptorFactory.Create(testResults);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunFinished(testResult));
|
||||
}
|
||||
|
||||
public void RunFinishedRemotely(byte[] testResultsData)
|
||||
{
|
||||
var remoteTestResult = Deserialize<RemoteTestResultDataWithTestData>(testResultsData);
|
||||
var testResult = m_AdaptorFactory.Create(remoteTestResult.results.First(), remoteTestResult);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.RunFinished(testResult));
|
||||
}
|
||||
|
||||
public void RunFailed(string failureMessage)
|
||||
{
|
||||
Debug.LogError(failureMessage);
|
||||
TryInvokeAllCallbacks(callbacks =>
|
||||
{
|
||||
var errorCallback = callbacks as IErrorCallbacks;
|
||||
if (errorCallback != null)
|
||||
{
|
||||
errorCallback.OnError(failureMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void TestStarted(ITest test)
|
||||
{
|
||||
var testRunnerTest = m_AdaptorFactory.Create(test);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestStarted(testRunnerTest));
|
||||
}
|
||||
|
||||
public void TestStartedRemotely(byte[] testStartedData)
|
||||
{
|
||||
var testData = Deserialize<RemoteTestResultDataWithTestData>(testStartedData);
|
||||
var testsToRun = m_AdaptorFactory.BuildTree(testData);
|
||||
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestStarted(testsToRun));
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResult result)
|
||||
{
|
||||
var testResult = m_AdaptorFactory.Create(result);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestFinished(testResult));
|
||||
}
|
||||
|
||||
public void TestFinishedRemotely(byte[] testResultsData)
|
||||
{
|
||||
var remoteTestResult = Deserialize<RemoteTestResultDataWithTestData>(testResultsData);
|
||||
var testResult = m_AdaptorFactory.Create(remoteTestResult.results.First(), remoteTestResult);
|
||||
TryInvokeAllCallbacks(callbacks => callbacks.TestFinished(testResult));
|
||||
}
|
||||
|
||||
public void TestTreeRebuild(ITest test)
|
||||
{
|
||||
m_AdaptorFactory.ClearTestsCache();
|
||||
var testAdaptor = m_AdaptorFactory.Create(test);
|
||||
TryInvokeAllCallbacks(callbacks =>
|
||||
{
|
||||
var rebuildCallbacks = callbacks as ITestTreeRebuildCallbacks;
|
||||
if (rebuildCallbacks != null)
|
||||
{
|
||||
rebuildCallbacks.TestTreeRebuild(testAdaptor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void TryInvokeAllCallbacks(Action<ICallbacks> callbackAction)
|
||||
{
|
||||
foreach (var testRunnerApiCallback in m_CallbacksProvider())
|
||||
{
|
||||
try
|
||||
{
|
||||
callbackAction(testRunnerApiCallback);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static T Deserialize<T>(byte[] data)
|
||||
{
|
||||
return JsonUtility.FromJson<T>(Encoding.UTF8.GetString(data));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0de03ebd74e2b474fa23d05ab42d0cd8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,28 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class CallbacksDelegatorListener : ScriptableObject, ITestRunnerListener
|
||||
{
|
||||
public void RunStarted(NUnit.Framework.Interfaces.ITest testsToRun)
|
||||
{
|
||||
CallbacksDelegator.instance.RunStarted(testsToRun);
|
||||
}
|
||||
|
||||
public void RunFinished(NUnit.Framework.Interfaces.ITestResult testResults)
|
||||
{
|
||||
CallbacksDelegator.instance.RunFinished(testResults);
|
||||
}
|
||||
|
||||
public void TestStarted(NUnit.Framework.Interfaces.ITest test)
|
||||
{
|
||||
CallbacksDelegator.instance.TestStarted(test);
|
||||
}
|
||||
|
||||
public void TestFinished(NUnit.Framework.Interfaces.ITestResult result)
|
||||
{
|
||||
CallbacksDelegator.instance.TestFinished(result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f3e1b3cbf3fac6a459b1a602167ad311
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class CallbacksHolder : ScriptableSingleton<CallbacksHolder>, ICallbacksHolder
|
||||
{
|
||||
private List<CallbackWithPriority> m_Callbacks = new List<CallbackWithPriority>();
|
||||
public void Add(ICallbacks callback, int priority)
|
||||
{
|
||||
m_Callbacks.Add(new CallbackWithPriority(callback, priority));
|
||||
}
|
||||
|
||||
public void Remove(ICallbacks callback)
|
||||
{
|
||||
m_Callbacks.RemoveAll(callbackWithPriority => callbackWithPriority.Callback == callback);
|
||||
}
|
||||
|
||||
public ICallbacks[] GetAll()
|
||||
{
|
||||
return m_Callbacks.OrderByDescending(callback => callback.Priority).Select(callback => callback.Callback).ToArray();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_Callbacks.Clear();
|
||||
}
|
||||
|
||||
private struct CallbackWithPriority
|
||||
{
|
||||
public ICallbacks Callback;
|
||||
public int Priority;
|
||||
public CallbackWithPriority(ICallbacks callback, int priority)
|
||||
{
|
||||
Callback = callback;
|
||||
Priority = priority;
|
||||
}
|
||||
}
|
||||
|
||||
// Sometimes - such as when we want to test the test framework itself - it's necessary to launch a test run from
|
||||
// inside a test. Because callbacks are registered globally, this can cause a lot of confusion (e.g. the in-test
|
||||
// run will emit UTP messages, utterly confusing UTR). In such circumstances the safest thing to do is to
|
||||
// temporarily suppress all registered callbacks for the duration of the in-test run. This method can be called
|
||||
// to set up a using() block which will suppress the callbacks for the scope.
|
||||
public IDisposable TemporarilySuppressCallbacks()
|
||||
{
|
||||
return new Suppressor(this);
|
||||
}
|
||||
|
||||
private sealed class Suppressor : IDisposable
|
||||
{
|
||||
private readonly CallbacksHolder _instance;
|
||||
private readonly List<CallbackWithPriority> _suppressed;
|
||||
|
||||
public Suppressor(CallbacksHolder instance)
|
||||
{
|
||||
_instance = instance;
|
||||
_suppressed = new List<CallbackWithPriority>(instance.m_Callbacks);
|
||||
instance.m_Callbacks.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_instance.m_Callbacks.AddRange(_suppressed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4884ccc3528cb2e40a0e6f0a19a2b35b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal.Filters;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of execution settings defining how to run tests, using the <see cref="TestRunnerApi"/>.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ExecutionSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance with a given set of filters, if any.
|
||||
/// </summary>
|
||||
/// <param name="filtersToExecute">Set of filters</param>
|
||||
public ExecutionSettings(params Filter[] filtersToExecute)
|
||||
{
|
||||
filters = filtersToExecute;
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
internal BuildTarget? targetPlatform;
|
||||
|
||||
/// <summary>
|
||||
/// An instance of <see cref="ITestRunSettings"/> to set up before running tests on a Player.
|
||||
/// </summary>
|
||||
// Note: Is not available after serialization
|
||||
public ITestRunSettings overloadTestRunSettings;
|
||||
|
||||
[SerializeField]
|
||||
internal Filter filter;
|
||||
///<summary>
|
||||
///A collection of <see cref="Filter"/> to execute tests on.
|
||||
///</summary>
|
||||
[SerializeField]
|
||||
public Filter[] filters;
|
||||
/// <summary>
|
||||
/// Note that this is only supported for EditMode tests, and that tests which take multiple frames (i.e. [UnityTest] tests, or tests with [UnitySetUp] or [UnityTearDown] scaffolding) will be filtered out.
|
||||
/// </summary>
|
||||
/// <returns>If true, the call to Execute() will run tests synchronously, guaranteeing that all tests have finished running by the time the call returns.</returns>
|
||||
[SerializeField]
|
||||
public bool runSynchronously;
|
||||
/// <summary>
|
||||
/// The time, in seconds, the editor should wait for heartbeats after starting a test run on a player. This defaults to 10 minutes.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public int playerHeartbeatTimeout = 60*10;
|
||||
|
||||
internal bool EditModeIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.EditMode));
|
||||
}
|
||||
|
||||
internal bool PlayModeInEditorIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.PlayMode) && targetPlatform == null);
|
||||
}
|
||||
|
||||
internal bool PlayerIncluded()
|
||||
{
|
||||
return filters.Any(f => IncludesTestMode(f.testMode, TestMode.PlayMode) && targetPlatform != null);
|
||||
}
|
||||
|
||||
private static bool IncludesTestMode(TestMode testMode, TestMode modeToCheckFor)
|
||||
{
|
||||
return (testMode & modeToCheckFor) == modeToCheckFor;
|
||||
}
|
||||
|
||||
internal ITestFilter BuildNUnitFilter()
|
||||
{
|
||||
return new OrFilter(filters.Select(f => f.ToRuntimeTestRunnerFilter(runSynchronously).BuildNUnitFilter()).ToArray());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: eea34a28297f9bc4c9f4c573bc8d5d1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools.TestRunner.GUI;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The filter class provides the <see cref="TestRunnerApi"/> with a specification of what tests to run when [running tests programmatically](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-run-tests.html).
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class Filter
|
||||
{
|
||||
/// <summary>
|
||||
/// An enum flag that specifies if Edit Mode or Play Mode tests should run.
|
||||
///</summary>
|
||||
[SerializeField]
|
||||
public TestMode testMode;
|
||||
/// <summary>
|
||||
/// The full name of the tests to match the filter. This is usually in the format FixtureName.TestName. If the test has test arguments, then include them in parenthesis. E.g. MyTestClass2.MyTestWithMultipleValues(1).
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] testNames;
|
||||
/// <summary>
|
||||
/// The same as testNames, except that it allows for Regex. This is useful for running specific fixtures or namespaces. E.g. "^MyNamespace\\." Runs any tests where the top namespace is MyNamespace.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] groupNames;
|
||||
/// <summary>
|
||||
/// The name of a [Category](https://nunit.org/docs/2.2.7/category.html) to include in the run. Any test or fixtures runs that have a Category matching the string.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] categoryNames;
|
||||
/// <summary>
|
||||
/// The name of assemblies included in the run. That is the assembly name, without the .dll file extension. E.g., MyTestAssembly
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public string[] assemblyNames;
|
||||
/// <summary>
|
||||
/// The <see cref="BuildTarget"/> platform to run the test on. If set to null, then the Editor is the target for the tests.
|
||||
/// </summary>
|
||||
[SerializeField]
|
||||
public BuildTarget? targetPlatform;
|
||||
|
||||
internal RuntimeTestRunnerFilter ToRuntimeTestRunnerFilter(bool synchronousOnly)
|
||||
{
|
||||
return new RuntimeTestRunnerFilter()
|
||||
{
|
||||
testNames = testNames,
|
||||
categoryNames = categoryNames,
|
||||
groupNames = groupNames,
|
||||
assemblyNames = assemblyNames,
|
||||
synchronousOnly = synchronousOnly
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 05f92e4a2414cb144a92157752dfa324
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// Callbacks in the <see cref="TestRunnerApi"/> for the test stages when running tests.
|
||||
/// </summary>
|
||||
public interface ICallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is started.
|
||||
/// </summary>
|
||||
/// <param name="testsToRun">The full loaded test tree.</param>
|
||||
void RunStarted(ITestAdaptor testsToRun);
|
||||
/// <summary>
|
||||
/// A callback invoked when a test run is finished.
|
||||
/// </summary>
|
||||
/// <param name="result">The result of the test run.</param>
|
||||
void RunFinished(ITestResultAdaptor result);
|
||||
/// <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(ITestAdaptor 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(ITestResultAdaptor result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 93eea84e53d0226479c9a584f19427b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ICallbacksDelegator
|
||||
{
|
||||
void RunStarted(ITest testsToRun);
|
||||
void RunStartedRemotely(byte[] testsToRunData);
|
||||
void RunFinished(ITestResult testResults);
|
||||
void RunFinishedRemotely(byte[] testResultsData);
|
||||
void RunFailed(string failureMessage);
|
||||
void TestStarted(ITest test);
|
||||
void TestStartedRemotely(byte[] testStartedData);
|
||||
void TestFinished(ITestResult result);
|
||||
void TestFinishedRemotely(byte[] testResultsData);
|
||||
void TestTreeRebuild(ITest test);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8f8f74fe8e363da42875d9cab025d3b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ICallbacksHolder
|
||||
{
|
||||
void Add(ICallbacks callback, int priority);
|
||||
void Remove(ICallbacks callback);
|
||||
ICallbacks[] GetAll();
|
||||
void Clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d742f2caefd9f934d9f19dad07a08e6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// An extended version of the <see cref="ICallbacks"/>, which get invoked if the test run fails due to a build error or if any <see cref="UnityEngine.TestTools.IPrebuildSetup"/> has a failure.
|
||||
/// </summary>
|
||||
public interface IErrorCallbacks : ICallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Method invoked on failure.
|
||||
/// </summary>
|
||||
/// <param name="message">
|
||||
/// The error message detailing the reason for the run to fail.
|
||||
/// </param>
|
||||
void OnError(string message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1a06c562b0c5eb046bcb876a29f93c98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,100 @@
|
|||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// ```ITestAdaptor``` is a representation of a node in the test tree implemented as a wrapper around the [NUnit](http://www.nunit.org/) [ITest](https://github.com/nunit/nunit/blob/master/src/NUnitFramework/framework/Interfaces/ITest.cs) interface.
|
||||
/// </summary>
|
||||
public interface ITestAdaptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of the test tree node. The ID can change if you add new tests to the suite. Use UniqueName, if you want to have a more permanent point of reference.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
/// <summary>
|
||||
/// The name of the test. E.g.,```MyTest```.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// The full name of the test. E.g., ```MyNamespace.MyTestClass.MyTest```.
|
||||
/// </summary>
|
||||
string FullName { get; }
|
||||
/// <summary>
|
||||
/// The total number of test cases in the node and all sub-nodes.
|
||||
/// </summary>
|
||||
int TestCaseCount { get; }
|
||||
/// <summary>
|
||||
/// Whether the node has any children.
|
||||
/// </summary>
|
||||
bool HasChildren { get; }
|
||||
/// <summary>
|
||||
/// True if the node is a test suite/fixture, false otherwise.
|
||||
/// </summary>
|
||||
bool IsSuite { get; }
|
||||
/// <summary>
|
||||
/// The child nodes.
|
||||
/// </summary>
|
||||
IEnumerable<ITestAdaptor> Children { get; }
|
||||
/// <summary>
|
||||
/// The parent node, if any.
|
||||
/// </summary>
|
||||
ITestAdaptor Parent { get; }
|
||||
/// <summary>
|
||||
/// The test case timeout in milliseconds. Note that this value is only available on TestFinished.
|
||||
/// </summary>
|
||||
int TestCaseTimeout { get; }
|
||||
/// <summary>
|
||||
/// The type of test class as an ```NUnit``` <see cref="ITypeInfo"/>. If the node is not a test class, then the value is null.
|
||||
/// </summary>
|
||||
ITypeInfo TypeInfo { get; }
|
||||
/// <summary>
|
||||
/// The Nunit <see cref="IMethodInfo"/> of the test method. If the node is not a test method, then the value is null.
|
||||
/// </summary>
|
||||
IMethodInfo Method { get; }
|
||||
/// <summary>
|
||||
/// An array of the categories applied to the test or fixture.
|
||||
/// </summary>
|
||||
string[] Categories { get; }
|
||||
/// <summary>
|
||||
/// Returns true if the node represents a test assembly, false otherwise.
|
||||
/// </summary>
|
||||
bool IsTestAssembly { get; }
|
||||
/// <summary>
|
||||
/// The run state of the test node. Either ```NotRunnable```, ```Runnable```, ```Explicit```, ```Skipped```, or ```Ignored```.
|
||||
/// </summary>
|
||||
RunState RunState { get; }
|
||||
/// <summary>
|
||||
/// The description of the test.
|
||||
/// </summary>
|
||||
string Description { get; }
|
||||
/// <summary>
|
||||
/// The skip reason. E.g., if ignoring the test.
|
||||
/// </summary>
|
||||
string SkipReason { get; }
|
||||
/// <summary>
|
||||
/// The ID of the parent node.
|
||||
/// </summary>
|
||||
string ParentId { get; }
|
||||
/// <summary>
|
||||
/// The full name of the parent node.
|
||||
/// </summary>
|
||||
string ParentFullName { get; }
|
||||
/// <summary>
|
||||
/// A unique generated name for the test node. E.g., ```Tests.dll/MyNamespace/MyTestClass/[Tests][MyNamespace.MyTestClass.MyTest]```.
|
||||
/// </summary>
|
||||
string UniqueName { get; }
|
||||
/// <summary>
|
||||
/// A unique name of the parent node. E.g., ```Tests.dll/MyNamespace/[Tests][MyNamespace.MyTestClass][suite]```.
|
||||
/// </summary>
|
||||
string ParentUniqueName { get; }
|
||||
/// <summary>
|
||||
/// The child index of the node in its parent.
|
||||
/// </summary>
|
||||
int ChildIndex { get; }
|
||||
/// <summary>
|
||||
/// The mode of the test. Either **Edit Mode** or **Play Mode**.
|
||||
/// </summary>
|
||||
TestMode TestMode { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 85dd7af03f02aea4aae13a3945e3b313
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestAdaptorFactory
|
||||
{
|
||||
ITestAdaptor Create(ITest test);
|
||||
ITestAdaptor Create(RemoteTestData testData);
|
||||
ITestResultAdaptor Create(ITestResult testResult);
|
||||
ITestResultAdaptor Create(RemoteTestResultData testResult, RemoteTestResultDataWithTestData allData);
|
||||
ITestAdaptor BuildTree(RemoteTestResultDataWithTestData data);
|
||||
IEnumerator<ITestAdaptor> BuildTreeAsync(RemoteTestResultDataWithTestData data);
|
||||
void ClearResultsCache();
|
||||
void ClearTestsCache();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 803abab0f7e17044db56f8760186dbd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The `ITestResultAdaptor` is the representation of the test results for a node in the test tree implemented as a wrapper around the [NUnit](http://www.nunit.org/) [ITest](https://github.com/nunit/nunit/blob/master/src/NUnitFramework/framework/Interfaces/ITestResults.cs) interface.
|
||||
/// </summary>
|
||||
public interface ITestResultAdaptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The test details of the test result tree node as a <see cref="TestAdaptor"/>
|
||||
/// </summary>
|
||||
ITestAdaptor Test { get; }
|
||||
///<summary>
|
||||
///The name of the test node.
|
||||
///</summary>
|
||||
string Name { get; }
|
||||
/// <summary>
|
||||
/// Gets the full name of the test result
|
||||
/// </summary>
|
||||
///<returns>
|
||||
///The name of the test result.
|
||||
///</returns>
|
||||
string FullName { get; }
|
||||
///<summary>
|
||||
///Gets the state of the result as a string.
|
||||
///</summary>
|
||||
///<returns>
|
||||
///It returns one of these values: `Inconclusive`, `Skipped`, `Skipped:Ignored`, `Skipped:Explicit`, `Passed`, `Failed`, `Failed:Error`, `Failed:Cancelled`, `Failed:Invalid`
|
||||
///</returns>
|
||||
string ResultState { get; }
|
||||
///<summary>
|
||||
///Gets the status of the test as an enum.
|
||||
///</summary>
|
||||
///<returns>
|
||||
///It returns one of these values:`Inconclusive`, `Skipped`, `Passed`, or `Failed`
|
||||
///</returns>
|
||||
TestStatus TestStatus { get; }
|
||||
/// <summary>
|
||||
/// Gets the elapsed time for running the test in seconds
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Time in seconds.
|
||||
/// </returns>
|
||||
double Duration { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets the time the test started running.
|
||||
/// </summary>
|
||||
///<returns>
|
||||
///A DataTime object.
|
||||
///</returns>
|
||||
DateTime StartTime { get; }
|
||||
///<summary>
|
||||
///Gets or sets the time the test finished running.
|
||||
///</summary>
|
||||
///<returns>
|
||||
///A DataTime object.
|
||||
///</returns>
|
||||
DateTime EndTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The message associated with a test failure or with not running the test
|
||||
/// </summary>
|
||||
string Message { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Any stacktrace associated with an error or failure. Not available in the Compact Framework 1.0.
|
||||
/// </summary>
|
||||
string StackTrace { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of asserts executed when running the test and all its children.
|
||||
/// </summary>
|
||||
int AssertCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that failed when running the test and all its children.
|
||||
/// </summary>
|
||||
int FailCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that passed when running the test and all its children.
|
||||
/// </summary>
|
||||
int PassCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of test cases that were skipped when running the test and all its children.
|
||||
/// </summary>
|
||||
int SkipCount { get; }
|
||||
|
||||
/// <summary>
|
||||
///The number of test cases that were inconclusive when running the test and all its children.
|
||||
/// </summary>
|
||||
int InconclusiveCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Accessing HasChildren should not force creation of the Children collection in classes implementing this interface.
|
||||
/// </summary>
|
||||
/// <returns>True if this result has any child results.</returns>
|
||||
bool HasChildren { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the the collection of child results.
|
||||
/// </summary>
|
||||
IEnumerable<ITestResultAdaptor> Children { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets any text output written to this result.
|
||||
/// </summary>
|
||||
string Output { get; }
|
||||
/// <summary>
|
||||
/// Use this to save the results to an XML file
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The test results as an `NUnit` XML node.
|
||||
/// </returns>
|
||||
TNode ToXml();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f90cfe4bf5cfb44f84a5b11387f2a42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// ITestRunSettings lets you set any of the global settings right before building a Player for a test run and then reverts the settings afterward. ITestRunSettings implements
|
||||
/// [IDisposable](https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.8), and runs after building the Player with tests.
|
||||
/// </summary>
|
||||
public interface ITestRunSettings : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A method called before building the Player.
|
||||
/// </summary>
|
||||
void Apply();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2ae2ce6274819484fa8747a28cebdf3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestRunnerApi
|
||||
{
|
||||
string Execute(ExecutionSettings executionSettings);
|
||||
void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks;
|
||||
void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks;
|
||||
void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a7842a837a4b13e41ae16193db753418
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal interface ITestTreeRebuildCallbacks : ICallbacks
|
||||
{
|
||||
void TestTreeRebuild(ITestAdaptor test);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4230e406313f1db43a4b548e7a3ad2e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The RunState enum indicates whether a test can be executed.
|
||||
/// </summary>
|
||||
public enum RunState
|
||||
{
|
||||
/// <summary>
|
||||
/// The test is not runnable.
|
||||
/// </summary>
|
||||
NotRunnable,
|
||||
|
||||
/// <summary>
|
||||
/// The test is runnable.
|
||||
/// </summary>
|
||||
Runnable,
|
||||
|
||||
/// <summary>
|
||||
/// The test can only be run explicitly
|
||||
/// </summary>
|
||||
Explicit,
|
||||
|
||||
/// <summary>
|
||||
/// The test has been skipped. This value may appear on a Test when certain attributes are used to skip the test.
|
||||
/// </summary>
|
||||
Skipped,
|
||||
|
||||
/// <summary>
|
||||
/// The test has been ignored. May appear on a Test, when the IgnoreAttribute is used.
|
||||
/// </summary>
|
||||
Ignored,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8bb59cb2f66d156418ca1bd1e2703233
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,142 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using NUnit.Framework.Internal;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestAdaptor : ITestAdaptor
|
||||
{
|
||||
internal TestAdaptor(ITest test, ITestAdaptor[] children = null)
|
||||
{
|
||||
Id = test.Id;
|
||||
Name = test.Name;
|
||||
var childIndex = -1;
|
||||
if (test.Properties["childIndex"].Count > 0)
|
||||
{
|
||||
childIndex = (int)test.Properties["childIndex"][0];
|
||||
}
|
||||
FullName = childIndex != -1 ? GetIndexedTestCaseName(test.FullName, childIndex) : test.FullName;
|
||||
TestCaseCount = test.TestCaseCount;
|
||||
HasChildren = test.HasChildren;
|
||||
IsSuite = test.IsSuite;
|
||||
if (UnityTestExecutionContext.CurrentContext != null)
|
||||
{
|
||||
TestCaseTimeout = UnityTestExecutionContext.CurrentContext.TestCaseTimeout;
|
||||
}
|
||||
else
|
||||
{
|
||||
TestCaseTimeout = CoroutineRunner.k_DefaultTimeout;
|
||||
}
|
||||
|
||||
TypeInfo = test.TypeInfo;
|
||||
Method = test.Method;
|
||||
Categories = test.GetAllCategoriesFromTest().Distinct().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();
|
||||
ParentFullName = test.GetParentFullName();
|
||||
UniqueName = test.GetUniqueName();
|
||||
ParentUniqueName = test.GetParentUniqueName();
|
||||
ChildIndex = childIndex;
|
||||
|
||||
if (test.Parent != null)
|
||||
{
|
||||
if (test.Parent.Parent == null) // Assembly level
|
||||
{
|
||||
TestMode = (TestMode)Enum.Parse(typeof(TestMode),test.Properties.Get("platform").ToString());
|
||||
}
|
||||
}
|
||||
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public void SetParent(ITestAdaptor parent)
|
||||
{
|
||||
Parent = parent;
|
||||
if (parent != null)
|
||||
{
|
||||
TestMode = parent.TestMode;
|
||||
}
|
||||
}
|
||||
|
||||
internal TestAdaptor(RemoteTestData test)
|
||||
{
|
||||
Id = test.id;
|
||||
Name = test.name;
|
||||
FullName = test.ChildIndex != -1 ? GetIndexedTestCaseName(test.fullName, test.ChildIndex) : test.fullName;
|
||||
TestCaseCount = test.testCaseCount;
|
||||
HasChildren = test.hasChildren;
|
||||
IsSuite = test.isSuite;
|
||||
m_ChildrenIds = test.childrenIds;
|
||||
TestCaseTimeout = test.testCaseTimeout;
|
||||
Categories = test.Categories;
|
||||
IsTestAssembly = test.IsTestAssembly;
|
||||
RunState = (RunState)Enum.Parse(typeof(RunState), test.RunState.ToString());
|
||||
Description = test.Description;
|
||||
SkipReason = test.SkipReason;
|
||||
ParentId = test.ParentId;
|
||||
UniqueName = test.UniqueName;
|
||||
ParentUniqueName = test.ParentUniqueName;
|
||||
ParentFullName = test.ParentFullName;
|
||||
ChildIndex = test.ChildIndex;
|
||||
TestMode = TestMode.PlayMode;
|
||||
}
|
||||
|
||||
internal void ApplyChildren(IEnumerable<TestAdaptor> allTests)
|
||||
{
|
||||
Children = m_ChildrenIds.Select(id => allTests.First(t => t.Id == id)).ToArray();
|
||||
if (!string.IsNullOrEmpty(ParentId))
|
||||
{
|
||||
Parent = allTests.FirstOrDefault(t => t.Id == ParentId);
|
||||
}
|
||||
}
|
||||
|
||||
public string Id { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string FullName { get; private set; }
|
||||
public int TestCaseCount { get; private set; }
|
||||
public bool HasChildren { get; private set; }
|
||||
public bool IsSuite { get; private set; }
|
||||
public IEnumerable<ITestAdaptor> Children { get; private set; }
|
||||
public ITestAdaptor Parent { get; private set; }
|
||||
public int TestCaseTimeout { get; private set; }
|
||||
public ITypeInfo TypeInfo { get; private set; }
|
||||
public IMethodInfo Method { get; private set; }
|
||||
private string[] m_ChildrenIds;
|
||||
public string[] Categories { get; private set; }
|
||||
public bool IsTestAssembly { get; private set; }
|
||||
public RunState RunState { get; }
|
||||
public string Description { get; }
|
||||
public string SkipReason { get; }
|
||||
public string ParentId { get; }
|
||||
public string ParentFullName { get; }
|
||||
public string UniqueName { get; }
|
||||
public string ParentUniqueName { get; }
|
||||
public int ChildIndex { get; }
|
||||
public TestMode TestMode { get; private set; }
|
||||
|
||||
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: 6e0e62db88935c74288c97c907243bd0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,91 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestAdaptorFactory : ITestAdaptorFactory
|
||||
{
|
||||
private Dictionary<string, TestAdaptor> m_TestAdaptorCache = new Dictionary<string, TestAdaptor>();
|
||||
private Dictionary<string, TestResultAdaptor> m_TestResultAdaptorCache = new Dictionary<string, TestResultAdaptor>();
|
||||
public ITestAdaptor Create(ITest test)
|
||||
{
|
||||
var uniqueName = test.GetUniqueName();
|
||||
if (m_TestAdaptorCache.ContainsKey(uniqueName))
|
||||
{
|
||||
return m_TestAdaptorCache[uniqueName];
|
||||
}
|
||||
|
||||
var adaptor = new TestAdaptor(test, test.Tests.Select(Create).ToArray());
|
||||
foreach (var child in adaptor.Children)
|
||||
{
|
||||
(child as TestAdaptor).SetParent(adaptor);
|
||||
}
|
||||
m_TestAdaptorCache[uniqueName] = adaptor;
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
public ITestAdaptor Create(RemoteTestData testData)
|
||||
{
|
||||
return new TestAdaptor(testData);
|
||||
}
|
||||
|
||||
public ITestResultAdaptor Create(ITestResult testResult)
|
||||
{
|
||||
var uniqueName = testResult.Test.GetUniqueName();
|
||||
if (m_TestResultAdaptorCache.ContainsKey(uniqueName))
|
||||
{
|
||||
return m_TestResultAdaptorCache[uniqueName];
|
||||
}
|
||||
var adaptor = new TestResultAdaptor(testResult, Create(testResult.Test), testResult.Children.Select(Create).ToArray());
|
||||
m_TestResultAdaptorCache[uniqueName] = adaptor;
|
||||
return adaptor;
|
||||
}
|
||||
|
||||
public ITestResultAdaptor Create(RemoteTestResultData testResult, RemoteTestResultDataWithTestData allData)
|
||||
{
|
||||
return new TestResultAdaptor(testResult, allData);
|
||||
}
|
||||
|
||||
public ITestAdaptor BuildTree(RemoteTestResultDataWithTestData data)
|
||||
{
|
||||
var tests = data.tests.Select(remoteTestData => new TestAdaptor(remoteTestData)).ToList();
|
||||
|
||||
foreach (var test in tests)
|
||||
{
|
||||
test.ApplyChildren(tests);
|
||||
}
|
||||
|
||||
return tests.First();
|
||||
}
|
||||
|
||||
public IEnumerator<ITestAdaptor> BuildTreeAsync(RemoteTestResultDataWithTestData data)
|
||||
{
|
||||
var tests = data.tests.Select(remoteTestData => new TestAdaptor(remoteTestData)).ToList();
|
||||
|
||||
for (var index = 0; index < tests.Count; index++)
|
||||
{
|
||||
var test = tests[index];
|
||||
test.ApplyChildren(tests);
|
||||
if (index % 100 == 0)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
yield return tests.First();
|
||||
}
|
||||
|
||||
public void ClearResultsCache()
|
||||
{
|
||||
m_TestResultAdaptorCache.Clear();
|
||||
}
|
||||
|
||||
public void ClearTestsCache()
|
||||
{
|
||||
m_TestAdaptorCache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0663d520c26b7c48a4135599e66acf8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// A flag indicating whether to run Edit Mode or Play Mode tests.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum TestMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Run EditMode tests.
|
||||
/// </summary>
|
||||
EditMode = 1 << 0,
|
||||
/// <summary>
|
||||
/// Run PlayMode tests.
|
||||
/// </summary>
|
||||
PlayMode = 1 << 1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cad095eccea17b741bc4cd264e7441cd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
internal class TestResultAdaptor : ITestResultAdaptor
|
||||
{
|
||||
private TNode m_Node;
|
||||
private ITestResult m_Result;
|
||||
|
||||
internal TestResultAdaptor(ITestResult result, ITestAdaptor test, ITestResultAdaptor[] children = null)
|
||||
{
|
||||
Test = test;
|
||||
Name = result.Name;
|
||||
FullName = result.FullName;
|
||||
ResultState = result.ResultState.ToString();
|
||||
TestStatus = ParseTestStatus(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;
|
||||
Children = children;
|
||||
m_Result = result;
|
||||
}
|
||||
|
||||
internal TestResultAdaptor(RemoteTestResultData result, RemoteTestResultDataWithTestData allData)
|
||||
{
|
||||
Test = new TestAdaptor(allData.tests.First(t => t.id == result.testId));
|
||||
Name = result.name;
|
||||
FullName = result.fullName;
|
||||
ResultState = result.resultState;
|
||||
TestStatus = ParseTestStatus(result.testStatus);
|
||||
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;
|
||||
Children = result.childrenIds.Select(childId => new TestResultAdaptor(allData.results.First(r => r.testId == childId), allData)).ToArray();
|
||||
m_Node = TNode.FromXml(result.xml);
|
||||
}
|
||||
|
||||
public ITestAdaptor Test { get; private set; }
|
||||
public string Name { get; private set; }
|
||||
public string FullName { get; private set; }
|
||||
public string ResultState { get; private set; }
|
||||
public TestStatus TestStatus { get; private set; }
|
||||
public double Duration { get; private set; }
|
||||
public DateTime StartTime { get; private set; }
|
||||
public DateTime EndTime { get; private set; }
|
||||
public string Message { get; private set; }
|
||||
public string StackTrace { get; private set; }
|
||||
public int AssertCount { get; private set; }
|
||||
public int FailCount { get; private set; }
|
||||
public int PassCount { get; private set; }
|
||||
public int SkipCount { get; private set; }
|
||||
public int InconclusiveCount { get; private set; }
|
||||
public bool HasChildren { get; private set; }
|
||||
public IEnumerable<ITestResultAdaptor> Children { get; private set; }
|
||||
public string Output { get; private set; }
|
||||
public TNode ToXml()
|
||||
{
|
||||
if (m_Node == null)
|
||||
{
|
||||
m_Node = m_Result.ToXml(true);
|
||||
}
|
||||
return m_Node;
|
||||
}
|
||||
|
||||
private static TestStatus ParseTestStatus(NUnit.Framework.Interfaces.TestStatus testStatus)
|
||||
{
|
||||
return (TestStatus)Enum.Parse(typeof(TestStatus), testStatus.ToString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d061ada5d3169454daf54243390b5fdb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,158 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using UnityEditor.TestTools.TestRunner.CommandLineTest;
|
||||
using UnityEditor.TestTools.TestRunner.TestRun;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The TestRunnerApi retrieves and runs tests programmatically from code inside the project, or inside other packages. TestRunnerApi is a [ScriptableObject](https://docs.unity3d.com/ScriptReference/ScriptableObject.html).
|
||||
///You can initialize the API like this:
|
||||
/// ```
|
||||
/// var testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
|
||||
/// ```
|
||||
/// Note: You can subscribe and receive test results in one instance of the API, even if the run starts from another instance.
|
||||
/// The TestRunnerApi supports the following workflows:
|
||||
/// - [How to run tests programmatically](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-run-tests.html)
|
||||
/// - [How to get test results](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-get-test-results.html)
|
||||
/// - [How to retrieve the list of tests](https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/extension-retrieve-test-list.html)
|
||||
/// </summary>
|
||||
public class TestRunnerApi : ScriptableObject, ITestRunnerApi
|
||||
{
|
||||
internal ICallbacksHolder callbacksHolder;
|
||||
|
||||
private ICallbacksHolder m_CallbacksHolder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (callbacksHolder == null)
|
||||
{
|
||||
return CallbacksHolder.instance;
|
||||
}
|
||||
|
||||
return callbacksHolder;
|
||||
}
|
||||
}
|
||||
|
||||
internal Func<ExecutionSettings,string> ScheduleJob = (executionSettings) =>
|
||||
{
|
||||
var runner = new TestJobRunner();
|
||||
return runner.RunJob(new TestJobData(executionSettings));
|
||||
};
|
||||
/// <summary>
|
||||
/// Starts a test run with a given set of executionSettings.
|
||||
/// </summary>
|
||||
/// <param name="executionSettings">Set of <see cref="ExecutionSettings"/></param>
|
||||
/// <returns>A GUID that identifies the TestJobData.</returns>
|
||||
public string Execute(ExecutionSettings executionSettings)
|
||||
{
|
||||
if (executionSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(executionSettings));
|
||||
}
|
||||
|
||||
if ((executionSettings.filters == null || executionSettings.filters.Length == 0) && executionSettings.filter != null)
|
||||
{
|
||||
// Map filter (singular) to filters (plural), for backwards compatibility.
|
||||
executionSettings.filters = new [] {executionSettings.filter};
|
||||
}
|
||||
|
||||
if (executionSettings.targetPlatform == null && executionSettings.filters != null &&
|
||||
executionSettings.filters.Length > 0)
|
||||
{
|
||||
executionSettings.targetPlatform = executionSettings.filters[0].targetPlatform;
|
||||
}
|
||||
|
||||
return ScheduleJob(executionSettings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up a given instance of <see cref="ICallbacks"/> to be invoked on test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">
|
||||
/// The test callbacks to be invoked.
|
||||
/// </param>
|
||||
/// <param name="priority">
|
||||
/// Sets the order in which the callbacks are invoked, starting with the highest value first.
|
||||
/// </param>
|
||||
public void RegisterCallbacks<T>(T testCallbacks, int priority = 0) where T : ICallbacks
|
||||
{
|
||||
if (testCallbacks == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testCallbacks));
|
||||
}
|
||||
|
||||
m_CallbacksHolder.Add(testCallbacks, priority);
|
||||
}
|
||||
/// <summary>
|
||||
/// Unregister an instance of <see cref="ICallbacks"/> to no longer receive callbacks from test runs.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Generic representing a type of callback.
|
||||
/// </typeparam>
|
||||
/// <param name="testCallbacks">The test callbacks to unregister.</param>
|
||||
public void UnregisterCallbacks<T>(T testCallbacks) where T : ICallbacks
|
||||
{
|
||||
if (testCallbacks == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(testCallbacks));
|
||||
}
|
||||
|
||||
m_CallbacksHolder.Remove(testCallbacks);
|
||||
}
|
||||
|
||||
internal void RetrieveTestList(ExecutionSettings executionSettings, Action<ITestAdaptor> callback)
|
||||
{
|
||||
if (executionSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(executionSettings));
|
||||
}
|
||||
|
||||
var firstFilter = executionSettings.filters?.FirstOrDefault() ?? executionSettings.filter;
|
||||
RetrieveTestList(firstFilter.testMode, callback);
|
||||
}
|
||||
/// <summary>
|
||||
/// Retrieve the full test tree as ITestAdaptor for a given test mode. This is obsolete. Use TestRunnerApi.RetrieveTestTree instead.
|
||||
/// </summary>
|
||||
/// <param name="testMode"></param>
|
||||
/// <param name="callback"></param>
|
||||
public void RetrieveTestList(TestMode testMode, Action<ITestAdaptor> callback)
|
||||
{
|
||||
if (callback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(callback));
|
||||
}
|
||||
|
||||
var platform = ParseTestMode(testMode);
|
||||
var testAssemblyProvider = new EditorLoadedTestAssemblyProvider(new EditorCompilationInterfaceProxy(), new EditorAssembliesProxy());
|
||||
var testAdaptorFactory = new TestAdaptorFactory();
|
||||
var testListCache = new TestListCache(testAdaptorFactory, new RemoteTestResultDataFactory(), TestListCacheData.instance);
|
||||
var testListProvider = new TestListProvider(testAssemblyProvider, new UnityTestAssemblyBuilder());
|
||||
var cachedTestListProvider = new CachingTestListProvider(testListProvider, testListCache, testAdaptorFactory);
|
||||
|
||||
var job = new TestListJob(cachedTestListProvider, platform, (testRoot) =>
|
||||
{
|
||||
callback(testRoot);
|
||||
});
|
||||
job.Start();
|
||||
}
|
||||
|
||||
internal static bool IsRunActive()
|
||||
{
|
||||
return RunData.instance.isRunning;
|
||||
}
|
||||
|
||||
private static TestPlatform ParseTestMode(TestMode testMode)
|
||||
{
|
||||
return (((testMode & TestMode.EditMode) == TestMode.EditMode) ? TestPlatform.EditMode : 0) | (((testMode & TestMode.PlayMode) == TestMode.PlayMode) ? TestPlatform.PlayMode : 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 68993ba529ae04440916cb7c23bf3279
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,28 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.Api
|
||||
{
|
||||
/// <summary>
|
||||
/// The TestStatus enum indicates the test result status.
|
||||
/// </summary>
|
||||
public enum TestStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The test ran with an inconclusive result.
|
||||
/// </summary>
|
||||
Inconclusive,
|
||||
|
||||
/// <summary>
|
||||
/// The test was skipped.
|
||||
/// </summary>
|
||||
Skipped,
|
||||
|
||||
/// <summary>
|
||||
/// The test ran and passed.
|
||||
/// </summary>
|
||||
Passed,
|
||||
|
||||
/// <summary>
|
||||
/// The test ran and failed.
|
||||
/// </summary>
|
||||
Failed
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ec94545c5b00344c9bd8e691f15d799
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyTitle("UnityEditor.TestRunner")]
|
||||
[assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("Unity.PerformanceTesting.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.IntegrationTests")]
|
||||
[assembly: InternalsVisibleTo("UnityEditor.TestRunner.Tests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.Editor")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageManagerUI.Develop.EditorTests")]
|
||||
[assembly: InternalsVisibleTo("Unity.PackageValidationSuite.Editor")]
|
||||
|
||||
[assembly: AssemblyVersion("1.0.0")]
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9db19a04003fca7439552acd4de9baa1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7602252bdb82b8d45ae3483c3a00d3e1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
internal class CommandLineOption : ICommandLineOption
|
||||
{
|
||||
Action<string> m_ArgAction;
|
||||
|
||||
public CommandLineOption(string argName, Action action)
|
||||
{
|
||||
ArgName = argName;
|
||||
m_ArgAction = s => action();
|
||||
}
|
||||
|
||||
public CommandLineOption(string argName, Action<string> action)
|
||||
{
|
||||
ArgName = argName;
|
||||
m_ArgAction = action;
|
||||
}
|
||||
|
||||
public CommandLineOption(string argName, Action<string[]> action)
|
||||
{
|
||||
ArgName = argName;
|
||||
m_ArgAction = s => action(SplitStringToArray(s));
|
||||
}
|
||||
|
||||
public string ArgName { get; private set; }
|
||||
|
||||
public void ApplyValue(string value)
|
||||
{
|
||||
m_ArgAction(value);
|
||||
}
|
||||
|
||||
static string[] SplitStringToArray(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a3529368f4cd0424a89aa51080a16b06
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
internal class CommandLineOptionSet
|
||||
{
|
||||
ICommandLineOption[] m_Options;
|
||||
|
||||
public CommandLineOptionSet(params ICommandLineOption[] options)
|
||||
{
|
||||
m_Options = options;
|
||||
}
|
||||
|
||||
public void Parse(string[] args)
|
||||
{
|
||||
var i = 0;
|
||||
while (i < args.Length)
|
||||
{
|
||||
var arg = args[i];
|
||||
if (!arg.StartsWith("-"))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
string value = null;
|
||||
if (i + 1 < args.Length && !args[i + 1].StartsWith("-"))
|
||||
{
|
||||
value = args[i + 1];
|
||||
i++;
|
||||
}
|
||||
|
||||
ApplyValueToMatchingOptions(arg, value);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyValueToMatchingOptions(string argName, string value)
|
||||
{
|
||||
foreach (var option in m_Options)
|
||||
{
|
||||
if ("-" + option.ArgName == argName)
|
||||
{
|
||||
option.ApplyValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 139c5eac101a4dc4fb3098e30c29f15e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
namespace UnityEditor.TestRunner.CommandLineParser
|
||||
{
|
||||
interface ICommandLineOption
|
||||
{
|
||||
string ArgName { get; }
|
||||
void ApplyValue(string value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f445ca0c614a846449fcd8ae648c24e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b477d1f29b65a674e9d5cdab4eb72b01
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using UnityEditor.TestRunner.TestLaunchers;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class Executer
|
||||
{
|
||||
private ITestRunnerApi m_TestRunnerApi;
|
||||
private ISettingsBuilder m_SettingsBuilder;
|
||||
private Action<string, object[]> m_LogErrorFormat;
|
||||
private Action<Exception> m_LogException;
|
||||
private Action<int> m_ExitEditorApplication;
|
||||
private Func<bool> m_ScriptCompilationFailedCheck;
|
||||
|
||||
public Executer(ITestRunnerApi testRunnerApi, ISettingsBuilder settingsBuilder, Action<string, object[]> logErrorFormat, Action<Exception> logException, Action<int> exitEditorApplication, Func<bool> scriptCompilationFailedCheck)
|
||||
{
|
||||
m_TestRunnerApi = testRunnerApi;
|
||||
m_SettingsBuilder = settingsBuilder;
|
||||
m_LogErrorFormat = logErrorFormat;
|
||||
m_LogException = logException;
|
||||
m_ExitEditorApplication = exitEditorApplication;
|
||||
m_ScriptCompilationFailedCheck = scriptCompilationFailedCheck;
|
||||
}
|
||||
|
||||
internal void InitializeAndExecuteRun(string[] commandLineArgs)
|
||||
{
|
||||
Api.ExecutionSettings executionSettings;
|
||||
try
|
||||
{
|
||||
executionSettings = m_SettingsBuilder.BuildApiExecutionSettings(commandLineArgs);
|
||||
if (executionSettings.targetPlatform.HasValue)
|
||||
RemotePlayerLogController.instance.SetBuildTarget(executionSettings.targetPlatform.Value);
|
||||
}
|
||||
catch (SetupException exception)
|
||||
{
|
||||
HandleSetupException(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Log("Executing tests with settings: " + ExecutionSettingsToString(executionSettings));
|
||||
m_TestRunnerApi.Execute(executionSettings);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
m_LogException(exception);
|
||||
m_ExitEditorApplication((int)ReturnCodes.RunError);
|
||||
}
|
||||
}
|
||||
|
||||
internal ExecutionSettings BuildExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
return m_SettingsBuilder.BuildExecutionSettings(commandLineArgs);
|
||||
}
|
||||
|
||||
internal enum ReturnCodes
|
||||
{
|
||||
Ok = 0,
|
||||
Failed = 2,
|
||||
RunError = 3,
|
||||
PlatformNotFoundReturnCode = 4
|
||||
}
|
||||
|
||||
internal void SetUpCallbacks(ExecutionSettings executionSettings)
|
||||
{
|
||||
RemotePlayerLogController.instance.SetLogsDirectory(executionSettings.DeviceLogsDirectory);
|
||||
|
||||
var resultSavingCallback = ScriptableObject.CreateInstance<ResultsSavingCallbacks>();
|
||||
resultSavingCallback.m_ResultFilePath = executionSettings.TestResultsFile;
|
||||
|
||||
var logSavingCallback = ScriptableObject.CreateInstance<LogSavingCallbacks>();
|
||||
|
||||
m_TestRunnerApi.RegisterCallbacks(resultSavingCallback);
|
||||
m_TestRunnerApi.RegisterCallbacks(logSavingCallback);
|
||||
m_TestRunnerApi.RegisterCallbacks(ScriptableObject.CreateInstance<ExitCallbacks>(), -10);
|
||||
}
|
||||
|
||||
internal void ExitOnCompileErrors()
|
||||
{
|
||||
if (m_ScriptCompilationFailedCheck())
|
||||
{
|
||||
var handling = s_ExceptionHandlingMapping.First(h => h.m_ExceptionType == SetupException.ExceptionType.ScriptCompilationFailed);
|
||||
m_LogErrorFormat(handling.m_Message, new object[0]);
|
||||
m_ExitEditorApplication(handling.m_ReturnCode);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSetupException(SetupException exception)
|
||||
{
|
||||
ExceptionHandling handling = s_ExceptionHandlingMapping.FirstOrDefault(h => h.m_ExceptionType == exception.Type) ?? new ExceptionHandling(exception.Type, "Unknown command line test run error. " + exception.Type, ReturnCodes.RunError);
|
||||
m_LogErrorFormat(handling.m_Message, exception.Details);
|
||||
m_ExitEditorApplication(handling.m_ReturnCode);
|
||||
}
|
||||
|
||||
private class ExceptionHandling
|
||||
{
|
||||
internal SetupException.ExceptionType m_ExceptionType;
|
||||
internal string m_Message;
|
||||
internal int m_ReturnCode;
|
||||
public ExceptionHandling(SetupException.ExceptionType exceptionType, string message, ReturnCodes returnCode)
|
||||
{
|
||||
m_ExceptionType = exceptionType;
|
||||
m_Message = message;
|
||||
m_ReturnCode = (int)returnCode;
|
||||
}
|
||||
}
|
||||
|
||||
static ExceptionHandling[] s_ExceptionHandlingMapping = new[]
|
||||
{
|
||||
new ExceptionHandling(SetupException.ExceptionType.ScriptCompilationFailed, "Scripts had compilation errors.", ReturnCodes.RunError),
|
||||
new ExceptionHandling(SetupException.ExceptionType.PlatformNotFound, "Test platform not found ({0}).", ReturnCodes.PlatformNotFoundReturnCode),
|
||||
new ExceptionHandling(SetupException.ExceptionType.TestSettingsFileNotFound, "Test settings file not found at {0}.", ReturnCodes.RunError)
|
||||
};
|
||||
|
||||
private static string ExecutionSettingsToString(Api.ExecutionSettings executionSettings)
|
||||
{
|
||||
if (executionSettings == null)
|
||||
{
|
||||
return "none";
|
||||
}
|
||||
|
||||
if (executionSettings.filters == null || executionSettings.filters.Length == 0)
|
||||
{
|
||||
return "no filter";
|
||||
}
|
||||
|
||||
return "test mode = " + executionSettings.filters[0].testMode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 083c6a3a5426382449369ddc12b691d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class ExecutionSettings
|
||||
{
|
||||
public string TestResultsFile;
|
||||
public string DeviceLogsDirectory;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c3a75354f6ceac94ca15ca9d96593290
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,52 @@
|
|||
using System;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class ExitCallbacks : ScriptableObject, IErrorCallbacks
|
||||
{
|
||||
internal static bool preventExit;
|
||||
|
||||
public void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
if (preventExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ExitCallbacksDataHolder.instance.AnyTestsExecuted)
|
||||
{
|
||||
Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, "No tests were executed");
|
||||
}
|
||||
|
||||
EditorApplication.Exit(ExitCallbacksDataHolder.instance.RunFailed ? (int)Executer.ReturnCodes.Failed : (int)Executer.ReturnCodes.Ok);
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
if (!test.IsSuite)
|
||||
{
|
||||
ExitCallbacksDataHolder.instance.AnyTestsExecuted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
if (!result.Test.IsSuite && (result.TestStatus == TestStatus.Failed || result.TestStatus == TestStatus.Inconclusive))
|
||||
{
|
||||
ExitCallbacksDataHolder.instance.RunFailed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnError(string message)
|
||||
{
|
||||
EditorApplication.Exit((int)Executer.ReturnCodes.RunError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1adaa8dcc4fda3d4cb4d3c8e0cb65d12
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class ExitCallbacksDataHolder : ScriptableSingleton<ExitCallbacksDataHolder>
|
||||
{
|
||||
[SerializeField]
|
||||
public bool AnyTestsExecuted;
|
||||
[SerializeField]
|
||||
public bool RunFailed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aab045daa0ad4b01843dcf44013d9653
|
||||
timeCreated: 1605189497
|
|
@ -0,0 +1,10 @@
|
|||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
interface ISettingsBuilder
|
||||
{
|
||||
Api.ExecutionSettings BuildApiExecutionSettings(string[] commandLineArgs);
|
||||
ExecutionSettings BuildExecutionSettings(string[] commandLineArgs);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8a13cbeb2099aca47bb456f49845f86c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using UnityEditor.TestRunner.TestLaunchers;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class LogSavingCallbacks : ScriptableObject, ICallbacks
|
||||
{
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
RemotePlayerLogController.instance.StartLogWriters();
|
||||
}
|
||||
|
||||
public virtual void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
RemotePlayerLogController.instance.StopLogWriters();
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8d20eedbe40f0ce41a4c4f633f225de8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor.DeploymentTargets;
|
||||
using UnityEditor.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class LogWriter : IDisposable
|
||||
{
|
||||
private string m_LogsDirectory;
|
||||
private string m_DeviceID;
|
||||
private Dictionary<string, StreamWriter> m_LogStreams;
|
||||
private DeploymentTargetLogger m_Logger;
|
||||
|
||||
internal LogWriter(string logsDirectory, string deviceID, DeploymentTargetLogger logger)
|
||||
{
|
||||
m_LogStreams = new Dictionary<string, StreamWriter>();
|
||||
m_Logger = logger;
|
||||
m_LogsDirectory = logsDirectory;
|
||||
m_DeviceID = deviceID;
|
||||
|
||||
logger.logMessage += WriteLogToFile;
|
||||
}
|
||||
|
||||
private void WriteLogToFile(string id, string logLine)
|
||||
{
|
||||
StreamWriter logStream;
|
||||
var streamExists = m_LogStreams.TryGetValue(id, out logStream);
|
||||
if (!streamExists)
|
||||
{
|
||||
var filePath = GetLogFilePath(m_LogsDirectory, m_DeviceID, id);
|
||||
logStream = CreateLogFile(filePath);
|
||||
|
||||
m_LogStreams.Add(id, logStream);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (logLine != null)
|
||||
logStream.WriteLine(logLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Writing {id} log failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
m_Logger.Stop();
|
||||
foreach (var logStream in m_LogStreams)
|
||||
{
|
||||
logStream.Value.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private StreamWriter CreateLogFile(string path)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Creating {0} device log: {1}", m_DeviceID, path);
|
||||
StreamWriter streamWriter = null;
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||
|
||||
streamWriter = File.CreateText(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Creating device log {path} file failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
|
||||
return streamWriter;
|
||||
}
|
||||
|
||||
private string GetLogFilePath(string lgosDirectory, string deviceID, string logID)
|
||||
{
|
||||
var fileName = "Device-" + deviceID + "-" + logID + ".txt";
|
||||
fileName = string.Join("_", fileName.Split(Path.GetInvalidFileNameChars()));
|
||||
return Paths.Combine(lgosDirectory, fileName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 05778dd1de4433d418793b6f3d3c18cf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEditor.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[Serializable]
|
||||
internal class ResultsSavingCallbacks : ScriptableObject, ICallbacks
|
||||
{
|
||||
[SerializeField]
|
||||
public string m_ResultFilePath;
|
||||
|
||||
public ResultsSavingCallbacks()
|
||||
{
|
||||
this.m_ResultFilePath = GetDefaultResultFilePath();
|
||||
}
|
||||
|
||||
public void RunStarted(ITestAdaptor testsToRun)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RunFinished(ITestResultAdaptor testResults)
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_ResultFilePath))
|
||||
{
|
||||
m_ResultFilePath = GetDefaultResultFilePath();
|
||||
}
|
||||
|
||||
var resultWriter = new ResultsWriter();
|
||||
resultWriter.WriteResultToFile(testResults, m_ResultFilePath);
|
||||
}
|
||||
|
||||
public void TestStarted(ITestAdaptor test)
|
||||
{
|
||||
}
|
||||
|
||||
public void TestFinished(ITestResultAdaptor result)
|
||||
{
|
||||
}
|
||||
|
||||
private static string GetDefaultResultFilePath()
|
||||
{
|
||||
var fileName = "TestResults-" + DateTime.Now.Ticks + ".xml";
|
||||
var projectPath = Directory.GetCurrentDirectory();
|
||||
return Paths.Combine(projectPath, fileName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ef563c5a6ecf64d4193dc144cb7d472a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class ResultsWriter
|
||||
{
|
||||
private const string k_nUnitVersion = "3.5.0.0";
|
||||
|
||||
private const string k_TestRunNode = "test-run";
|
||||
private const string k_Id = "id";
|
||||
private const string k_Testcasecount = "testcasecount";
|
||||
private const string k_Result = "result";
|
||||
private const string k_Total = "total";
|
||||
private const string k_Passed = "passed";
|
||||
private const string k_Failed = "failed";
|
||||
private const string k_Inconclusive = "inconclusive";
|
||||
private const string k_Skipped = "skipped";
|
||||
private const string k_Asserts = "asserts";
|
||||
private const string k_EngineVersion = "engine-version";
|
||||
private const string k_ClrVersion = "clr-version";
|
||||
private const string k_StartTime = "start-time";
|
||||
private const string k_EndTime = "end-time";
|
||||
private const string k_Duration = "duration";
|
||||
|
||||
private const string k_TimeFormat = "u";
|
||||
|
||||
public void WriteResultToFile(ITestResultAdaptor result, string filePath)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Saving results to: {0}", filePath);
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(filePath))
|
||||
{
|
||||
CreateDirectory(filePath);
|
||||
}
|
||||
|
||||
using (var fileStream = File.CreateText(filePath))
|
||||
{
|
||||
WriteResultToStream(result, fileStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError("Saving result file failed.");
|
||||
Debug.LogException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateDirectory(string filePath)
|
||||
{
|
||||
var driectoryPath = Path.GetDirectoryName(filePath);
|
||||
if (!String.IsNullOrEmpty(driectoryPath))
|
||||
{
|
||||
Directory.CreateDirectory(driectoryPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteResultToStream(ITestResultAdaptor result, StreamWriter streamWriter, XmlWriterSettings settings = null)
|
||||
{
|
||||
settings = settings ?? new XmlWriterSettings();
|
||||
settings.Indent = true;
|
||||
settings.NewLineOnAttributes = false;
|
||||
|
||||
using (var xmlWriter = XmlWriter.Create(streamWriter, settings))
|
||||
{
|
||||
WriteResultsToXml(result, xmlWriter);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteResultsToXml(ITestResultAdaptor result, XmlWriter xmlWriter)
|
||||
{
|
||||
// XML format as specified at https://github.com/nunit/docs/wiki/Test-Result-XML-Format
|
||||
|
||||
var testRunNode = new TNode(k_TestRunNode);
|
||||
|
||||
testRunNode.AddAttribute(k_Id, "2");
|
||||
testRunNode.AddAttribute(k_Testcasecount, (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString());
|
||||
testRunNode.AddAttribute(k_Result, result.ResultState.ToString());
|
||||
testRunNode.AddAttribute(k_Total, (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString());
|
||||
testRunNode.AddAttribute(k_Passed, result.PassCount.ToString());
|
||||
testRunNode.AddAttribute(k_Failed, result.FailCount.ToString());
|
||||
testRunNode.AddAttribute(k_Inconclusive, result.InconclusiveCount.ToString());
|
||||
testRunNode.AddAttribute(k_Skipped, result.SkipCount.ToString());
|
||||
testRunNode.AddAttribute(k_Asserts, result.AssertCount.ToString());
|
||||
testRunNode.AddAttribute(k_EngineVersion, k_nUnitVersion);
|
||||
testRunNode.AddAttribute(k_ClrVersion, Environment.Version.ToString());
|
||||
testRunNode.AddAttribute(k_StartTime, result.StartTime.ToString(k_TimeFormat));
|
||||
testRunNode.AddAttribute(k_EndTime, result.EndTime.ToString(k_TimeFormat));
|
||||
testRunNode.AddAttribute(k_Duration, result.Duration.ToString());
|
||||
|
||||
var resultNode = result.ToXml();
|
||||
testRunNode.ChildNodes.Add(resultNode);
|
||||
|
||||
testRunNode.WriteTo(xmlWriter);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29d603e0a726a9043b3503112271844a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class RunData : ScriptableSingleton<RunData>
|
||||
{
|
||||
public bool isRunning;
|
||||
public ExecutionSettings executionSettings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3f8c1075884df0249b80e23a0598f9c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,29 @@
|
|||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class RunSettings : ITestRunSettings
|
||||
{
|
||||
private ITestSettings m_TestSettings;
|
||||
public RunSettings(ITestSettings testSettings)
|
||||
{
|
||||
this.m_TestSettings = testSettings;
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (m_TestSettings != null)
|
||||
{
|
||||
m_TestSettings.SetupProjectParameters();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_TestSettings != null)
|
||||
{
|
||||
m_TestSettings.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 59d3f5586b341a74c84c8f72144a4568
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestRunner.CommandLineParser;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEditor.TestTools.TestRunner.GUI;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class SettingsBuilder : ISettingsBuilder
|
||||
{
|
||||
private ITestSettingsDeserializer m_TestSettingsDeserializer;
|
||||
private Action<string> m_LogAction;
|
||||
private Action<string> m_LogWarningAction;
|
||||
private Func<string, bool> m_FileExistsCheck;
|
||||
private Func<bool> m_ScriptCompilationFailedCheck;
|
||||
public SettingsBuilder(ITestSettingsDeserializer testSettingsDeserializer, Action<string> logAction, Action<string> logWarningAction, Func<string, bool> fileExistsCheck, Func<bool> scriptCompilationFailedCheck)
|
||||
{
|
||||
m_LogAction = logAction;
|
||||
m_LogWarningAction = logWarningAction;
|
||||
m_FileExistsCheck = fileExistsCheck;
|
||||
m_ScriptCompilationFailedCheck = scriptCompilationFailedCheck;
|
||||
m_TestSettingsDeserializer = testSettingsDeserializer;
|
||||
}
|
||||
|
||||
public Api.ExecutionSettings BuildApiExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
var quit = false;
|
||||
string testPlatform = TestMode.EditMode.ToString();
|
||||
string[] testFilters = null;
|
||||
string[] testCategories = null;
|
||||
string testSettingsFilePath = null;
|
||||
int testRepetitions = 1;
|
||||
int? playerHeartbeatTimeout = null;
|
||||
bool runSynchronously = false;
|
||||
string[] testAssemblyNames = null;
|
||||
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("quit", () => { quit = true; }),
|
||||
new CommandLineOption("testPlatform", platform => { testPlatform = platform; }),
|
||||
new CommandLineOption("editorTestsFilter", filters => { testFilters = filters; }),
|
||||
new CommandLineOption("testFilter", filters => { testFilters = filters; }),
|
||||
new CommandLineOption("editorTestsCategories", catagories => { testCategories = catagories; }),
|
||||
new CommandLineOption("testCategory", catagories => { testCategories = catagories; }),
|
||||
new CommandLineOption("testSettingsFile", settingsFilePath => { testSettingsFilePath = settingsFilePath; }),
|
||||
new CommandLineOption("testRepetitions", reps => { testRepetitions = int.Parse(reps); }),
|
||||
new CommandLineOption("playerHeartbeatTimeout", timeout => { playerHeartbeatTimeout = int.Parse(timeout); }),
|
||||
new CommandLineOption("runSynchronously", () => { runSynchronously = true; }),
|
||||
new CommandLineOption("assemblyNames", assemblyNames => { testAssemblyNames = assemblyNames; })
|
||||
);
|
||||
optionSet.Parse(commandLineArgs);
|
||||
|
||||
DisplayQuitWarningIfQuitIsGiven(quit);
|
||||
|
||||
CheckForScriptCompilationErrors();
|
||||
|
||||
LogParametersForRun(testPlatform, testFilters, testCategories, testSettingsFilePath);
|
||||
|
||||
var testSettings = GetTestSettings(testSettingsFilePath);
|
||||
|
||||
var filter = new Filter()
|
||||
{
|
||||
groupNames = testFilters,
|
||||
categoryNames = testCategories,
|
||||
assemblyNames = testAssemblyNames
|
||||
};
|
||||
|
||||
var buildTarget = SetFilterAndGetBuildTarget(testPlatform, filter);
|
||||
|
||||
RerunCallbackData.instance.runFilters = new []{new UITestRunnerFilter()
|
||||
{
|
||||
categoryNames = filter.categoryNames,
|
||||
groupNames = filter.groupNames,
|
||||
testRepetitions = testRepetitions
|
||||
}};
|
||||
|
||||
RerunCallbackData.instance.testMode = filter.testMode;
|
||||
|
||||
var settings = new Api.ExecutionSettings()
|
||||
{
|
||||
filters = new []{filter},
|
||||
overloadTestRunSettings = new RunSettings(testSettings),
|
||||
targetPlatform = buildTarget,
|
||||
runSynchronously = runSynchronously
|
||||
};
|
||||
|
||||
if (playerHeartbeatTimeout != null)
|
||||
{
|
||||
settings.playerHeartbeatTimeout = playerHeartbeatTimeout.Value;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public ExecutionSettings BuildExecutionSettings(string[] commandLineArgs)
|
||||
{
|
||||
string resultFilePath = null;
|
||||
string deviceLogsDirectory = null;
|
||||
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("editorTestsResultFile", filePath => { resultFilePath = filePath; }),
|
||||
new CommandLineOption("testResults", filePath => { resultFilePath = filePath; }),
|
||||
new CommandLineOption("deviceLogs", dirPath => { deviceLogsDirectory = dirPath; })
|
||||
);
|
||||
optionSet.Parse(commandLineArgs);
|
||||
|
||||
return new ExecutionSettings()
|
||||
{
|
||||
TestResultsFile = resultFilePath,
|
||||
DeviceLogsDirectory = deviceLogsDirectory
|
||||
};
|
||||
}
|
||||
|
||||
void DisplayQuitWarningIfQuitIsGiven(bool quitIsGiven)
|
||||
{
|
||||
if (quitIsGiven)
|
||||
{
|
||||
m_LogWarningAction("Running tests from command line arguments will not work when \"quit\" is specified.");
|
||||
}
|
||||
}
|
||||
|
||||
void CheckForScriptCompilationErrors()
|
||||
{
|
||||
if (m_ScriptCompilationFailedCheck())
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.ScriptCompilationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void LogParametersForRun(string testPlatform, string[] testFilters, string[] testCategories, string testSettingsFilePath)
|
||||
{
|
||||
m_LogAction("Running tests for " + testPlatform);
|
||||
if (testFilters != null && testFilters.Length > 0)
|
||||
{
|
||||
m_LogAction("With test filter: " + string.Join(", ", testFilters));
|
||||
}
|
||||
if (testCategories != null && testCategories.Length > 0)
|
||||
{
|
||||
m_LogAction("With test categories: " + string.Join(", ", testCategories));
|
||||
}
|
||||
if (!string.IsNullOrEmpty(testSettingsFilePath))
|
||||
{
|
||||
m_LogAction("With test settings file: " + testSettingsFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
ITestSettings GetTestSettings(string testSettingsFilePath)
|
||||
{
|
||||
ITestSettings testSettings = null;
|
||||
if (!string.IsNullOrEmpty(testSettingsFilePath))
|
||||
{
|
||||
if (!m_FileExistsCheck(testSettingsFilePath))
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.TestSettingsFileNotFound, testSettingsFilePath);
|
||||
}
|
||||
|
||||
testSettings = m_TestSettingsDeserializer.GetSettingsFromJsonFile(testSettingsFilePath);
|
||||
}
|
||||
return testSettings;
|
||||
}
|
||||
|
||||
static BuildTarget? SetFilterAndGetBuildTarget(string testPlatform, Filter filter)
|
||||
{
|
||||
BuildTarget? buildTarget = null;
|
||||
if (testPlatform.ToLower() == "editmode")
|
||||
{
|
||||
filter.testMode = TestMode.EditMode;
|
||||
}
|
||||
else if (testPlatform.ToLower() == "playmode")
|
||||
{
|
||||
filter.testMode = TestMode.PlayMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
buildTarget = (BuildTarget)Enum.Parse(typeof(BuildTarget), testPlatform, true);
|
||||
|
||||
filter.testMode = TestMode.PlayMode;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
throw new SetupException(SetupException.ExceptionType.PlatformNotFound, testPlatform);
|
||||
}
|
||||
}
|
||||
return buildTarget;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7468a027a77337478e133b40b42b4f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
internal class SetupException : Exception
|
||||
{
|
||||
public ExceptionType Type { get; }
|
||||
public object[] Details { get; }
|
||||
|
||||
public SetupException(ExceptionType type, params object[] details)
|
||||
{
|
||||
Type = type;
|
||||
Details = details;
|
||||
}
|
||||
|
||||
public enum ExceptionType
|
||||
{
|
||||
ScriptCompilationFailed,
|
||||
PlatformNotFound,
|
||||
TestSettingsFileNotFound,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 63572993f2104574099a48392460b211
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestRunner.CommandLineParser;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Compilation;
|
||||
using System.Linq;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.CommandLineTest
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
static class TestStarter
|
||||
{
|
||||
static TestStarter()
|
||||
{
|
||||
if (!ShouldRunTests())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (EditorApplication.isCompiling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (RunData.instance.isRunning)
|
||||
{
|
||||
executer.ExitOnCompileErrors();
|
||||
executer.SetUpCallbacks(RunData.instance.executionSettings);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.update += UpdateWatch;
|
||||
}
|
||||
|
||||
static void UpdateWatch()
|
||||
{
|
||||
EditorApplication.update -= UpdateWatch;
|
||||
|
||||
if (RunData.instance.isRunning)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RunData.instance.isRunning = true;
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
RunData.instance.executionSettings = executer.BuildExecutionSettings(commandLineArgs);
|
||||
executer.SetUpCallbacks(RunData.instance.executionSettings);
|
||||
executer.InitializeAndExecuteRun(commandLineArgs);
|
||||
}
|
||||
|
||||
static bool ShouldRunTests()
|
||||
{
|
||||
var shouldRunTests = false;
|
||||
var optionSet = new CommandLineOptionSet(
|
||||
new CommandLineOption("runTests", () => { shouldRunTests = true; }),
|
||||
new CommandLineOption("runEditorTests", () => { shouldRunTests = true; })
|
||||
);
|
||||
optionSet.Parse(Environment.GetCommandLineArgs());
|
||||
return shouldRunTests;
|
||||
}
|
||||
|
||||
static Executer s_Executer;
|
||||
|
||||
static Executer executer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_Executer == null)
|
||||
{
|
||||
Func<bool> compilationCheck = () => EditorUtility.scriptCompilationFailed;
|
||||
Action<string> actionLogger = (string msg) => { Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, msg); };
|
||||
var apiSettingsBuilder = new SettingsBuilder(new TestSettingsDeserializer(() => new TestSettings()), actionLogger, Debug.LogWarning, File.Exists, compilationCheck);
|
||||
s_Executer = new Executer(ScriptableObject.CreateInstance<TestRunnerApi>(), apiSettingsBuilder, Debug.LogErrorFormat, Debug.LogException, EditorApplication.Exit, compilationCheck);
|
||||
}
|
||||
|
||||
return s_Executer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4d616d1a494edd144b262cf6cd5e5fda
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7e609b27ad2caa14c83dd9951b6c13c6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class AssetsDatabaseHelper : IAssetsDatabaseHelper
|
||||
{
|
||||
public void OpenAssetInItsDefaultExternalEditor(string assetPath, int line)
|
||||
{
|
||||
var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||||
AssetDatabase.OpenAsset(asset, line);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 740b3785866edda4b8d1e1a05570a5f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,138 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using Unity.CodeEditor;
|
||||
using UnityEditor.Utils;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class GuiHelper : IGuiHelper
|
||||
{
|
||||
public GuiHelper(IMonoCecilHelper monoCecilHelper, IAssetsDatabaseHelper assetsDatabaseHelper)
|
||||
{
|
||||
MonoCecilHelper = monoCecilHelper;
|
||||
AssetsDatabaseHelper = assetsDatabaseHelper;
|
||||
Editor = new DefaultExternalCodeEditor();
|
||||
GetCSFiles = (dirPath, fileExtension) =>
|
||||
{
|
||||
return Directory.GetFiles(dirPath, $"*{fileExtension}", SearchOption.AllDirectories)
|
||||
.Select(Paths.UnifyDirectorySeparator);
|
||||
};
|
||||
}
|
||||
internal Func<string, string, IEnumerable<string>> GetCSFiles;
|
||||
protected IMonoCecilHelper MonoCecilHelper { get; private set; }
|
||||
public IAssetsDatabaseHelper AssetsDatabaseHelper { get; private set; }
|
||||
public IExternalCodeEditor Editor { get; internal set; }
|
||||
private const string FileExtension = ".cs";
|
||||
|
||||
public void OpenScriptInExternalEditor(Type type, MethodInfo method)
|
||||
{
|
||||
var fileOpenInfo = GetFileOpenInfo(type, method);
|
||||
|
||||
if (string.IsNullOrEmpty(fileOpenInfo.FilePath))
|
||||
{
|
||||
Debug.LogWarning("Failed to open test method source code in external editor. Inconsistent filename and yield return operator in target method.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileOpenInfo.LineNumber == 1)
|
||||
{
|
||||
Debug.LogWarning("Failed to get a line number for unity test method. So please find it in opened file in external editor.");
|
||||
}
|
||||
|
||||
if (!fileOpenInfo.FilePath.Contains("Assets"))
|
||||
{
|
||||
Editor.OpenProject(fileOpenInfo.FilePath, fileOpenInfo.LineNumber, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(fileOpenInfo.FilePath, fileOpenInfo.LineNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IFileOpenInfo GetFileOpenInfo(Type type, MethodInfo method)
|
||||
{
|
||||
var fileOpenInfo = MonoCecilHelper.TryGetCecilFileOpenInfo(type, method);
|
||||
if (string.IsNullOrEmpty(fileOpenInfo.FilePath))
|
||||
{
|
||||
var dirPath = Paths.UnifyDirectorySeparator(Application.dataPath);
|
||||
var allCsFiles = GetCSFiles(dirPath, FileExtension);
|
||||
|
||||
var fileName = allCsFiles.FirstOrDefault(x =>
|
||||
x.Split(Path.DirectorySeparatorChar).Last().Equals(string.Concat(GetTestFileName(type), FileExtension)));
|
||||
|
||||
fileOpenInfo.FilePath = fileName ?? string.Empty;
|
||||
}
|
||||
|
||||
if (!fileOpenInfo.FilePath.Contains("Assets"))
|
||||
{
|
||||
return fileOpenInfo;
|
||||
}
|
||||
fileOpenInfo.FilePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath);
|
||||
|
||||
return fileOpenInfo;
|
||||
}
|
||||
|
||||
internal static string GetTestFileName(Type type)
|
||||
{
|
||||
//This handles the case of a test in a nested class, getting the name of the base class
|
||||
if (type.FullName != null && type.Namespace!=null && type.FullName.Contains("+"))
|
||||
{
|
||||
var removedNamespace = type.FullName.Substring(type.Namespace.Length+1);
|
||||
return removedNamespace.Substring(0,removedNamespace.IndexOf("+", StringComparison.Ordinal));
|
||||
}
|
||||
return type.Name;
|
||||
}
|
||||
public string FilePathToAssetsRelativeAndUnified(string filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
return string.Empty;
|
||||
|
||||
filePath = Paths.UnifyDirectorySeparator(filePath);
|
||||
var length = Paths.UnifyDirectorySeparator(Application.dataPath).Length - "Assets".Length;
|
||||
|
||||
return filePath.Substring(length);
|
||||
}
|
||||
|
||||
public bool OpenScriptInExternalEditor(string stacktrace)
|
||||
{
|
||||
if (string.IsNullOrEmpty(stacktrace))
|
||||
return false;
|
||||
|
||||
var regex = new Regex("in (?<path>.*):{1}(?<line>[0-9]+)");
|
||||
|
||||
var matchingLines = stacktrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => regex.IsMatch(x)).ToList();
|
||||
if (!matchingLines.Any())
|
||||
return false;
|
||||
|
||||
var fileOpenInfos = matchingLines
|
||||
.Select(x => regex.Match(x))
|
||||
.Select(x =>
|
||||
new FileOpenInfo
|
||||
{
|
||||
FilePath = x.Groups["path"].Value,
|
||||
LineNumber = int.Parse(x.Groups["line"].Value)
|
||||
}).ToList();
|
||||
|
||||
var fileOpenInfo = fileOpenInfos
|
||||
.FirstOrDefault(openInfo => !string.IsNullOrEmpty(openInfo.FilePath) && File.Exists(openInfo.FilePath));
|
||||
|
||||
if (fileOpenInfo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath);
|
||||
AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(filePath, fileOpenInfo.LineNumber);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0138170d24533e47b8e6c250c6d7fbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal interface IAssetsDatabaseHelper
|
||||
{
|
||||
void OpenAssetInItsDefaultExternalEditor(string assetPath, int line);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 208e46d59ff6e304db0318377d20f5a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal interface IGuiHelper
|
||||
{
|
||||
bool OpenScriptInExternalEditor(string stacktrace);
|
||||
void OpenScriptInExternalEditor(Type type, MethodInfo method);
|
||||
IFileOpenInfo GetFileOpenInfo(Type type, MethodInfo method);
|
||||
string FilePathToAssetsRelativeAndUnified(string filePath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fd57cf917f61bbb42b8f030436426ddd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 07ea0326ed848fb4489187cb58f96113
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,12 @@
|
|||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class RenderingOptions
|
||||
{
|
||||
public string nameFilter;
|
||||
public bool showSucceeded;
|
||||
public bool showFailed;
|
||||
public bool showIgnored;
|
||||
public bool showNotRunned;
|
||||
public string[] categories;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87357ff0dec4ef348a295235835c6ee4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,174 @@
|
|||
// ****************************************************************
|
||||
// Based on nUnit 2.6.2 (http://www.nunit.org/)
|
||||
// ****************************************************************
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for ResultSummarizer.
|
||||
/// </summary>
|
||||
internal class ResultSummarizer
|
||||
{
|
||||
private int m_ErrorCount = -1;
|
||||
private int m_FailureCount;
|
||||
private int m_IgnoreCount = -1;
|
||||
private int m_InconclusiveCount = -1;
|
||||
private int m_NotRunnable = -1;
|
||||
private int m_ResultCount;
|
||||
private int m_SkipCount;
|
||||
private int m_SuccessCount;
|
||||
private int m_TestsRun;
|
||||
|
||||
private TimeSpan m_Duration = TimeSpan.FromSeconds(0);
|
||||
|
||||
public ResultSummarizer(IEnumerable<TestRunnerResult> results)
|
||||
{
|
||||
foreach (var result in results)
|
||||
Summarize(result);
|
||||
}
|
||||
|
||||
public bool success
|
||||
{
|
||||
get { return m_FailureCount == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases for which results
|
||||
/// have been summarized. Any tests excluded by use of
|
||||
/// Category or Explicit attributes are not counted.
|
||||
/// </summary>
|
||||
public int ResultCount
|
||||
{
|
||||
get { return m_ResultCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases actually run, which
|
||||
/// is the same as ResultCount, less any Skipped, Ignored
|
||||
/// or NonRunnable tests.
|
||||
/// </summary>
|
||||
public int TestsRun
|
||||
{
|
||||
get { return m_TestsRun; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of tests that passed
|
||||
/// </summary>
|
||||
public int Passed
|
||||
{
|
||||
get { return m_SuccessCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases that had an error.
|
||||
/// </summary>
|
||||
public int errors
|
||||
{
|
||||
get { return m_ErrorCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases that failed.
|
||||
/// </summary>
|
||||
public int failures
|
||||
{
|
||||
get { return m_FailureCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases that failed.
|
||||
/// </summary>
|
||||
public int inconclusive
|
||||
{
|
||||
get { return m_InconclusiveCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases that were not runnable
|
||||
/// due to errors in the signature of the class or method.
|
||||
/// Such tests are also counted as Errors.
|
||||
/// </summary>
|
||||
public int notRunnable
|
||||
{
|
||||
get { return m_NotRunnable; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of test cases that were skipped.
|
||||
/// </summary>
|
||||
public int Skipped
|
||||
{
|
||||
get { return m_SkipCount; }
|
||||
}
|
||||
|
||||
public int ignored
|
||||
{
|
||||
get { return m_IgnoreCount; }
|
||||
}
|
||||
|
||||
public double duration
|
||||
{
|
||||
get { return m_Duration.TotalSeconds; }
|
||||
}
|
||||
|
||||
public int testsNotRun
|
||||
{
|
||||
get { return m_SkipCount + m_IgnoreCount + m_NotRunnable; }
|
||||
}
|
||||
|
||||
public void Summarize(TestRunnerResult result)
|
||||
{
|
||||
m_Duration += TimeSpan.FromSeconds(result.duration);
|
||||
m_ResultCount++;
|
||||
|
||||
if (result.resultStatus != TestRunnerResult.ResultStatus.NotRun)
|
||||
{
|
||||
//TODO implement missing features
|
||||
// if(result.IsIgnored)
|
||||
// {
|
||||
// m_IgnoreCount++;
|
||||
// return;
|
||||
// }
|
||||
|
||||
m_SkipCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (result.resultStatus)
|
||||
{
|
||||
case TestRunnerResult.ResultStatus.Passed:
|
||||
m_SuccessCount++;
|
||||
m_TestsRun++;
|
||||
break;
|
||||
case TestRunnerResult.ResultStatus.Failed:
|
||||
m_FailureCount++;
|
||||
m_TestsRun++;
|
||||
break;
|
||||
//TODO implement missing features
|
||||
// case TestResultState.Error:
|
||||
// case TestResultState.Cancelled:
|
||||
// m_ErrorCount++;
|
||||
// m_TestsRun++;
|
||||
// break;
|
||||
// case TestResultState.Inconclusive:
|
||||
// m_InconclusiveCount++;
|
||||
// m_TestsRun++;
|
||||
// break;
|
||||
// case TestResultState.NotRunnable:
|
||||
// m_NotRunnable++;
|
||||
// // errorCount++;
|
||||
// break;
|
||||
// case TestResultState.Ignored:
|
||||
// m_IgnoreCount++;
|
||||
// break;
|
||||
default:
|
||||
m_SkipCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 95a2914724952ef40bb590d0607fc878
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,104 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class TestFilterSettings
|
||||
{
|
||||
public bool showSucceeded;
|
||||
public bool showFailed;
|
||||
public bool showIgnored;
|
||||
public bool showNotRun;
|
||||
|
||||
public string filterByName;
|
||||
public int filterByCategory;
|
||||
|
||||
private GUIContent m_SucceededBtn;
|
||||
private GUIContent m_FailedBtn;
|
||||
private GUIContent m_IgnoredBtn;
|
||||
private GUIContent m_NotRunBtn;
|
||||
|
||||
public string[] availableCategories;
|
||||
|
||||
private readonly string m_PrefsKey;
|
||||
|
||||
public TestFilterSettings(string prefsKey)
|
||||
{
|
||||
availableCategories = null;
|
||||
m_PrefsKey = prefsKey;
|
||||
Load();
|
||||
UpdateCounters(Enumerable.Empty<TestRunnerResult>());
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
showSucceeded = EditorPrefs.GetBool(m_PrefsKey + ".ShowSucceeded", true);
|
||||
showFailed = EditorPrefs.GetBool(m_PrefsKey + ".ShowFailed", true);
|
||||
showIgnored = EditorPrefs.GetBool(m_PrefsKey + ".ShowIgnored", true);
|
||||
showNotRun = EditorPrefs.GetBool(m_PrefsKey + ".ShowNotRun", true);
|
||||
filterByName = EditorPrefs.GetString(m_PrefsKey + ".FilterByName", string.Empty);
|
||||
filterByCategory = EditorPrefs.GetInt(m_PrefsKey + ".FilterByCategory", 0);
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
EditorPrefs.SetBool(m_PrefsKey + ".ShowSucceeded", showSucceeded);
|
||||
EditorPrefs.SetBool(m_PrefsKey + ".ShowFailed", showFailed);
|
||||
EditorPrefs.SetBool(m_PrefsKey + ".ShowIgnored", showIgnored);
|
||||
EditorPrefs.SetBool(m_PrefsKey + ".ShowNotRun", showNotRun);
|
||||
EditorPrefs.SetString(m_PrefsKey + ".FilterByName", filterByName);
|
||||
EditorPrefs.SetInt(m_PrefsKey + ".FilterByCategory", filterByCategory);
|
||||
}
|
||||
|
||||
public void UpdateCounters(IEnumerable<TestRunnerResult> results)
|
||||
{
|
||||
var summary = new ResultSummarizer(results);
|
||||
|
||||
m_SucceededBtn = new GUIContent(summary.Passed.ToString(), Icons.s_SuccessImg, "Show tests that succeeded");
|
||||
m_FailedBtn = new GUIContent((summary.errors + summary.failures + summary.inconclusive).ToString(), Icons.s_FailImg, "Show tests that failed");
|
||||
m_IgnoredBtn = new GUIContent((summary.ignored + summary.notRunnable).ToString(), Icons.s_IgnoreImg, "Show tests that are ignored");
|
||||
m_NotRunBtn = new GUIContent((summary.testsNotRun - summary.ignored - summary.notRunnable).ToString(), Icons.s_UnknownImg, "Show tests that didn't run");
|
||||
}
|
||||
|
||||
public string[] GetSelectedCategories()
|
||||
{
|
||||
if (availableCategories == null)
|
||||
return new string[0];
|
||||
|
||||
return availableCategories.Where((c, i) => (filterByCategory & (1 << i)) != 0).ToArray();
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
|
||||
filterByName = GUILayout.TextField(filterByName, "ToolbarSeachTextField", GUILayout.MinWidth(100), GUILayout.MaxWidth(250), GUILayout.ExpandWidth(true));
|
||||
if (GUILayout.Button(GUIContent.none, string.IsNullOrEmpty(filterByName) ? "ToolbarSeachCancelButtonEmpty" : "ToolbarSeachCancelButton"))
|
||||
filterByName = string.Empty;
|
||||
|
||||
if (availableCategories != null && availableCategories.Length > 0)
|
||||
filterByCategory = EditorGUILayout.MaskField(filterByCategory, availableCategories, EditorStyles.toolbarDropDown, GUILayout.MaxWidth(90));
|
||||
|
||||
showSucceeded = GUILayout.Toggle(showSucceeded, m_SucceededBtn, EditorStyles.toolbarButton);
|
||||
showFailed = GUILayout.Toggle(showFailed, m_FailedBtn, EditorStyles.toolbarButton);
|
||||
showIgnored = GUILayout.Toggle(showIgnored, m_IgnoredBtn, EditorStyles.toolbarButton);
|
||||
showNotRun = GUILayout.Toggle(showNotRun, m_NotRunBtn, EditorStyles.toolbarButton);
|
||||
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
Save();
|
||||
}
|
||||
|
||||
public RenderingOptions BuildRenderingOptions()
|
||||
{
|
||||
var options = new RenderingOptions();
|
||||
options.showSucceeded = showSucceeded;
|
||||
options.showFailed = showFailed;
|
||||
options.showIgnored = showIgnored;
|
||||
options.showNotRunned = showNotRun;
|
||||
options.nameFilter = filterByName;
|
||||
options.categories = GetSelectedCategories();
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 046c3854296c5ec48bac50da6ca248ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,110 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine.TestRunner.NUnitExtensions;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Filters;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class TestTreeViewBuilder
|
||||
{
|
||||
public List<TestRunnerResult> results = new List<TestRunnerResult>();
|
||||
private readonly Dictionary<string, TestRunnerResult> m_OldTestResults;
|
||||
private readonly TestRunnerUIFilter m_UIFilter;
|
||||
private readonly ITestAdaptor m_TestListRoot;
|
||||
|
||||
private readonly List<string> m_AvailableCategories = new List<string>();
|
||||
|
||||
public string[] AvailableCategories
|
||||
{
|
||||
get { return m_AvailableCategories.Distinct().OrderBy(a => a).ToArray(); }
|
||||
}
|
||||
|
||||
public TestTreeViewBuilder(ITestAdaptor tests, Dictionary<string, TestRunnerResult> oldTestResultResults, TestRunnerUIFilter uiFilter)
|
||||
{
|
||||
m_AvailableCategories.Add(CategoryFilterExtended.k_DefaultCategory);
|
||||
m_OldTestResults = oldTestResultResults;
|
||||
m_TestListRoot = tests;
|
||||
m_UIFilter = uiFilter;
|
||||
}
|
||||
|
||||
public TreeViewItem BuildTreeView(TestFilterSettings settings, bool sceneBased, string sceneName)
|
||||
{
|
||||
var rootItem = new TreeViewItem(int.MaxValue, 0, null, "Invisible Root Item");
|
||||
ParseTestTree(0, rootItem, m_TestListRoot);
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
private bool IsFilteredOutByUIFilter(ITestAdaptor test, TestRunnerResult result)
|
||||
{
|
||||
if (m_UIFilter.PassedHidden && result.resultStatus == TestRunnerResult.ResultStatus.Passed)
|
||||
return true;
|
||||
if (m_UIFilter.FailedHidden && (result.resultStatus == TestRunnerResult.ResultStatus.Failed || result.resultStatus == TestRunnerResult.ResultStatus.Inconclusive))
|
||||
return true;
|
||||
if (m_UIFilter.NotRunHidden && (result.resultStatus == TestRunnerResult.ResultStatus.NotRun || result.resultStatus == TestRunnerResult.ResultStatus.Skipped))
|
||||
return true;
|
||||
if (m_UIFilter.CategoryFilter.Length > 0)
|
||||
return !test.Categories.Any(category => m_UIFilter.CategoryFilter.Contains(category));
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ParseTestTree(int depth, TreeViewItem rootItem, ITestAdaptor testElement)
|
||||
{
|
||||
m_AvailableCategories.AddRange(testElement.Categories);
|
||||
|
||||
var testElementId = testElement.UniqueName;
|
||||
if (!testElement.HasChildren)
|
||||
{
|
||||
m_OldTestResults.TryGetValue(testElementId, out var result);
|
||||
|
||||
if (result != null &&
|
||||
(result.ignoredOrSkipped
|
||||
|| result.notRunnable
|
||||
|| testElement.RunState == RunState.NotRunnable
|
||||
|| testElement.RunState == RunState.Ignored
|
||||
|| testElement.RunState == RunState.Skipped
|
||||
)
|
||||
)
|
||||
{
|
||||
//if the test was or becomes ignored or not runnable, we recreate the result in case it has changed
|
||||
result = null;
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new TestRunnerResult(testElement);
|
||||
}
|
||||
results.Add(result);
|
||||
|
||||
var test = new TestTreeViewItem(testElement, depth, rootItem);
|
||||
if (!IsFilteredOutByUIFilter(testElement, result))
|
||||
rootItem.AddChild(test);
|
||||
test.SetResult(result);
|
||||
return;
|
||||
}
|
||||
|
||||
m_OldTestResults.TryGetValue(testElementId, out var groupResult);
|
||||
if (groupResult == null)
|
||||
{
|
||||
groupResult = new TestRunnerResult(testElement);
|
||||
}
|
||||
|
||||
results.Add(groupResult);
|
||||
var group = new TestTreeViewItem(testElement, depth, rootItem);
|
||||
group.SetResult(groupResult);
|
||||
|
||||
depth++;
|
||||
foreach (var child in testElement.Children)
|
||||
{
|
||||
ParseTestTree(depth, group, child);
|
||||
}
|
||||
|
||||
|
||||
if (testElement.IsTestAssembly && !testElement.HasChildren)
|
||||
return;
|
||||
|
||||
if (group.hasChildren)
|
||||
rootItem.AddChild(group);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue