142 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			142 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.IO; | ||
|  | using System.Linq; | ||
|  | using System.Reflection; | ||
|  | using System.Text.RegularExpressions; | ||
|  | using Unity.CodeEditor; | ||
|  | using UnityEditor.Utils; | ||
|  | using UnityEngine; | ||
|  | 
 | ||
|  | namespace UnityEditor.TestTools.TestRunner.GUI | ||
|  | { | ||
|  |     internal class GuiHelper : IGuiHelper | ||
|  |     { | ||
|  |         public GuiHelper(IMonoCecilHelper monoCecilHelper, IAssetsDatabaseHelper assetsDatabaseHelper) | ||
|  |         { | ||
|  |             MonoCecilHelper = monoCecilHelper; | ||
|  |             AssetsDatabaseHelper = assetsDatabaseHelper; | ||
|  |             GetCSFiles = (dirPath, fileExtension) => | ||
|  |             { | ||
|  |                 return Directory.GetFiles(dirPath, $"*{fileExtension}", SearchOption.AllDirectories) | ||
|  |                     .Select(Paths.UnifyDirectorySeparator); | ||
|  |             }; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal Func<string, string, IEnumerable<string>> GetCSFiles; | ||
|  |         protected IMonoCecilHelper MonoCecilHelper { get; private set; } | ||
|  |         public IAssetsDatabaseHelper AssetsDatabaseHelper { get; private set; } | ||
|  |         public IExternalCodeEditor Editor { get; internal set; } | ||
|  |         private const string FileExtension = ".cs"; | ||
|  | 
 | ||
|  |         public void OpenScriptInExternalEditor(Type type, MethodInfo method) | ||
|  |         { | ||
|  |             var fileOpenInfo = GetFileOpenInfo(type, method); | ||
|  | 
 | ||
|  |             if (string.IsNullOrEmpty(fileOpenInfo.FilePath)) | ||
|  |             { | ||
|  |                 Debug.LogWarning("Failed to open test method source code in external editor. Inconsistent filename and yield return operator in target method."); | ||
|  | 
 | ||
|  |                 return; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (fileOpenInfo.LineNumber == 1) | ||
|  |             { | ||
|  |                 Debug.LogWarning("Failed to get a line number for unity test method. So please find it in opened file in external editor."); | ||
|  |             } | ||
|  | 
 | ||
|  |             if (!fileOpenInfo.FilePath.Contains("Assets")) | ||
|  |             { | ||
|  |                 (Editor ?? CodeEditor.CurrentEditor).OpenProject(fileOpenInfo.FilePath, fileOpenInfo.LineNumber, 1); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(fileOpenInfo.FilePath, fileOpenInfo.LineNumber); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public IFileOpenInfo GetFileOpenInfo(Type type, MethodInfo method) | ||
|  |         { | ||
|  |             var fileOpenInfo = MonoCecilHelper.TryGetCecilFileOpenInfo(type, method); | ||
|  |             if (string.IsNullOrEmpty(fileOpenInfo.FilePath)) | ||
|  |             { | ||
|  |                 var dirPath = Paths.UnifyDirectorySeparator(Application.dataPath); | ||
|  |                 var allCsFiles = GetCSFiles(dirPath, FileExtension); | ||
|  | 
 | ||
|  |                 var fileName = allCsFiles.FirstOrDefault(x => | ||
|  |                     x.Split(Path.DirectorySeparatorChar).Last().Equals(string.Concat(GetTestFileName(type), FileExtension))); | ||
|  | 
 | ||
|  |                 fileOpenInfo.FilePath = fileName ?? string.Empty; | ||
|  |             } | ||
|  | 
 | ||
|  |             if (!fileOpenInfo.FilePath.Contains("Assets")) | ||
|  |             { | ||
|  |                 return fileOpenInfo; | ||
|  |             } | ||
|  |             fileOpenInfo.FilePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath); | ||
|  | 
 | ||
|  |             return fileOpenInfo; | ||
|  |         } | ||
|  | 
 | ||
|  |         internal static string GetTestFileName(Type type) | ||
|  |         { | ||
|  |             //This handles the case of a test in a nested class, getting the name of the base class | ||
|  |             if (type.FullName != null && type.Namespace != null && type.FullName.Contains("+")) | ||
|  |             { | ||
|  |                 var removedNamespace = type.FullName.Substring(type.Namespace.Length + 1); | ||
|  |                 return removedNamespace.Substring(0, removedNamespace.IndexOf("+", StringComparison.Ordinal)); | ||
|  |             } | ||
|  |             return type.Name; | ||
|  |         } | ||
|  | 
 | ||
|  |         public string FilePathToAssetsRelativeAndUnified(string filePath) | ||
|  |         { | ||
|  |             if (string.IsNullOrEmpty(filePath)) | ||
|  |                 return string.Empty; | ||
|  | 
 | ||
|  | #if UNITY_2021_3_OR_NEWER | ||
|  |             return Path.GetRelativePath(Directory.GetCurrentDirectory(), filePath); | ||
|  | #else | ||
|  |             filePath = Paths.UnifyDirectorySeparator(filePath); | ||
|  |             var length = Paths.UnifyDirectorySeparator(Application.dataPath).Length - "Assets".Length; | ||
|  | 
 | ||
|  |             return filePath.Substring(length); | ||
|  | #endif | ||
|  |         } | ||
|  | 
 | ||
|  |         public bool OpenScriptInExternalEditor(string stacktrace) | ||
|  |         { | ||
|  |             if (string.IsNullOrEmpty(stacktrace)) | ||
|  |                 return false; | ||
|  | 
 | ||
|  |             var regex = new Regex("in (?<path>.*):{1}(?<line>[0-9]+)"); | ||
|  | 
 | ||
|  |             var matchingLines = stacktrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => regex.IsMatch(x)).ToList(); | ||
|  |             if (!matchingLines.Any()) | ||
|  |                 return false; | ||
|  | 
 | ||
|  |             var fileOpenInfos = matchingLines | ||
|  |                 .Select(x => regex.Match(x)) | ||
|  |                 .Select(x => | ||
|  |                     new FileOpenInfo | ||
|  |                     { | ||
|  |                         FilePath = x.Groups["path"].Value, | ||
|  |                         LineNumber = int.Parse(x.Groups["line"].Value) | ||
|  |                     }).ToList(); | ||
|  | 
 | ||
|  |             var fileOpenInfo = fileOpenInfos | ||
|  |                 .FirstOrDefault(openInfo => !string.IsNullOrEmpty(openInfo.FilePath) && File.Exists(openInfo.FilePath)); | ||
|  | 
 | ||
|  |             if (fileOpenInfo == null) | ||
|  |             { | ||
|  |                 return false; | ||
|  |             } | ||
|  | 
 | ||
|  |             var filePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath); | ||
|  |             AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(filePath, fileOpenInfo.LineNumber); | ||
|  | 
 | ||
|  |             return true; | ||
|  |         } | ||
|  |     } | ||
|  | } |