diff --git a/Light.GuardClauses.SingleFile.cs b/Light.GuardClauses.SingleFile.cs index 91c0437..d746365 100644 --- a/Light.GuardClauses.SingleFile.cs +++ b/Light.GuardClauses.SingleFile.cs @@ -3881,6 +3881,94 @@ public static ReadOnlyMemory MustBeLowerCase(this ReadOnlyMemory par return parameter; } + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustBeNegative(this sbyte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter < 0)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustBeNegative(this sbyte parameter, Func exceptionFactory) + { + if (!(parameter < 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustBeNegative(this short parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter < 0)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustBeNegative(this short parameter, Func exceptionFactory) + { + if (!(parameter < 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is negative (less than zero), or otherwise /// throws an . @@ -4275,7 +4363,7 @@ public static TItem MustBeOneOf(this TItem parameter, [NotNu [MethodImpl(MethodImplOptions.AggressiveInlining)] public static sbyte MustBePositive(this sbyte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter > (sbyte)0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -4298,7 +4386,7 @@ public static sbyte MustBePositive(this sbyte parameter, [CallerArgumentExpressi [ContractAnnotation("exceptionFactory:null => halt")] public static sbyte MustBePositive(this sbyte parameter, Func exceptionFactory) { - if (!(parameter > (sbyte)0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -4319,7 +4407,7 @@ public static sbyte MustBePositive(this sbyte parameter, Func [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte MustBePositive(this byte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter > (byte)0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -4342,7 +4430,7 @@ public static byte MustBePositive(this byte parameter, [CallerArgumentExpression [ContractAnnotation("exceptionFactory:null => halt")] public static byte MustBePositive(this byte parameter, Func exceptionFactory) { - if (!(parameter > (byte)0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -4363,7 +4451,7 @@ public static byte MustBePositive(this byte parameter, Func exc [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short MustBePositive(this short parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter > (short)0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -4386,7 +4474,7 @@ public static short MustBePositive(this short parameter, [CallerArgumentExpressi [ContractAnnotation("exceptionFactory:null => halt")] public static short MustBePositive(this short parameter, Func exceptionFactory) { - if (!(parameter > (short)0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -4407,7 +4495,7 @@ public static short MustBePositive(this short parameter, Func [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort MustBePositive(this ushort parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter > (ushort)0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -4430,7 +4518,7 @@ public static ushort MustBePositive(this ushort parameter, [CallerArgumentExpres [ContractAnnotation("exceptionFactory:null => halt")] public static ushort MustBePositive(this ushort parameter, Func exceptionFactory) { - if (!(parameter > (ushort)0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -8300,6 +8388,94 @@ public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T return parameter; } + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustNotBeNegative(this sbyte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter >= 0)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustNotBeNegative(this sbyte parameter, Func exceptionFactory) + { + if (!(parameter >= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustNotBeNegative(this short parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter >= 0)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustNotBeNegative(this short parameter, Func exceptionFactory) + { + if (!(parameter >= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise /// throws an . @@ -8845,7 +9021,7 @@ public static TItem MustNotBeOneOf(this TItem parameter, [No /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int MustNotBePositive(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static sbyte MustNotBePositive(this sbyte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { if (!(parameter <= 0)) { @@ -8868,7 +9044,7 @@ public static int MustNotBePositive(this int parameter, [CallerArgumentExpressio /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static int MustNotBePositive(this int parameter, Func exceptionFactory) + public static sbyte MustNotBePositive(this sbyte parameter, Func exceptionFactory) { if (!(parameter <= 0)) { @@ -8889,9 +9065,9 @@ public static int MustNotBePositive(this int parameter, Func exc /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long MustNotBePositive(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static byte MustNotBePositive(this byte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter <= 0L)) + if (!(parameter <= 0)) { Throw.MustNotBePositive(parameter, parameterName, message); } @@ -8912,9 +9088,9 @@ public static long MustNotBePositive(this long parameter, [CallerArgumentExpress /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static long MustNotBePositive(this long parameter, Func exceptionFactory) + public static byte MustNotBePositive(this byte parameter, Func exceptionFactory) { - if (!(parameter <= 0L)) + if (!(parameter <= 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -8933,9 +9109,9 @@ public static long MustNotBePositive(this long parameter, Func /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static decimal MustNotBePositive(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static short MustNotBePositive(this short parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter <= 0m)) + if (!(parameter <= 0)) { Throw.MustNotBePositive(parameter, parameterName, message); } @@ -8956,9 +9132,9 @@ public static decimal MustNotBePositive(this decimal parameter, [CallerArgumentE /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static decimal MustNotBePositive(this decimal parameter, Func exceptionFactory) + public static short MustNotBePositive(this short parameter, Func exceptionFactory) { - if (!(parameter <= 0m)) + if (!(parameter <= 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -8974,12 +9150,12 @@ public static decimal MustNotBePositive(this decimal parameter, FuncThe name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when is greater than zero or NaN. + /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBePositive(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ushort MustNotBePositive(this ushort parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter <= 0f)) + if (!(parameter <= 0)) { Throw.MustNotBePositive(parameter, parameterName, message); } @@ -8996,13 +9172,13 @@ public static float MustNotBePositive(this float parameter, [CallerArgumentExpre /// The delegate that creates your custom exception. is passed to this delegate. /// /// - /// Your custom exception thrown when is greater than zero or NaN. + /// Your custom exception thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static float MustNotBePositive(this float parameter, Func exceptionFactory) + public static ushort MustNotBePositive(this ushort parameter, Func exceptionFactory) { - if (!(parameter <= 0f)) + if (!(parameter <= 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -9018,12 +9194,12 @@ public static float MustNotBePositive(this float parameter, FuncThe name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when is greater than zero or NaN. + /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBePositive(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static int MustNotBePositive(this int parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter <= 0d)) + if (!(parameter <= 0)) { Throw.MustNotBePositive(parameter, parameterName, message); } @@ -9040,13 +9216,13 @@ public static double MustNotBePositive(this double parameter, [CallerArgumentExp /// The delegate that creates your custom exception. is passed to this delegate. /// /// - /// Your custom exception thrown when is greater than zero or NaN. + /// Your custom exception thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static double MustNotBePositive(this double parameter, Func exceptionFactory) + public static int MustNotBePositive(this int parameter, Func exceptionFactory) { - if (!(parameter <= 0d)) + if (!(parameter <= 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -9062,12 +9238,12 @@ public static double MustNotBePositive(this double parameter, FuncThe name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when is greater than . + /// Thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TimeSpan MustNotBePositive(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static uint MustNotBePositive(this uint parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!(parameter <= TimeSpan.Zero)) + if (!(parameter <= 0U)) { Throw.MustNotBePositive(parameter, parameterName, message); } @@ -9084,13 +9260,13 @@ public static TimeSpan MustNotBePositive(this TimeSpan parameter, [CallerArgumen /// The delegate that creates your custom exception. is passed to this delegate. /// /// - /// Your custom exception thrown when is greater than . + /// Your custom exception thrown when is greater than zero. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("exceptionFactory:null => halt")] - public static TimeSpan MustNotBePositive(this TimeSpan parameter, Func exceptionFactory) + public static uint MustNotBePositive(this uint parameter, Func exceptionFactory) { - if (!(parameter <= TimeSpan.Zero)) + if (!(parameter <= 0U)) { Throw.CustomException(exceptionFactory, parameter); } @@ -9099,39 +9275,303 @@ public static TimeSpan MustNotBePositive(this TimeSpan parameter, Func - /// Ensures that and do not point to the same object instance, or otherwise - /// throws a . + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . /// - /// The first reference to be checked. - /// The second reference to be checked. + /// The value to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when both and point to the same object. + /// + /// Thrown when is greater than zero. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class + public static long MustNotBePositive(this long parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (ReferenceEquals(parameter, other)) + if (!(parameter <= 0L)) { - Throw.SameObjectReference(parameter, parameterName, message); + Throw.MustNotBePositive(parameter, parameterName, message); } return parameter; } /// - /// Ensures that and do not point to the same object instance, or otherwise + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise /// throws your custom exception. /// - /// The first reference to be checked. - /// The second reference to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Thrown when both and point to the same object. + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) - where T : class + [ContractAnnotation("exceptionFactory:null => halt")] + public static long MustNotBePositive(this long parameter, Func exceptionFactory) { - if (ReferenceEquals(parameter, other)) + if (!(parameter <= 0L)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong MustNotBePositive(this ulong parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter <= 0UL)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ulong MustNotBePositive(this ulong parameter, Func exceptionFactory) + { + if (!(parameter <= 0UL)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static decimal MustNotBePositive(this decimal parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter <= 0m)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static decimal MustNotBePositive(this decimal parameter, Func exceptionFactory) + { + if (!(parameter <= 0m)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBePositive(this float parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter <= 0f)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static float MustNotBePositive(this float parameter, Func exceptionFactory) + { + if (!(parameter <= 0f)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBePositive(this double parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter <= 0d)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero or NaN. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static double MustNotBePositive(this double parameter, Func exceptionFactory) + { + if (!(parameter <= 0d)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TimeSpan MustNotBePositive(this TimeSpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static TimeSpan MustNotBePositive(this TimeSpan parameter, Func exceptionFactory) + { + if (!(parameter <= TimeSpan.Zero)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that and do not point to the same object instance, or otherwise + /// throws a . + /// + /// The first reference to be checked. + /// The second reference to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when both and point to the same object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class + { + if (ReferenceEquals(parameter, other)) + { + Throw.SameObjectReference(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that and do not point to the same object instance, or otherwise + /// throws your custom exception. + /// + /// The first reference to be checked. + /// The second reference to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Thrown when both and point to the same object. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) + where T : class + { + if (ReferenceEquals(parameter, other)) { Throw.CustomException(exceptionFactory, parameter); } @@ -9235,6 +9675,182 @@ public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this strin return parameter; } + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustNotBeZero(this sbyte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustNotBeZero(this sbyte parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte MustNotBeZero(this byte parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static byte MustNotBeZero(this byte parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustNotBeZero(this short parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustNotBeZero(this short parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort MustNotBeZero(this ushort parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ushort MustNotBeZero(this ushort parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . @@ -9279,6 +9895,50 @@ public static int MustNotBeZero(this int parameter, Func excepti return parameter; } + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint MustNotBeZero(this uint parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0U) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static uint MustNotBeZero(this uint parameter, Func exceptionFactory) + { + if (parameter == 0U) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . @@ -9323,6 +9983,50 @@ public static long MustNotBeZero(this long parameter, Func exce return parameter; } + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong MustNotBeZero(this ulong parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter == 0UL) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ulong MustNotBeZero(this ulong parameter, Func exceptionFactory) + { + if (parameter == 0UL) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . diff --git a/ai-plans/0173-must-be-positive-feature-parity.md b/ai-plans/0173-must-be-positive-feature-parity.md new file mode 100644 index 0000000..09dfdbc --- /dev/null +++ b/ai-plans/0173-must-be-positive-feature-parity.md @@ -0,0 +1,51 @@ +# Integral Sign-Guard Feature Parity + +## Rationale + +Plan 0168 addressed parent issue #162 (point 5) by adding concrete `MustBePositive` overloads for the integral types previously covered only by the modern generic `INumber` API. The other four sign-guard families remain asymmetric: their .NET Standard assets and portable source exports omit smaller signed integral types and, where the predicate remains meaningful, unsigned integral types, although the .NET 10 generic overloads already define their behavior. + +Extend the concrete integral surface of `MustBeNegative`, `MustNotBeNegative`, `MustNotBePositive`, and `MustNotBeZero` on every supported target while preserving the existing generic APIs and exception contracts. Omit unsigned overloads whose predicate would be constant rather than adding APIs that either always fail or cannot validate anything. + +## Acceptance Criteria + +- [x] `MustBeNegative` and `MustNotBeNegative` each have default and custom-exception-factory overloads for `sbyte` and `short`, but do not add concrete unsigned overloads whose checks would always fail or always succeed. +- [x] `MustNotBePositive` and `MustNotBeZero` each have default and custom-exception-factory overloads for `sbyte`, `byte`, `short`, `ushort`, `uint`, and `ulong`. +- [x] Every new overload is available on .NET Standard 2.0, .NET Standard 2.1, and .NET 10, returns the original exactly typed value when its predicate succeeds, and matches the corresponding existing .NET 10 generic semantics. +- [x] Default failures preserve each guard family's existing `ArgumentOutOfRangeException` contract, including caller-argument-expression parameter names, optional custom messages, and the standard generated message containing the rejected value. +- [x] Each new factory overload passes the original value with its exact integral type, invokes the factory only when validation fails, propagates the factory's exception, and throws `ArgumentNullException` through the existing `Throw.CustomException` convention when a failing check receives a null factory. +- [x] Automated tests cover signed and applicable unsigned boundary behavior for every new overload, return values, exception parameter names and messages, factory arguments and exceptions, factories not invoked on success, and null-factory failures. +- [x] Source-export tests verify the exact per-family concrete type matrix in portable and modern exports, including the deliberate omission of unsigned `MustBeNegative` and `MustNotBeNegative` overloads; generic `INumber` overloads appear only in modern exports; and every custom-exception-factory overload is trimmed when that option is disabled. +- [x] The committed .NET Standard 2.0 `Light.GuardClauses.SingleFile.cs` distribution is regenerated with the expanded APIs, and generated source validates for both supported source-export targets. +- [x] The sign-guard documentation describes the per-family concrete integral surface, and the package release notes mention the expanded support across all five sign-guard families. +- [x] The complete solution restores and builds without warnings in Release configuration, and all automated tests pass on the pinned SDK. + +## Technical Details + +Add the concrete overload pairs outside the `NET8_0_OR_GREATER` regions according to this matrix: + +| Guard | New concrete types | Predicate | +| --- | --- | --- | +| `MustBeNegative` | `sbyte`, `short` | Less than zero | +| `MustNotBeNegative` | `sbyte`, `short` | Greater than or equal to zero | +| `MustNotBePositive` | `sbyte`, `byte`, `short`, `ushort`, `uint`, `ulong` | Less than or equal to zero | +| `MustNotBeZero` | `sbyte`, `byte`, `short`, `ushort`, `uint`, `ulong` | Not equal to zero | + +Unsigned overloads are appropriate for `MustNotBePositive`, where zero succeeds and positive values fail, and `MustNotBeZero`, where zero fails and positive values succeed. Do not add concrete unsigned overloads to `MustBeNegative`, which could never succeed, or `MustNotBeNegative`, which could never fail. + +For each listed guard and type, follow the annotations, XML documentation, parameter ordering, and implementation conventions of the corresponding existing `int` and `long` pair. The following is an illustrative shape for the concrete APIs, not a new generic API: + +```csharp +public static T Guard( + this T parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null +); + +public static T Guard(this T parameter, Func exceptionFactory); +``` + +Compare values directly to type-appropriate zero without widening to `int` or `long`, so return values, throw-helper inputs, and factory arguments retain their exact types. The generic throw helpers already support these types, so no new exception or `Throw` API is required. + +Keep all `INumber` overloads under `NET8_0_OR_GREATER`. Normal modern calls for types in the matrix should resolve to the new concrete overloads, while explicitly generic calls, deliberately omitted unsigned combinations, and numeric types such as `Half` remain supported by the generic surface with identical predicates. Retain focused tests for that distinction. Do not add `nint` or `nuint` overloads. + +Extend the four guard-specific test classes and `NumericCustomFactorySuccessTests` with the new concrete matrix, retaining focused modern tests for explicitly generic calls and types without concrete overloads. Strengthen `SourceFileMergerWhitelistTests.SignGuardWhitelistsUseTargetSpecificSurface` to cover every sign-guard family and both factory settings, update `docs/assertion-overview.md` and `src/Directory.Build.props`, and regenerate the root `Light.GuardClauses.SingleFile.cs` through the committed source-export settings. No microbenchmarks are required because these overloads add only primitive comparisons equivalent to the existing generic behavior. diff --git a/docs/assertion-overview.md b/docs/assertion-overview.md index 204911e..6479db6 100644 --- a/docs/assertion-overview.md +++ b/docs/assertion-overview.md @@ -13,7 +13,7 @@ The package has .NET Standard 2.0, .NET Standard 2.1, and .NET 10 assets. The co The .NET 10 asset additionally provides: - generic `INumber` overloads for `IsApproximately`, `MustBeApproximately`, `MustNotBeApproximately`, `IsGreaterThanOrApproximately`, `MustBeGreaterThanOrApproximately`, `IsLessThanOrApproximately`, and `MustBeLessThanOrApproximately`; -- generic `INumber` overloads for `MustBePositive`, `MustBeNegative`, `MustNotBePositive`, `MustNotBeNegative`, and `MustNotBeZero`; for `MustBePositive`, these extend the common concrete integral overloads to remaining numeric types such as `Half`; +- generic `INumber` overloads for `MustBePositive`, `MustBeNegative`, `MustNotBePositive`, `MustNotBeNegative`, and `MustNotBeZero`, extending the common concrete integral overloads to remaining numeric types such as `Half`; - generic `IFloatingPointIeee754` overloads for `IsFinite` and `MustBeFinite`, including `Half` but excluding `decimal`; - `Span`, `ReadOnlySpan`, `Memory`, and `ReadOnlyMemory` overloads for `IsEmailAddress` and `MustBeEmailAddress`; and - trimming annotations on the type-relation helpers where supported by the framework. @@ -72,7 +72,7 @@ UUIDv7 validation checks the version-7 nibble and RFC/IETF `10xx` variant bits d | `IsGreaterThanOrApproximately`, `MustBeGreaterThanOrApproximately` | Accept values greater than or within tolerance of the comparison value | | `IsLessThanOrApproximately`, `MustBeLessThanOrApproximately` | Accept values less than or within tolerance of the comparison value | -The five sign guard families have concrete overloads for `int`, `long`, `decimal`, `float`, `double`, and `TimeSpan` on all package targets. `MustBePositive` additionally has concrete `sbyte`, `byte`, `short`, `ushort`, `uint`, and `ulong` overloads on every target. The .NET 10 asset adds the generic `INumber` overloads listed above. All checks compare the value against zero with the type's comparison operators. Consequently, `NaN` is rejected by the four sign guards and accepted by `MustNotBeZero`, positive and negative infinity satisfy the guards matching their sign (compose with `MustBeFinite` to reject non-finite values), and negative zero — including `decimal`'s signed zero representations — behaves exactly like zero. `MustNotBeZero` uses exact equality; tolerance-based comparisons remain the domain of the approximation guards. +The five sign guard families have concrete overloads for `int`, `long`, `decimal`, `float`, `double`, and `TimeSpan` on all package targets. `MustBePositive`, `MustNotBePositive`, and `MustNotBeZero` additionally have concrete `sbyte`, `byte`, `short`, `ushort`, `uint`, and `ulong` overloads on every target, while `MustBeNegative` and `MustNotBeNegative` add concrete `sbyte` and `short` overloads — unsigned overloads are omitted for these two families because their predicates would be constant. The .NET 10 asset adds the generic `INumber` overloads listed above. All checks compare the value against zero with the type's comparison operators. Consequently, `NaN` is rejected by the four sign guards and accepted by `MustNotBeZero`, positive and negative infinity satisfy the guards matching their sign (compose with `MustBeFinite` to reject non-finite values), and negative zero — including `decimal`'s signed zero representations — behaves exactly like zero. `MustNotBeZero` uses exact equality; tolerance-based comparisons remain the domain of the approximation guards. Create ranges with the `Range` fluent API: diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7a9fee9..3535a46 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -21,7 +21,7 @@ -------------------------------- - new assertions: MustBeAssignableTo, MustBeConcreteClass, MustBeUri, ObjectDisposed - - expanded MustBePositive support for sbyte, byte, short, ushort, uint, and ulong on all target frameworks + - expanded sign-guard support on all target frameworks: MustBePositive, MustNotBePositive, and MustNotBeZero for sbyte, byte, short, ushort, uint, and ulong; MustBeNegative and MustNotBeNegative for sbyte and short diff --git a/src/Light.GuardClauses/Check.MustBeNegative.cs b/src/Light.GuardClauses/Check.MustBeNegative.cs index b813eb1..910edf4 100644 --- a/src/Light.GuardClauses/Check.MustBeNegative.cs +++ b/src/Light.GuardClauses/Check.MustBeNegative.cs @@ -1,15 +1,111 @@ using System; -#if NET8_0_OR_GREATER -using System.Numerics; -#endif using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustBeNegative( + this sbyte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustBeNegative(this sbyte parameter, Func exceptionFactory) + { + if (!(parameter < 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustBeNegative( + this short parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter < 0)) + { + Throw.MustBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is negative (less than zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero or positive. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustBeNegative(this short parameter, Func exceptionFactory) + { + if (!(parameter < 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is negative (less than zero), or otherwise /// throws an . diff --git a/src/Light.GuardClauses/Check.MustBePositive.cs b/src/Light.GuardClauses/Check.MustBePositive.cs index db440ff..5db72b6 100644 --- a/src/Light.GuardClauses/Check.MustBePositive.cs +++ b/src/Light.GuardClauses/Check.MustBePositive.cs @@ -1,10 +1,10 @@ using System; -#if NET8_0_OR_GREATER -using System.Numerics; -#endif using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif namespace Light.GuardClauses; @@ -27,7 +27,7 @@ public static sbyte MustBePositive( string? message = null ) { - if (!(parameter > (sbyte) 0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -50,7 +50,7 @@ public static sbyte MustBePositive( [ContractAnnotation("exceptionFactory:null => halt")] public static sbyte MustBePositive(this sbyte parameter, Func exceptionFactory) { - if (!(parameter > (sbyte) 0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -75,7 +75,7 @@ public static byte MustBePositive( string? message = null ) { - if (!(parameter > (byte) 0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -98,7 +98,7 @@ public static byte MustBePositive( [ContractAnnotation("exceptionFactory:null => halt")] public static byte MustBePositive(this byte parameter, Func exceptionFactory) { - if (!(parameter > (byte) 0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -123,7 +123,7 @@ public static short MustBePositive( string? message = null ) { - if (!(parameter > (short) 0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -146,7 +146,7 @@ public static short MustBePositive( [ContractAnnotation("exceptionFactory:null => halt")] public static short MustBePositive(this short parameter, Func exceptionFactory) { - if (!(parameter > (short) 0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } @@ -171,7 +171,7 @@ public static ushort MustBePositive( string? message = null ) { - if (!(parameter > (ushort) 0)) + if (!(parameter > 0)) { Throw.MustBePositive(parameter, parameterName, message); } @@ -194,7 +194,7 @@ public static ushort MustBePositive( [ContractAnnotation("exceptionFactory:null => halt")] public static ushort MustBePositive(this ushort parameter, Func exceptionFactory) { - if (!(parameter > (ushort) 0)) + if (!(parameter > 0)) { Throw.CustomException(exceptionFactory, parameter); } diff --git a/src/Light.GuardClauses/Check.MustNotBeNegative.cs b/src/Light.GuardClauses/Check.MustNotBeNegative.cs index c1f941e..d256417 100644 --- a/src/Light.GuardClauses/Check.MustNotBeNegative.cs +++ b/src/Light.GuardClauses/Check.MustNotBeNegative.cs @@ -1,15 +1,111 @@ using System; -#if NET8_0_OR_GREATER -using System.Numerics; -#endif using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustNotBeNegative( + this sbyte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustNotBeNegative(this sbyte parameter, Func exceptionFactory) + { + if (!(parameter >= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustNotBeNegative( + this short parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter >= 0)) + { + Throw.MustNotBeNegative(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is less than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustNotBeNegative(this short parameter, Func exceptionFactory) + { + if (!(parameter >= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not negative (greater than or equal to zero), or otherwise /// throws an . diff --git a/src/Light.GuardClauses/Check.MustNotBePositive.cs b/src/Light.GuardClauses/Check.MustNotBePositive.cs index e0e356d..21ffa82 100644 --- a/src/Light.GuardClauses/Check.MustNotBePositive.cs +++ b/src/Light.GuardClauses/Check.MustNotBePositive.cs @@ -1,15 +1,207 @@ using System; -#if NET8_0_OR_GREATER -using System.Numerics; -#endif using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustNotBePositive( + this sbyte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustNotBePositive(this sbyte parameter, Func exceptionFactory) + { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte MustNotBePositive( + this byte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static byte MustNotBePositive(this byte parameter, Func exceptionFactory) + { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustNotBePositive( + this short parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustNotBePositive(this short parameter, Func exceptionFactory) + { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort MustNotBePositive( + this ushort parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ushort MustNotBePositive(this ushort parameter, Func exceptionFactory) + { + if (!(parameter <= 0)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not positive (less than or equal to zero), or otherwise /// throws an . @@ -58,6 +250,54 @@ public static int MustNotBePositive(this int parameter, Func exc return parameter; } + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint MustNotBePositive( + this uint parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0U)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static uint MustNotBePositive(this uint parameter, Func exceptionFactory) + { + if (!(parameter <= 0U)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not positive (less than or equal to zero), or otherwise /// throws an . @@ -106,6 +346,54 @@ public static long MustNotBePositive(this long parameter, Func return parameter; } + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong MustNotBePositive( + this ulong parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!(parameter <= 0UL)) + { + Throw.MustNotBePositive(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not positive (less than or equal to zero), or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is greater than zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ulong MustNotBePositive(this ulong parameter, Func exceptionFactory) + { + if (!(parameter <= 0UL)) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not positive (less than or equal to zero), or otherwise /// throws an . diff --git a/src/Light.GuardClauses/Check.MustNotBeZero.cs b/src/Light.GuardClauses/Check.MustNotBeZero.cs index 3e6ac56..a00906c 100644 --- a/src/Light.GuardClauses/Check.MustNotBeZero.cs +++ b/src/Light.GuardClauses/Check.MustNotBeZero.cs @@ -1,15 +1,207 @@ using System; -#if NET8_0_OR_GREATER -using System.Numerics; -#endif using System.Runtime.CompilerServices; using JetBrains.Annotations; using Light.GuardClauses.ExceptionFactory; +#if NET8_0_OR_GREATER +using System.Numerics; +#endif namespace Light.GuardClauses; public static partial class Check { + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static sbyte MustNotBeZero( + this sbyte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static sbyte MustNotBeZero(this sbyte parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte MustNotBeZero( + this byte parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static byte MustNotBeZero(this byte parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short MustNotBeZero( + this short parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static short MustNotBeZero(this short parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort MustNotBeZero( + this ushort parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ushort MustNotBeZero(this ushort parameter, Func exceptionFactory) + { + if (parameter == 0) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . @@ -58,6 +250,54 @@ public static int MustNotBeZero(this int parameter, Func excepti return parameter; } + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint MustNotBeZero( + this uint parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0U) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static uint MustNotBeZero(this uint parameter, Func exceptionFactory) + { + if (parameter == 0U) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . @@ -106,6 +346,54 @@ public static long MustNotBeZero(this long parameter, Func exce return parameter; } + /// + /// Ensures that the specified is not zero, or otherwise + /// throws an . + /// + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong MustNotBeZero( + this ulong parameter, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (parameter == 0UL) + { + Throw.MustNotBeZero(parameter, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is not zero, or otherwise + /// throws your custom exception. + /// + /// The value to be checked. + /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// + /// + /// Your custom exception thrown when is zero. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static ulong MustNotBeZero(this ulong parameter, Func exceptionFactory) + { + if (parameter == 0UL) + { + Throw.CustomException(exceptionFactory, parameter); + } + + return parameter; + } + /// /// Ensures that the specified is not zero, or otherwise /// throws an . diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 8ca3d97..31b6d38 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -11,4 +11,5 @@ - `dotnet test` for usual test runs. - `dotnet test -- --coverage --coverage-output-format cobertura --coverage-settings CodeCoverage.config` for test coverage metrics (run from tests/Light.GuardClauses.Tests). The settings file excludes JetBrains.Annotations and Regex source generator output from the report. +- `dotnet test --nologo` fails with "Zero tests ran" (exit code 5) because the Microsoft.Testing.Platform runner forwards `--nologo` to the test app, which rejects it. Run `dotnet test` without `--nologo`. - Line coverage of 100% is not achievable: the closing brace after a call to a `Throw.*` helper is an unreachable sequence point because these helpers never return (285 such lines as of 2026-07-15). diff --git a/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs b/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs index 1161e61..df02168 100644 --- a/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs +++ b/tests/Light.GuardClauses.SourceCodeTransformation.Tests/SourceFileMergerWhitelistTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -284,34 +285,75 @@ public static void SignGuardWhitelistsUseTargetSpecificSurface() var modernCode = File.ReadAllText(modernFile); var portableWithoutFactoriesCode = File.ReadAllText(portableWithoutFactoriesFile); var modernWithoutFactoriesCode = File.ReadAllText(modernWithoutFactoriesFile); - string[] concreteMustBePositiveTypes = - [ - "sbyte", - "byte", - "short", - "ushort", - "int", - "uint", - "long", - "ulong", - "decimal", - "float", - "double", - "TimeSpan", - ]; - - foreach (var type in concreteMustBePositiveTypes) + var concreteTypesByAssertion = new Dictionary + { + ["MustBePositive"] = + [ + "sbyte", "byte", "short", "ushort", "int", "uint", + "long", "ulong", "decimal", "float", "double", "TimeSpan", + ], + ["MustBeNegative"] = + [ + "sbyte", "short", "int", "long", "decimal", "float", "double", "TimeSpan", + ], + ["MustNotBePositive"] = + [ + "sbyte", "byte", "short", "ushort", "int", "uint", + "long", "ulong", "decimal", "float", "double", "TimeSpan", + ], + ["MustNotBeNegative"] = + [ + "sbyte", "short", "int", "long", "decimal", "float", "double", "TimeSpan", + ], + ["MustNotBeZero"] = + [ + "sbyte", "byte", "short", "ushort", "int", "uint", + "long", "ulong", "decimal", "float", "double", "TimeSpan", + ], + }; + string[] omittedUnsignedTypes = ["byte", "ushort", "uint", "ulong"]; + + foreach (var (assertion, concreteTypes) in concreteTypesByAssertion) + { + foreach (var type in concreteTypes) + { + portableCode.Should().Contain($"public static {type} {assertion}("); + modernCode.Should().Contain($"public static {type} {assertion}("); + if (assertion == "MustNotBeZero") + { + portableCode.Should() + .NotContain( + $"{assertion}(this {type} parameter, Func<{type}, Exception> exceptionFactory)" + ); + modernCode.Should() + .NotContain( + $"{assertion}(this {type} parameter, Func<{type}, Exception> exceptionFactory)" + ); + } + else + { + portableCode.Should() + .Contain( + $"{assertion}(this {type} parameter, Func<{type}, Exception> exceptionFactory)" + ); + modernCode.Should() + .Contain( + $"{assertion}(this {type} parameter, Func<{type}, Exception> exceptionFactory)" + ); + } + } + } + + foreach (var type in omittedUnsignedTypes) + { + portableCode.Should().NotContain($"public static {type} MustBeNegative("); + modernCode.Should().NotContain($"public static {type} MustBeNegative("); + portableCode.Should().NotContain($"public static {type} MustNotBeNegative("); + modernCode.Should().NotContain($"public static {type} MustNotBeNegative("); + } + + foreach (var type in concreteTypesByAssertion["MustBePositive"]) { - portableCode.Should().Contain($"public static {type} MustBePositive("); - modernCode.Should().Contain($"public static {type} MustBePositive("); - portableCode.Should() - .Contain( - $"MustBePositive(this {type} parameter, Func<{type}, Exception> exceptionFactory)" - ); - modernCode.Should() - .Contain( - $"MustBePositive(this {type} parameter, Func<{type}, Exception> exceptionFactory)" - ); portableWithoutFactoriesCode.Should().Contain($"public static {type} MustBePositive("); modernWithoutFactoriesCode.Should().Contain($"public static {type} MustBePositive("); portableWithoutFactoriesCode.Should() @@ -324,11 +366,11 @@ public static void SignGuardWhitelistsUseTargetSpecificSurface() ); } - portableCode.Should().Contain("public static decimal MustBeNegative("); - portableCode.Should().Contain("public static TimeSpan MustNotBeNegative("); - portableCode.Should().Contain("public static double MustNotBeZero("); - portableCode.Should().NotContain("MustNotBeZero(this int parameter, Func exceptionFactory)"); portableCode.Should().NotContain("public static T MustBePositive("); + portableCode.Should().NotContain("public static T MustBeNegative("); + portableCode.Should().NotContain("public static T MustNotBePositive("); + portableCode.Should().NotContain("public static T MustNotBeNegative("); + portableCode.Should().NotContain("public static T MustNotBeZero("); portableCode.Should().NotContain("INumber"); modernCode.Should().Contain("MustBePositive"); modernCode.Should().Contain("MustBeNegative"); @@ -336,6 +378,8 @@ public static void SignGuardWhitelistsUseTargetSpecificSurface() modernCode.Should().Contain("MustNotBeNegative"); modernCode.Should().Contain("MustNotBeZero"); modernCode.Should().Contain("INumber"); + modernCode.Should() + .NotContain("MustNotBeZero(this T parameter, Func exceptionFactory)"); portableWithoutFactoriesCode.Should().NotContain("public static T MustBePositive("); modernWithoutFactoriesCode.Should().Contain("public static T MustBePositive("); modernWithoutFactoriesCode.Should() @@ -583,10 +627,18 @@ public static void ObjectDisposedWhitelistExportsGuardThrowHelperAndExceptionFac ); var sourceCode = File.ReadAllText(targetFile); - sourceCode.Should().Contain("public static void ObjectDisposed(bool condition, string? objectName = null, string? message = null)"); - sourceCode.Should().Contain("public static void ObjectDisposed(bool condition, Func exceptionFactory)"); - sourceCode.Should().Contain("public static void ObjectDisposed(bool condition, T parameter, Func exceptionFactory)"); - sourceCode.Should().Contain("public static void ObjectDisposed(string? objectName = null, string? message = null) => throw new ObjectDisposedException(objectName, message);"); + sourceCode.Should().Contain( + "public static void ObjectDisposed(bool condition, string? objectName = null, string? message = null)" + ); + sourceCode.Should().Contain( + "public static void ObjectDisposed(bool condition, Func exceptionFactory)" + ); + sourceCode.Should().Contain( + "public static void ObjectDisposed(bool condition, T parameter, Func exceptionFactory)" + ); + sourceCode.Should().Contain( + "public static void ObjectDisposed(string? objectName = null, string? message = null) => throw new ObjectDisposedException(objectName, message);" + ); sourceCode.Should().Contain("public static void CustomException(Func exceptionFactory)"); sourceCode.Should().NotContain("public static void InvalidOperation("); } @@ -744,8 +796,12 @@ public static void ObjectDisposedWhitelistTrimsExceptionFactoryOverloads() ); var sourceCode = File.ReadAllText(targetFile); - sourceCode.Should().Contain("public static void ObjectDisposed(bool condition, string? objectName = null, string? message = null)"); - sourceCode.Should().Contain("public static void ObjectDisposed(string? objectName = null, string? message = null) => throw new ObjectDisposedException(objectName, message);"); + sourceCode.Should().Contain( + "public static void ObjectDisposed(bool condition, string? objectName = null, string? message = null)" + ); + sourceCode.Should().Contain( + "public static void ObjectDisposed(string? objectName = null, string? message = null) => throw new ObjectDisposedException(objectName, message);" + ); sourceCode.Should().NotContain("Func exceptionFactory"); sourceCode.Should().NotContain("ObjectDisposed"); sourceCode.Should().NotContain("public static void CustomException("); diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs index f083f75..a6a668c 100644 --- a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustBeNegativeTests.cs @@ -6,6 +6,34 @@ namespace Light.GuardClauses.Tests.ComparableAssertions; public static class MustBeNegativeTests { + [Fact] + public static void NegativeSBytesAreAccepted() + { + ((sbyte) -1).MustBeNegative().Should().Be(-1); + sbyte.MinValue.MustBeNegative().Should().Be(sbyte.MinValue); + } + + [Fact] + public static void NonNegativeSBytesAreRejected() + { + CheckIntegralIsRejected((sbyte) 0, value => value.MustBeNegative()); + CheckIntegralIsRejected(sbyte.MaxValue, value => value.MustBeNegative()); + } + + [Fact] + public static void NegativeInt16sAreAccepted() + { + ((short) -1).MustBeNegative().Should().Be(-1); + short.MinValue.MustBeNegative().Should().Be(short.MinValue); + } + + [Fact] + public static void NonNegativeInt16sAreRejected() + { + CheckIntegralIsRejected((short) 0, value => value.MustBeNegative()); + CheckIntegralIsRejected(short.MaxValue, value => value.MustBeNegative()); + } + [Theory] [InlineData(-1)] [InlineData(-42)] @@ -100,7 +128,7 @@ public static void NegativeZerosAreRejectedLikeZero() negativeZeroFloat.Should().Throw(); negativeZeroDouble.Should().Throw(); - CheckDecimalIsRejected(new decimal(0, 0, 0, true, 0)); + CheckDecimalIsRejected(new (0, 0, 0, true, 0)); CheckDecimalIsRejected(0.000m); } @@ -135,9 +163,23 @@ public static void DefaultExceptionCapturesExpressionAndValue() public static void CustomMessage() => Test.CustomMessage(message => 1.MustBeNegative(message: message)); + [Fact] + public static void NewIntegralOverloadPropagatesParameterNameAndCustomMessage() + { + const sbyte invalidValue = 0; + + var act = () => invalidValue.MustBeNegative("quantity", "A negative quantity is required."); + + act.Should().Throw() + .WithParameterName("quantity") + .WithMessage("A negative quantity is required.*"); + } + [Fact] public static void CustomFactoriesReceiveValues() { + Test.CustomException((sbyte) 0, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException((short) 0, (value, factory) => value.MustBeNegative(factory)); Test.CustomException(0, (value, factory) => value.MustBeNegative(factory)); Test.CustomException(1L, (value, factory) => value.MustBeNegative(factory)); Test.CustomException(0m, (value, factory) => value.MustBeNegative(factory)); @@ -146,24 +188,47 @@ public static void CustomFactoriesReceiveValues() Test.CustomException(TimeSpan.Zero, (value, factory) => value.MustBeNegative(factory)); } + [Fact] + public static void NullFactoriesThrowArgumentNullExceptionForNewIntegralOverloads() + { + CheckNullFactory(() => ((sbyte) 0).MustBeNegative(null!)); + CheckNullFactory(() => ((short) 0).MustBeNegative(null!)); + } + #if NET8_0_OR_GREATER [Fact] - public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + public static void ExplicitGenericOverloadsRemainAvailable() { - ((short) -5).MustBeNegative().Should().Be((short) -5); + ((short) -5).MustBeNegative().Should().Be(-5); ((Half) (-1.5f)).MustBeNegative().Should().Be((Half) (-1.5f)); - var zeroShort = () => ((short) 0).MustBeNegative(); + var zeroShort = () => ((short) 0).MustBeNegative(); var unsignedByte = () => ((byte) 3).MustBeNegative(); var nanHalf = () => Half.NaN.MustBeNegative(); zeroShort.Should().Throw().WithMessage("*must be negative*"); unsignedByte.Should().Throw().WithMessage("*must be negative*"); nanHalf.Should().Throw().WithMessage("*must be negative*"); - Test.CustomException((short) 3, (value, factory) => value.MustBeNegative(factory)); + Test.CustomException( + (short) 3, + (value, factory) => value.MustBeNegative(factory) + ); } #endif + private static void CheckIntegralIsRejected(T value, Func guard) + { + var act = () => guard(value); + + act.Should().Throw() + .WithParameterName(nameof(value)) + .WithMessage($"*value must be negative, but it actually is {value}*"); + } + + private static void CheckNullFactory(Action act) => + act.Should().Throw() + .WithParameterName("exceptionFactory"); + private static void CheckDecimalIsRejected(decimal value) { var act = () => value.MustBeNegative(); diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs index 4b191e4..f694bdd 100644 --- a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeNegativeTests.cs @@ -6,6 +6,36 @@ namespace Light.GuardClauses.Tests.ComparableAssertions; public static class MustNotBeNegativeTests { + [Fact] + public static void NonNegativeSBytesAreAccepted() + { + ((sbyte) 0).MustNotBeNegative().Should().Be(0); + ((sbyte) 1).MustNotBeNegative().Should().Be(1); + sbyte.MaxValue.MustNotBeNegative().Should().Be(sbyte.MaxValue); + } + + [Fact] + public static void NegativeSBytesAreRejected() + { + CheckIntegralIsRejected((sbyte) -1, value => value.MustNotBeNegative()); + CheckIntegralIsRejected(sbyte.MinValue, value => value.MustNotBeNegative()); + } + + [Fact] + public static void NonNegativeInt16sAreAccepted() + { + ((short) 0).MustNotBeNegative().Should().Be(0); + ((short) 1).MustNotBeNegative().Should().Be(1); + short.MaxValue.MustNotBeNegative().Should().Be(short.MaxValue); + } + + [Fact] + public static void NegativeInt16sAreRejected() + { + CheckIntegralIsRejected((short) -1, value => value.MustNotBeNegative()); + CheckIntegralIsRejected(short.MinValue, value => value.MustNotBeNegative()); + } + [Theory] [InlineData(0)] [InlineData(1)] @@ -129,9 +159,23 @@ public static void DefaultExceptionCapturesExpressionAndValue() public static void CustomMessage() => Test.CustomMessage(message => (-1).MustNotBeNegative(message: message)); + [Fact] + public static void NewIntegralOverloadPropagatesParameterNameAndCustomMessage() + { + const short invalidValue = -1; + + var act = () => invalidValue.MustNotBeNegative("quantity", "A non-negative quantity is required."); + + act.Should().Throw() + .WithParameterName("quantity") + .WithMessage("A non-negative quantity is required.*"); + } + [Fact] public static void CustomFactoriesReceiveValues() { + Test.CustomException((sbyte) -1, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException((short) -1, (value, factory) => value.MustNotBeNegative(factory)); Test.CustomException(-1, (value, factory) => value.MustNotBeNegative(factory)); Test.CustomException(-1L, (value, factory) => value.MustNotBeNegative(factory)); Test.CustomException(-0.5m, (value, factory) => value.MustNotBeNegative(factory)); @@ -140,24 +184,46 @@ public static void CustomFactoriesReceiveValues() Test.CustomException(TimeSpan.FromTicks(-1), (value, factory) => value.MustNotBeNegative(factory)); } + [Fact] + public static void NullFactoriesThrowArgumentNullExceptionForNewIntegralOverloads() + { + CheckNullFactory(() => ((sbyte) -1).MustNotBeNegative(null!)); + CheckNullFactory(() => ((short) -1).MustNotBeNegative(null!)); + } + #if NET8_0_OR_GREATER [Fact] - public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + public static void ExplicitGenericOverloadsRemainAvailable() { - ((short) 0).MustNotBeNegative().Should().Be((short) 0); - ((short) 5).MustNotBeNegative().Should().Be((short) 5); - ((byte) 3).MustNotBeNegative().Should().Be((byte) 3); + ((short) 5).MustNotBeNegative().Should().Be(5); + ((byte) 3).MustNotBeNegative().Should().Be(3); Half.Zero.MustNotBeNegative().Should().Be(Half.Zero); - var negativeShort = () => ((short) -3).MustNotBeNegative(); + var negativeShort = () => ((short) -3).MustNotBeNegative(); var nanHalf = () => Half.NaN.MustNotBeNegative(); negativeShort.Should().Throw().WithMessage("*must not be negative*"); nanHalf.Should().Throw().WithMessage("*must not be negative*"); - Test.CustomException((short) -3, (value, factory) => value.MustNotBeNegative(factory)); + Test.CustomException( + (short) -3, + (value, factory) => value.MustNotBeNegative(factory) + ); } #endif + private static void CheckIntegralIsRejected(T value, Func guard) + { + var act = () => guard(value); + + act.Should().Throw() + .WithParameterName(nameof(value)) + .WithMessage($"*value must not be negative, but it actually is {value}*"); + } + + private static void CheckNullFactory(Action act) => + act.Should().Throw() + .WithParameterName("exceptionFactory"); + private static void CheckDecimalIsRejected(decimal value) { var act = () => value.MustNotBeNegative(); diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs index f2e60d8..c3f6def 100644 --- a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBePositiveTests.cs @@ -6,6 +6,56 @@ namespace Light.GuardClauses.Tests.ComparableAssertions; public static class MustNotBePositiveTests { + [Fact] + public static void NonPositiveSBytesAreAccepted() + { + ((sbyte) 0).MustNotBePositive().Should().Be(0); + ((sbyte) -1).MustNotBePositive().Should().Be(-1); + sbyte.MinValue.MustNotBePositive().Should().Be(sbyte.MinValue); + } + + [Fact] + public static void PositiveSBytesAreRejected() + { + CheckIntegralIsRejected((sbyte) 1, value => value.MustNotBePositive()); + CheckIntegralIsRejected(sbyte.MaxValue, value => value.MustNotBePositive()); + } + + [Fact] + public static void ZeroByteIsAccepted() => ((byte) 0).MustNotBePositive().Should().Be(0); + + [Fact] + public static void PositiveBytesAreRejected() + { + CheckIntegralIsRejected((byte) 1, value => value.MustNotBePositive()); + CheckIntegralIsRejected(byte.MaxValue, value => value.MustNotBePositive()); + } + + [Fact] + public static void NonPositiveInt16sAreAccepted() + { + ((short) 0).MustNotBePositive().Should().Be(0); + ((short) -1).MustNotBePositive().Should().Be(-1); + short.MinValue.MustNotBePositive().Should().Be(short.MinValue); + } + + [Fact] + public static void PositiveInt16sAreRejected() + { + CheckIntegralIsRejected((short) 1, value => value.MustNotBePositive()); + CheckIntegralIsRejected(short.MaxValue, value => value.MustNotBePositive()); + } + + [Fact] + public static void ZeroUInt16IsAccepted() => ((ushort) 0).MustNotBePositive().Should().Be(0); + + [Fact] + public static void PositiveUInt16sAreRejected() + { + CheckIntegralIsRejected((ushort) 1, value => value.MustNotBePositive()); + CheckIntegralIsRejected(ushort.MaxValue, value => value.MustNotBePositive()); + } + [Theory] [InlineData(0)] [InlineData(-1)] @@ -22,6 +72,16 @@ public static void PositiveInt32sAreRejected(int value) act.Should().Throw().WithMessage("*must not be positive*"); } + [Fact] + public static void ZeroUInt32IsAccepted() => 0U.MustNotBePositive().Should().Be(0U); + + [Fact] + public static void PositiveUInt32sAreRejected() + { + CheckIntegralIsRejected(1U, value => value.MustNotBePositive()); + CheckIntegralIsRejected(uint.MaxValue, value => value.MustNotBePositive()); + } + [Theory] [InlineData(0L)] [InlineData(-1L)] @@ -38,6 +98,16 @@ public static void PositiveInt64sAreRejected(long value) act.Should().Throw().WithMessage("*must not be positive*"); } + [Fact] + public static void ZeroUInt64IsAccepted() => 0UL.MustNotBePositive().Should().Be(0UL); + + [Fact] + public static void PositiveUInt64sAreRejected() + { + CheckIntegralIsRejected(1UL, value => value.MustNotBePositive()); + CheckIntegralIsRejected(ulong.MaxValue, value => value.MustNotBePositive()); + } + [Fact] public static void NonPositiveDecimalsAreAccepted() { @@ -129,35 +199,78 @@ public static void DefaultExceptionCapturesExpressionAndValue() public static void CustomMessage() => Test.CustomMessage(message => 1.MustNotBePositive(message: message)); + [Fact] + public static void NewIntegralOverloadPropagatesParameterNameAndCustomMessage() + { + const ushort invalidValue = 1; + + var act = () => invalidValue.MustNotBePositive("quantity", "A non-positive quantity is required."); + + act.Should().Throw() + .WithParameterName("quantity") + .WithMessage("A non-positive quantity is required.*"); + } + [Fact] public static void CustomFactoriesReceiveValues() { + Test.CustomException((sbyte) 1, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException((byte) 1, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException((short) 1, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException((ushort) 1, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(1, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(1U, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(1L, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException(1UL, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(0.5m, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(float.NaN, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(double.PositiveInfinity, (value, factory) => value.MustNotBePositive(factory)); Test.CustomException(TimeSpan.FromTicks(1), (value, factory) => value.MustNotBePositive(factory)); } + [Fact] + public static void NullFactoriesThrowArgumentNullExceptionForNewIntegralOverloads() + { + CheckNullFactory(() => ((sbyte) 1).MustNotBePositive(null!)); + CheckNullFactory(() => ((byte) 1).MustNotBePositive(null!)); + CheckNullFactory(() => ((short) 1).MustNotBePositive(null!)); + CheckNullFactory(() => ((ushort) 1).MustNotBePositive(null!)); + CheckNullFactory(() => 1U.MustNotBePositive(null!)); + CheckNullFactory(() => 1UL.MustNotBePositive(null!)); + } + #if NET8_0_OR_GREATER [Fact] - public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + public static void ExplicitGenericOverloadsRemainAvailable() { - ((short) 0).MustNotBePositive().Should().Be((short) 0); - ((short) -5).MustNotBePositive().Should().Be((short) -5); - ((byte) 0).MustNotBePositive().Should().Be((byte) 0); + ((short) -5).MustNotBePositive().Should().Be(-5); Half.Zero.MustNotBePositive().Should().Be(Half.Zero); - var positiveShort = () => ((short) 3).MustNotBePositive(); + var positiveShort = () => ((short) 3).MustNotBePositive(); var nanHalf = () => Half.NaN.MustNotBePositive(); positiveShort.Should().Throw().WithMessage("*must not be positive*"); nanHalf.Should().Throw().WithMessage("*must not be positive*"); - Test.CustomException((short) 3, (value, factory) => value.MustNotBePositive(factory)); + Test.CustomException( + (short) 3, + (value, factory) => value.MustNotBePositive(factory) + ); } #endif + private static void CheckIntegralIsRejected(T value, Func guard) + { + var act = () => guard(value); + + act.Should().Throw() + .WithParameterName(nameof(value)) + .WithMessage($"*value must not be positive, but it actually is {value}*"); + } + + private static void CheckNullFactory(Action act) => + act.Should().Throw() + .WithParameterName("exceptionFactory"); + private static void CheckDecimalIsRejected(decimal value) { var act = () => value.MustNotBePositive(); diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs index 08d8f32..384c237 100644 --- a/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeZeroTests.cs @@ -6,6 +6,54 @@ namespace Light.GuardClauses.Tests.ComparableAssertions; public static class MustNotBeZeroTests { + [Fact] + public static void NonZeroSBytesAreAccepted() + { + ((sbyte) 1).MustNotBeZero().Should().Be(1); + ((sbyte) -1).MustNotBeZero().Should().Be(-1); + sbyte.MinValue.MustNotBeZero().Should().Be(sbyte.MinValue); + sbyte.MaxValue.MustNotBeZero().Should().Be(sbyte.MaxValue); + } + + [Fact] + public static void ZeroSByteIsRejected() => + CheckIntegralIsRejected((sbyte) 0, value => value.MustNotBeZero()); + + [Fact] + public static void NonZeroBytesAreAccepted() + { + ((byte) 1).MustNotBeZero().Should().Be(1); + byte.MaxValue.MustNotBeZero().Should().Be(byte.MaxValue); + } + + [Fact] + public static void ZeroByteIsRejected() => + CheckIntegralIsRejected((byte) 0, value => value.MustNotBeZero()); + + [Fact] + public static void NonZeroInt16sAreAccepted() + { + ((short) 1).MustNotBeZero().Should().Be(1); + ((short) -1).MustNotBeZero().Should().Be(-1); + short.MinValue.MustNotBeZero().Should().Be(short.MinValue); + short.MaxValue.MustNotBeZero().Should().Be(short.MaxValue); + } + + [Fact] + public static void ZeroInt16IsRejected() => + CheckIntegralIsRejected((short) 0, value => value.MustNotBeZero()); + + [Fact] + public static void NonZeroUInt16sAreAccepted() + { + ((ushort) 1).MustNotBeZero().Should().Be(1); + ushort.MaxValue.MustNotBeZero().Should().Be(ushort.MaxValue); + } + + [Fact] + public static void ZeroUInt16IsRejected() => + CheckIntegralIsRejected((ushort) 0, value => value.MustNotBeZero()); + [Theory] [InlineData(1)] [InlineData(-1)] @@ -13,6 +61,17 @@ public static class MustNotBeZeroTests [InlineData(int.MaxValue)] public static void NonZeroInt32sAreAccepted(int value) => value.MustNotBeZero().Should().Be(value); + [Fact] + public static void NonZeroUInt32sAreAccepted() + { + 1U.MustNotBeZero().Should().Be(1U); + uint.MaxValue.MustNotBeZero().Should().Be(uint.MaxValue); + } + + [Fact] + public static void ZeroUInt32IsRejected() => + CheckIntegralIsRejected(0U, value => value.MustNotBeZero()); + [Theory] [InlineData(1L)] [InlineData(-1L)] @@ -20,6 +79,17 @@ public static class MustNotBeZeroTests [InlineData(long.MaxValue)] public static void NonZeroInt64sAreAccepted(long value) => value.MustNotBeZero().Should().Be(value); + [Fact] + public static void NonZeroUInt64sAreAccepted() + { + 1UL.MustNotBeZero().Should().Be(1UL); + ulong.MaxValue.MustNotBeZero().Should().Be(ulong.MaxValue); + } + + [Fact] + public static void ZeroUInt64IsRejected() => + CheckIntegralIsRejected(0UL, value => value.MustNotBeZero()); + [Fact] public static void NonZeroDecimalsAreAccepted() { @@ -68,7 +138,7 @@ public static void NegativeZerosAreRejectedLikeZero() { CheckFloatIsRejected(-0f); CheckDoubleIsRejected(-0d); - CheckDecimalIsRejected(new decimal(0, 0, 0, true, 0)); + CheckDecimalIsRejected(new (0, 0, 0, true, 0)); CheckDecimalIsRejected(0.000m); } @@ -95,36 +165,80 @@ public static void DefaultExceptionCapturesExpressionAndValue() public static void CustomMessage() => Test.CustomMessage(message => 0.MustNotBeZero(message: message)); + [Fact] + public static void NewIntegralOverloadPropagatesParameterNameAndCustomMessage() + { + const ulong invalidValue = 0UL; + + var act = () => invalidValue.MustNotBeZero("quantity", "A non-zero quantity is required."); + + act.Should().Throw() + .WithParameterName("quantity") + .WithMessage("A non-zero quantity is required.*"); + } + [Fact] public static void CustomFactoriesReceiveValues() { + Test.CustomException((sbyte) 0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException((byte) 0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException((short) 0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException((ushort) 0, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0U, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(0L, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException(0UL, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(0m, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(0f, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(0d, (value, factory) => value.MustNotBeZero(factory)); Test.CustomException(TimeSpan.Zero, (value, factory) => value.MustNotBeZero(factory)); } + [Fact] + public static void NullFactoriesThrowArgumentNullExceptionForNewIntegralOverloads() + { + CheckNullFactory(() => ((sbyte) 0).MustNotBeZero(null!)); + CheckNullFactory(() => ((byte) 0).MustNotBeZero(null!)); + CheckNullFactory(() => ((short) 0).MustNotBeZero(null!)); + CheckNullFactory(() => ((ushort) 0).MustNotBeZero(null!)); + CheckNullFactory(() => 0U.MustNotBeZero(null!)); + CheckNullFactory(() => 0UL.MustNotBeZero(null!)); + } + #if NET8_0_OR_GREATER [Fact] - public static void GenericOverloadsCoverTypesWithoutConcreteOverloads() + public static void ExplicitGenericOverloadsRemainAvailable() { - ((short) 5).MustNotBeZero().Should().Be((short) 5); - ((byte) 3).MustNotBeZero().Should().Be((byte) 3); + ((short) 5).MustNotBeZero().Should().Be(5); Half.NaN.MustNotBeZero().Should().Be(Half.NaN); - var zeroShort = () => ((short) 0).MustNotBeZero(); + var zeroShort = () => ((short) 0).MustNotBeZero(); var zeroHalf = () => Half.Zero.MustNotBeZero(); var negativeZeroHalf = () => Half.NegativeZero.MustNotBeZero(); zeroShort.Should().Throw().WithMessage("*must not be zero*"); zeroHalf.Should().Throw().WithMessage("*must not be zero*"); negativeZeroHalf.Should().Throw().WithMessage("*must not be zero*"); - Test.CustomException((short) 0, (value, factory) => value.MustNotBeZero(factory)); + Test.CustomException( + (short) 0, + (value, factory) => value.MustNotBeZero(factory) + ); } #endif + private static void CheckIntegralIsRejected(T value, Func guard) + { + var act = () => guard(value); + + act.Should().Throw() + .WithParameterName(nameof(value)) + .WithMessage($"*value must not be zero, but it actually is {value}*"); + } + + private static void CheckNullFactory(Action act) => + act.Should().Throw() + .WithParameterName("exceptionFactory"); + private static void CheckInt32IsRejected(int value) { var act = () => value.MustNotBeZero(); diff --git a/tests/Light.GuardClauses.Tests/ComparableAssertions/NumericCustomFactorySuccessTests.cs b/tests/Light.GuardClauses.Tests/ComparableAssertions/NumericCustomFactorySuccessTests.cs index 14098ab..d3119b9 100644 --- a/tests/Light.GuardClauses.Tests/ComparableAssertions/NumericCustomFactorySuccessTests.cs +++ b/tests/Light.GuardClauses.Tests/ComparableAssertions/NumericCustomFactorySuccessTests.cs @@ -13,19 +13,19 @@ private static InvalidOperationException FactoryMustNotBeInvoked() => [Fact] public static void MustBePositive_SByte() => - ((sbyte) 1).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be((sbyte) 1); + ((sbyte) 1).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(1); [Fact] public static void MustBePositive_Byte() => - ((byte) 2).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be((byte) 2); + ((byte) 2).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(2); [Fact] public static void MustBePositive_Short() => - ((short) 3).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be((short) 3); + ((short) 3).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(3); [Fact] public static void MustBePositive_UShort() => - ((ushort) 4).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be((ushort) 4); + ((ushort) 4).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(4); [Fact] public static void MustBePositive_Int() => @@ -61,44 +61,76 @@ public static void MustBePositive_TimeSpan() => [Fact] public static void MustBePositive_Generic() => - Check.MustBePositive((short) 13, _ => FactoryMustNotBeInvoked()).Should().Be(13); + ((short) 13).MustBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(13); + + [Fact] + public static void MustBeNegative_SByte() => + ((sbyte) -1).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-1); + + [Fact] + public static void MustBeNegative_Short() => + ((short) -2).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-2); [Fact] public static void MustBeNegative_Int() => - (-1).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-1); + (-3).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-3); [Fact] public static void MustBeNegative_Long() => - (-2L).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-2L); + (-4L).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-4L); [Fact] public static void MustBeNegative_Decimal() => - (-3m).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-3m); + (-5m).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-5m); [Fact] public static void MustBeNegative_Float() => - (-4f).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-4f); + (-6f).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-6f); [Fact] public static void MustBeNegative_Double() => - (-5d).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-5d); + (-7d).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-7d); [Fact] public static void MustBeNegative_TimeSpan() => - TimeSpan.FromTicks(-6).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(TimeSpan.FromTicks(-6)); + TimeSpan.FromTicks(-8).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(TimeSpan.FromTicks(-8)); [Fact] public static void MustBeNegative_Generic() => - ((short) -7).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-7); + ((short) -9).MustBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(-9); + + [Fact] + public static void MustNotBePositive_SByte() => + ((sbyte) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBePositive_Byte() => + ((byte) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBePositive_Short() => + ((short) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBePositive_UShort() => + ((ushort) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); [Fact] public static void MustNotBePositive_Int() => 0.MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + [Fact] + public static void MustNotBePositive_UInt() => + 0U.MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0U); + [Fact] public static void MustNotBePositive_Long() => 0L.MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0L); + [Fact] + public static void MustNotBePositive_ULong() => + 0UL.MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0UL); + [Fact] public static void MustNotBePositive_Decimal() => 0m.MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0m); @@ -117,7 +149,15 @@ public static void MustNotBePositive_TimeSpan() => [Fact] public static void MustNotBePositive_Generic() => - ((short) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + ((short) 0).MustNotBePositive(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBeNegative_SByte() => + ((sbyte) 0).MustNotBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBeNegative_Short() => + ((short) 0).MustNotBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(0); [Fact] public static void MustNotBeNegative_Int() => @@ -145,33 +185,57 @@ public static void MustNotBeNegative_TimeSpan() => [Fact] public static void MustNotBeNegative_Generic() => - ((short) 0).MustNotBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(0); + ((short) 0).MustNotBeNegative(_ => FactoryMustNotBeInvoked()).Should().Be(0); + + [Fact] + public static void MustNotBeZero_SByte() => + ((sbyte) 1).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(1); + + [Fact] + public static void MustNotBeZero_Byte() => + ((byte) 2).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(2); + + [Fact] + public static void MustNotBeZero_Short() => + ((short) 3).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(3); + + [Fact] + public static void MustNotBeZero_UShort() => + ((ushort) 4).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(4); [Fact] public static void MustNotBeZero_Int() => - 1.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(1); + 5.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(5); + + [Fact] + public static void MustNotBeZero_UInt() => + 6U.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(6U); [Fact] public static void MustNotBeZero_Long() => - 2L.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(2L); + 7L.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(7L); + + [Fact] + public static void MustNotBeZero_ULong() => + 8UL.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(8UL); [Fact] public static void MustNotBeZero_Decimal() => - 3m.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(3m); + 9m.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(9m); [Fact] public static void MustNotBeZero_Float() => - 4f.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(4f); + 10f.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(10f); [Fact] public static void MustNotBeZero_Double() => - 5d.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(5d); + 11d.MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(11d); [Fact] public static void MustNotBeZero_TimeSpan() => - TimeSpan.FromTicks(6).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(TimeSpan.FromTicks(6)); + TimeSpan.FromTicks(12).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(TimeSpan.FromTicks(12)); [Fact] public static void MustNotBeZero_Generic() => - ((short) 7).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(7); + ((short) 13).MustNotBeZero(_ => FactoryMustNotBeInvoked()).Should().Be(13); }