Skip to content

Commit 66be15f

Browse files
committed
feat: add MustNotBeApproximately
Signed-off-by: Kenny Pflug <kenny.pflug@live.de>
1 parent b139299 commit 66be15f

File tree

4 files changed

+588
-0
lines changed

4 files changed

+588
-0
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using BenchmarkDotNet.Attributes;
3+
4+
namespace Light.GuardClauses.Performance.ComparableAssertions;
5+
6+
[MemoryDiagnoser]
7+
// ReSharper disable once ClassCanBeSealed.Global -- Benchmark.NET derives from this class with dynamically created code
8+
public class MustNotBeApproximatelyBenchmark
9+
{
10+
[Benchmark]
11+
public double MustNotBeApproximately() => 5.2.MustNotBeApproximately(5.1, 0.5);
12+
13+
[Benchmark(Baseline = true)]
14+
public double Imperative() => Imperative(5.2, 5.1, 0.5);
15+
16+
private static double Imperative(double parameter, double other, double tolerance)
17+
{
18+
if (Math.Abs(parameter - other) < tolerance)
19+
{
20+
throw new ArgumentOutOfRangeException(nameof(parameter), $"Value is approximately {other}");
21+
}
22+
23+
return parameter;
24+
}
25+
}
Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
using System;
2+
using FluentAssertions;
3+
using Xunit;
4+
5+
namespace Light.GuardClauses.Tests.ComparableAssertions;
6+
7+
public static class MustNotBeApproximatelyTests
8+
{
9+
[Theory]
10+
[InlineData(5.3, 5.0, 0.2)]
11+
[InlineData(10.4, 10.3, 0.01)]
12+
[InlineData(3.15, 3.14, 0.001)]
13+
[InlineData(-42.002, -42.0001, 0.001)]
14+
public static void ValuesNotApproximatelyEqual_Double(double value, double other, double tolerance) =>
15+
value.MustNotBeApproximately(other, tolerance).Should().Be(value);
16+
17+
[Theory]
18+
[InlineData(5.3f, 5.0f, 0.2f)]
19+
[InlineData(10.4f, 10.3f, 0.01f)]
20+
[InlineData(3.15f, 3.14f, 0.001f)]
21+
[InlineData(-42.002f, -42.0001f, 0.001f)]
22+
public static void ValuesNotApproximatelyEqual_Float(float value, float other, float tolerance) =>
23+
value.MustNotBeApproximately(other, tolerance).Should().Be(value);
24+
25+
[Theory]
26+
[InlineData(5.0, 5.05, 0.1)]
27+
[InlineData(100.0, 99.95, 0.1)]
28+
[InlineData(-20.0, -20.05, 0.1)]
29+
[InlineData(0.0001, 0.00015, 0.0001)]
30+
public static void ValuesApproximatelyEqual_Double(double value, double other, double tolerance)
31+
{
32+
var act = () => value.MustNotBeApproximately(other, tolerance, nameof(value));
33+
34+
var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
35+
exceptionAssertion.Message.Should().Contain(
36+
$"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
37+
);
38+
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
39+
}
40+
41+
[Theory]
42+
[InlineData(5.0f, 5.05f, 0.1f)]
43+
[InlineData(100.0f, 99.95f, 0.1f)]
44+
[InlineData(-20.0f, -20.05f, 0.1f)]
45+
[InlineData(0.0001f, 0.00015f, 0.0001f)]
46+
public static void ValuesApproximatelyEqual_Float(float value, float other, float tolerance)
47+
{
48+
var act = () => value.MustNotBeApproximately(other, tolerance, nameof(value));
49+
50+
var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
51+
exceptionAssertion.Message.Should().Contain(
52+
$"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
53+
);
54+
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
55+
}
56+
57+
[Fact]
58+
public static void DefaultTolerance_Double()
59+
{
60+
// Should throw - difference is 0.00005 which is less than default tolerance 0.0001
61+
const double value = 1.00005;
62+
Action act = () => value.MustNotBeApproximately(1.0, "parameter");
63+
act.Should().Throw<ArgumentOutOfRangeException>()
64+
.WithParameterName("parameter");
65+
66+
// Should pass - difference is 0.0002 which is greater than default tolerance 0.0001
67+
const double value2 = 1.0002;
68+
value2.MustNotBeApproximately(1.0).Should().Be(value2);
69+
}
70+
71+
[Fact]
72+
public static void DefaultTolerance_Float()
73+
{
74+
// Should throw - difference is 0.00005f which is less than default tolerance 0.0001f
75+
const float value = 1.00005f;
76+
Action act = () => value.MustNotBeApproximately(1.0f, "parameter");
77+
act.Should().Throw<ArgumentOutOfRangeException>()
78+
.WithParameterName("parameter");
79+
80+
// Should pass - difference is 0.0002f which is greater than default tolerance 0.0001f
81+
const float value2 = 1.0002f;
82+
value2.MustNotBeApproximately(1.0f).Should().Be(value2);
83+
}
84+
85+
[Fact]
86+
public static void CustomException_Double() =>
87+
Test.CustomException(
88+
5.0,
89+
5.0000000001,
90+
(x, y, exceptionFactory) => x.MustNotBeApproximately(y, exceptionFactory)
91+
);
92+
93+
[Fact]
94+
public static void CustomExceptionWithTolerance_Double() =>
95+
Test.CustomException(
96+
5.0,
97+
5.05,
98+
0.1,
99+
(x, y, z, exceptionFactory) => x.MustNotBeApproximately(y, z, exceptionFactory)
100+
);
101+
102+
[Fact]
103+
public static void CustomException_Float() =>
104+
Test.CustomException(
105+
5.0f,
106+
5.000001f,
107+
(x, y, exceptionFactory) => x.MustNotBeApproximately(y, exceptionFactory)
108+
);
109+
110+
[Fact]
111+
public static void CustomExceptionWithTolerance_Float() =>
112+
Test.CustomException(
113+
5.0f,
114+
5.05f,
115+
0.1f,
116+
(x, y, z, exceptionFactory) => x.MustNotBeApproximately(y, z, exceptionFactory)
117+
);
118+
119+
[Fact]
120+
public static void NoCustomExceptionThrown_Double() =>
121+
5.2.MustNotBeApproximately(5.0, 0.1, (_, _, _) => null).Should().Be(5.2);
122+
123+
[Fact]
124+
public static void NoCustomExceptionThrown_Float() =>
125+
5.2f.MustNotBeApproximately(5.0f, 0.1f, (_, _, _) => null).Should().Be(5.2f);
126+
127+
[Fact]
128+
public static void CustomMessage_Double() =>
129+
Test.CustomMessage<ArgumentOutOfRangeException>(
130+
message => 100.0.MustNotBeApproximately(100.05, 0.1, message: message)
131+
);
132+
133+
[Fact]
134+
public static void CustomMessage_Float() =>
135+
Test.CustomMessage<ArgumentOutOfRangeException>(
136+
message => 100.0f.MustNotBeApproximately(100.05f, 0.1f, message: message)
137+
);
138+
139+
[Fact]
140+
public static void CallerArgumentExpression_Double()
141+
{
142+
const double seventyEightO1 = 78.1;
143+
144+
var act = () => seventyEightO1.MustNotBeApproximately(78.099999);
145+
146+
act.Should().Throw<ArgumentOutOfRangeException>()
147+
.WithParameterName(nameof(seventyEightO1));
148+
}
149+
150+
[Fact]
151+
public static void CallerArgumentExpressionWithTolerance_Double()
152+
{
153+
const double pi = 3.14159;
154+
155+
var act = () => pi.MustNotBeApproximately(3.14, 0.01);
156+
157+
act.Should().Throw<ArgumentOutOfRangeException>()
158+
.WithParameterName(nameof(pi));
159+
}
160+
161+
[Fact]
162+
public static void CallerArgumentExpression_Float()
163+
{
164+
const float seventyEightO1 = 78.1f;
165+
166+
var act = () => seventyEightO1.MustNotBeApproximately(78.100005f);
167+
168+
act.Should().Throw<ArgumentOutOfRangeException>()
169+
.WithParameterName(nameof(seventyEightO1));
170+
}
171+
172+
[Fact]
173+
public static void CallerArgumentExpressionWithTolerance_Float()
174+
{
175+
const float pi = 3.14159f;
176+
177+
var act = () => pi.MustNotBeApproximately(3.14f, 0.01f);
178+
179+
act.Should().Throw<ArgumentOutOfRangeException>()
180+
.WithParameterName(nameof(pi));
181+
}
182+
183+
#if NET8_0
184+
[Theory]
185+
[InlineData(5.3, 5.0, 0.2)]
186+
[InlineData(10.4, 10.3, 0.01)]
187+
[InlineData(3.15, 3.14, 0.001)]
188+
[InlineData(-42.002, -42.0001, 0.001)]
189+
public static void ValuesNotApproximatelyEqual_Generic(double value, double other, double tolerance) =>
190+
value.MustNotBeApproximately<double>(other, tolerance).Should().Be(value);
191+
192+
[Theory]
193+
[InlineData(5.0, 5.05, 0.1)]
194+
[InlineData(100.0, 99.95, 0.1)]
195+
[InlineData(-20.0, -20.05, 0.1)]
196+
[InlineData(0.0001, 0.00015, 0.0001)]
197+
public static void ValuesApproximatelyEqual_Generic(double value, double other, double tolerance)
198+
{
199+
var act = () => value.MustNotBeApproximately<double>(other, tolerance, nameof(value));
200+
201+
var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
202+
exceptionAssertion.Message.Should().Contain(
203+
$"{nameof(value)} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
204+
);
205+
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
206+
}
207+
208+
[Fact]
209+
public static void CustomExceptionWithTolerance_Generic() =>
210+
Test.CustomException(
211+
5.0,
212+
5.05,
213+
0.1,
214+
(x, y, t, exceptionFactory) => x.MustNotBeApproximately<double>(y, t, exceptionFactory)
215+
);
216+
217+
[Fact]
218+
public static void NoCustomExceptionThrown_Generic() =>
219+
5.2.MustNotBeApproximately<double>(5.0, 0.1, (_, _, _) => null).Should().Be(5.2);
220+
221+
[Fact]
222+
public static void CustomMessage_Generic() =>
223+
Test.CustomMessage<ArgumentOutOfRangeException>(
224+
message => 100.0.MustNotBeApproximately<double>(100.05, 0.1, message: message)
225+
);
226+
227+
[Fact]
228+
public static void CallerArgumentExpressionWithTolerance_Generic()
229+
{
230+
const double seventyEightO1 = 78.1;
231+
232+
var act = () => seventyEightO1.MustNotBeApproximately<double>(78.0, 0.2);
233+
234+
act.Should().Throw<ArgumentOutOfRangeException>()
235+
.WithParameterName(nameof(seventyEightO1));
236+
}
237+
#endif
238+
}

0 commit comments

Comments
 (0)