test
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class AssetsDatabaseHelper : IAssetsDatabaseHelper
|
||||
{
|
||||
public void OpenAssetInItsDefaultExternalEditor(string assetPath, int line)
|
||||
{
|
||||
var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
|
||||
AssetDatabase.OpenAsset(asset, line);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 740b3785866edda4b8d1e1a05570a5f8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e88eef57957d4440c8a7ff2ef9dd3d97
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for dealing with common bit operations.
|
||||
/// </summary>
|
||||
internal static class BitUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates the cardinality of an integer, treating the value as a bit set.
|
||||
/// Optimization based on http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel.
|
||||
/// </summary>
|
||||
/// <param name="integer">The input integer value.</param>
|
||||
/// <returns>The number of bits set in the provided input integer value.</returns>
|
||||
internal static int GetCardinality(int integer)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
integer = integer - ((integer >> 1) & 0x55555555);
|
||||
integer = (integer & 0x33333333) + ((integer >> 2) & 0x33333333);
|
||||
integer = (((integer + (integer >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
|
||||
}
|
||||
|
||||
return integer;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24a5f331fec74c9aa9e1e5d74b5a9589
|
||||
timeCreated: 1601747849
|
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A flag enum content provider to be used with the <see cref="SelectionDropDown" /> control.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The flag enum type.</typeparam>
|
||||
internal class FlagEnumContentProvider<T> : ISelectionDropDownContentProvider where T : Enum
|
||||
{
|
||||
private readonly Action<T> m_ValueChangedCallback;
|
||||
private readonly T[] m_Values;
|
||||
internal Func<string, string> DisplayNameGenerator = ObjectNames.NicifyVariableName;
|
||||
private T m_CurrentValue;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="FlagEnumContentProvider{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial selection value.</param>
|
||||
/// <param name="valueChangedCallback">The callback to be invoked on selection change.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown if the generic enum parameter type is not integer based
|
||||
/// or if the initial selection value is empty.
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentNullException">Thrown if the provided change callback is null.</exception>
|
||||
public FlagEnumContentProvider(T initialValue, Action<T> valueChangedCallback)
|
||||
{
|
||||
if (Enum.GetUnderlyingType(typeof(T)) != typeof(int))
|
||||
{
|
||||
throw new ArgumentException("Argument underlying type must be integer.");
|
||||
}
|
||||
|
||||
if ((int)(object)initialValue == 0)
|
||||
{
|
||||
throw new ArgumentException("The initial value must not be an empty set.", nameof(initialValue));
|
||||
}
|
||||
|
||||
if (valueChangedCallback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueChangedCallback), "The value change callback must not be null.");
|
||||
}
|
||||
|
||||
m_CurrentValue = initialValue;
|
||||
m_Values = (T[])Enum.GetValues(typeof(T));
|
||||
m_ValueChangedCallback = valueChangedCallback;
|
||||
}
|
||||
|
||||
public int Count => m_Values.Length;
|
||||
public bool IsMultiSelection => true;
|
||||
|
||||
public string GetName(int index)
|
||||
{
|
||||
return ValidateIndexBounds(index) ? DisplayNameGenerator(m_Values[index].ToString()) : string.Empty;
|
||||
}
|
||||
|
||||
public int[] SeparatorIndices => new int[0];
|
||||
|
||||
public bool IsSelected(int index)
|
||||
{
|
||||
return ValidateIndexBounds(index) && IsSet(m_Values[index]);
|
||||
}
|
||||
|
||||
public void SelectItem(int index)
|
||||
{
|
||||
if (!ValidateIndexBounds(index))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ChangeValue(m_Values[index]))
|
||||
{
|
||||
m_ValueChangedCallback(m_CurrentValue);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ChangeValue(T flag)
|
||||
{
|
||||
var value = flag;
|
||||
var count = GetSetCount();
|
||||
|
||||
if (IsSet(value))
|
||||
{
|
||||
if (count == 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_CurrentValue = FlagEnumUtility.RemoveFlag(m_CurrentValue, flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
m_CurrentValue = FlagEnumUtility.SetFlag(m_CurrentValue, flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSet(T flag)
|
||||
{
|
||||
return FlagEnumUtility.HasFlag(m_CurrentValue, flag);
|
||||
}
|
||||
|
||||
private int GetSetCount()
|
||||
{
|
||||
return BitUtility.GetCardinality((int)(object)m_CurrentValue);
|
||||
}
|
||||
|
||||
private bool ValidateIndexBounds(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
Debug.LogError($"Requesting item index {index} from a collection of size {Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d31403ec6b334194bdeb4c5ebad64097
|
||||
timeCreated: 1600072403
|
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for dealing with common enumerator operations.
|
||||
/// </summary>
|
||||
internal static class FlagEnumUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks for the presence of a flag in a flag enum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value to check for the presence of the flag.</param>
|
||||
/// <param name="flag">The flag whose presence is to be checked.</param>
|
||||
/// <typeparam name="T">The flag enum type.</typeparam>
|
||||
/// <returns></returns>
|
||||
internal static bool HasFlag<T>(T value, T flag) where T : Enum
|
||||
{
|
||||
ValidateUnderlyingType<T>();
|
||||
|
||||
var intValue = (int)(object)value;
|
||||
var intFlag = (int)(object)flag;
|
||||
return (intValue & intFlag) == intFlag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a flag in a flag enum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value where the flag should be set.</param>
|
||||
/// <param name="flag">The flag to be set.</param>
|
||||
/// <typeparam name="T">The flag enum type.</typeparam>
|
||||
/// <returns>The input value with the flag set.</returns>
|
||||
internal static T SetFlag<T>(T value, T flag) where T : Enum
|
||||
{
|
||||
ValidateUnderlyingType<T>();
|
||||
|
||||
var intValue = (int)(object)value;
|
||||
var intFlag = (int)(object)flag;
|
||||
var result = intValue | intFlag;
|
||||
return (T)Enum.ToObject(typeof(T), result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a flag in a flag enum value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value where the flag should be removed.</param>
|
||||
/// <param name="flag">The flag to be removed.</param>
|
||||
/// <typeparam name="T">The flag enum type.</typeparam>
|
||||
/// <returns>The input value with the flag removed.</returns>
|
||||
internal static T RemoveFlag<T>(T value, T flag) where T : Enum
|
||||
{
|
||||
ValidateUnderlyingType<T>();
|
||||
|
||||
var intValue = (int)(object)value;
|
||||
var intFlag = (int)(object)flag;
|
||||
var result = intValue & ~intFlag;
|
||||
return (T)Enum.ToObject(typeof(T), result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the underlying type of an enum is integer.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The enum type.</typeparam>
|
||||
/// <exception cref="ArgumentException">Thrown if the underlying type of the enum type parameter is not integer.</exception>
|
||||
private static void ValidateUnderlyingType<T>() where T : Enum
|
||||
{
|
||||
if (Enum.GetUnderlyingType(typeof(T)) != typeof(int))
|
||||
{
|
||||
throw new ArgumentException("Argument underlying type must be integer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ea6cc55375344f10834cf6fa65197525
|
||||
timeCreated: 1600072466
|
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic type content provider to be used with the <see cref="SelectionDropDown" /> control.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of values represented by content elements.</typeparam>
|
||||
internal class GenericItemContentProvider<T> : ISelectionDropDownContentProvider where T : IEquatable<T>
|
||||
{
|
||||
private readonly ISelectableItem<T>[] m_Items;
|
||||
private readonly Action<T> m_ValueChangedCallback;
|
||||
private T m_CurrentValue;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="GenericItemContentProvider{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial selection value.</param>
|
||||
/// <param name="items">The set of selectable items.</param>
|
||||
/// <param name="separatorIndices">The indices of items which should be followed by separator lines.</param>
|
||||
/// <param name="valueChangedCallback"></param>
|
||||
/// <exception cref="ArgumentNullException">Thrown if any of the provided arguments is null, except for the separator indices.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown if the set of items is empty or does not contain the initial selection value.</exception>
|
||||
public GenericItemContentProvider(T initialValue, ISelectableItem<T>[] items, int[] separatorIndices, Action<T> valueChangedCallback)
|
||||
{
|
||||
if (initialValue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(initialValue), "The initial selection value must not be null.");
|
||||
}
|
||||
|
||||
if (items == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(items), "The set of items must not be null.");
|
||||
}
|
||||
|
||||
if (valueChangedCallback == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(valueChangedCallback), "The value change callback must not be null.");
|
||||
}
|
||||
|
||||
if (items.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("The set of items must not be empty.", nameof(items));
|
||||
}
|
||||
|
||||
if (!items.Any(i => i.Value.Equals(initialValue)))
|
||||
{
|
||||
throw new ArgumentException("The initial selection value must be in the items set.", nameof(items));
|
||||
}
|
||||
|
||||
m_CurrentValue = initialValue;
|
||||
m_Items = items;
|
||||
SeparatorIndices = separatorIndices ?? new int[0];
|
||||
m_ValueChangedCallback = valueChangedCallback;
|
||||
}
|
||||
|
||||
public int Count => m_Items.Length;
|
||||
public bool IsMultiSelection => false;
|
||||
|
||||
public string GetName(int index)
|
||||
{
|
||||
return ValidateIndexBounds(index) ? m_Items[index].DisplayName : string.Empty;
|
||||
}
|
||||
|
||||
public int[] SeparatorIndices { get; }
|
||||
|
||||
public void SelectItem(int index)
|
||||
{
|
||||
if (!ValidateIndexBounds(index))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsSelected(index))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_CurrentValue = m_Items[index].Value;
|
||||
m_ValueChangedCallback(m_CurrentValue);
|
||||
}
|
||||
|
||||
public bool IsSelected(int index)
|
||||
{
|
||||
return ValidateIndexBounds(index) && m_Items[index].Value.Equals(m_CurrentValue);
|
||||
}
|
||||
|
||||
private bool ValidateIndexBounds(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
Debug.LogError($"Requesting item index {index} from a collection of size {Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7348ecf02a70497d9a09bd05ad2038ac
|
||||
timeCreated: 1600072422
|
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a content element which can be used with the <see cref="GenericItemContentProvider{T}" /> content provider.
|
||||
/// </summary>
|
||||
internal interface ISelectableItem<out T>
|
||||
{
|
||||
/// <summary>
|
||||
/// The value represented by this item.
|
||||
/// </summary>
|
||||
T Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name to be used when displaying this item.
|
||||
/// </summary>
|
||||
string DisplayName { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b076e8f3150431baf926fe9ee030b1e
|
||||
timeCreated: 1600072411
|
@@ -0,0 +1,46 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a content provider that can be used with the <see cref="SelectionDropDown" /> control.
|
||||
/// </summary>
|
||||
internal interface ISelectionDropDownContentProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The total number of items to display.
|
||||
/// </summary>
|
||||
int Count { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Multiple selection support.
|
||||
/// Multiple selection dropdowns don't get closed on selection change.
|
||||
/// </summary>
|
||||
bool IsMultiSelection { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The indices of items which should be followed by separator lines.
|
||||
/// </summary>
|
||||
int[] SeparatorIndices { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the display name of the item at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item whose display name is to be returned.</param>
|
||||
/// <returns>The display name of the item at the specified index.</returns>
|
||||
string GetName(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Signals a request to select the item at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to be selected.</param>
|
||||
void SelectItem(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the selection status of the item at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item whose selection status is to be returned.</param>
|
||||
/// <returns><c>true</c> if the item is currently selected; otherwise, <c>false</c>. </returns>
|
||||
bool IsSelected(int index);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2247a4dfeae4607949780b219a7d3c8
|
||||
timeCreated: 1600072397
|
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
internal class MultiValueContentProvider<T> : ISelectionDropDownContentProvider where T : IEquatable<T>
|
||||
{
|
||||
private T[] m_Values;
|
||||
private bool[] m_Selected;
|
||||
private Action<T[]> m_SelectionChangeCallback;
|
||||
|
||||
public MultiValueContentProvider(T[] values, T[] selectedValues, Action<T[]> selectionChangeCallback)
|
||||
{
|
||||
m_Values = values ?? throw new ArgumentNullException(nameof(values));
|
||||
if (selectedValues == null)
|
||||
{
|
||||
m_Selected = new bool[values.Length];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Selected = values.Select(value => selectedValues.Contains(value)).ToArray();
|
||||
}
|
||||
m_SelectionChangeCallback = selectionChangeCallback;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return m_Values.Length; }
|
||||
}
|
||||
|
||||
public bool IsMultiSelection
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
public int[] SeparatorIndices
|
||||
{
|
||||
get { return new int[0]; }
|
||||
}
|
||||
public string GetName(int index)
|
||||
{
|
||||
if (!ValidateIndexBounds(index))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return m_Values[index].ToString();
|
||||
}
|
||||
|
||||
public void SelectItem(int index)
|
||||
{
|
||||
if (!ValidateIndexBounds(index))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Selected[index] = !m_Selected[index];
|
||||
m_SelectionChangeCallback.Invoke(m_Values.Where((v, i) => m_Selected[i]).ToArray());
|
||||
}
|
||||
|
||||
public bool IsSelected(int index)
|
||||
{
|
||||
if (!ValidateIndexBounds(index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_Selected[index];
|
||||
}
|
||||
|
||||
private bool ValidateIndexBounds(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count)
|
||||
{
|
||||
Debug.LogError($"Requesting item index {index} from a collection of size {Count}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb736ed47b6da4b499431bf9c6de5890
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A default implementation of the <see cref="ISelectableItem{T}" /> interface.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value represented by this content element.</typeparam>
|
||||
internal class SelectableItemContent<T> : ISelectableItem<T>
|
||||
{
|
||||
private readonly string m_DisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SelectableItemContent{T}" /> class
|
||||
/// </summary>
|
||||
/// <param name="itemValue">The value represented by this item.</param>
|
||||
/// <param name="displayName">The display name of this item.</param>
|
||||
public SelectableItemContent(T itemValue, string displayName)
|
||||
{
|
||||
Value = itemValue;
|
||||
m_DisplayName = displayName;
|
||||
}
|
||||
|
||||
public T Value { get; }
|
||||
|
||||
public string DisplayName => m_DisplayName ?? string.Empty;
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7d5e5417ccd4aa0bfa39228c674f142
|
||||
timeCreated: 1600072417
|
@@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A DropDown editor control accepting <see cref="ISelectionDropDownContentProvider" />-based content providers.
|
||||
/// </summary>
|
||||
internal class SelectionDropDown : PopupWindowContent
|
||||
{
|
||||
private static readonly int k_ControlId = typeof(SelectionDropDown).GetHashCode();
|
||||
private readonly ISelectionDropDownContentProvider m_ContentProvider;
|
||||
private readonly Vector2 m_ContentSize;
|
||||
private Vector2 m_ScrollPosition = Vector2.zero;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="SelectionDropDown" /> editor control.
|
||||
/// </summary>
|
||||
/// <param name="contentProvider">The content provider to use.</param>
|
||||
public SelectionDropDown(ISelectionDropDownContentProvider contentProvider)
|
||||
{
|
||||
m_ContentProvider = contentProvider;
|
||||
var width = CalculateContentWidth();
|
||||
var height = CalculateContentHeight();
|
||||
m_ContentSize = new Vector2(width, height);
|
||||
}
|
||||
|
||||
public override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
editorWindow.wantsMouseMove = true;
|
||||
editorWindow.wantsMouseEnterLeaveWindow = true;
|
||||
}
|
||||
|
||||
public override void OnClose()
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
base.OnClose();
|
||||
}
|
||||
|
||||
public override Vector2 GetWindowSize()
|
||||
{
|
||||
return m_ContentSize;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
var evt = Event.current;
|
||||
var contentRect = new Rect(Styles.TopMargin, 0, 1, m_ContentSize.y);
|
||||
m_ScrollPosition = UnityEngine.GUI.BeginScrollView(rect, m_ScrollPosition, contentRect);
|
||||
{
|
||||
var yPos = Styles.TopMargin;
|
||||
for (var i = 0; i < m_ContentProvider.Count; ++i)
|
||||
{
|
||||
var itemRect = new Rect(0, yPos, rect.width, Styles.LineHeight);
|
||||
var separatorOffset = 0f;
|
||||
|
||||
switch (evt.type)
|
||||
{
|
||||
case EventType.Repaint:
|
||||
var content = new GUIContent(m_ContentProvider.GetName(i));
|
||||
var hover = itemRect.Contains(evt.mousePosition);
|
||||
var on = m_ContentProvider.IsSelected(i);
|
||||
Styles.MenuItem.Draw(itemRect, content, hover, false, on, false);
|
||||
separatorOffset = DrawSeparator(i, itemRect);
|
||||
break;
|
||||
|
||||
case EventType.MouseDown:
|
||||
if (evt.button == 0 && itemRect.Contains(evt.mousePosition))
|
||||
{
|
||||
m_ContentProvider.SelectItem(i);
|
||||
if (!m_ContentProvider.IsMultiSelection)
|
||||
{
|
||||
editorWindow.Close();
|
||||
}
|
||||
|
||||
evt.Use();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EventType.MouseEnterWindow:
|
||||
GUIUtility.hotControl = k_ControlId;
|
||||
evt.Use();
|
||||
break;
|
||||
|
||||
case EventType.MouseLeaveWindow:
|
||||
GUIUtility.hotControl = 0;
|
||||
evt.Use();
|
||||
break;
|
||||
|
||||
case EventType.MouseUp:
|
||||
case EventType.MouseMove:
|
||||
evt.Use();
|
||||
break;
|
||||
}
|
||||
|
||||
yPos += Styles.LineHeight + separatorOffset;
|
||||
}
|
||||
}
|
||||
UnityEngine.GUI.EndScrollView();
|
||||
}
|
||||
|
||||
private float CalculateContentWidth()
|
||||
{
|
||||
var maxItemWidth = 0f;
|
||||
for (var i = 0; i < m_ContentProvider.Count; ++i)
|
||||
{
|
||||
var itemContent = new GUIContent(m_ContentProvider.GetName(i));
|
||||
var itemWidth = Styles.MenuItem.CalcSize(itemContent).x;
|
||||
maxItemWidth = Mathf.Max(itemWidth, maxItemWidth);
|
||||
}
|
||||
|
||||
return maxItemWidth;
|
||||
}
|
||||
|
||||
private float CalculateContentHeight()
|
||||
{
|
||||
return m_ContentProvider.Count * Styles.LineHeight
|
||||
+ m_ContentProvider.SeparatorIndices.Length * Styles.SeparatorHeight
|
||||
+ Styles.TopMargin + Styles.BottomMargin;
|
||||
}
|
||||
|
||||
private float DrawSeparator(int i, Rect itemRect)
|
||||
{
|
||||
if (Array.IndexOf(m_ContentProvider.SeparatorIndices, i) < 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
var separatorRect = GetSeparatorRect(itemRect);
|
||||
DrawRect(separatorRect, Styles.SeparatorColor);
|
||||
return Styles.SeparatorHeight;
|
||||
}
|
||||
|
||||
private static Rect GetSeparatorRect(Rect itemRect)
|
||||
{
|
||||
var x = itemRect.x + Styles.SeparatorMargin;
|
||||
var y = itemRect.y + itemRect.height + Styles.SeparatorHeight * 0.15f;
|
||||
var width = itemRect.width - 2 * Styles.SeparatorMargin;
|
||||
const float height = 1f;
|
||||
|
||||
return new Rect(x, y, width, height);
|
||||
}
|
||||
|
||||
private static void DrawRect(Rect rect, Color color)
|
||||
{
|
||||
var originalColor = UnityEngine.GUI.color;
|
||||
UnityEngine.GUI.color *= color;
|
||||
UnityEngine.GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
|
||||
UnityEngine.GUI.color = originalColor;
|
||||
}
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public const float LineHeight = EditorGUI.kSingleLineHeight;
|
||||
public const float TopMargin = 3f;
|
||||
public const float BottomMargin = 1f;
|
||||
public const float SeparatorHeight = 4f;
|
||||
public const float SeparatorMargin = 3f;
|
||||
public static readonly GUIStyle MenuItem = "MenuItem";
|
||||
public static readonly Color SeparatorColor = EditorGUIUtility.isProSkin
|
||||
? new Color(0.32f, 0.32f, 0.32f, 1.333f)
|
||||
: new Color(0.6f, 0.6f, 0.6f, 1.333f);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8f9b43d4e034c2d997f4eb6a0d85a96
|
||||
timeCreated: 1600072477
|
@@ -0,0 +1,141 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0138170d24533e47b8e6c250c6d7fbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal interface IAssetsDatabaseHelper
|
||||
{
|
||||
void OpenAssetInItsDefaultExternalEditor(string assetPath, int line);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 208e46d59ff6e304db0318377d20f5a1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal interface IGuiHelper
|
||||
{
|
||||
bool OpenScriptInExternalEditor(string stacktrace);
|
||||
void OpenScriptInExternalEditor(Type type, MethodInfo method);
|
||||
IFileOpenInfo GetFileOpenInfo(Type type, MethodInfo method);
|
||||
string FilePathToAssetsRelativeAndUnified(string filePath);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd57cf917f61bbb42b8f030436426ddd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 92e3104769da143888a712cdea27d950
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f47c66e80010e4020b6803b930eb432c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f03c073fcc564ab582ac38999beb4b6d
|
||||
timeCreated: 1603203112
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d92c578e28043ef95d4b703e008be64
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 409ee14a63784dc0ab5d002081211814
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32829ea9e75c475295f73ff867e2f9d0
|
||||
timeCreated: 1603203107
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cd0deb81d984e58952ccd7e1dd6b2bb
|
||||
timeCreated: 1603203104
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5f4a89476c1448abc7e0a9719b13b36
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7f72702c2f04b999739380ef9c0de5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c702cb84a2a4576bf275a76bc17f8e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bace19a170f47bb8d19645cfc580796
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07ea0326ed848fb4489187cb58f96113
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEditor.TestTools.TestRunner.Api;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestRunner.NUnitExtensions.Filters;
|
||||
|
||||
namespace UnityEditor.TestTools.TestRunner.GUI
|
||||
{
|
||||
internal class TestTreeViewBuilder
|
||||
{
|
||||
internal struct TestCount
|
||||
{
|
||||
public int TotalTestCount;
|
||||
public int TotalFailedTestCount;
|
||||
}
|
||||
|
||||
public List<TestRunnerResult> results = new List<TestRunnerResult>();
|
||||
public readonly Dictionary<string, TestTreeViewItem> m_treeFiltered = new Dictionary<string, TestTreeViewItem>();
|
||||
private readonly Dictionary<string, TestRunnerResult> m_OldTestResults;
|
||||
private readonly TestRunnerUIFilter m_UIFilter;
|
||||
private readonly ITestAdaptor[] m_TestListRoots;
|
||||
private readonly Dictionary<string, List<TestRunnerResult>> m_ChildrenResults;
|
||||
private readonly bool m_runningOnPlatform;
|
||||
|
||||
private readonly List<string> m_AvailableCategories = new List<string>();
|
||||
|
||||
public string[] AvailableCategories
|
||||
{
|
||||
get { return m_AvailableCategories.Distinct().OrderBy(a => a).ToArray(); }
|
||||
}
|
||||
|
||||
public TestTreeViewBuilder(ITestAdaptor[] tests, Dictionary<string, TestRunnerResult> oldTestResultResults, TestRunnerUIFilter uiFilter, bool runningOnPlatform)
|
||||
{
|
||||
m_AvailableCategories.Add(CategoryFilterExtended.k_DefaultCategory);
|
||||
m_OldTestResults = oldTestResultResults;
|
||||
m_ChildrenResults = new Dictionary<string, List<TestRunnerResult>>();
|
||||
m_TestListRoots = tests;
|
||||
m_UIFilter = uiFilter;
|
||||
m_runningOnPlatform = runningOnPlatform;
|
||||
}
|
||||
|
||||
public TreeViewItem BuildTreeView()
|
||||
{
|
||||
m_treeFiltered.Clear();
|
||||
var rootItem = new TreeViewItem(int.MaxValue, 0, null, "Invisible Root Item");
|
||||
foreach (var testRoot in m_TestListRoots)
|
||||
{
|
||||
ParseTestTree(0, rootItem, testRoot);
|
||||
}
|
||||
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
private bool IsFilteredOutByUIFilter(ITestAdaptor test, TestRunnerResult result)
|
||||
{
|
||||
if (m_UIFilter.PassedHidden && result.resultStatus == TestRunnerResult.ResultStatus.Passed)
|
||||
return true;
|
||||
if (m_UIFilter.FailedHidden && (result.resultStatus == TestRunnerResult.ResultStatus.Failed || result.resultStatus == TestRunnerResult.ResultStatus.Inconclusive))
|
||||
return true;
|
||||
if (m_UIFilter.NotRunHidden && (result.resultStatus == TestRunnerResult.ResultStatus.NotRun || result.resultStatus == TestRunnerResult.ResultStatus.Skipped))
|
||||
return true;
|
||||
if (!string.IsNullOrEmpty(m_UIFilter.m_SearchString) && result.FullName.IndexOf(m_UIFilter.m_SearchString, StringComparison.InvariantCultureIgnoreCase) < 0)
|
||||
return true;
|
||||
if (m_UIFilter.CategoryFilter.Length > 0)
|
||||
return !test.Categories.Any(category => m_UIFilter.CategoryFilter.Contains(category));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private TestCount ParseTestTree(int depth, TreeViewItem rootItem, ITestAdaptor testElement)
|
||||
{
|
||||
if (testElement == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var testCount = new TestCount();
|
||||
|
||||
m_AvailableCategories.AddRange(testElement.Categories);
|
||||
|
||||
var testElementId = testElement.UniqueName;
|
||||
if (!testElement.HasChildren)
|
||||
{
|
||||
m_OldTestResults.TryGetValue(testElementId, out var result);
|
||||
|
||||
if (result != null && !m_runningOnPlatform &&
|
||||
(result.ignoredOrSkipped
|
||||
|| result.notRunnable
|
||||
|| testElement.RunState == RunState.NotRunnable
|
||||
|| testElement.RunState == RunState.Ignored
|
||||
|| testElement.RunState == RunState.Skipped
|
||||
)
|
||||
)
|
||||
{
|
||||
// if the test was or becomes ignored or not runnable, we recreate the result in case it has changed
|
||||
// It does not apply if we are running on a platform, as evaluation of runstate needs to be evaluated on the player.
|
||||
result = null;
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new TestRunnerResult(testElement);
|
||||
}
|
||||
results.Add(result);
|
||||
|
||||
var test = new TestTreeViewItem(testElement, depth, rootItem);
|
||||
if (!IsFilteredOutByUIFilter(testElement, result))
|
||||
{
|
||||
rootItem.AddChild(test);
|
||||
if (!m_treeFiltered.ContainsKey(test.FullName))
|
||||
m_treeFiltered.Add(test.FullName, test);
|
||||
}
|
||||
else
|
||||
{
|
||||
return testCount;
|
||||
}
|
||||
test.SetResult(result);
|
||||
testCount.TotalTestCount = 1;
|
||||
testCount.TotalFailedTestCount = result.resultStatus == TestRunnerResult.ResultStatus.Failed ? 1 : 0;
|
||||
if (m_ChildrenResults != null && testElement.Parent != null)
|
||||
{
|
||||
m_ChildrenResults.TryGetValue(testElement.ParentUniqueName, out var resultList);
|
||||
if (resultList != null)
|
||||
{
|
||||
resultList.Add(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
resultList = new List<TestRunnerResult> {result};
|
||||
m_ChildrenResults.Add(testElement.ParentUniqueName, resultList);
|
||||
}
|
||||
}
|
||||
|
||||
return testCount;
|
||||
}
|
||||
|
||||
var groupResult = new TestRunnerResult(testElement);
|
||||
results.Add(groupResult);
|
||||
var group = new TestTreeViewItem(testElement, depth, rootItem);
|
||||
|
||||
depth++;
|
||||
|
||||
foreach (var child in testElement.Children)
|
||||
{
|
||||
var childTestCount = ParseTestTree(depth, group, child);
|
||||
|
||||
testCount.TotalTestCount += childTestCount.TotalTestCount;
|
||||
testCount.TotalFailedTestCount += childTestCount.TotalFailedTestCount;
|
||||
}
|
||||
|
||||
|
||||
if (testElement.IsTestAssembly && !testElement.HasChildren)
|
||||
{
|
||||
return testCount;
|
||||
}
|
||||
|
||||
if (group.hasChildren)
|
||||
rootItem.AddChild(group);
|
||||
|
||||
group.TotalChildrenCount = testCount.TotalTestCount;
|
||||
group.TotalSuccessChildrenCount = testCount.TotalFailedTestCount;
|
||||
groupResult.CalculateParentResult(testElementId, m_ChildrenResults);
|
||||
group.SetResult(groupResult);
|
||||
|
||||
return testCount;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user