-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
## [1.4.1] - 2023-11-27 - Multiple improvements to the UI, including better dropdowns, filtering, and a new test list view for Player. - Fixed uncategorized UI tests filtering for parameterized tests (DSTR-219). - In async tests, any failing logs will now first be evaluated after the async method has completed. (DSTR-839)
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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."); | ||
} | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; } | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.