test
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class EditModeLauncherContextSettings : IDisposable
|
||||
{
|
||||
private bool m_RunInBackground;
|
||||
|
||||
public EditModeLauncherContextSettings()
|
||||
{
|
||||
SetupProjectParameters();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
CleanupProjectParameters();
|
||||
}
|
||||
|
||||
private void SetupProjectParameters()
|
||||
{
|
||||
m_RunInBackground = Application.runInBackground;
|
||||
Application.runInBackground = true;
|
||||
}
|
||||
|
||||
private void CleanupProjectParameters()
|
||||
{
|
||||
Application.runInBackground = m_RunInBackground;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a582090813554df479fb9ca03e9857d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3cb89150b8a4060b89a32725a8e267e
|
||||
timeCreated: 1685434437
|
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal abstract class AttributeFinderBase : IAttributeFinder
|
||||
{
|
||||
public abstract IEnumerable<Type> Search(ITest tests, ITestFilter filter, RuntimePlatform testTargetPlatform);
|
||||
}
|
||||
|
||||
internal interface IAttributeFinder
|
||||
{
|
||||
IEnumerable<Type> Search(ITest tests, ITestFilter filter, RuntimePlatform testTargetPlatform);
|
||||
}
|
||||
|
||||
internal abstract class AttributeFinderBase<T1, T2> : AttributeFinderBase where T2 : Attribute
|
||||
{
|
||||
private readonly Func<T2, Type> m_TypeSelector;
|
||||
protected AttributeFinderBase(Func<T2, Type> typeSelector)
|
||||
{
|
||||
m_TypeSelector = typeSelector;
|
||||
}
|
||||
|
||||
public override IEnumerable<Type> Search(ITest tests, ITestFilter filter, RuntimePlatform testTargetPlatform)
|
||||
{
|
||||
var selectedTests = new List<ITest>();
|
||||
GetMatchingTests(tests, filter, ref selectedTests, testTargetPlatform);
|
||||
|
||||
var result = new List<Type>();
|
||||
result.AddRange(GetTypesFromPrebuildAttributes(selectedTests));
|
||||
result.AddRange(GetTypesFromInterface(selectedTests, testTargetPlatform));
|
||||
|
||||
return result.Distinct();
|
||||
}
|
||||
|
||||
private static void GetMatchingTests(ITest tests, ITestFilter filter, ref List<ITest> resultList, RuntimePlatform testTargetPlatform)
|
||||
{
|
||||
foreach (var test in tests.Tests)
|
||||
{
|
||||
if (IsTestEnabledOnPlatform(test, testTargetPlatform))
|
||||
{
|
||||
if (test.IsSuite)
|
||||
{
|
||||
GetMatchingTests(test, filter, ref resultList, testTargetPlatform);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filter.Pass(test))
|
||||
resultList.Add(test);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTestEnabledOnPlatform(ITest test, RuntimePlatform testTargetPlatform)
|
||||
{
|
||||
if (test.Method == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var attributesFromMethods = test.Method.GetCustomAttributes<UnityPlatformAttribute>(true);
|
||||
var attributesFromTypes = test.Method.TypeInfo.GetCustomAttributes<UnityPlatformAttribute>(true);
|
||||
|
||||
if (attributesFromMethods.Length == 0 && attributesFromTypes.Length == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!attributesFromMethods.All(a => a.IsPlatformSupported(testTargetPlatform)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!attributesFromTypes.All(a => a.IsPlatformSupported(testTargetPlatform)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerable<Type> GetTypesFromPrebuildAttributes(IEnumerable<ITest> tests)
|
||||
{
|
||||
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
allAssemblies = allAssemblies.Where(x => x.GetReferencedAssemblies().Any(z => z.Name == "UnityEditor.TestRunner")).ToArray();
|
||||
var attributesFromAssemblies = allAssemblies.SelectMany(assembly => assembly.GetCustomAttributes(typeof(T2), true).OfType<T2>());
|
||||
var attributesFromMethods = tests.SelectMany(t => t.Method.GetCustomAttributes<T2>(true).Select(attribute => attribute));
|
||||
var attributesFromTypes = tests.SelectMany(t => t.Method.TypeInfo.GetCustomAttributes<T2>(true).Select(attribute => attribute));
|
||||
|
||||
var result = new List<T2>();
|
||||
result.AddRange(attributesFromAssemblies);
|
||||
result.AddRange(attributesFromMethods);
|
||||
result.AddRange(attributesFromTypes);
|
||||
|
||||
return result.Select(m_TypeSelector).Where(type => type != null);
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetTypesFromInterface(IEnumerable<ITest> selectedTests, RuntimePlatform testTargetPlatform)
|
||||
{
|
||||
var typesWithInterfaces = selectedTests.Where(t => typeof(T1).IsAssignableFrom(t.Method.TypeInfo.Type) && IsTestEnabledOnPlatform(t, testTargetPlatform));
|
||||
return typesWithInterfaces.Select(t => t.Method.TypeInfo.Type);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d4de3d4682a8d641907cc75e4fb950e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class DelayedCallback
|
||||
{
|
||||
private Action m_Callback;
|
||||
private double m_CallbackTime;
|
||||
private double m_Delay;
|
||||
|
||||
public DelayedCallback(Action function, double timeFromNow)
|
||||
{
|
||||
m_Callback = function;
|
||||
m_CallbackTime = EditorApplication.timeSinceStartup + timeFromNow;
|
||||
m_Delay = timeFromNow;
|
||||
EditorApplication.update += Update;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
EditorApplication.update -= Update;
|
||||
m_CallbackTime = 0.0;
|
||||
m_Callback = null;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (EditorApplication.timeSinceStartup > m_CallbackTime)
|
||||
{
|
||||
// Clear state before firing callback to ensure reset (callback could call ExitGUI)
|
||||
var callback = m_Callback;
|
||||
Clear();
|
||||
|
||||
callback?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (m_Callback != null)
|
||||
{
|
||||
m_CallbackTime = EditorApplication.timeSinceStartup + m_Delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9d121df8c444236a5b38ccfadfdd1a7
|
||||
timeCreated: 1583140472
|
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.TestTools.TestRunner.GUI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestRunner.TestLaunchers
|
||||
{
|
||||
internal static class FilePathMetaInfo
|
||||
{
|
||||
[Serializable]
|
||||
private struct FileReference
|
||||
{
|
||||
public string FilePath;
|
||||
public int LineNumber;
|
||||
}
|
||||
|
||||
private enum PathType
|
||||
{
|
||||
ProjectRepositoryPath,
|
||||
ProjectPath,
|
||||
}
|
||||
|
||||
public static void TryCreateFile(ITest runnerLoadedTest, BuildPlayerOptions playerBuildOptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
var metaFileDestinationPath = GetMetaDestinationPath(playerBuildOptions);
|
||||
var repositoryPath = GetPathFromArgs(PathType.ProjectRepositoryPath);
|
||||
// if no path is given, early out so we do not pollute the build player folder with the file path file.
|
||||
if (string.IsNullOrEmpty(repositoryPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a dictionary for the test names and their file paths
|
||||
var testFilePaths = new Dictionary<string, FileReference>();
|
||||
RecursivelyPopulateFileReferences(runnerLoadedTest, testFilePaths, repositoryPath, new GuiHelper(new MonoCecilHelper(), new AssetsDatabaseHelper()));
|
||||
SaveToJsonFile(testFilePaths, metaFileDestinationPath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogWarning("Saving test file path meta info failed: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
// This function serializes dictionary to json file, all the logic would not be necessary if Unity was able to serialize Dictionaries, or if we could use Newtonsoft.Json.
|
||||
// This function could be changed later on, or we can use different data structure than Dictionary.
|
||||
private static void SaveToJsonFile(Dictionary<string, FileReference> testFilePaths, string metaFileDestinationPath)
|
||||
{
|
||||
using (var fileStream = File.CreateText(Path.Combine(metaFileDestinationPath, "TestFileReferences.json")))
|
||||
{
|
||||
fileStream.WriteLine("{");
|
||||
|
||||
foreach (var testFilePath in testFilePaths)
|
||||
{
|
||||
fileStream.WriteLine($" \"{JavaScriptStringEncode(testFilePath.Key)}\": {{");
|
||||
fileStream.WriteLine($" \"filePath\": \"{JavaScriptStringEncode(testFilePath.Value.FilePath)}\",");
|
||||
fileStream.WriteLine($" \"lineNumber\": {testFilePath.Value.LineNumber}");
|
||||
// check if it is the last element in the dictionary
|
||||
if (testFilePath.Key != testFilePaths.Keys.Last())
|
||||
{
|
||||
fileStream.WriteLine(" },");
|
||||
}
|
||||
else
|
||||
{
|
||||
fileStream.WriteLine(" }");
|
||||
}
|
||||
}
|
||||
|
||||
fileStream.WriteLine("}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetMetaDestinationPath(BuildPlayerOptions playerBuildOptions)
|
||||
{
|
||||
// If we are Auto-Running the player, use project path instead of player build path because it will be wiped out after successful run.
|
||||
if ((playerBuildOptions.options & BuildOptions.AutoRunPlayer) != 0)
|
||||
{
|
||||
return Path.Combine(GetPathFromArgs(PathType.ProjectPath));
|
||||
}
|
||||
|
||||
// if the buildOutputPath is for a file, then get the directory of it
|
||||
return File.Exists(playerBuildOptions.locationPathName) ? Path.GetDirectoryName(playerBuildOptions.locationPathName) : playerBuildOptions.locationPathName;
|
||||
}
|
||||
|
||||
private static void RecursivelyPopulateFileReferences(ITest test, Dictionary<string, FileReference> testFilePaths, string repositoryPath, IGuiHelper guiHelper)
|
||||
{
|
||||
if (test.HasChildren)
|
||||
{
|
||||
foreach (var child in test.Tests)
|
||||
{
|
||||
RecursivelyPopulateFileReferences(child, testFilePaths, repositoryPath, guiHelper);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var testMethod = test.Method;
|
||||
if (testMethod == null)
|
||||
{
|
||||
testMethod = test.Parent.Method;
|
||||
if (testMethod == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var methodInfo = test.Method.MethodInfo;
|
||||
var type = test.TypeInfo.Type;
|
||||
var fileOpenInfo = guiHelper.GetFileOpenInfo(type, methodInfo);
|
||||
var filePathString = Path.Combine(repositoryPath, fileOpenInfo.FilePath);
|
||||
var lineNumber = fileOpenInfo.LineNumber;
|
||||
var fileReference = new FileReference
|
||||
{
|
||||
FilePath = filePathString,
|
||||
LineNumber = lineNumber
|
||||
};
|
||||
// Cannot be simplified with .TryAdd because Unity 2020.3 and below does not have it.
|
||||
if (!testFilePaths.ContainsKey(test.FullName))
|
||||
{
|
||||
testFilePaths.Add(test.FullName, fileReference);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPathFromArgs(PathType type)
|
||||
{
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
|
||||
string lookFor;
|
||||
switch (type)
|
||||
{
|
||||
case PathType.ProjectRepositoryPath:
|
||||
lookFor = "-projectRepositoryPath";
|
||||
break;
|
||||
case PathType.ProjectPath:
|
||||
lookFor = "-projectPath";
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Invalid PathType");
|
||||
}
|
||||
|
||||
for (var i = 0; i < commandLineArgs.Length; i++)
|
||||
{
|
||||
if (commandLineArgs[i].Equals(lookFor))
|
||||
{
|
||||
return commandLineArgs[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Below implementation is copy-paste from HttpUtility.JavaScriptStringEncode
|
||||
private static string JavaScriptStringEncode(string value) {
|
||||
if (String.IsNullOrEmpty(value)) {
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
StringBuilder b = null;
|
||||
int startIndex = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < value.Length; i++) {
|
||||
char c = value[i];
|
||||
|
||||
// Append the unhandled characters (that do not require special treament)
|
||||
// to the string builder when special characters are detected.
|
||||
if (CharRequiresJavaScriptEncoding(c)) {
|
||||
if (b == null) {
|
||||
b = new StringBuilder(value.Length + 5);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
b.Append(value, startIndex, count);
|
||||
}
|
||||
|
||||
startIndex = i + 1;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '\r':
|
||||
b.Append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
b.Append("\\t");
|
||||
break;
|
||||
case '\"':
|
||||
b.Append("\\\"");
|
||||
break;
|
||||
case '\\':
|
||||
b.Append("\\\\");
|
||||
break;
|
||||
case '\n':
|
||||
b.Append("\\n");
|
||||
break;
|
||||
case '\b':
|
||||
b.Append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
b.Append("\\f");
|
||||
break;
|
||||
default:
|
||||
if (CharRequiresJavaScriptEncoding(c)) {
|
||||
AppendCharAsUnicodeJavaScript(b, c);
|
||||
}
|
||||
else {
|
||||
count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (b == null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
b.Append(value, startIndex, count);
|
||||
}
|
||||
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
private static bool CharRequiresJavaScriptEncoding(char c) {
|
||||
return c < 0x20 // control chars always have to be encoded
|
||||
|| c == '\"' // chars which must be encoded per JSON spec
|
||||
|| c == '\\'
|
||||
|| c == '\'' // HTML-sensitive chars encoded for safety
|
||||
|| c == '<'
|
||||
|| c == '>'
|
||||
|| c == '&'
|
||||
|| c == '\u0085' // newline chars (see Unicode 6.2, Table 5-1 [http://www.unicode.org/versions/Unicode6.2.0/ch05.pdf]) have to be encoded (DevDiv #663531)
|
||||
|| c == '\u2028'
|
||||
|| c == '\u2029';
|
||||
}
|
||||
|
||||
private static void AppendCharAsUnicodeJavaScript(StringBuilder builder, char c) {
|
||||
builder.Append("\\u");
|
||||
builder.Append(((int)c).ToString("x4", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 759855a435cd4b22ade51836cc8b00d6
|
||||
timeCreated: 1685434461
|
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PlayerLauncherBuildOptions
|
||||
{
|
||||
public BuildPlayerOptions BuildPlayerOptions;
|
||||
public string PlayerDirectory;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
str.AppendLine("locationPathName = " + BuildPlayerOptions.locationPathName);
|
||||
str.AppendLine("target = " + BuildPlayerOptions.target);
|
||||
str.AppendLine("scenes = " + string.Join(", ", BuildPlayerOptions.scenes));
|
||||
str.AppendLine("assetBundleManifestPath = " + BuildPlayerOptions.assetBundleManifestPath);
|
||||
str.AppendLine("options.Development = " + ((BuildPlayerOptions.options & BuildOptions.Development) != 0));
|
||||
str.AppendLine("options.AutoRunPlayer = " + ((BuildPlayerOptions.options & BuildOptions.AutoRunPlayer) != 0));
|
||||
str.AppendLine("options.ForceEnableAssertions = " + ((BuildPlayerOptions.options & BuildOptions.ForceEnableAssertions) != 0));
|
||||
return str.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a0bd678385f98e4d8eabdfc07d62b4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PostbuildCleanupAttributeFinder : AttributeFinderBase<IPostBuildCleanup, PostBuildCleanupAttribute>
|
||||
{
|
||||
public PostbuildCleanupAttributeFinder() : base(attribute => attribute.TargetClass) {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c2dfcbbb77359547bcaa7cdabd47ebb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PrebuildSetupAttributeFinder : AttributeFinderBase<IPrebuildSetup, PrebuildSetupAttribute>
|
||||
{
|
||||
public PrebuildSetupAttributeFinder() : base(attribute => attribute.TargetClass) {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c4ccfb0896bcf44da13e152b267aa49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebc4d20cc106cea49b1df1153f0b3b5e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class AndroidPlatformSetup : IPlatformSetup
|
||||
{
|
||||
private string m_oldApplicationIdentifier;
|
||||
private string m_oldDeviceSocketAddress;
|
||||
[SerializeField]
|
||||
private bool m_Stripping;
|
||||
|
||||
private bool RequiresLegacyConnectionMechanism =>
|
||||
#if !UNITY_2021_2_OR_NEWER
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
private void PerformLegacySetup()
|
||||
{
|
||||
m_oldDeviceSocketAddress = EditorUserBuildSettings.androidDeviceSocketAddress;
|
||||
|
||||
var androidDeviceConnection = Environment.GetEnvironmentVariable("ANDROID_DEVICE_CONNECTION");
|
||||
if (androidDeviceConnection != null)
|
||||
{
|
||||
EditorUserBuildSettings.androidDeviceSocketAddress = androidDeviceConnection;
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformLegacyCleanup()
|
||||
{
|
||||
EditorUserBuildSettings.androidDeviceSocketAddress = m_oldDeviceSocketAddress;
|
||||
}
|
||||
|
||||
private void PerformLegacyPostSuccessfulLaunchAction()
|
||||
{
|
||||
var connectionResult = -1;
|
||||
var maxTryCount = 30;
|
||||
var tryCount = maxTryCount;
|
||||
while (tryCount-- > 0 && connectionResult == -1)
|
||||
{
|
||||
connectionResult = EditorConnectionInternal.ConnectPlayerProxy(IPAddress.Loopback.ToString(), 34999);
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Editor Connection", "Connecting to the player",
|
||||
1 - ((float)tryCount / maxTryCount)))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
throw new TestLaunchFailedException();
|
||||
}
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (connectionResult == -1)
|
||||
throw new TestLaunchFailedException(
|
||||
"Timed out trying to connect to the player. Player failed to launch or crashed soon after launching");
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
m_oldApplicationIdentifier = PlayerSettings.GetApplicationIdentifier(NamedBuildTarget.Android);
|
||||
PlayerSettings.SetApplicationIdentifier(NamedBuildTarget.Android, "com.UnityTestRunner.UnityTestRunner");
|
||||
#else
|
||||
m_oldApplicationIdentifier = PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.Android);
|
||||
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, "com.UnityTestRunner.UnityTestRunner");
|
||||
#endif
|
||||
|
||||
|
||||
if (RequiresLegacyConnectionMechanism)
|
||||
PerformLegacySetup();
|
||||
|
||||
EditorUserBuildSettings.waitForPlayerConnection = true;
|
||||
m_Stripping = PlayerSettings.stripEngineCode;
|
||||
PlayerSettings.stripEngineCode = false;
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
PlayerSettings.stripEngineCode = m_Stripping;
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
if (RequiresLegacyConnectionMechanism)
|
||||
PerformLegacyPostSuccessfulLaunchAction();
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
if (RequiresLegacyConnectionMechanism)
|
||||
PerformLegacyCleanup();
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
PlayerSettings.SetApplicationIdentifier(NamedBuildTarget.Android, m_oldApplicationIdentifier);
|
||||
#else
|
||||
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, m_oldApplicationIdentifier);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 961642509dec50b44a293d26240140ec
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
[Serializable]
|
||||
internal class ApplePlatformSetup : IPlatformSetup
|
||||
{
|
||||
[SerializeField]
|
||||
private bool m_Stripping;
|
||||
|
||||
private bool m_RunOnSimulator;
|
||||
private List<int> m_XcodesOpenBeforeTests;
|
||||
private List<int> m_XcodesToCloseAfterTests;
|
||||
private List<int> m_SimulatorsOpenBeforeTests;
|
||||
|
||||
public ApplePlatformSetup(BuildTarget buildTarget)
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
// Camera and fonts are stripped out and app crashes on iOS when test runner is trying to add a scene with... camera and text
|
||||
m_Stripping = PlayerSettings.stripEngineCode;
|
||||
PlayerSettings.stripEngineCode = false;
|
||||
|
||||
m_RunOnSimulator = EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS
|
||||
? PlayerSettings.iOS.sdkVersion == iOSSdkVersion.SimulatorSDK
|
||||
: PlayerSettings.tvOS.sdkVersion == tvOSSdkVersion.Simulator;
|
||||
|
||||
// Gather IDs of Xcodes that were already open before building and running the tests
|
||||
if (Application.isEditor && Application.isBatchMode)
|
||||
{
|
||||
m_XcodesOpenBeforeTests = new List<int>();
|
||||
foreach (var xcode in Process.GetProcessesByName("Xcode"))
|
||||
{
|
||||
m_XcodesOpenBeforeTests.Add(xcode.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
// Restoring player setting as early as possible
|
||||
PlayerSettings.stripEngineCode = m_Stripping;
|
||||
|
||||
// Gather IDs of Xcodes that were opened while building the tests
|
||||
if (Application.isEditor && Application.isBatchMode)
|
||||
{
|
||||
m_XcodesToCloseAfterTests = new List<int>();
|
||||
foreach (var xcode in Process.GetProcessesByName("Xcode"))
|
||||
{
|
||||
if (!m_XcodesOpenBeforeTests?.Contains(xcode.Id) ?? false)
|
||||
m_XcodesToCloseAfterTests.Add(xcode.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
// Gather IDs of Simulator processes that were already open before successfully building the tests
|
||||
if (Application.isEditor && Application.isBatchMode && m_RunOnSimulator)
|
||||
{
|
||||
m_SimulatorsOpenBeforeTests = new List<int>();
|
||||
foreach (var simulator in Process.GetProcessesByName("Simulator"))
|
||||
{
|
||||
m_SimulatorsOpenBeforeTests.Add(simulator.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
// Close Xcode that was opened while building the tests (batchmode only)
|
||||
// should be only one such Xcode, otherwise skip to avoid closing unrelated Xcodes
|
||||
if (Application.isEditor && Application.isBatchMode &&
|
||||
m_XcodesToCloseAfterTests != null && m_XcodesToCloseAfterTests.Count == 1)
|
||||
{
|
||||
var xcodeToClose = Process.GetProcessById(m_XcodesToCloseAfterTests[0]);
|
||||
|
||||
if (xcodeToClose.ProcessName == "Xcode")
|
||||
{
|
||||
xcodeToClose.CloseMainWindow();
|
||||
xcodeToClose.Close();
|
||||
}
|
||||
}
|
||||
|
||||
// Close all Simulator processes that were opened after successfully building the tests (batchmode only)
|
||||
if (Application.isEditor && Application.isBatchMode && m_RunOnSimulator)
|
||||
{
|
||||
foreach (var simulator in Process.GetProcessesByName("Simulator"))
|
||||
{
|
||||
if (!m_SimulatorsOpenBeforeTests?.Contains(simulator.Id) ?? false)
|
||||
{
|
||||
simulator.CloseMainWindow();
|
||||
simulator.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6c189a159d3bde4c964cee562e508ea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal interface IPlatformSetup
|
||||
{
|
||||
void Setup();
|
||||
void PostBuildAction();
|
||||
void PostSuccessfulBuildAction();
|
||||
void PostSuccessfulLaunchAction();
|
||||
void CleanUp();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d614808f9add8a4f8e4860db2c7af0d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class LuminPlatformSetup : IPlatformSetup
|
||||
{
|
||||
private const string kDeviceAddress = "127.0.0.1";
|
||||
private const int kDevicePort = 55000;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
var connectionResult = -1;
|
||||
var maxTryCount = 100;
|
||||
var tryCount = maxTryCount;
|
||||
while (tryCount-- > 0 && connectionResult == -1)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
connectionResult = EditorConnectionInternal.ConnectPlayerProxy(kDeviceAddress, kDevicePort);
|
||||
if (EditorUtility.DisplayCancelableProgressBar("Editor Connection", "Connecting to the player",
|
||||
1 - ((float)tryCount / maxTryCount)))
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
throw new TestLaunchFailedException();
|
||||
}
|
||||
}
|
||||
EditorUtility.ClearProgressBar();
|
||||
if (connectionResult == -1)
|
||||
throw new TestLaunchFailedException(
|
||||
"Timed out trying to connect to the player. Player failed to launch or crashed soon after launching");
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c38ae0585d6a55042a2d678330689685
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
[Serializable]
|
||||
internal class PlatformSpecificSetup
|
||||
{
|
||||
[SerializeField]
|
||||
private ApplePlatformSetup m_AppleiOSPlatformSetup = new ApplePlatformSetup(BuildTarget.iOS);
|
||||
[SerializeField]
|
||||
private ApplePlatformSetup m_AppleTvOSPlatformSetup = new ApplePlatformSetup(BuildTarget.tvOS);
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
[SerializeField]
|
||||
private XboxOnePlatformSetup m_XboxOnePlatformSetup = new XboxOnePlatformSetup();
|
||||
#endif
|
||||
[SerializeField]
|
||||
private AndroidPlatformSetup m_AndroidPlatformSetup = new AndroidPlatformSetup();
|
||||
[SerializeField]
|
||||
private SwitchPlatformSetup m_SwitchPlatformSetup = new SwitchPlatformSetup();
|
||||
#if UNITY_2019_3_OR_NEWER
|
||||
[SerializeField]
|
||||
private StadiaPlatformSetup m_StadiaPlatformSetup = new StadiaPlatformSetup();
|
||||
#endif
|
||||
[SerializeField]
|
||||
private UwpPlatformSetup m_UwpPlatformSetup = new UwpPlatformSetup();
|
||||
|
||||
[SerializeField]
|
||||
private LuminPlatformSetup m_LuminPlatformSetup = new LuminPlatformSetup();
|
||||
|
||||
|
||||
private IDictionary<BuildTarget, IPlatformSetup> m_SetupTypes;
|
||||
|
||||
[SerializeField]
|
||||
private BuildTarget m_Target;
|
||||
|
||||
public PlatformSpecificSetup()
|
||||
{
|
||||
}
|
||||
|
||||
public PlatformSpecificSetup(BuildTarget target)
|
||||
{
|
||||
m_Target = target;
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
var dictionary = GetSetup();
|
||||
|
||||
if (!dictionary.ContainsKey(m_Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[m_Target].Setup();
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
var dictionary = GetSetup();
|
||||
|
||||
if (!dictionary.ContainsKey(m_Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[m_Target].PostBuildAction();
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
var dictionary = GetSetup();
|
||||
|
||||
if (!dictionary.ContainsKey(m_Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[m_Target].PostSuccessfulBuildAction();
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
var dictionary = GetSetup();
|
||||
|
||||
if (!dictionary.ContainsKey(m_Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[m_Target].PostSuccessfulLaunchAction();
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
var dictionary = GetSetup();
|
||||
|
||||
if (!dictionary.ContainsKey(m_Target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dictionary[m_Target].CleanUp();
|
||||
}
|
||||
|
||||
private IDictionary<BuildTarget, IPlatformSetup> GetSetup()
|
||||
{
|
||||
m_SetupTypes = new Dictionary<BuildTarget, IPlatformSetup>
|
||||
{
|
||||
{BuildTarget.iOS, m_AppleiOSPlatformSetup},
|
||||
{BuildTarget.tvOS, m_AppleTvOSPlatformSetup},
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
{BuildTarget.XboxOne, m_XboxOnePlatformSetup},
|
||||
#endif
|
||||
{BuildTarget.Android, m_AndroidPlatformSetup},
|
||||
{BuildTarget.WSAPlayer, m_UwpPlatformSetup},
|
||||
#if !UNITY_2022_2_OR_NEWER
|
||||
{BuildTarget.Lumin, m_LuminPlatformSetup},
|
||||
#endif
|
||||
#if UNITY_2019_3_OR_NEWER && !UNITY_2023_1_OR_NEWER
|
||||
{BuildTarget.Stadia, m_StadiaPlatformSetup},
|
||||
#endif
|
||||
{BuildTarget.Switch, m_SwitchPlatformSetup}
|
||||
};
|
||||
return m_SetupTypes;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cccd50ebf7384242bda4d7bcb282ebf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class StadiaPlatformSetup : IPlatformSetup
|
||||
{
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa19b42bd3dc35e40a618448bd330270
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class SwitchPlatformSetup : IPlatformSetup
|
||||
{
|
||||
public void Setup()
|
||||
{
|
||||
EditorUserBuildSettings.switchCreateRomFile = true;
|
||||
EditorUserBuildSettings.switchNVNGraphicsDebugger = false;
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
EditorUserBuildSettings.switchNVNDrawValidation_Heavy = true; // catches more graphics errors
|
||||
#else
|
||||
EditorUserBuildSettings.switchNVNDrawValidation = true; // catches more graphics errors
|
||||
#endif
|
||||
EditorUserBuildSettings.development = true;
|
||||
EditorUserBuildSettings.switchRedirectWritesToHostMount = true;
|
||||
|
||||
// We can use these when more debugging is required:
|
||||
//EditorUserBuildSettings.switchNVNDrawValidation = false; // cannot be used with shader debug
|
||||
//EditorUserBuildSettings.switchNVNGraphicsDebugger = true;
|
||||
//EditorUserBuildSettings.switchNVNShaderDebugging = true;
|
||||
//EditorUserBuildSettings.switchCreateSolutionFile = true; // for shorter iteration time
|
||||
//EditorUserBuildSettings.allowDebugging = true; // managed debugger can be attached
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adf7bea9401c1834380d55601add6cfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using UnityEditor.Build;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class UwpPlatformSetup : IPlatformSetup
|
||||
{
|
||||
private const string k_SettingsBuildConfiguration = "BuildConfiguration";
|
||||
private bool m_InternetClientServer;
|
||||
private bool m_PrivateNetworkClientServer;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
m_InternetClientServer = PlayerSettings.WSA.GetCapability(PlayerSettings.WSACapability.InternetClientServer);
|
||||
m_PrivateNetworkClientServer = PlayerSettings.WSA.GetCapability(PlayerSettings.WSACapability.PrivateNetworkClientServer);
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.InternetClientServer, true);
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.PrivateNetworkClientServer, true);
|
||||
|
||||
// This setting is initialized only when Window Store App is selected from the Build Settings window, and
|
||||
// is typically an empty strings when running tests via UTR on the command-line.
|
||||
bool wsaSettingNotInitialized = string.IsNullOrEmpty(EditorUserBuildSettings.wsaArchitecture);
|
||||
|
||||
// If WSA build settings aren't fully initialized or running from a build machine, specify a default build configuration.
|
||||
// Otherwise we can use the existing configuration specified by the user in Build Settings.
|
||||
if (Environment.GetEnvironmentVariable("UNITY_THISISABUILDMACHINE") == "1" || wsaSettingNotInitialized)
|
||||
{
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
#else
|
||||
EditorUserBuildSettings.wsaSubtarget = WSASubtarget.PC;
|
||||
#endif
|
||||
|
||||
EditorUserBuildSettings.wsaArchitecture = "x64";
|
||||
EditorUserBuildSettings.SetPlatformSettings(BuildPipeline.GetBuildTargetName(BuildTarget.WSAPlayer), k_SettingsBuildConfiguration, WSABuildType.Debug.ToString());
|
||||
EditorUserBuildSettings.wsaUWPBuildType = WSAUWPBuildType.ExecutableOnly;
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
PlayerSettings.SetIl2CppCompilerConfiguration(NamedBuildTarget.WindowsStoreApps, Il2CppCompilerConfiguration.Debug);
|
||||
#else
|
||||
PlayerSettings.SetIl2CppCompilerConfiguration(BuildTargetGroup.WSA, Il2CppCompilerConfiguration.Debug);
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.InternetClientServer, m_InternetClientServer);
|
||||
PlayerSettings.WSA.SetCapability(PlayerSettings.WSACapability.PrivateNetworkClientServer, m_PrivateNetworkClientServer);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 667c6ad86a0b7a548aaa5c287f2c2861
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
|
||||
internal class XboxOnePlatformSetup : IPlatformSetup
|
||||
{
|
||||
private XboxOneDeployMethod oldXboxOneDeployMethod;
|
||||
private XboxOneDeployDrive oldXboxOneDeployDrive;
|
||||
private string oldXboxOneAdditionalDebugPorts;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
oldXboxOneDeployMethod = EditorUserBuildSettings.xboxOneDeployMethod;
|
||||
oldXboxOneDeployDrive = EditorUserBuildSettings.xboxOneDeployDrive;
|
||||
oldXboxOneAdditionalDebugPorts = EditorUserBuildSettings.xboxOneAdditionalDebugPorts;
|
||||
|
||||
EditorUserBuildSettings.xboxOneDeployMethod = XboxOneDeployMethod.Package;
|
||||
EditorUserBuildSettings.xboxOneDeployDrive = XboxOneDeployDrive.Default;
|
||||
|
||||
// This causes the XboxOne post processing systems to open this port in your package manifest.
|
||||
// In addition it will open the ephemeral range for debug connections as well.
|
||||
// Failure to do this will cause connection problems.
|
||||
EditorUserBuildSettings.xboxOneAdditionalDebugPorts = "34999";
|
||||
}
|
||||
|
||||
public void PostBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void PostSuccessfulLaunchAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void CleanUp()
|
||||
{
|
||||
EditorUserBuildSettings.xboxOneDeployMethod = oldXboxOneDeployMethod;
|
||||
EditorUserBuildSettings.xboxOneDeployDrive = oldXboxOneDeployDrive;
|
||||
|
||||
// This causes the XboxOne post processing systems to open this port in your package manifest.
|
||||
// In addition it will open the ephemeral range for debug connections as well.
|
||||
// Failure to do this will cause connection problems.
|
||||
EditorUserBuildSettings.xboxOneAdditionalDebugPorts = oldXboxOneAdditionalDebugPorts;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aed7ab02155e43341a2dbcb7bc17c160
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,302 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEditor.TestRunner.TestLaunchers;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestRunner.Utils;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
using UnityEngine.TestTools.TestRunner.Callbacks;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class TestLaunchFailedException : Exception
|
||||
{
|
||||
public TestLaunchFailedException() {}
|
||||
public TestLaunchFailedException(string message) : base(message) {}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal class PlayerLauncher : RuntimeTestLauncherBase
|
||||
{
|
||||
private readonly BuildTarget m_TargetPlatform;
|
||||
private ITestRunSettings m_OverloadTestRunSettings;
|
||||
private string m_SceneName;
|
||||
private Scene m_Scene;
|
||||
private int m_HeartbeatTimeout;
|
||||
private string m_PlayerWithTestsPath;
|
||||
private PlaymodeTestsController m_Runner;
|
||||
|
||||
internal PlayerLauncherBuildOptions playerBuildOptions { get; private set; }
|
||||
|
||||
public PlayerLauncher(PlaymodeTestsControllerSettings settings, BuildTarget? targetPlatform, ITestRunSettings overloadTestRunSettings, int heartbeatTimeout, string playerWithTestsPath, string scenePath, Scene scene, PlaymodeTestsController runner) : base(settings)
|
||||
{
|
||||
m_TargetPlatform = targetPlatform ?? EditorUserBuildSettings.activeBuildTarget;
|
||||
m_OverloadTestRunSettings = overloadTestRunSettings;
|
||||
m_HeartbeatTimeout = heartbeatTimeout;
|
||||
m_PlayerWithTestsPath = playerWithTestsPath;
|
||||
m_SceneName = scenePath;
|
||||
m_Scene = scene;
|
||||
m_Runner = runner;
|
||||
}
|
||||
|
||||
protected override RuntimePlatform? TestTargetPlatform
|
||||
{
|
||||
get { return BuildTargetConverter.TryConvertToRuntimePlatform(m_TargetPlatform); }
|
||||
}
|
||||
|
||||
public override void Run()
|
||||
{
|
||||
var editorConnectionTestCollector = RemoteTestRunController.instance;
|
||||
editorConnectionTestCollector.hideFlags = HideFlags.HideAndDontSave;
|
||||
editorConnectionTestCollector.Init(m_TargetPlatform, m_HeartbeatTimeout);
|
||||
|
||||
var remotePlayerLogController = RemotePlayerLogController.instance;
|
||||
remotePlayerLogController.hideFlags = HideFlags.HideAndDontSave;
|
||||
|
||||
using (var settings = new PlayerLauncherContextSettings(m_OverloadTestRunSettings))
|
||||
{
|
||||
PrepareScene(m_SceneName, m_Scene, m_Runner);
|
||||
|
||||
var filter = m_Settings.BuildNUnitFilter();
|
||||
var runner = LoadTests(filter);
|
||||
var exceptionThrown = ExecutePreBuildSetupMethods(runner.LoadedTest, filter);
|
||||
if (exceptionThrown)
|
||||
{
|
||||
ReopenOriginalScene(m_Settings.originalScene);
|
||||
CallbacksDelegator.instance.RunFailed("Run Failed: One or more errors in a prebuild setup. See the editor log for details.");
|
||||
return;
|
||||
}
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(m_Scene);
|
||||
EditorSceneManager.SaveScene(m_Scene);
|
||||
|
||||
playerBuildOptions = GetBuildOptions(m_SceneName);
|
||||
|
||||
var success = BuildAndRunPlayer(playerBuildOptions);
|
||||
|
||||
FilePathMetaInfo.TryCreateFile(runner.LoadedTest, playerBuildOptions.BuildPlayerOptions);
|
||||
ExecutePostBuildCleanupMethods(runner.LoadedTest, filter);
|
||||
|
||||
ReopenOriginalScene(m_Settings.originalScene);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Object.DestroyImmediate(editorConnectionTestCollector);
|
||||
Debug.LogError("Player build failed");
|
||||
throw new TestLaunchFailedException("Player build failed");
|
||||
}
|
||||
|
||||
if ((playerBuildOptions.BuildPlayerOptions.options & BuildOptions.AutoRunPlayer) != 0)
|
||||
{
|
||||
editorConnectionTestCollector.PostSuccessfulBuildAction();
|
||||
}
|
||||
|
||||
var runSettings = m_OverloadTestRunSettings as PlayerLauncherTestRunSettings;
|
||||
if (success && runSettings != null && runSettings.buildOnly)
|
||||
{
|
||||
EditorUtility.RevealInFinder(playerBuildOptions.BuildPlayerOptions.locationPathName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PrepareScene(string sceneName, Scene scene, PlaymodeTestsController runner)
|
||||
{
|
||||
runner.AddEventHandlerMonoBehaviour<PlayModeRunnerCallback>();
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
if (!commandLineArgs.Contains("-doNotReportTestResultsBackToEditor"))
|
||||
{
|
||||
runner.AddEventHandlerMonoBehaviour<RemoteTestResultSender>();
|
||||
}
|
||||
runner.AddEventHandlerMonoBehaviour<PlayerQuitHandler>();
|
||||
runner.AddEventHandlerScriptableObject<TestRunCallbackListener>();
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
AssetDatabase.SaveAssets();
|
||||
EditorSceneManager.SaveScene(scene, sceneName, false);
|
||||
}
|
||||
|
||||
private static bool BuildAndRunPlayer(PlayerLauncherBuildOptions buildOptions)
|
||||
{
|
||||
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "Building player with following options:\n{0}", buildOptions);
|
||||
|
||||
#if !UNITY_2021_2_OR_NEWER
|
||||
// Android has to be in listen mode to establish player connection
|
||||
// Only flip connect to host if we are older than Unity 2021.2
|
||||
if (buildOptions.BuildPlayerOptions.target == BuildTarget.Android)
|
||||
{
|
||||
buildOptions.BuildPlayerOptions.options &= ~BuildOptions.ConnectToHost;
|
||||
}
|
||||
#endif
|
||||
// For now, so does Lumin
|
||||
#if !UNITY_2022_2_OR_NEWER
|
||||
if (buildOptions.BuildPlayerOptions.target == BuildTarget.Lumin)
|
||||
{
|
||||
buildOptions.BuildPlayerOptions.options &= ~BuildOptions.ConnectToHost;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
// WebGL has to be in close on quit mode to ensure that the browser tab is closed when the player finishes running tests
|
||||
if (buildOptions.BuildPlayerOptions.target == BuildTarget.WebGL)
|
||||
{
|
||||
PlayerSettings.WebGL.closeOnQuit = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
var result = BuildPipeline.BuildPlayer(buildOptions.BuildPlayerOptions);
|
||||
if (result.summary.result != BuildResult.Succeeded)
|
||||
Debug.LogError(result.SummarizeErrors());
|
||||
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
// Clean up WebGL close on quit mode
|
||||
if (buildOptions.BuildPlayerOptions.target == BuildTarget.WebGL)
|
||||
{
|
||||
PlayerSettings.WebGL.closeOnQuit = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result.summary.result == BuildResult.Succeeded;
|
||||
}
|
||||
|
||||
internal PlayerLauncherBuildOptions GetBuildOptions(string scenePath)
|
||||
{
|
||||
var buildOnly = false;
|
||||
var runSettings = m_OverloadTestRunSettings as PlayerLauncherTestRunSettings;
|
||||
if (runSettings != null)
|
||||
{
|
||||
buildOnly = runSettings.buildOnly;
|
||||
}
|
||||
|
||||
var buildOptions = new BuildPlayerOptions();
|
||||
|
||||
var scenes = new List<string> { scenePath };
|
||||
scenes.AddRange(EditorBuildSettings.scenes.Select(x => x.path));
|
||||
buildOptions.scenes = scenes.ToArray();
|
||||
|
||||
buildOptions.options |= BuildOptions.Development | BuildOptions.ConnectToHost | BuildOptions.IncludeTestAssemblies | BuildOptions.StrictMode;
|
||||
buildOptions.target = m_TargetPlatform;
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
buildOptions.subtarget = EditorUserBuildSettings.GetActiveSubtargetFor(m_TargetPlatform);
|
||||
#endif
|
||||
|
||||
if (EditorUserBuildSettings.waitForPlayerConnection)
|
||||
buildOptions.options |= BuildOptions.WaitForPlayerConnection;
|
||||
|
||||
if (EditorUserBuildSettings.allowDebugging)
|
||||
buildOptions.options |= BuildOptions.AllowDebugging;
|
||||
|
||||
if (EditorUserBuildSettings.installInBuildFolder)
|
||||
buildOptions.options |= BuildOptions.InstallInBuildFolder;
|
||||
else if (!buildOnly)
|
||||
buildOptions.options |= BuildOptions.AutoRunPlayer;
|
||||
|
||||
var buildTargetGroup = EditorUserBuildSettings.activeBuildTargetGroup;
|
||||
buildOptions.targetGroup = buildTargetGroup;
|
||||
|
||||
//Check if Lz4 is supported for the current buildtargetgroup and enable it if need be
|
||||
if (PostprocessBuildPlayer.SupportsLz4Compression(buildTargetGroup, m_TargetPlatform))
|
||||
{
|
||||
if (EditorUserBuildSettings.GetCompressionType(buildTargetGroup) == Compression.Lz4)
|
||||
buildOptions.options |= BuildOptions.CompressWithLz4;
|
||||
else if (EditorUserBuildSettings.GetCompressionType(buildTargetGroup) == Compression.Lz4HC)
|
||||
buildOptions.options |= BuildOptions.CompressWithLz4HC;
|
||||
}
|
||||
|
||||
string buildLocation;
|
||||
if (buildOnly)
|
||||
{
|
||||
buildLocation = buildOptions.locationPathName = runSettings.buildOnlyLocationPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
var reduceBuildLocationPathLength = false;
|
||||
|
||||
//Some platforms hit MAX_PATH limits during the build process, in these cases minimize the path length
|
||||
if ((m_TargetPlatform == BuildTarget.WSAPlayer)
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
|| (m_TargetPlatform == BuildTarget.XboxOne)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
reduceBuildLocationPathLength = true;
|
||||
}
|
||||
|
||||
var uniqueTempPathInProject = FileUtil.GetUniqueTempPathInProject();
|
||||
var playerDirectoryName = "PlayerWithTests";
|
||||
|
||||
//Some platforms hit MAX_PATH limits during the build process, in these cases minimize the path length
|
||||
if (reduceBuildLocationPathLength)
|
||||
{
|
||||
playerDirectoryName = "PwT";
|
||||
uniqueTempPathInProject = Path.GetTempFileName();
|
||||
File.Delete(uniqueTempPathInProject);
|
||||
Directory.CreateDirectory(uniqueTempPathInProject);
|
||||
}
|
||||
|
||||
buildLocation = Path.Combine(string.IsNullOrEmpty(m_PlayerWithTestsPath) ? Path.GetFullPath(uniqueTempPathInProject) : m_PlayerWithTestsPath, playerDirectoryName);
|
||||
|
||||
// iOS builds create a folder with Xcode project instead of an executable, therefore no executable name is added
|
||||
if (m_TargetPlatform == BuildTarget.iOS)
|
||||
{
|
||||
buildOptions.locationPathName = buildLocation;
|
||||
}
|
||||
else
|
||||
{
|
||||
string extensionForBuildTarget =
|
||||
PostprocessBuildPlayer.GetExtensionForBuildTarget(buildTargetGroup, buildOptions.target,
|
||||
buildOptions.options);
|
||||
var playerExecutableName = "PlayerWithTests";
|
||||
if (!string.IsNullOrEmpty(extensionForBuildTarget))
|
||||
playerExecutableName += $".{extensionForBuildTarget}";
|
||||
|
||||
buildOptions.locationPathName = Path.Combine(buildLocation, playerExecutableName);
|
||||
}
|
||||
}
|
||||
|
||||
return new PlayerLauncherBuildOptions
|
||||
{
|
||||
BuildPlayerOptions = ModifyBuildOptions(buildOptions),
|
||||
PlayerDirectory = buildLocation,
|
||||
};
|
||||
}
|
||||
|
||||
private BuildPlayerOptions ModifyBuildOptions(BuildPlayerOptions buildOptions)
|
||||
{
|
||||
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(x => x.GetReferencedAssemblies().Any(z => z.Name == "UnityEditor.TestRunner")).ToArray();
|
||||
var attributes = allAssemblies.SelectMany(assembly => assembly.GetCustomAttributes(typeof(TestPlayerBuildModifierAttribute), true).OfType<TestPlayerBuildModifierAttribute>()).ToArray();
|
||||
var modifiers = attributes.Select(attribute => attribute.ConstructModifier()).ToArray();
|
||||
|
||||
foreach (var modifier in modifiers)
|
||||
{
|
||||
buildOptions = modifier.ModifyOptions(buildOptions);
|
||||
}
|
||||
|
||||
return buildOptions;
|
||||
}
|
||||
|
||||
private static bool ShouldReduceBuildLocationPathLength(BuildTarget target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
#if UNITY_2020_2_OR_NEWER
|
||||
case BuildTarget.GameCoreXboxOne:
|
||||
case BuildTarget.GameCoreXboxSeries:
|
||||
#else
|
||||
case BuildTarget.XboxOne:
|
||||
#endif
|
||||
case BuildTarget.WSAPlayer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d973fc1524e4d724081553934c55958c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PlayerLauncherContextSettings : IDisposable
|
||||
{
|
||||
private ITestRunSettings m_OverloadSettings;
|
||||
|
||||
private EditorBuildSettingsScene[] m_EditorBuildSettings;
|
||||
#pragma warning disable 618
|
||||
private ResolutionDialogSetting m_DisplayResolutionDialog;
|
||||
#pragma warning restore 618
|
||||
private bool m_RunInBackground;
|
||||
private FullScreenMode m_FullScreenMode;
|
||||
private bool m_ResizableWindow;
|
||||
private bool m_ShowUnitySplashScreen;
|
||||
private string m_OldproductName;
|
||||
#if !UNITY_6000_0_OR_NEWER
|
||||
private string m_OldAotOptions;
|
||||
#endif
|
||||
#pragma warning disable 618
|
||||
private Lightmapping.GIWorkflowMode m_OldLightmapping;
|
||||
#pragma warning restore 618
|
||||
private bool m_explicitNullChecks;
|
||||
|
||||
private bool m_Disposed;
|
||||
|
||||
public PlayerLauncherContextSettings(ITestRunSettings overloadSettings)
|
||||
{
|
||||
m_OverloadSettings = overloadSettings;
|
||||
SetupProjectParameters();
|
||||
|
||||
if (overloadSettings != null)
|
||||
{
|
||||
overloadSettings.Apply();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!m_Disposed)
|
||||
{
|
||||
CleanupProjectParameters();
|
||||
if (m_OverloadSettings != null)
|
||||
{
|
||||
m_OverloadSettings.Dispose();
|
||||
}
|
||||
|
||||
m_Disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupProjectParameters()
|
||||
{
|
||||
EditorApplication.LockReloadAssemblies();
|
||||
|
||||
m_EditorBuildSettings = EditorBuildSettings.scenes;
|
||||
|
||||
#pragma warning disable 618
|
||||
m_DisplayResolutionDialog = PlayerSettings.displayResolutionDialog;
|
||||
PlayerSettings.displayResolutionDialog = ResolutionDialogSetting.Disabled;
|
||||
#pragma warning restore 618
|
||||
|
||||
m_RunInBackground = PlayerSettings.runInBackground;
|
||||
PlayerSettings.runInBackground = true;
|
||||
|
||||
m_FullScreenMode = PlayerSettings.fullScreenMode;
|
||||
PlayerSettings.fullScreenMode = FullScreenMode.Windowed;
|
||||
|
||||
#if !UNITY_6000_0_OR_NEWER
|
||||
m_OldAotOptions = PlayerSettings.aotOptions;
|
||||
PlayerSettings.aotOptions = "nimt-trampolines=1024";
|
||||
#endif
|
||||
|
||||
m_ResizableWindow = PlayerSettings.resizableWindow;
|
||||
PlayerSettings.resizableWindow = true;
|
||||
|
||||
m_ShowUnitySplashScreen = PlayerSettings.SplashScreen.show;
|
||||
PlayerSettings.SplashScreen.show = false;
|
||||
PlayerSettings.SplashScreen.showUnityLogo = false;
|
||||
|
||||
m_OldproductName = PlayerSettings.productName;
|
||||
PlayerSettings.productName = string.Join("_", Application.productName.Split(Path.GetInvalidFileNameChars()));
|
||||
|
||||
#pragma warning disable 618
|
||||
m_OldLightmapping = Lightmapping.giWorkflowMode;
|
||||
Lightmapping.giWorkflowMode = Lightmapping.GIWorkflowMode.OnDemand;
|
||||
#pragma warning restore 618
|
||||
|
||||
m_explicitNullChecks = EditorUserBuildSettings.explicitNullChecks;
|
||||
EditorUserBuildSettings.explicitNullChecks = true;
|
||||
}
|
||||
|
||||
private void CleanupProjectParameters()
|
||||
{
|
||||
EditorBuildSettings.scenes = m_EditorBuildSettings;
|
||||
|
||||
PlayerSettings.fullScreenMode = m_FullScreenMode;
|
||||
PlayerSettings.runInBackground = m_RunInBackground;
|
||||
#pragma warning disable 618
|
||||
PlayerSettings.displayResolutionDialog = m_DisplayResolutionDialog;
|
||||
#pragma warning restore 618
|
||||
PlayerSettings.resizableWindow = m_ResizableWindow;
|
||||
PlayerSettings.SplashScreen.show = m_ShowUnitySplashScreen;
|
||||
PlayerSettings.SplashScreen.showUnityLogo = m_ShowUnitySplashScreen;
|
||||
PlayerSettings.productName = m_OldproductName;
|
||||
#if !UNITY_6000_0_OR_NEWER
|
||||
PlayerSettings.aotOptions = m_OldAotOptions;
|
||||
#endif
|
||||
#pragma warning disable 618
|
||||
Lightmapping.giWorkflowMode = m_OldLightmapping;
|
||||
#pragma warning restore 618
|
||||
EditorUserBuildSettings.explicitNullChecks = m_explicitNullChecks;
|
||||
|
||||
EditorApplication.UnlockReloadAssemblies();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6965880f76f40194593cb53a88f74005
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PlayerLauncherTestRunSettings : ITestRunSettings
|
||||
{
|
||||
public bool buildOnly { set; get; }
|
||||
|
||||
public string buildOnlyLocationPath { set; get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
void ITestRunSettings.Apply()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1cba6f3ed484514097080a3bb835958
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestRunner.Utils;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
using UnityEngine.TestTools.TestRunner.Callbacks;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal class PlaymodeLauncher
|
||||
{
|
||||
public static bool IsRunning; // This flag is being used by the graphics test framework to detect EditMode/PlayMode.
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3217d58bbd1d2b4aaee933e2e8b9195
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.DeploymentTargets;
|
||||
using UnityEditor.TestTools.TestRunner.CommandLineTest;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestRunner.TestLaunchers
|
||||
{
|
||||
[Serializable]
|
||||
internal class RemotePlayerLogController : ScriptableSingleton<RemotePlayerLogController>
|
||||
{
|
||||
private List<LogWriter> m_LogWriters;
|
||||
|
||||
private Dictionary<string, DeploymentTargetLogger> m_Loggers;
|
||||
|
||||
private string m_DeviceLogsDirectory;
|
||||
|
||||
public void SetBuildTarget(BuildTarget buildTarget)
|
||||
{
|
||||
m_Loggers = GetDeploymentTargetLoggers(buildTarget);
|
||||
|
||||
if (m_Loggers == null)
|
||||
Debug.Log("Deployment target logger could not be created");
|
||||
}
|
||||
|
||||
public void SetLogsDirectory(string dir)
|
||||
{
|
||||
m_DeviceLogsDirectory = dir;
|
||||
}
|
||||
|
||||
public void StartLogWriters()
|
||||
{
|
||||
if (m_DeviceLogsDirectory == null || m_Loggers == null)
|
||||
return;
|
||||
|
||||
m_LogWriters = new List<LogWriter>();
|
||||
|
||||
foreach (var logger in m_Loggers)
|
||||
{
|
||||
m_LogWriters.Add(new LogWriter(m_DeviceLogsDirectory, logger.Key, logger.Value));
|
||||
logger.Value.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void StopLogWriters()
|
||||
{
|
||||
if (m_LogWriters == null)
|
||||
return;
|
||||
|
||||
foreach (var logWriter in m_LogWriters)
|
||||
{
|
||||
logWriter.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, DeploymentTargetLogger> GetDeploymentTargetLoggers(BuildTarget buildTarget)
|
||||
{
|
||||
DeploymentTargetManager deploymentTargetManager;
|
||||
|
||||
try
|
||||
{
|
||||
deploymentTargetManager = DeploymentTargetManager.CreateInstance(EditorUserBuildSettings.activeBuildTargetGroup, buildTarget);
|
||||
|
||||
if (deploymentTargetManager == null)
|
||||
return null;
|
||||
}
|
||||
catch (NotSupportedException ex)
|
||||
{
|
||||
Debug.Log(ex.Message);
|
||||
Debug.Log("Deployment target logger not initialised");
|
||||
return null;
|
||||
}
|
||||
|
||||
var targets = deploymentTargetManager.GetKnownTargets();
|
||||
var loggers = new Dictionary<string, DeploymentTargetLogger>();
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (target.status != DeploymentTargetStatus.Ready) continue;
|
||||
|
||||
var logger = deploymentTargetManager.GetTargetLogger(target.id);
|
||||
logger.Clear();
|
||||
loggers.Add(target.id, logger);
|
||||
}
|
||||
|
||||
return loggers;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edd2a1fe1acbbde43aad39862bb3f4a8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.Networking.PlayerConnection;
|
||||
using UnityEditor.TestTools.TestRunner;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking.PlayerConnection;
|
||||
using UnityEngine.TestRunner.TestLaunchers;
|
||||
|
||||
namespace UnityEditor.TestRunner.TestLaunchers
|
||||
{
|
||||
[Serializable]
|
||||
internal class RemoteTestRunController : ScriptableSingleton<RemoteTestRunController>
|
||||
{
|
||||
internal const int k_HeartbeatTimeout = 60 * 10;
|
||||
|
||||
[SerializeField]
|
||||
internal bool isRunning;
|
||||
|
||||
[SerializeField]
|
||||
private bool m_RegisteredConnectionCallbacks;
|
||||
|
||||
[SerializeField]
|
||||
private int m_HearbeatTimeOut;
|
||||
|
||||
private enum MessageType
|
||||
{
|
||||
TestStarted,
|
||||
TestFinished,
|
||||
RunStarted,
|
||||
RunFinished
|
||||
}
|
||||
[Serializable]
|
||||
private struct Message
|
||||
{
|
||||
public MessageEventArgs MessageArgs;
|
||||
public MessageType Type;
|
||||
|
||||
public Message(MessageEventArgs messageArgs, MessageType type)
|
||||
{
|
||||
MessageArgs = messageArgs;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private List<Message> m_IncomingMessages = new List<Message>();
|
||||
|
||||
[SerializeField]
|
||||
private bool m_RegisteredMessageCallback;
|
||||
|
||||
private TestTools.TestRunner.DelayedCallback m_TimeoutCallback;
|
||||
|
||||
public void Init(BuildTarget buildTarget, int heartbeatTimeout)
|
||||
{
|
||||
isRunning = true;
|
||||
m_HearbeatTimeOut = heartbeatTimeout;
|
||||
EditorConnection.instance.Initialize();
|
||||
if (!m_RegisteredConnectionCallbacks)
|
||||
{
|
||||
EditorConnection.instance.Initialize();
|
||||
DelegateEditorConnectionEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private void DelegateEditorConnectionEvents()
|
||||
{
|
||||
m_RegisteredConnectionCallbacks = true;
|
||||
//This is needed because RemoteTestResultReceiver is not a ScriptableObject
|
||||
EditorConnection.instance.Register(PlayerConnectionMessageIds.playerAliveHeartbeat, PlayerAliveHeartbeat);
|
||||
|
||||
// When a message comes in, we should not immediately process it but instead enqueue it for processing later
|
||||
// in the frame. The problem this solves is that Unity only reserves about 1ms worth of time every frame to
|
||||
// process message from the player connection. When some tests run in a player, it can take the editor
|
||||
// minutes to react to all messages we receive because we only do 1ms of processing, then render all of the
|
||||
// editor etc. -- Instead, we use that 1ms time-window to enqueue messages and then react to them later
|
||||
// during the frame. This reduces the waiting time from minutes to seconds.
|
||||
EditorConnection.instance.Register(PlayerConnectionMessageIds.testStartedMessageId, args => EnqueueMessage(new Message(args, MessageType.TestStarted)));
|
||||
EditorConnection.instance.Register(PlayerConnectionMessageIds.testFinishedMessageId, args => EnqueueMessage(new Message(args, MessageType.TestFinished)));
|
||||
EditorConnection.instance.Register(PlayerConnectionMessageIds.runStartedMessageId, args => EnqueueMessage(new Message(args, MessageType.RunStarted)));
|
||||
EditorConnection.instance.Register(PlayerConnectionMessageIds.runFinishedMessageId, args => EnqueueMessage(new Message(args, MessageType.RunFinished)));
|
||||
}
|
||||
|
||||
private void FlushMessageQueue()
|
||||
{
|
||||
EditorApplication.update -= FlushMessageQueue;
|
||||
m_RegisteredMessageCallback = false;
|
||||
foreach (var msg in m_IncomingMessages)
|
||||
{
|
||||
switch (msg.Type)
|
||||
{
|
||||
case MessageType.TestFinished:
|
||||
{
|
||||
CallbacksDelegator.instance.TestFinishedRemotely(msg.MessageArgs.data);
|
||||
break;
|
||||
}
|
||||
case MessageType.TestStarted:
|
||||
{
|
||||
CallbacksDelegator.instance.TestStartedRemotely(msg.MessageArgs.data);
|
||||
break;
|
||||
}
|
||||
case MessageType.RunStarted:
|
||||
{
|
||||
RunStarted(msg.MessageArgs);
|
||||
break;
|
||||
}
|
||||
case MessageType.RunFinished:
|
||||
{
|
||||
RunFinished(msg.MessageArgs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_IncomingMessages.Clear();
|
||||
}
|
||||
|
||||
private void EnqueueMessage(Message message)
|
||||
{
|
||||
m_TimeoutCallback?.Reset();
|
||||
if (!m_RegisteredMessageCallback)
|
||||
{
|
||||
EditorApplication.update += FlushMessageQueue;
|
||||
m_RegisteredMessageCallback = true;
|
||||
}
|
||||
m_IncomingMessages.Add(message);
|
||||
}
|
||||
|
||||
private void RunStarted(MessageEventArgs messageEventArgs)
|
||||
{
|
||||
m_TimeoutCallback?.Reset();
|
||||
CallbacksDelegator.instance.RunStartedRemotely(messageEventArgs.data);
|
||||
}
|
||||
|
||||
private void RunFinished(MessageEventArgs messageEventArgs)
|
||||
{
|
||||
m_TimeoutCallback?.Clear();
|
||||
EditorConnection.instance.Send(PlayerConnectionMessageIds.quitPlayerMessageId, null, messageEventArgs.playerId);
|
||||
EditorConnection.instance.DisconnectAll();
|
||||
|
||||
CallbacksDelegator.instance.RunFinishedRemotely(messageEventArgs.data);
|
||||
isRunning = false;
|
||||
}
|
||||
|
||||
private void PlayerAliveHeartbeat(MessageEventArgs messageEventArgs)
|
||||
{
|
||||
m_TimeoutCallback?.Reset();
|
||||
}
|
||||
|
||||
private void TimeoutCallback()
|
||||
{
|
||||
CallbacksDelegator.instance.RunFailed($"Test execution timed out. No activity received from the player in {m_HearbeatTimeOut} seconds.");
|
||||
}
|
||||
|
||||
public void PostSuccessfulBuildAction()
|
||||
{
|
||||
m_TimeoutCallback = new TestTools.TestRunner.DelayedCallback(TimeoutCallback, m_HearbeatTimeOut);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d36034e63ad8254b9b2f55280fcc040
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using UnityEditor.Events;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Runner;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.NUnitExtensions;
|
||||
using UnityEngine.TestTools.TestRunner;
|
||||
using UnityEngine.TestTools.Utils;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal abstract class RuntimeTestLauncherBase : TestLauncherBase
|
||||
{
|
||||
internal readonly PlaymodeTestsControllerSettings m_Settings;
|
||||
|
||||
protected RuntimeTestLauncherBase(PlaymodeTestsControllerSettings mSettings)
|
||||
{
|
||||
m_Settings = mSettings;
|
||||
}
|
||||
|
||||
protected UnityTestAssemblyRunner LoadTests(ITestFilter filter)
|
||||
{
|
||||
var editorLoadedTestAssemblyProvider = new EditorLoadedTestAssemblyProvider(new EditorCompilationInterfaceProxy(), new EditorAssembliesProxy());
|
||||
var assembliesWithTests = editorLoadedTestAssemblyProvider.GetAssembliesGroupedByType(TestPlatform.PlayMode).Select(x => x.Assembly.GetName().Name).ToList();
|
||||
|
||||
var nUnitTestAssemblyRunner = new UnityTestAssemblyRunner(new UnityTestAssemblyBuilder(m_Settings.orderedTestNames, m_Settings.randomOrderSeed), null, UnityTestExecutionContext.CurrentContext);
|
||||
var assemblyProvider = new PlayerTestAssemblyProvider(new AssemblyLoadProxy(), assembliesWithTests);
|
||||
nUnitTestAssemblyRunner.Load(assemblyProvider.GetUserAssemblies().Select(a => a.Assembly).ToArray(), TestPlatform.PlayMode, UnityTestAssemblyBuilder.GetNUnitTestBuilderSettings(TestPlatform.PlayMode));
|
||||
return nUnitTestAssemblyRunner;
|
||||
}
|
||||
|
||||
protected static void ReopenOriginalScene(string originalSceneName)
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
|
||||
if (!string.IsNullOrEmpty(originalSceneName))
|
||||
{
|
||||
EditorSceneManager.OpenScene(originalSceneName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class PlaymodeTestsControllerExtensions
|
||||
{
|
||||
internal static T AddEventHandlerMonoBehaviour<T>(this PlaymodeTestsController controller) where T : MonoBehaviour, ITestRunnerListener
|
||||
{
|
||||
var eventHandler = controller.gameObject.AddComponent<T>();
|
||||
SetListeners(controller, eventHandler);
|
||||
return eventHandler;
|
||||
}
|
||||
|
||||
internal static T AddEventHandlerScriptableObject<T>(this PlaymodeTestsController controller) where T : ScriptableObject, ITestRunnerListener
|
||||
{
|
||||
var eventListener = ScriptableObject.CreateInstance<T>();
|
||||
AddEventHandlerScriptableObject(controller, eventListener);
|
||||
return eventListener;
|
||||
}
|
||||
|
||||
internal static void AddEventHandlerScriptableObject(this PlaymodeTestsController controller, ITestRunnerListener obj)
|
||||
{
|
||||
SetListeners(controller, obj);
|
||||
}
|
||||
|
||||
private static void SetListeners(PlaymodeTestsController controller, ITestRunnerListener eventHandler)
|
||||
{
|
||||
UnityEventTools.AddPersistentListener(controller.testStartedEvent, eventHandler.TestStarted);
|
||||
UnityEventTools.AddPersistentListener(controller.testFinishedEvent, eventHandler.TestFinished);
|
||||
UnityEventTools.AddPersistentListener(controller.runStartedEvent, eventHandler.RunStarted);
|
||||
UnityEventTools.AddPersistentListener(controller.runFinishedEvent, eventHandler.RunFinished);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0efb23ecb373b6d4bbe5217485785138
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using NUnit.Framework.Interfaces;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
using UnityEngine.TestTools.Logging;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner
|
||||
{
|
||||
internal abstract class TestLauncherBase
|
||||
{
|
||||
public abstract void Run();
|
||||
|
||||
protected virtual RuntimePlatform? TestTargetPlatform
|
||||
{
|
||||
get { return Application.platform; }
|
||||
}
|
||||
|
||||
protected bool ExecutePreBuildSetupMethods(ITest tests, ITestFilter testRunnerFilter)
|
||||
{
|
||||
using (new ProfilerMarker(nameof(ExecutePreBuildSetupMethods)).Auto()) {
|
||||
var attributeFinder = new PrebuildSetupAttributeFinder();
|
||||
var logString = "Executing setup for: {0}";
|
||||
return ExecuteMethods<IPrebuildSetup>(tests, testRunnerFilter, attributeFinder, logString, targetClass => targetClass.Setup(), TestTargetPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
public void ExecutePostBuildCleanupMethods(ITest tests, ITestFilter testRunnerFilter)
|
||||
{
|
||||
using (new ProfilerMarker(nameof(ExecutePostBuildCleanupMethods)).Auto())
|
||||
ExecutePostBuildCleanupMethods(tests, testRunnerFilter, TestTargetPlatform);
|
||||
}
|
||||
|
||||
public static void ExecutePostBuildCleanupMethods(ITest tests, ITestFilter testRunnerFilter, RuntimePlatform? testTargetPlatform)
|
||||
{
|
||||
using (new ProfilerMarker(nameof(ExecutePostBuildCleanupMethods)).Auto()) {
|
||||
var attributeFinder = new PostbuildCleanupAttributeFinder();
|
||||
var logString = "Executing cleanup for: {0}";
|
||||
ExecuteMethods<IPostBuildCleanup>(tests, testRunnerFilter, attributeFinder, logString, targetClass => targetClass.Cleanup(), testTargetPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ExecuteMethods<T>(ITest tests, ITestFilter testRunnerFilter, AttributeFinderBase attributeFinder, string logString, Action<T> action, RuntimePlatform? testTargetPlatform)
|
||||
{
|
||||
var exceptionsThrown = false;
|
||||
|
||||
if (testTargetPlatform == null)
|
||||
{
|
||||
Debug.LogError("Could not determine test target platform from build target " + EditorUserBuildSettings.activeBuildTarget);
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var targetClassType in attributeFinder.Search(tests, testRunnerFilter, testTargetPlatform.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
var targetClass = (T)Activator.CreateInstance(targetClassType);
|
||||
|
||||
Debug.LogFormat(logString, targetClassType.FullName);
|
||||
|
||||
using (var logScope = new LogScope())
|
||||
{
|
||||
action(targetClass);
|
||||
logScope.EvaluateLogScope(true);
|
||||
}
|
||||
}
|
||||
catch (InvalidCastException) {}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
exceptionsThrown = true;
|
||||
}
|
||||
}
|
||||
|
||||
return exceptionsThrown;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cddf785b0d07434d8e0607c97b09135
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user