/*
* 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;
}
}
}