Skip to content

Commit 8d3c1fd

Browse files
committed
Merge branch 'fix/be-within-runs'
2 parents 5fb1a59 + 76e3d16 commit 8d3c1fd

2 files changed

Lines changed: 30 additions & 5 deletions

File tree

‎include/matchers/numeric/be_within.hpp‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,26 @@ template <typename A, typename E>
4242
BeWithin<A, E> BeWithinHelper<A, E>::of(E expected) {
4343
auto matcher = BeWithin<A, E>(expectation, tolerance, expected, ""); // No unit specified
4444
matcher.set_message(msg);
45+
matcher.run(); // of() completes the expectation, like every other to_* matcher
4546
return matcher;
4647
}
4748

4849
template <typename A, typename E>
4950
BeWithin<A, E> BeWithinHelper<A, E>::percent_of(E expected) {
5051
auto matcher = BeWithin<A, E>(expectation, tolerance, expected, "%"); // Percent unit specified
5152
matcher.set_message(msg);
53+
matcher.run(); // percent_of() completes the expectation, like every other to_* matcher
5254
return matcher;
5355
}
5456

5557
template <typename A, typename E>
5658
bool BeWithin<A, E>::match() {
57-
if (!this->expected()) {
58-
return false;
59-
}
60-
return std::abs(this->actual() - this->expected()) <= this->tolerance;
59+
const auto actual = static_cast<long double>(this->actual());
60+
const auto expected = static_cast<long double>(this->expected());
61+
// A percentage tolerance is relative to the expected value.
62+
const auto tolerance = unit == "%" ? std::abs(expected) * static_cast<long double>(this->tolerance) / 100
63+
: static_cast<long double>(this->tolerance);
64+
return std::abs(actual - expected) <= tolerance;
6165
}
6266

6367
template <typename A, typename E>

‎spec/matchers/be_within_spec.cpp‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <algorithm>
12
#include <source_location>
23

34
#include "cppspec.hpp"
@@ -59,6 +60,20 @@ describe be_within_spec("expect(actual).to_be_within(delta).of(expected)", $ {
5960
// }).to_fail_with("expected 5.51 to be within 0.5 of 5.0");
6061
// });
6162

63+
it("reports a failure through expect() when actual is outside the tolerance", _ {
64+
expect(4.49).to_be_within(0.5).of(5.0);
65+
const bool failed = std::ranges::any_of(self.get_results(), [](const Result& r) { return r.is_failure(); });
66+
self.clear_results();
67+
expect(failed).to_be_true();
68+
});
69+
70+
it("passes when expected is zero", _ {
71+
expect(0.05).to_be_within(0.1).of(0.0);
72+
const bool failed = std::ranges::any_of(self.get_results(), [](const Result& r) { return r.is_failure(); });
73+
self.clear_results();
74+
expect(failed).to_be_false();
75+
});
76+
6277
it("provides a description", _ {
6378
double d = 5.1;
6479
ExpectationValue ex(self, d, std::source_location::current());
@@ -80,8 +95,14 @@ describe be_within_spec("expect(actual).to_be_within(delta).of(expected)", $ {
8095
expect(matcher.run()).to_fail_with("expected 20.1 to be within 10% of 10");
8196
});
8297

98+
it("scales the tolerance by expected", _ {
99+
auto ex = ExpectationValue(12.0, std::source_location::current());
100+
auto matcher = Matchers::BeWithinHelper(ex, 10.0).percent_of(10.0);
101+
expect(matcher.run()).to_fail_with("expected 12 to be within 10% of 10");
102+
});
103+
83104
it("provides a description", _ {
84-
auto d = 5.1;
105+
auto d = 5.01; // within 0.5% of 5: percent_of() now runs the matcher
85106
ExpectationValue<double> ex(self, d, std::source_location::current());
86107
Matchers::BeWithin matcher = Matchers::BeWithinHelper(ex, 0.5).percent_of(5.0);
87108
expect(matcher.description()).to_equal("be within 0.5% of 5");

0 commit comments

Comments
 (0)