Skip to content

Commit

Permalink
Merge pull request #99 from MASSHUU12/create-more-random-extensions
Browse files Browse the repository at this point in the history
Create more random extensions
  • Loading branch information
MASSHUU12 authored Jul 1, 2024
2 parents 629f6bf + cf7c006 commit 5cf4677
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 9 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,25 @@ All notable changes to this project will be documented in this file.
- Extensions:
- ConfirmIsOdd
- ConfirmIsEven
- ConfirmCloseTo
- Extension classes:
- RandomEnumExtensions
- RandomBooleanExtensions
- RandomNetworkExtensions
- Confirm class with assertions:
- IsEnumValue
- IsNotEnumValue
- IsEnumName
- IsNotEnumName
- IsTrue
- IsFalse
- Throws
- NotThrows

### Changed

- Numeric extensions now use 'INumber<T>' generic constraint
instead of 'IComparable, IConvertible, IComparable<T>, IEquatable<T>'.

## [0.5.0-beta 2024-06-29]

Expand Down
29 changes: 29 additions & 0 deletions addons/confirma/src/classes/Confirm.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
using System;
using Confirma.Exceptions;
using Confirma.Extensions;

namespace Confirma.Classes;

public static class Confirm
{
public static bool IsTrue(bool expression, string? message = null)
{
if (expression) return true;

throw new ConfirmAssertException(message ?? "Expected true but was false");
}

public static bool IsFalse(bool expression, string? message = null)
{
if (!expression) return true;

throw new ConfirmAssertException(message ?? "Expected false but was true");
}

#region IsEnumValue
public static int IsEnumValue<T>(int value, string? message = null)
where T : struct, Enum
Expand Down Expand Up @@ -77,4 +92,18 @@ public static string IsNotEnumName<T>(string name, string? message = null)
);
}
#endregion

#region Throws
public static Action Throws<T>(Action action, string? message = null)
where T : Exception
{
return action.ConfirmThrows<T>(message);
}

public static Action NotThrows<T>(Action action, string? message = null)
where T : Exception
{
return action.ConfirmNotThrows<T>(message);
}
#endregion
}
38 changes: 29 additions & 9 deletions addons/confirma/src/extensions/ConfirmNumericExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Numerics;
using Confirma.Exceptions;

namespace Confirma.Extensions;
Expand All @@ -10,7 +11,7 @@ public static class ConfirmNumericExtensions
/// Zero is not considered positive.
/// </remarks>
public static T ConfirmIsPositive<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) > 0) return actual;

Expand All @@ -21,7 +22,7 @@ public static T ConfirmIsPositive<T>(this T actual, string? message = null)
}

public static T ConfirmIsNotPositive<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) <= 0) return actual;

Expand All @@ -37,7 +38,7 @@ public static T ConfirmIsNotPositive<T>(this T actual, string? message = null)
/// Zero is not considered negative.
/// </remarks>
public static T ConfirmIsNegative<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) < 0) return actual;

Expand All @@ -48,7 +49,7 @@ public static T ConfirmIsNegative<T>(this T actual, string? message = null)
}

public static T ConfirmIsNotNegative<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) >= 0) return actual;

Expand All @@ -64,7 +65,7 @@ public static T ConfirmIsNotNegative<T>(this T actual, string? message = null)
/// Zero is not considered signed or unsigned.
/// </remarks>
public static T ConfirmSign<T>(this T actual, bool sign, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (sign && actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) < 0) return actual;
if (!sign && actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) > 0) return actual;
Expand All @@ -78,7 +79,7 @@ public static T ConfirmSign<T>(this T actual, bool sign, string? message = null)

#region ConfirmIsZero
public static T ConfirmIsZero<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) == 0) return actual;

Expand All @@ -89,7 +90,7 @@ public static T ConfirmIsZero<T>(this T actual, string? message = null)
}

public static T ConfirmIsNotZero<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if (actual.CompareTo((T)Convert.ChangeType(0, typeof(T))) != 0) return actual;

Expand All @@ -101,18 +102,37 @@ public static T ConfirmIsNotZero<T>(this T actual, string? message = null)
#endregion

public static T ConfirmIsOdd<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if ((Convert.ToInt64(actual) & 1) != 0) return actual;

throw new ConfirmAssertException(message ?? $"Expected {actual} to be odd.");
}

public static T ConfirmIsEven<T>(this T actual, string? message = null)
where T : IComparable, IConvertible, IComparable<T>, IEquatable<T>
where T : INumber<T>
{
if ((Convert.ToInt64(actual) & 1) == 0) return actual;

throw new ConfirmAssertException(message ?? $"Expected {actual} to be even.");
}

public static T ConfirmCloseTo<T>(
this T actual,
T expected,
T tolerance,
string? message = null
)
where T : INumber<T>
{
T diff = actual - expected;
T abs = diff < (T)Convert.ChangeType(0, typeof(T)) ? -diff : diff;

if (abs <= tolerance) return actual;

throw new ConfirmAssertException(
message ??
$"{actual} is not close to {expected} within tolerance {tolerance}."
);
}
}
21 changes: 21 additions & 0 deletions addons/confirma/src/extensions/RandomBooleanExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Confirma.Extensions;

