/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * Licensed under the Oculus SDK License Agreement (the "License"); * you may not use the Oculus SDK except in compliance with the License, * which is provided at the time of installation or download, or which * otherwise accompanies this software in either electronic or hard copy form. * * You may obtain a copy of the License at * * https://developer.oculus.com/licenses/oculussdk/ * * Unless required by applicable law or agreed to in writing, the Oculus SDK * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.Assertions; namespace Oculus.Interaction { public static class AssertUtils { public const string HiglightColor = "#3366ff"; /// /// Asserts that the expression is True. /// In case of failure, it will print an error showing where the failure happened. /// /// The component to which the value expression belongs /// The expression that should be true /// Optional parameter specifying the reason the assert failed /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component). /// Optional parameter suggesting how to fix the problem. [Conditional("UNITY_ASSERTIONS")] public static void AssertIsTrue(this Component component, bool value, string whyItFailed = null, string whereItFailed = null, string howToFix = null) { string gameObjectName = component.name; string componentName = component.GetType().Name; Assert.IsTrue(value, (whereItFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? "") + (howToFix ?? "")); } /// /// Asserts that an Aspect exists. /// In case of failure, it will print an error showing why it failed, where it failed /// and suggest how to fix it. /// /// The type of Aspect to be checked /// The component requiring this Aspect /// The Aspect to be checked /// The expected location of the Aspect /// Optional parameter specifying the reason the assert failed. /// If none is provided it will print the expected aspect type and location. /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component). /// Optional parameter suggesting how to fix the problem. /// If none is provided it will suggest assigning an Aspect of the required type to the expected location. [Conditional("UNITY_ASSERTIONS")] public static void AssertAspect(this Component component, TValue aspect, string aspectLocation, string whyItFailed = null, string whereFailed = null, string howToFix = null) where TValue : class { string gameObjectName = component.name; string componentName = component.GetType().Name; string niceVariableName = Nicify(aspectLocation); string aspectType = typeof(TValue).Name; Assert.IsNotNull(aspect, (whereFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? $"Could not find the required Aspect {aspectType} in the associated {niceVariableName}. ") + (howToFix ?? $"Assign an Aspect of type {aspectType} to the associated {niceVariableName}.")); } /// /// Asserts that a Serialized Field in a Component is not null. /// In case of failure, it will print an error showing why it failed, where it failed /// and suggest how to fix it. /// /// The type of field to be checked /// The component to which this field belongs. /// The value of the field. /// The printed name of the serialized field. /// Optional parameter specifying the reason the assert failed. /// If none is provided it will indicate that the field value was null. /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component->Field Name). /// Optional parameter suggesting how to fix the problem. /// If none is provided it will suggest assigning a value of the required type to the field. [Conditional("UNITY_ASSERTIONS")] public static void AssertField(this Component component, TValue value, string variableName, string whyItFailed = null, string whereItFailed = null, string howToFix = null) where TValue : class { string gameObjectName = component.name; string componentName = component.GetType().Name; string niceVariableName = Nicify(variableName); string variableType = typeof(TValue).Name; Assert.IsNotNull(value, (whereItFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? $"Required {niceVariableName} reference is missing. ") + (howToFix ?? $"Assign a {variableType} to the field {niceVariableName}.")); } /// /// Asserts that a Serialized collection in a Component is not null, is not empty and /// all of its items exist. /// In case of failure, it will print an error showing why it failed, where it failed /// and suggest how to fix it. /// /// The type of the items in the collection. /// The component to which this collection belongs. /// The value of the collection. /// The printed name of the serialized collection. /// Optional parameter specifying the reason the assert failed. /// If none is provided it will indicate that the collection value needs at least one valid item. /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component->Collection Name). /// Optional parameter suggesting how to fix the problem. /// If none is provided it will suggest assigning at least one valid item of the required type to the collection. [Conditional("UNITY_ASSERTIONS")] public static void AssertCollectionField(this Component component, IEnumerable value, string variableName, string whyItFailed = null, string whereFailed = null, string howToFix = null) { string gameObjectName = component.name; string componentName = component.GetType().Name; string niceVariableName = Nicify(variableName); string variableType = typeof(TValue).Name; Assert.IsTrue(value != null && value.Count() > 0, (whereFailed ?? $"At GameObject {gameObjectName}: the {componentName} component has an missing or empty {niceVariableName} collection. ") + (whyItFailed ?? "") + (howToFix ?? $"Assign at least one {variableType} to the collection {niceVariableName}. ")); component.AssertCollectionItems(value, variableName); } /// /// Asserts that each item in a Serialized collection in a Component is not null. /// Note that the collection it might contain 0 items. /// In case of failure, it will print an error showing why it failed, where it failed /// and suggest how to fix it. /// /// The type of the items in the collection /// The component to which the collection belongs. /// The value of the collection. /// The printed name of the serialized collection. /// Optional parameter specifying the reason the assert failed. /// If none is provided it will indicate, for each failed item, that the item must not be null. /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component->Collection Name). /// Optional parameter suggesting how to fix the problem. /// If none is provided it will suggest assigning a valid item of the required type to the collection at each missing index. [Conditional("UNITY_ASSERTIONS")] public static void AssertCollectionItems(this Component component, IEnumerable value, string variableName, string whyItFailed = null, string whereItFailed = null, string howToFix = null) { string gameObjectName = component.name; string componentName = component.GetType().Name; string niceVariableName = Nicify(variableName); string variableType = typeof(TValue).Name; int index = 0; foreach (TValue item in value) { Assert.IsFalse(item is null, (whereItFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? $"Invalid item in the collection {niceVariableName} at index {index}. ") + (howToFix ?? $"Assign a {variableType} to the collection {niceVariableName} at index {index}. ")); index++; } } /// /// Shows a Warning for each item in a Serialized collection in a Component that is null. /// It works just for UnityEngine.Object collections, /// Note that the collection it might contain 0 items. /// In case of failure, it will print a warning showing why it failed, where it failed /// and suggest how to fix it. /// /// The component to which the collection belongs. /// The value of the collection. /// The printed name of the serialized collection. /// Optional parameter specifying the reason the assert failed. /// If none is provided it will indicate, for each failed item, that the item must not be null. /// Optional parameter specifying where the failure can be found. /// If none is provided it will print the component address (GameObject->Component->Collection Name). /// Optional parameter suggesting how to fix the problem. /// If none is provided it will suggest assigning a valid item of the required type to the collection at each missing index. [Conditional("UNITY_ASSERTIONS")] public static void WarnInspectorCollectionItems(this Component component, IEnumerable value, string variableName, string whyItFailed = null, string whereItFailed = null, string howToFix = null) { string gameObjectName = component.name; string componentName = component.GetType().Name; string niceVariableName = Nicify(variableName); string variableType = typeof(UnityEngine.Object).Name; int index = 0; foreach (UnityEngine.Object item in value) { string message = (whereItFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? $"Invalid item in the collection {niceVariableName} at index {index}. ") + (howToFix ?? $"Assign a {variableType} to the collection {niceVariableName} at index {index} or remove the entry. "); if (item == null) { UnityEngine.Debug.LogWarning(message, component); } index++; } } /// /// Displays a generic warning using the same identifiable messages as the Asserts /// /// Component throwing the warning /// Variable that failed /// The failure reason, empty by default. /// The location of the failure. Points to the component by default /// How to fix the issue, empty by default. [Conditional("UNITY_ASSERTIONS")] public static void LogWarning(this Component component, string whyItFailed = null, string whereItFailed = null, string howToFix = null) { string gameObjectName = component.name; string componentName = component.GetType().Name; UnityEngine.Debug.LogWarning( (whereItFailed ?? $"At GameObject {gameObjectName}, component {componentName}. ") + (whyItFailed ?? string.Empty) + (howToFix ?? string.Empty) , component); } /// /// Make a displayable name for a variable. /// This function will insert spaces before capital letters and remove optional m_, _ or k followed by uppercase letter in front of the name. /// /// The variable name as used in code /// The nicified variable public static string Nicify(string variableName) { variableName = Regex.Replace(variableName, "_([a-z])", match => match.Value.ToUpper(), RegexOptions.Compiled); variableName = Regex.Replace(variableName, "m_|_", " ", RegexOptions.Compiled); variableName = Regex.Replace(variableName, "k([A-Z])", "$1", RegexOptions.Compiled); variableName = Regex.Replace(variableName, "([A-Z])", " $1", RegexOptions.Compiled); variableName = variableName.Trim(); return variableName; } } }