GuiHelper.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Text.RegularExpressions;
  6. using UnityEditor.Utils;
  7. using UnityEngine;
  8. namespace UnityEditor.TestTools.TestRunner.GUI
  9. {
  10. internal class GuiHelper : IGuiHelper
  11. {
  12. public GuiHelper(IMonoCecilHelper monoCecilHelper, IAssetsDatabaseHelper assetsDatabaseHelper)
  13. {
  14. MonoCecilHelper = monoCecilHelper;
  15. AssetsDatabaseHelper = assetsDatabaseHelper;
  16. }
  17. protected IMonoCecilHelper MonoCecilHelper { get; private set; }
  18. public IAssetsDatabaseHelper AssetsDatabaseHelper { get; private set; }
  19. public void OpenScriptInExternalEditor(Type type, MethodInfo method)
  20. {
  21. var fileOpenInfo = GetFileOpenInfo(type, method);
  22. if (string.IsNullOrEmpty(fileOpenInfo.FilePath))
  23. {
  24. Debug.LogWarning("Failed to open test method source code in external editor. Inconsistent filename and yield return operator in target method.");
  25. return;
  26. }
  27. if (fileOpenInfo.LineNumber == 1)
  28. {
  29. Debug.LogWarning("Failed to get a line number for unity test method. So please find it in opened file in external editor.");
  30. }
  31. AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(fileOpenInfo.FilePath, fileOpenInfo.LineNumber);
  32. }
  33. public IFileOpenInfo GetFileOpenInfo(Type type, MethodInfo method)
  34. {
  35. const string fileExtension = ".cs";
  36. var fileOpenInfo = MonoCecilHelper.TryGetCecilFileOpenInfo(type, method);
  37. if (string.IsNullOrEmpty(fileOpenInfo.FilePath))
  38. {
  39. var dirPath = Paths.UnifyDirectorySeparator(Application.dataPath);
  40. var allCsFiles = Directory.GetFiles(dirPath, $"*{fileExtension}", SearchOption.AllDirectories)
  41. .Select(Paths.UnifyDirectorySeparator);
  42. var fileName = allCsFiles.FirstOrDefault(x =>
  43. x.Split(Path.DirectorySeparatorChar).Last().Equals(string.Concat(type.Name, fileExtension)));
  44. fileOpenInfo.FilePath = fileName ?? string.Empty;
  45. }
  46. fileOpenInfo.FilePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath);
  47. return fileOpenInfo;
  48. }
  49. public string FilePathToAssetsRelativeAndUnified(string filePath)
  50. {
  51. if (string.IsNullOrEmpty(filePath))
  52. return string.Empty;
  53. filePath = Paths.UnifyDirectorySeparator(filePath);
  54. var length = Paths.UnifyDirectorySeparator(Application.dataPath).Length - "Assets".Length;
  55. return filePath.Substring(length);
  56. }
  57. public bool OpenScriptInExternalEditor(string stacktrace)
  58. {
  59. if (string.IsNullOrEmpty(stacktrace))
  60. return false;
  61. var regex = new Regex("in (?<path>.*):{1}(?<line>[0-9]+)");
  62. var matchingLines = stacktrace.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(x => regex.IsMatch(x)).ToList();
  63. if (!matchingLines.Any())
  64. return false;
  65. var fileOpenInfos = matchingLines
  66. .Select(x => regex.Match(x))
  67. .Select(x =>
  68. new FileOpenInfo
  69. {
  70. FilePath = x.Groups["path"].Value,
  71. LineNumber = int.Parse(x.Groups["line"].Value)
  72. }).ToList();
  73. var fileOpenInfo = fileOpenInfos
  74. .FirstOrDefault(openInfo => !string.IsNullOrEmpty(openInfo.FilePath) && File.Exists(openInfo.FilePath));
  75. if (fileOpenInfo == null)
  76. {
  77. return false;
  78. }
  79. var filePath = FilePathToAssetsRelativeAndUnified(fileOpenInfo.FilePath);
  80. AssetsDatabaseHelper.OpenAssetInItsDefaultExternalEditor(filePath, fileOpenInfo.LineNumber);
  81. return true;
  82. }
  83. }
  84. }