using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using UnityEditor; using UnityEditor.Scripting.ScriptCompilation; using UnityEngine.TestTools; using UnityEngine.TestTools.Utils; namespace UnityEditor.TestTools.TestRunner { internal class EditorLoadedTestAssemblyProvider : IEditorLoadedTestAssemblyProvider { private const string k_NunitAssemblyName = "nunit.framework"; private const string k_TestRunnerAssemblyName = "UnityEngine.TestRunner"; internal const string k_PerformanceTestingAssemblyName = "Unity.PerformanceTesting"; private readonly IEditorAssembliesProxy m_EditorAssembliesProxy; private readonly ScriptAssembly[] m_AllEditorScriptAssemblies; private readonly PrecompiledAssembly[] m_AllPrecompiledAssemblies; public EditorLoadedTestAssemblyProvider(IEditorCompilationInterfaceProxy compilationInterfaceProxy, IEditorAssembliesProxy editorAssembliesProxy) { m_EditorAssembliesProxy = editorAssembliesProxy; m_AllEditorScriptAssemblies = compilationInterfaceProxy.GetAllEditorScriptAssemblies(); m_AllPrecompiledAssemblies = compilationInterfaceProxy.GetAllPrecompiledAssemblies(); } public List GetAssembliesGroupedByType(TestPlatform mode) { var assemblies = GetAssembliesGroupedByTypeAsync(mode); while (assemblies.MoveNext()) { } return assemblies.Current.Where(pair => mode.IsFlagIncluded(pair.Key)).SelectMany(pair => pair.Value).ToList(); } public IEnumerator>> GetAssembliesGroupedByTypeAsync(TestPlatform mode) { IAssemblyWrapper[] loadedAssemblies = m_EditorAssembliesProxy.loadedAssemblies; IDictionary> result = new Dictionary> { {TestPlatform.EditMode, new List() }, {TestPlatform.PlayMode, new List() } }; var filteredAssemblies = FilterAssembliesWithTestReference(loadedAssemblies); foreach (var loadedAssembly in filteredAssemblies) { var assemblyName = new FileInfo(loadedAssembly.Location).Name; var scriptAssemblies = m_AllEditorScriptAssemblies.Where(x => x.Filename == assemblyName).ToList(); var precompiledAssemblies = m_AllPrecompiledAssemblies.Where(x => new FileInfo(x.Path).Name == assemblyName).ToList(); if (scriptAssemblies.Count < 1 && precompiledAssemblies.Count < 1) { continue; } var assemblyFlags = scriptAssemblies.Any() ? scriptAssemblies.Single().Flags : precompiledAssemblies.Single().Flags; var assemblyType = (assemblyFlags & AssemblyFlags.EditorOnly) == AssemblyFlags.EditorOnly ? TestPlatform.EditMode : TestPlatform.PlayMode; result[assemblyType].Add(loadedAssembly); yield return null; } yield return result; } private IAssemblyWrapper[] FilterAssembliesWithTestReference(IAssemblyWrapper[] loadedAssemblies) { var resultsCache = new Dictionary(); var loadedAssembliesDict = loadedAssemblies.ToDictionary(asm => asm.Name.Name, asm => asm); return loadedAssemblies .Where(assembly => FilterAssemblyForTestReference(assembly, loadedAssembliesDict, resultsCache, new HashSet())) .ToArray(); } private bool FilterAssemblyForTestReference(IAssemblyWrapper assemblyToFilter, IReadOnlyDictionary loadedAssemblies, IDictionary resultsCache, HashSet visitedAssemblies) { if (!visitedAssemblies.Add(assemblyToFilter.Name.FullName)) { return false; } if (resultsCache.TryGetValue(assemblyToFilter, out var existingResult)) { return existingResult; } foreach (var reference in assemblyToFilter.GetReferencedAssemblies()) { if (IsTestReference(reference) || (loadedAssemblies.TryGetValue(reference.Name, out var referencedAssembly) && FilterAssemblyForTestReference(referencedAssembly, loadedAssemblies, resultsCache, visitedAssemblies))) { resultsCache[assemblyToFilter] = true; return true; } } resultsCache[assemblyToFilter] = false; return false; } private static bool IsTestReference(AssemblyName assemblyName) { return assemblyName.Name == k_NunitAssemblyName || assemblyName.Name == k_TestRunnerAssemblyName || assemblyName.Name == k_PerformanceTestingAssemblyName; } } }