Files
FlaxEngine/Source/Engine/Debug/Assert.cs
2024-02-26 19:00:48 +01:00

393 lines
16 KiB
C#

// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
#pragma warning disable 1591
namespace FlaxEngine.Assertions
{
/// <summary>
/// The Assert class contains assertion methods for setting invariants in the code.
/// </summary>
[DebuggerStepThrough]
[HideInEditor]
public static class Assert
{
/// <summary>
/// Should an exception be thrown on a failure.
/// </summary>
public static bool RaiseExceptions = true;
/// <summary>
/// Asserts that the values are approximately equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// Note: Every time you call the method with tolerance specified, a new instance of Assertions.Comparers.FloatComparer
/// is created. For performance reasons you might want to instance your own comparer and pass it to the AreEqual method.
/// If the tolerance is not specifies, a default comparer is used and the issue does not occur.
/// </summary>
/// <param name="expected"></param>
/// <param name="actual"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreApproximatelyEqual(float expected, float actual)
{
AreEqual(expected, actual, null, FloatComparer.ComparerWithDefaultTolerance);
}
/// <summary>
/// Asserts that the values are approximately equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// Note: Every time you call the method with tolerance specified, a new instance of Assertions.Comparers.FloatComparer
/// is created. For performance reasons you might want to instance your own comparer and pass it to the AreEqual method.
/// If the tolerance is not specifies, a default comparer is used and the issue does not occur.
/// </summary>
/// <param name="expected"></param>
/// <param name="actual"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreApproximatelyEqual(float expected, float actual, string message)
{
AreEqual(expected, actual, message, FloatComparer.ComparerWithDefaultTolerance);
}
/// <summary>
/// Asserts that the values are approximately equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// Note: Every time you call the method with tolerance specified, a new instance of Assertions.Comparers.FloatComparer
/// is created. For performance reasons you might want to instance your own comparer and pass it to the AreEqual method.
/// If the tolerance is not specifies, a default comparer is used and the issue does not occur.
/// </summary>
/// <param name="tolerance">Tolerance of approximation.</param>
/// <param name="expected"></param>
/// <param name="actual"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreApproximatelyEqual(float expected, float actual, float tolerance)
{
AreApproximatelyEqual(expected, actual, tolerance, null);
}
/// <summary>
/// Asserts that the values are approximately equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// Note: Every time you call the method with tolerance specified, a new instance of Assertions.Comparers.FloatComparer
/// is created. For performance reasons you might want to instance your own comparer and pass it to the AreEqual method.
/// If the tolerance is not specifies, a default comparer is used and the issue does not occur.
/// </summary>
/// <param name="tolerance">Tolerance of approximation.</param>
/// <param name="expected"></param>
/// <param name="actual"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreApproximatelyEqual(float expected, float actual, float tolerance, string message)
{
AreEqual(expected, actual, message, new FloatComparer(tolerance));
}
/// <summary>
/// Asserts that the values are approximately equal.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="expected">The expected value.</param>
/// <param name="actual">The actual value.</param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreEqual<T>(T expected, T actual)
{
AreEqual(expected, actual, null);
}
/// <summary>
/// Asserts that the values are approximately equal.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="expected">The expected value.</param>
/// <param name="actual">The actual value.</param>
/// <param name="message">The error message.</param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreEqual<T>(T expected, T actual, string message)
{
AreEqual(expected, actual, message, EqualityComparer<T>.Default);
}
/// <summary>
/// Asserts that the values are approximately equal.
/// </summary>
/// <typeparam name="T">The value type.</typeparam>
/// <param name="expected">The expected value.</param>
/// <param name="actual">The actual value.</param>
/// <param name="message">The error message.</param>
/// <param name="comparer">The equality comparer.</param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreEqual<T>(T expected, T actual, string message, IEqualityComparer<T> comparer)
{
if (typeof(Object).IsAssignableFrom(typeof(T)))
{
AreEqual((object)expected as Object, (object)actual as Object, message);
return;
}
if (!comparer.Equals(actual, expected))
Fail(GetEqualityMessage(actual, expected, true), message);
}
/// <summary>
/// Asserts that the values are approximately equal.
/// </summary>
/// <param name="expected">The expected value.</param>
/// <param name="actual">The actual value.</param>
/// <param name="message">The error message.</param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreEqual(Object expected, Object actual, string message)
{
if (actual != expected)
Fail(GetEqualityMessage(actual, expected, true), message);
}
/// <summary>
/// Asserts that the values are approximately not equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// </summary>
/// <param name="expected"></param>
/// <param name="actual"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotApproximatelyEqual(float expected, float actual)
{
AreNotEqual(expected, actual, null, FloatComparer.ComparerWithDefaultTolerance);
}
/// <summary>
/// Asserts that the values are approximately not equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// </summary>
/// <param name="expected"></param>
/// <param name="actual"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotApproximatelyEqual(float expected, float actual, string message)
{
AreNotEqual(expected, actual, message, FloatComparer.ComparerWithDefaultTolerance);
}
/// <summary>
/// Asserts that the values are approximately not equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// </summary>
/// <param name="tolerance">Tolerance of approximation.</param>
/// <param name="expected"></param>
/// <param name="actual"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotApproximatelyEqual(float expected, float actual, float tolerance)
{
AreNotApproximatelyEqual(expected, actual, tolerance, null);
}
/// <summary>
/// Asserts that the values are approximately not equal. An absolute error check is used for approximate equality check
/// (|a-b| &lt; tolerance). Default tolerance is 0.00001f.
/// </summary>
/// <param name="tolerance">Tolerance of approximation.</param>
/// <param name="expected"></param>
/// <param name="actual"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotApproximatelyEqual(float expected, float actual, float tolerance, string message)
{
AreNotEqual(expected, actual, message, new FloatComparer(tolerance));
}
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotEqual<T>(T expected, T actual)
{
AreNotEqual(expected, actual, null);
}
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotEqual<T>(T expected, T actual, string message)
{
AreNotEqual(expected, actual, message, EqualityComparer<T>.Default);
}
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotEqual<T>(T expected, T actual, string message, IEqualityComparer<T> comparer)
{
if (typeof(Object).IsAssignableFrom(typeof(T)))
{
AreNotEqual((object)expected as Object, (object)actual as Object, message);
return;
}
if (comparer.Equals(actual, expected))
Fail(GetEqualityMessage(actual, expected, false), message);
}
[Conditional("FLAX_ASSERTIONS")]
public static void AreNotEqual(Object expected, Object actual, string message)
{
if (actual == expected)
Fail(GetEqualityMessage(actual, expected, false), message);
}
public static void Fail(string message = "", string userMessage = "")
{
if (RaiseExceptions)
throw new AssertionException(message, userMessage);
if (message == null)
message = "Assertion has failed\n";
if (userMessage != null)
message = string.Concat(userMessage, '\n', message);
Debug.LogAssertion(message);
}
/// <summary>
/// Asserts that the condition is false.
/// </summary>
/// <param name="condition"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void IsFalse(bool condition)
{
IsFalse(condition, null);
}
/// <summary>
/// Asserts that the condition is false.
/// </summary>
/// <param name="condition"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void IsFalse(bool condition, string message)
{
if (condition)
Fail(BooleanFailureMessage(false), message);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNotNull<T>(T value)
where T : class
{
IsNotNull(value, null);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNotNull<T>(T value, string message)
where T : class
{
if (typeof(Object).IsAssignableFrom(typeof(T)))
IsNotNull((object)value as Object, message);
else if (value == null)
Fail(NullFailureMessage(null, false), message);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNotNull(Object value, string message)
{
if (value == null)
Fail(NullFailureMessage(null, false), message);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNull<T>(T value)
where T : class
{
IsNull(value, null);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNull<T>(T value, string message)
where T : class
{
if (typeof(Object).IsAssignableFrom(typeof(T)))
IsNull((object)value as Object, message);
else if (value != null)
Fail(NullFailureMessage(value, true), message);
}
[Conditional("FLAX_ASSERTIONS")]
public static void IsNull(Object value, string message)
{
if (value != null)
Fail(NullFailureMessage(value, true), message);
}
/// <summary>
/// Asserts that the condition is true.
/// </summary>
/// <param name="condition"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void IsTrue(bool condition)
{
IsTrue(condition, null);
}
/// <summary>
/// Asserts that the condition is true.
/// </summary>
/// <param name="condition"></param>
/// <param name="message"></param>
[Conditional("FLAX_ASSERTIONS")]
public static void IsTrue(bool condition, string message)
{
if (!condition)
Fail(BooleanFailureMessage(true), message);
}
/// <summary>
/// Expect action to fail
/// </summary>
/// <param name="exceptionType">Type of exception to expect</param>
/// <param name="action">Action to expect</param>
/// <param name="message">User custom message to display</param>
[Conditional("FLAX_ASSERTIONS")]
public static void ExceptionExpected(Type exceptionType, Action action, string message = "")
{
try
{
action();
}
catch (Exception e)
{
if (exceptionType != e.GetType())
{
Fail(GetMessage("Expected exception of type " + exceptionType.FullName + " got " + e.GetType().FullName), message);
}
return;
}
Fail(GetMessage("Expected exception of type " + exceptionType.FullName), message);
}
public static string BooleanFailureMessage(bool expected)
{
return GetMessage(string.Concat("Value was ", !expected), expected.ToString());
}
public static string GetEqualityMessage(object actual, object expected, bool expectEqual)
{
string str = string.Format("Values are {0}equal.", new object[] { !expectEqual ? string.Empty : "not " });
object[] objArray =
{
actual,
expected,
null
};
objArray[2] = !expectEqual ? "!=" : "==";
return GetMessage(str, string.Format("{0} {2} {1}", objArray));
}
public static string GetMessage(string failureMessage)
{
return string.Format("{0} {1}", new object[]
{
"Assertion failed.",
failureMessage
});
}
public static string GetMessage(string failureMessage, string expected)
{
return GetMessage($"{failureMessage}\nExpected: {expected}");
}
public static string NullFailureMessage(object value, bool expectNull)
{
return GetMessage(string.Format("Value was {0}Null", new object[] { !expectNull ? string.Empty : "not " }), string.Format("Value was {0}Null", new object[] { !expectNull ? "not " : string.Empty }));
}
}
}