using NUnit.Framework; using System; using System.Collections.Generic; using System.Linq; using UnityEditor.XR.OpenXR.Features; using static UnityEditor.XR.OpenXR.Features.OpenXRFeatureSetManager; namespace UnityEditor.XR.OpenXR.Tests { internal static class OpenXREditorTestHelpers { public delegate bool FeatureInfoPredicate(FeatureHelpersInternal.FeatureInfo featureInfo); public static bool FeatureEnabled(FeatureHelpersInternal.FeatureInfo featureInfo) => featureInfo.Feature.enabled; public static bool FeatureDisabled(FeatureHelpersInternal.FeatureInfo featureInfo) => !featureInfo.Feature.enabled; private static Dictionary s_FeatureInfos; private static BuildTargetGroup[] s_BuildTargetGroups = ((BuildTargetGroup[])Enum.GetValues(typeof(BuildTargetGroup))).Distinct().ToArray(); /// /// Return the distinct list of build target groups to test /// public static BuildTargetGroup[] GetBuildTargetGroups() => s_BuildTargetGroups; /// /// Clear the FeatureInfo cache /// public static void ClearFeatureInfos() { s_FeatureInfos = null; } /// /// Helper function to retrieve the cached FeatureInfos for a given build target group /// /// Build target group /// Array of FeatureInfos for the build target group public static FeatureHelpersInternal.FeatureInfo[] GetFeatureInfos(BuildTargetGroup buildTargetGroup) { if (null == s_FeatureInfos) s_FeatureInfos = new Dictionary(); if (!s_FeatureInfos.TryGetValue(buildTargetGroup, out var featureInfos)) { featureInfos = FeatureHelpersInternal.GetAllFeatureInfo(buildTargetGroup).Features.ToArray(); s_FeatureInfos[buildTargetGroup] = featureInfos; } return featureInfos; } /// /// Helper function to retrieve a subset of FeatureInfos in . /// /// Build target group /// Specific feature identifiers to retrieve /// Array of FeatureInfos matching request public static FeatureHelpersInternal.FeatureInfo[] GetFeatureInfos(BuildTargetGroup buildTargetGroup, string[] featureIds) { return GetFeatureInfos(buildTargetGroup).Where(f => featureIds.Contains(f.Attribute.FeatureId)).ToArray(); } /// /// Enable or Disable a feature set /// /// Build target group containing feature set /// Identifier of feature /// True to enable, false to disable /// True if the FeatureSet enabled state should be considered changed /// True if the FeatureSet enabled state should be automatically commmitted with `SetFeaturesFromEnabledFeatureSets` public static void EnableFeatureSet(BuildTargetGroup buildTargetGroup, string featureSetId, bool enabled = true, bool changed = true, bool commit = true) { var featureSetInfo = GetFeatureSetInfoWithId(buildTargetGroup, featureSetId); Assert.IsNotNull(featureSetInfo, $"FeatureSetInfo '{featureSetId}' not found "); featureSetInfo.isEnabled = enabled; featureSetInfo.wasEnabled = changed ? !enabled : enabled; if (commit) SetFeaturesFromEnabledFeatureSets(buildTargetGroup); } /// /// Enable/Disable all feature sets for /// /// Build Target Group /// Enabled state /// True if the feature set should be marked as changed, false if not public static void EnableFeatureSets(BuildTargetGroup buildTargetGroup, bool enabled, bool changed = true, bool commit = true) { foreach (var featureSetInfo in FeatureSetInfosForBuildTarget(buildTargetGroup)) { featureSetInfo.isEnabled = enabled; featureSetInfo.wasEnabled = changed ? !enabled : enabled; } if (commit) SetFeaturesFromEnabledFeatureSets(buildTargetGroup); } public static void EnableFeatureInfos(BuildTargetGroup buildTargetGroup, string[] featureIds, bool enable, FeatureInfoPredicate predicate = null) { foreach (var featureInfo in GetFeatureInfos(buildTargetGroup, featureIds)) { if (null == predicate || predicate(featureInfo)) featureInfo.Feature.enabled = enable; } } public static void EnableFeatureInfos(BuildTargetGroup buildTargetGroup, bool enable, FeatureInfoPredicate predicate = null) { foreach (var featureInfo in GetFeatureInfos(buildTargetGroup)) { if (null == predicate || predicate(featureInfo)) featureInfo.Feature.enabled = enable; } } /// /// Builds a comma separated list of features that pass the within /// /// Feature infos /// Delegate used to check status /// Optional list of feature identifiers exclude /// Comma separated list of features public static string FeaturesToString(FeatureHelpersInternal.FeatureInfo[] featureInfos, FeatureInfoPredicate check, string[] exclude = null) { return string.Join(",", featureInfos .Where(f => (exclude == null || !exclude.Contains(f.Attribute.FeatureId)) && check(f)) .Select(f => f.Attribute.FeatureId)); } /// /// Builds a comma separated list of features from in that pass the /// /// Build Target Group /// Feature ids to include /// Delegate used to check status /// Comma separated list of features public static string FeaturesToString(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check) { return FeaturesToString(GetFeatureInfos(buildTargetGroup).Where(f => featureIds.Contains(f.Attribute.FeatureId)).ToArray(), check); } /// /// Builds a comma separated list of features identifier that pass the within /// /// Build Target Group /// Delegate used to check status /// Optional list of feature identifiers to exclude from the list /// Comma separated list of features public static string FeaturesToString(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check, string[] exclude = null) { return FeaturesToString(GetFeatureInfos(buildTargetGroup), check, exclude: exclude); } /// /// Checks if all features for the pass the . /// /// Build Target Group /// Delegate used to check status /// True if the check passes, false if not public static bool CheckAllFeatures(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check) { return GetFeatureInfos(buildTargetGroup).All(f => check(f)); } /// /// Check that all features in in match the pass the . /// /// Build target group /// Features to check /// Delegate used to check status /// True if the check passes, false if not public static bool CheckAllFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check) { return GetFeatureInfos(buildTargetGroup).All(f => !featureIds.Contains(f.Attribute.FeatureId) || check(f)); } /// /// Check that only features in for pass the . /// /// Build target group /// Features to check /// Delegate used to check a FeatureInfo /// True if the check passes, false if not public static bool CheckOnlyFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check) { return GetFeatureInfos(buildTargetGroup).All(f => featureIds.Contains(f.Attribute.FeatureId) == check(f)); } private static string AssertFeaturesMessage(string features) => $"The following features failed the check: {features}"; public static void AssertOnlyFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check) { Assert.IsTrue( CheckOnlyFeatures(buildTargetGroup, featureIds, check), AssertFeaturesMessage(FeaturesToString(buildTargetGroup, featureIds, (f) => !check(f)))); } public static void AssertAllFeatures(BuildTargetGroup buildTargetGroup, string[] featureIds, FeatureInfoPredicate check) { Assert.IsTrue( CheckAllFeatures(buildTargetGroup, featureIds, check), AssertFeaturesMessage(FeaturesToString(buildTargetGroup, featureIds, (f) => !check(f)))); } public static void AssertAllFeatures(BuildTargetGroup buildTargetGroup, FeatureInfoPredicate check) { Assert.IsTrue( CheckAllFeatures(buildTargetGroup, check), AssertFeaturesMessage(FeaturesToString(buildTargetGroup, (f) => !check(f)))); } public static void AssertFeatureEnabled(FeatureHelpersInternal.FeatureInfo featureInfo, bool enabled = true) { Assert.IsTrue(featureInfo.Feature.enabled == enabled, $"{featureInfo.Attribute.FeatureId} should be {(enabled ? "enabled" : "disabled")}"); } public static bool FeatureIsOptional(FeatureSetInfo featureSet, FeatureHelpersInternal.FeatureInfo feature) { return Array.IndexOf(featureSet.featureIds, feature.Attribute.FeatureId) > -1 && (featureSet.requiredFeatureIds == null || Array.IndexOf(featureSet.requiredFeatureIds, feature.Attribute.FeatureId) == -1) && (featureSet.defaultFeatureIds == null || Array.IndexOf(featureSet.defaultFeatureIds, feature.Attribute.FeatureId) == -1); } } }