This commit is contained in:
2025-01-17 13:10:42 +01:00
commit 4536213c91
15115 changed files with 1442174 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <inheritdoc />
internal class ActiveFolderTemplateAssetCreator : IActiveFolderTemplateAssetCreator
{
/// <inheritdoc />
public string GetActiveFolderPath()
{
return ProjectWindowUtil.GetActiveFolderPath();
}
/// <inheritdoc />
public void CreateFolderWithTemplates(string defaultName, params string[] templateNames)
{
ProjectWindowUtil.CreateFolderWithTemplates(defaultName, templateNames);
}
/// <inheritdoc />
public void CreateScriptAssetFromTemplateFile(string defaultName, string templatePath)
{
ProjectWindowUtil.CreateScriptAssetFromTemplateFile(templatePath, defaultName);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f47c66e80010e4020b6803b930eb432c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using System;
using System.IO;
using System.Linq;
using UnityEditor.Scripting.ScriptCompilation;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <inheritdoc />
internal class CustomScriptAssemblyMappingFinder : ICustomScriptAssemblyMappingFinder
{
/// <inheritdoc />
/// <exception cref="ArgumentNullException">The provided <paramref name="folderPath" /> string argument is null.</exception>
public ICustomScriptAssembly FindCustomScriptAssemblyFromFolderPath(string folderPath)
{
if (folderPath == null)
{
throw new ArgumentNullException(nameof(folderPath));
}
var scriptInFolderPath = Path.Combine(folderPath, "Foo.cs");
var customScriptAssembly = FindCustomScriptAssemblyFromScriptPath(scriptInFolderPath);
return customScriptAssembly;
}
/// <summary>
/// Finds the Custom Script Assembly associated with the provided script asset path.
/// </summary>
/// <param name="scriptPath">The script path to check.</param>
/// <returns>The associated <see cref="ICustomScriptAssembly" />; null if none.</returns>
private static ICustomScriptAssembly FindCustomScriptAssemblyFromScriptPath(string scriptPath)
{
try
{
var customScriptAssembly = EditorCompilationInterface.Instance.FindCustomScriptAssemblyFromScriptPath(scriptPath);
return new CustomScriptAssemblyWrapper(customScriptAssembly);
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// Custom Script Assembly wrapper.
/// </summary>
internal class CustomScriptAssemblyWrapper : ICustomScriptAssembly
{
internal readonly CustomScriptAssembly targetAssembly;
/// <summary>
/// Creates a new instance of the <see cref="CustomScriptAssemblyWrapper" /> class.
/// </summary>
/// <param name="assembly">The <see cref="CustomScriptAssembly" /> to be represented by the wrapper.</param>
/// <exception cref="ArgumentNullException">The provided <paramref name="assembly" /> argument is null.</exception>
internal CustomScriptAssemblyWrapper(CustomScriptAssembly assembly)
{
targetAssembly = assembly
?? throw new ArgumentNullException(nameof(assembly), "The provided assembly must not be null.");
}
/// <inheritdoc />
public bool HasPrecompiledReference(string libraryFilename)
{
var precompiledReferences = targetAssembly.PrecompiledReferences;
var libraryReferenceExists = precompiledReferences != null
&& precompiledReferences.Any(r => Path.GetFileName(r) == libraryFilename);
return libraryReferenceExists;
}
/// <inheritdoc />
public bool HasAssemblyFlag(AssemblyFlags flag)
{
var hasAssemblyFlag = (targetAssembly.AssemblyFlags & flag) == flag;
return hasAssemblyFlag;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f03c073fcc564ab582ac38999beb4b6d
timeCreated: 1603203112

View File

@@ -0,0 +1,93 @@
using System;
using System.Linq;
using UnityEditor.Scripting.ScriptCompilation;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <inheritdoc />
internal class FolderPathTestCompilationContextProvider : IFolderPathTestCompilationContextProvider
{
internal const string nUnitLibraryFilename = "nunit.framework.dll";
private static ICustomScriptAssemblyMappingFinder s_CustomScriptAssemblyMappingFinder;
internal static ICustomScriptAssemblyMappingFinder CustomScriptAssemblyMappingFinder
{
private get => s_CustomScriptAssemblyMappingFinder ?? (s_CustomScriptAssemblyMappingFinder = new CustomScriptAssemblyMappingFinder());
set => s_CustomScriptAssemblyMappingFinder = value;
}
/// <summary>
/// Checks if the provided folder path belongs to a Custom Test Assembly.
/// A Custom Test Assembly is defined by a valid reference to the precompiled NUnit library.
/// </summary>
/// <param name="folderPath">The folder path to check.</param>
/// <returns>True if a custom test assembly associated with the provided folder can be found; false otherwise.</returns>
/// <exception cref="ArgumentNullException">The <paramref name="folderPath" /> string argument is null.</exception>
public bool FolderPathBelongsToCustomTestAssembly(string folderPath)
{
if (folderPath == null)
{
throw new ArgumentNullException(nameof(folderPath));
}
var customScriptAssembly = CustomScriptAssemblyMappingFinder.FindCustomScriptAssemblyFromFolderPath(folderPath);
var assemblyIsCustomTestAssembly = customScriptAssembly != null
&& customScriptAssembly.HasPrecompiledReference(nUnitLibraryFilename);
return assemblyIsCustomTestAssembly;
}
/// <summary>
/// Checks if the provided folder path belongs to an assembly capable of compiling Test Scripts.
/// Unless the <see cref="PlayerSettings.playModeTestRunnerEnabled" /> setting is enabled,
/// a Test Script can only be compiled in a Custom Test Assembly
/// or an (implicit or explicit) <see cref="AssemblyFlags.EditorOnly" /> assembly.
/// </summary>
/// <param name="folderPath">The folder path to check.</param>
/// <returns>True if Test Scripts can be successfully compiled when added to this folder path; false otherwise.</returns>
/// <exception cref="ArgumentNullException">The <paramref name="folderPath" /> string argument is null.</exception>
public bool TestScriptWillCompileInFolderPath(string folderPath)
{
if (folderPath == null)
{
throw new ArgumentNullException(nameof(folderPath));
}
if (PlayerSettings.playModeTestRunnerEnabled)
{
return true;
}
var customScriptAssembly = CustomScriptAssemblyMappingFinder.FindCustomScriptAssemblyFromFolderPath(folderPath);
if (customScriptAssembly != null)
{
var assemblyCanCompileTestScripts = customScriptAssembly.HasPrecompiledReference(nUnitLibraryFilename)
|| customScriptAssembly.HasAssemblyFlag(AssemblyFlags.EditorOnly);
return assemblyCanCompileTestScripts;
}
var isImplicitEditorAssembly = FolderPathBelongsToImplicitEditorAssembly(folderPath);
return isImplicitEditorAssembly;
}
/// <summary>
/// Checks if the provided folder path is a special editor path that belongs to an implicit editor assembly.
/// </summary>
/// <param name="folderPath">The folder path to check.</param>
/// <returns>True if the folder path belongs to an implicit editor assembly; false otherwise.</returns>
/// <exception cref="ArgumentNullException">The <paramref name="folderPath" /> string argument is null.</exception>
internal static bool FolderPathBelongsToImplicitEditorAssembly(string folderPath)
{
if (folderPath == null)
{
throw new ArgumentNullException(nameof(folderPath));
}
const char unityPathSeparator = '/';
var unityFormatPath = folderPath.Replace('\\', unityPathSeparator);
var folderComponents = unityFormatPath.Split(unityPathSeparator);
var folderComponentsIncludeEditorFolder = folderComponents.Any(n => n.ToLower().Equals("editor"));
return folderComponentsIncludeEditorFolder;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d92c578e28043ef95d4b703e008be64
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// Provides basic utility methods for creating assets in the active Project Browser folder path.
/// </summary>
internal interface IActiveFolderTemplateAssetCreator
{
/// <summary>
/// The active Project Browser folder path relative to the root project folder.
/// </summary>
/// <returns>The active folder path string.</returns>
string GetActiveFolderPath();
/// <summary>
/// Creates a new folder asset in the active folder path with assets defined by provided templates.
/// </summary>
/// <param name="defaultName">The default name of the folder.</param>
/// <param name="templateNames">The names of templates to be used when creating embedded assets.</param>
void CreateFolderWithTemplates(string defaultName, params string[] templateNames);
/// <summary>
/// Creates a new script asset in the active folder path defined by the provided template.
/// </summary>
/// <param name="defaultName">The default name of the new script asset.</param>
/// <param name="templatePath">The template to be used when creating the asset.</param>
void CreateScriptAssetFromTemplateFile(string defaultName, string templatePath);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 409ee14a63784dc0ab5d002081211814
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
using System;
using UnityEditor.Scripting.ScriptCompilation;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// Provides a wrapper for a Custom Script Assembly and exposes its basic properties.
/// </summary>
internal interface ICustomScriptAssembly
{
/// <summary>
/// Checks if the Custom Script Assembly is referencing the provided precompiled library.
/// </summary>
/// <param name="libraryFilename">The name of the precompiled library reference to be checked.</param>
/// <returns>True if the assembly references the provided precompiled library; false otherwise.</returns>
bool HasPrecompiledReference(string libraryFilename);
/// <summary>
/// Checks if the Custom Script Assembly has the provided <see cref="AssemblyFlags" /> value set.
/// </summary>
/// <param name="flag">The <see cref="AssemblyFlags" /> value to check against.</param>
/// <returns>True if the provided <paramref name="flag" /> value is set; false otherwise.</returns>
bool HasAssemblyFlag(AssemblyFlags flag);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 32829ea9e75c475295f73ff867e2f9d0
timeCreated: 1603203107

View File

@@ -0,0 +1,17 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// Provides mapping information from folder paths to their corresponding Custom Script Assembly scope.
/// </summary>
internal interface ICustomScriptAssemblyMappingFinder
{
/// <summary>
/// Finds the Custom Script Assembly associated with the provided folder path.
/// </summary>
/// <param name="folderPath">The folder path to check.</param>
/// <returns>The associated <see cref="ICustomScriptAssembly" />; null if none.</returns>
ICustomScriptAssembly FindCustomScriptAssemblyFromFolderPath(string folderPath);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0cd0deb81d984e58952ccd7e1dd6b2bb
timeCreated: 1603203104

View File

@@ -0,0 +1,20 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// Provides Test Script compilation context associated with project folder paths.
/// </summary>
internal interface IFolderPathTestCompilationContextProvider
{
/// <summary>
/// Checks if the provided folder path belongs to a Custom Test Assembly.
/// </summary>
bool FolderPathBelongsToCustomTestAssembly(string folderPath);
/// <summary>
/// Checks if the provided folder path belongs to an assembly capable of compiling Test Scripts.
/// </summary>
bool TestScriptWillCompileInFolderPath(string folderPath);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e5f4a89476c1448abc7e0a9719b13b36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,33 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// Provides an interface for creating test assets from templates.
/// </summary>
internal interface ITestScriptAssetsCreator
{
/// <summary>
/// Creates a new folder in the active folder path with an associated Test Script Assembly definition.
/// </summary>
/// <param name="isEditorOnly">Should the assembly definition be editor-only?</param>
void AddNewFolderWithTestAssemblyDefinition(bool isEditorOnly = false);
/// <summary>
/// Checks if the active folder path already contains a Test Script Assembly definition.
/// </summary>
/// <returns>True if the active folder path contains a Test Script Assembly; false otherwise.</returns>
bool ActiveFolderContainsTestAssemblyDefinition();
/// <summary>
/// Adds a new Test Script asset in the active folder path.
/// </summary>
void AddNewTestScript();
/// <summary>
/// Checks if a Test Script asset can be compiled in the active folder path.
/// </summary>
/// <returns>True if a Test Script can be compiled in the active folder path; false otherwise.</returns>
bool TestScriptWillCompileInActiveFolder();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e7f72702c2f04b999739380ef9c0de5f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
using System;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <summary>
/// The set of Menu Items dedicated to creating test assets: Test Scripts and Custom Test Assemblies.
/// </summary>
internal static class TestScriptAssetMenuItems
{
internal const string addNewFolderWithTestAssemblyDefinitionMenuItem = "Assets/Create/Testing/Tests Assembly Folder";
internal const string addNewTestScriptMenuItem = "Assets/Create/Testing/C# Test Script";
/// <summary>
/// Adds a new folder asset and an associated Custom Test Assembly in the active folder path.
/// </summary>
[MenuItem(addNewFolderWithTestAssemblyDefinitionMenuItem, false, 83)]
public static void AddNewFolderWithTestAssemblyDefinition()
{
TestScriptAssetsCreator.Instance.AddNewFolderWithTestAssemblyDefinition();
}
/// <summary>
/// Checks if it is possible to add a new Custom Test Assembly inside the active folder path.
/// </summary>
/// <returns>False if the active folder path already contains a Custom Test Assembly; true otherwise.</returns>
[MenuItem(addNewFolderWithTestAssemblyDefinitionMenuItem, true, 83)]
public static bool CanAddNewFolderWithTestAssemblyDefinition()
{
var testAssemblyAlreadyExists = TestScriptAssetsCreator.Instance.ActiveFolderContainsTestAssemblyDefinition();
return !testAssemblyAlreadyExists;
}
/// <summary>
/// Adds a new Test Script asset in the active folder path.
/// </summary>
[MenuItem(addNewTestScriptMenuItem, false, 83)]
public static void AddNewTestScript()
{
TestScriptAssetsCreator.Instance.AddNewTestScript();
}
/// <summary>
/// Checks if it is possible to add a new Test Script in the active folder path.
/// </summary>
/// <returns>True if a Test Script can be compiled in the active folder path; false otherwise.</returns>
[MenuItem(addNewTestScriptMenuItem, true, 83)]
public static bool CanAddNewTestScript()
{
var testScriptWillCompile = TestScriptAssetsCreator.Instance.TestScriptWillCompileInActiveFolder();
return testScriptWillCompile;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c702cb84a2a4576bf275a76bc17f8e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,78 @@
using System;
using System.IO;
namespace UnityEditor.TestTools.TestRunner.GUI.TestAssets
{
/// <inheritdoc />
internal class TestScriptAssetsCreator : ITestScriptAssetsCreator
{
private const string k_AssemblyDefinitionEditModeTestTemplate = "92-Assembly Definition-NewEditModeTestAssembly.asmdef.txt";
internal const string assemblyDefinitionTestTemplate = "92-Assembly Definition-NewTestAssembly.asmdef.txt";
internal const string resourcesTemplatePath = "Resources/ScriptTemplates";
internal const string testScriptTemplate = "83-C# Script-NewTestScript.cs.txt";
internal const string defaultNewTestAssemblyFolderName = "Tests";
internal const string defaultNewTestScriptName = "NewTestScript.cs";
private static IFolderPathTestCompilationContextProvider s_FolderPathCompilationContext;
private static IActiveFolderTemplateAssetCreator s_ActiveFolderTemplateAssetCreator;
private static ITestScriptAssetsCreator s_Instance;
internal static IFolderPathTestCompilationContextProvider FolderPathContext
{
private get => s_FolderPathCompilationContext ?? (s_FolderPathCompilationContext = new FolderPathTestCompilationContextProvider());
set => s_FolderPathCompilationContext = value;
}
internal static IActiveFolderTemplateAssetCreator ActiveFolderTemplateAssetCreator
{
private get => s_ActiveFolderTemplateAssetCreator ?? (s_ActiveFolderTemplateAssetCreator = new ActiveFolderTemplateAssetCreator());
set => s_ActiveFolderTemplateAssetCreator = value;
}
internal static ITestScriptAssetsCreator Instance => s_Instance ?? (s_Instance = new TestScriptAssetsCreator());
private static string ActiveFolderPath => ActiveFolderTemplateAssetCreator.GetActiveFolderPath();
private static string ScriptTemplatesResourcesPath => Path.Combine(EditorApplication.applicationContentsPath, resourcesTemplatePath);
#if UNITY_2023_3_OR_NEWER
private static string ScriptTemplatePath => Path.Combine(ScriptTemplatesResourcesPath, AssetsMenuUtility.GetScriptTemplatePath(ScriptTemplate.CSharp_NewTestScript));
#else
private static string ScriptTemplatePath => Path.Combine(ScriptTemplatesResourcesPath, testScriptTemplate);
#endif
/// <inheritdoc />
public void AddNewFolderWithTestAssemblyDefinition(bool isEditorOnly = false)
{
#if UNITY_2023_3_OR_NEWER
var assemblyDefinitionTemplate =
AssetsMenuUtility.GetScriptTemplatePath(isEditorOnly
? ScriptTemplate.AsmDef_NewEditModeTestAssembly
: ScriptTemplate.AsmDef_NewTestAssembly);
#else
var assemblyDefinitionTemplate = isEditorOnly ? k_AssemblyDefinitionEditModeTestTemplate : assemblyDefinitionTestTemplate;
#endif
ActiveFolderTemplateAssetCreator.CreateFolderWithTemplates(defaultNewTestAssemblyFolderName, assemblyDefinitionTemplate);
}
/// <inheritdoc />
public void AddNewTestScript()
{
var destPath = Path.Combine(ActiveFolderTemplateAssetCreator.GetActiveFolderPath(), defaultNewTestScriptName);
ActiveFolderTemplateAssetCreator.CreateScriptAssetFromTemplateFile(destPath, ScriptTemplatePath);
}
/// <inheritdoc />
public bool ActiveFolderContainsTestAssemblyDefinition()
{
return FolderPathContext.FolderPathBelongsToCustomTestAssembly(ActiveFolderPath);
}
/// <inheritdoc />
public bool TestScriptWillCompileInActiveFolder()
{
return FolderPathContext.TestScriptWillCompileInFolderPath(ActiveFolderPath);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1bace19a170f47bb8d19645cfc580796
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: