2023-06-21 13:28:15 -04:00
|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.IO;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Reflection;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
using Unity.CodeEditor;
|
|
|
|
using UnityEditor.Utils;
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
namespace UnityEditor.TestTools.TestRunner.GUI
|
|
|
|
{
|
|
|
|
internal class GuiHelper : IGuiHelper
|
|
|
|
{
|
|
|
|
public GuiHelper(IMonoCecilHelper monoCecilHelper, IAssetsDatabaseHelper assetsDatabaseHelper)
|
|
|
|
{
|
|
|
|
MonoCecilHelper = monoCecilHelper;
|
|
|
|
AssetsDatabaseHelper = assetsDatabaseHelper;
|
|
|
|
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;
|
|
|
|
|
|
|
|
filePath = Paths.UnifyDirectorySeparator(filePath);
|
|
|
|
var length = Paths.UnifyDirectorySeparator(Application.dataPath).Length - "Assets".Length;
|
|
|
|
|
|
|
|
return filePath.Substring(length);
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool OpenScriptInExternalEditor(string stacktrace)
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(stacktrace))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var regex = new Regex("in (?<path>.*):{1}(?<line>[0-9]+)");
|
|
|
|
|
|
|
|
var matchingLines = stacktrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => regex.IsMatch(x)).ToList();
|
|
|
|
if (!matchingLines.Any())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var fileOpenInfos = matchingLines
|
|
|
|
.Select(x => regex.Match(x))
|
|
|
|
.Select(x =>
|
|
|
|
new FileOpenInfo
|
|
|
|
{
|
|
|
|
FilePath = x.Groups["path"].Value,
|
|
|
|
LineNumber = int.Parse(x.Groups["line"].Value)
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
var fileOpenInfo = fileOpenInfos
|
|
|
|
.FirstOrDefault(openInfo => !string.IsNullOrEmpty(openInfo.FilePath) && File.Exists(openInfo.FilePath));
|
|
|
|
|
|
|
|
if (fileOpenInfo == null)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
var filePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath);
|
|
|
|
AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(filePath, fileOpenInfo.LineNumber);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|