public static class RandomBooleanExtensions
{
public static bool NextBool(this Random rg)
{
return rg.Next(0, 2) == 1;
}

public static bool? NextNullableBool(this Random rg)
{
return rg.Next(0, 3) switch
{
0 => false,
1 => true,
_ => null,
};
}
}
59 changes: 59 additions & 0 deletions addons/confirma/src/extensions/RandomNetworkExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;

namespace Confirma.Extensions;

public static class RandomNetworkExtensions
{
private readonly static List<string> _domains = new() {
"gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "proton.me"
};

public static IPAddress NextIPAddress(this Random rg)
{
var data = new byte[4];
rg.NextBytes(data);

data[0] |= 1;

return new(data);
}

public static IPAddress NextIP6Address(this Random rg)
{
var data = new byte[16];
rg.NextBytes(data);

data[0] |= 1;

return new(data);
}

public static string NextEmail(this Random rg, int minLength = 8, int maxLength = 12)
{
if (minLength < 1 || maxLength < minLength)
throw new ArgumentException("Invalid length parameters");

int length = rg.Next(minLength, maxLength + 1);
var localPart = new StringBuilder(length);

for (int i = 0; i < length; i++)
{
if (i < length - 1 && rg.Next(6) == 0)
localPart.Append(rg.Next(2) == 0 ? '-' : '_');
else
{
if (rg.Next(2) == 0)
localPart.Append((char)('a' + rg.Next(26)));
else
localPart.Append(rg.Next(10));
}
}

var domain = _domains.ElementAt(rg.Next(_domains.Count));
return $"{localPart}@{domain}";
}
}
30 changes: 30 additions & 0 deletions tests/ConfirmNumericTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,34 @@ public static void ConfirmIsEven_WhenIsOdd(int actual)
action.ConfirmThrows<ConfirmAssertException>();
}
#endregion

#region ConfirmCloseTo
[TestCase(5f, 6f, 2f)]
[TestCase(5f, 5.1f, 0.1f)]
[TestCase(6f, 5f, 2f)]
[TestCase(5.1f, 5f, 0.1f)]
[TestCase(-5f, -4.5f, 0.5f)]
public static void ConfirmCloseTo_WhenCloseTo(
float actual,
float expected,
float tolerance
)
{
actual.ConfirmCloseTo(expected, tolerance);
}

[TestCase(5d, 0d, 1d)]
[TestCase(5d, 15d, 1d)]
[TestCase(0d, 0.1d, 0.01d)]
public static void ConfirmCloseTo_WhenNotCloseTo(
double actual,
double expected,
double tolerance
)
{
Action action = () => actual.ConfirmCloseTo(expected, tolerance);

action.ConfirmThrows<ConfirmAssertException>();
}
#endregion
}
31 changes: 31 additions & 0 deletions tests/ConfirmTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,35 @@ public static void IsEnumName_WhenIsNotEnumName(string name, bool ignoreCase)
action.ConfirmThrows<ConfirmAssertException>();
}
#endregion

#region IsTrue
[TestCase]
public static void IsTrue_WhenIsTrue()
{
Confirm.IsTrue(true);
}

[TestCase]
public static void IsTrue_WhenIsFalse()
{
Action action = () => Confirm.IsTrue(false);

action.ConfirmThrows<ConfirmAssertException>();
}
#endregion

#region IsFalse
public static void IsFalse_WhenIsFalse()
{
Confirm.IsFalse(false);
}

[TestCase]
public static void IsFalse_WhenIsTrue()
{
Action action = () => Confirm.IsFalse(true);

action.ConfirmThrows<ConfirmAssertException>();
}
#endregion
}
53 changes: 53 additions & 0 deletions tests/RandomBooleanTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Confirma.Attributes;
using Confirma.Extensions;

namespace Confirma.Tests;

[TestClass]
[Parallelizable]
public static class RandomBooleanTest
{
private readonly static Random rg = new();

[Repeat(5)]
[TestCase]
public static void NextBool()
{
const uint ITERATIONS = 100000;
uint trueCount = 0;

for (int i = 0; i < ITERATIONS; i++) if (rg.NextBool()) trueCount++;

var truePercentage = (double)trueCount / ITERATIONS * 100;
truePercentage.ConfirmCloseTo(50, 1);
}

[Repeat(5)]
[TestCase]
public static void NextNullableBool()
{
const uint ITERATIONS = 100000;

uint trueCount = 0;
uint falseCount = 0;
uint nullCount = 0;

for (int i = 0; i < ITERATIONS; i++)
{
bool? result = rg.NextNullableBool();

if (result == true) trueCount++;
else if (result == false) falseCount++;
else nullCount++;
}

var truePercentage = (double)trueCount / ITERATIONS * 100;
var falsePercentage = (double)falseCount / ITERATIONS * 100;
var nullPercentage = (double)nullCount / ITERATIONS * 100;

nullPercentage.ConfirmCloseTo(33.33, 1);
truePercentage.ConfirmCloseTo(33.33, 1);
falsePercentage.ConfirmCloseTo(33.33, 1);
}
}
Loading

0 comments on commit 5cf4677

Please sign in to comment.