Makes DoubleNear() print the diff between the actual and the expected value when the match fails.

Also fix bogus MSVC warning about "alignment of a member was sensitive to packing".
Also bring in gtest 701.
This commit is contained in:
kosak 2015-01-08 02:38:14 +00:00
parent 61adbcc5c6
commit 6b81780310
3 changed files with 73 additions and 37 deletions

View File

@ -1879,20 +1879,23 @@ template <typename FloatType>
class FloatingEqMatcher { class FloatingEqMatcher {
public: public:
// Constructor for FloatingEqMatcher. // Constructor for FloatingEqMatcher.
// The matcher's input will be compared with rhs. The matcher treats two // The matcher's input will be compared with expected. The matcher treats two
// NANs as equal if nan_eq_nan is true. Otherwise, under IEEE standards, // NANs as equal if nan_eq_nan is true. Otherwise, under IEEE standards,
// equality comparisons between NANs will always return false. We specify a // equality comparisons between NANs will always return false. We specify a
// negative max_abs_error_ term to indicate that ULP-based approximation will // negative max_abs_error_ term to indicate that ULP-based approximation will
// be used for comparison. // be used for comparison.
FloatingEqMatcher(FloatType rhs, bool nan_eq_nan) : FloatingEqMatcher(FloatType expected, bool nan_eq_nan) :
rhs_(rhs), nan_eq_nan_(nan_eq_nan), max_abs_error_(-1) { expected_(expected), nan_eq_nan_(nan_eq_nan), max_abs_error_(-1) {
} }
// Constructor that supports a user-specified max_abs_error that will be used // Constructor that supports a user-specified max_abs_error that will be used
// for comparison instead of ULP-based approximation. The max absolute // for comparison instead of ULP-based approximation. The max absolute
// should be non-negative. // should be non-negative.
FloatingEqMatcher(FloatType rhs, bool nan_eq_nan, FloatType max_abs_error) : FloatingEqMatcher(FloatType expected, bool nan_eq_nan,
rhs_(rhs), nan_eq_nan_(nan_eq_nan), max_abs_error_(max_abs_error) { FloatType max_abs_error)
: expected_(expected),
nan_eq_nan_(nan_eq_nan),
max_abs_error_(max_abs_error) {
GTEST_CHECK_(max_abs_error >= 0) GTEST_CHECK_(max_abs_error >= 0)
<< ", where max_abs_error is" << max_abs_error; << ", where max_abs_error is" << max_abs_error;
} }
@ -1901,16 +1904,18 @@ class FloatingEqMatcher {
template <typename T> template <typename T>
class Impl : public MatcherInterface<T> { class Impl : public MatcherInterface<T> {
public: public:
Impl(FloatType rhs, bool nan_eq_nan, FloatType max_abs_error) : Impl(FloatType expected, bool nan_eq_nan, FloatType max_abs_error)
rhs_(rhs), nan_eq_nan_(nan_eq_nan), max_abs_error_(max_abs_error) {} : expected_(expected),
nan_eq_nan_(nan_eq_nan),
max_abs_error_(max_abs_error) {}
virtual bool MatchAndExplain(T value, virtual bool MatchAndExplain(T value,
MatchResultListener* /* listener */) const { MatchResultListener* listener) const {
const FloatingPoint<FloatType> lhs(value), rhs(rhs_); const FloatingPoint<FloatType> actual(value), expected(expected_);
// Compares NaNs first, if nan_eq_nan_ is true. // Compares NaNs first, if nan_eq_nan_ is true.
if (lhs.is_nan() || rhs.is_nan()) { if (actual.is_nan() || expected.is_nan()) {
if (lhs.is_nan() && rhs.is_nan()) { if (actual.is_nan() && expected.is_nan()) {
return nan_eq_nan_; return nan_eq_nan_;
} }
// One is nan; the other is not nan. // One is nan; the other is not nan.
@ -1918,12 +1923,24 @@ class FloatingEqMatcher {
} }
if (HasMaxAbsError()) { if (HasMaxAbsError()) {
// We perform an equality check so that inf will match inf, regardless // We perform an equality check so that inf will match inf, regardless
// of error bounds. If the result of value - rhs_ would result in // of error bounds. If the result of value - expected_ would result in
// overflow or if either value is inf, the default result is infinity, // overflow or if either value is inf, the default result is infinity,
// which should only match if max_abs_error_ is also infinity. // which should only match if max_abs_error_ is also infinity.
return value == rhs_ || fabs(value - rhs_) <= max_abs_error_; if (value == expected_) {
return true;
}
const FloatType diff = value - expected_;
if (fabs(diff) <= max_abs_error_) {
return true;
}
if (listener->IsInterested()) {
*listener << "which is " << diff << " from " << expected_;
}
return false;
} else { } else {
return lhs.AlmostEquals(rhs); return actual.AlmostEquals(expected);
} }
} }
@ -1933,14 +1950,14 @@ class FloatingEqMatcher {
// after outputting. // after outputting.
const ::std::streamsize old_precision = os->precision( const ::std::streamsize old_precision = os->precision(
::std::numeric_limits<FloatType>::digits10 + 2); ::std::numeric_limits<FloatType>::digits10 + 2);
if (FloatingPoint<FloatType>(rhs_).is_nan()) { if (FloatingPoint<FloatType>(expected_).is_nan()) {
if (nan_eq_nan_) { if (nan_eq_nan_) {
*os << "is NaN"; *os << "is NaN";
} else { } else {
*os << "never matches"; *os << "never matches";
} }
} else { } else {
*os << "is approximately " << rhs_; *os << "is approximately " << expected_;
if (HasMaxAbsError()) { if (HasMaxAbsError()) {
*os << " (absolute error <= " << max_abs_error_ << ")"; *os << " (absolute error <= " << max_abs_error_ << ")";
} }
@ -1952,14 +1969,14 @@ class FloatingEqMatcher {
// As before, get original precision. // As before, get original precision.
const ::std::streamsize old_precision = os->precision( const ::std::streamsize old_precision = os->precision(
::std::numeric_limits<FloatType>::digits10 + 2); ::std::numeric_limits<FloatType>::digits10 + 2);
if (FloatingPoint<FloatType>(rhs_).is_nan()) { if (FloatingPoint<FloatType>(expected_).is_nan()) {
if (nan_eq_nan_) { if (nan_eq_nan_) {
*os << "isn't NaN"; *os << "isn't NaN";
} else { } else {
*os << "is anything"; *os << "is anything";
} }
} else { } else {
*os << "isn't approximately " << rhs_; *os << "isn't approximately " << expected_;
if (HasMaxAbsError()) { if (HasMaxAbsError()) {
*os << " (absolute error > " << max_abs_error_ << ")"; *os << " (absolute error > " << max_abs_error_ << ")";
} }
@ -1973,7 +1990,7 @@ class FloatingEqMatcher {
return max_abs_error_ >= 0; return max_abs_error_ >= 0;
} }
const FloatType rhs_; const FloatType expected_;
const bool nan_eq_nan_; const bool nan_eq_nan_;
// max_abs_error will be used for value comparison when >= 0. // max_abs_error will be used for value comparison when >= 0.
const FloatType max_abs_error_; const FloatType max_abs_error_;
@ -1981,27 +1998,29 @@ class FloatingEqMatcher {
GTEST_DISALLOW_ASSIGN_(Impl); GTEST_DISALLOW_ASSIGN_(Impl);
}; };
// The following 3 type conversion operators allow FloatEq(rhs) and // The following 3 type conversion operators allow FloatEq(expected) and
// NanSensitiveFloatEq(rhs) to be used as a Matcher<float>, a // NanSensitiveFloatEq(expected) to be used as a Matcher<float>, a
// Matcher<const float&>, or a Matcher<float&>, but nothing else. // Matcher<const float&>, or a Matcher<float&>, but nothing else.
// (While Google's C++ coding style doesn't allow arguments passed // (While Google's C++ coding style doesn't allow arguments passed
// by non-const reference, we may see them in code not conforming to // by non-const reference, we may see them in code not conforming to
// the style. Therefore Google Mock needs to support them.) // the style. Therefore Google Mock needs to support them.)
operator Matcher<FloatType>() const { operator Matcher<FloatType>() const {
return MakeMatcher(new Impl<FloatType>(rhs_, nan_eq_nan_, max_abs_error_)); return MakeMatcher(
new Impl<FloatType>(expected_, nan_eq_nan_, max_abs_error_));
} }
operator Matcher<const FloatType&>() const { operator Matcher<const FloatType&>() const {
return MakeMatcher( return MakeMatcher(
new Impl<const FloatType&>(rhs_, nan_eq_nan_, max_abs_error_)); new Impl<const FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
} }
operator Matcher<FloatType&>() const { operator Matcher<FloatType&>() const {
return MakeMatcher(new Impl<FloatType&>(rhs_, nan_eq_nan_, max_abs_error_)); return MakeMatcher(
new Impl<FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
} }
private: private:
const FloatType rhs_; const FloatType expected_;
const bool nan_eq_nan_; const bool nan_eq_nan_;
// max_abs_error will be used for value comparison when >= 0. // max_abs_error will be used for value comparison when >= 0.
const FloatType max_abs_error_; const FloatType max_abs_error_;
@ -2489,9 +2508,10 @@ class ContainerEqMatcher {
typedef typename View::type StlContainer; typedef typename View::type StlContainer;
typedef typename View::const_reference StlContainerReference; typedef typename View::const_reference StlContainerReference;
// We make a copy of rhs in case the elements in it are modified // We make a copy of expected in case the elements in it are modified
// after this matcher is created. // after this matcher is created.
explicit ContainerEqMatcher(const Container& rhs) : rhs_(View::Copy(rhs)) { explicit ContainerEqMatcher(const Container& expected)
: expected_(View::Copy(expected)) {
// Makes sure the user doesn't instantiate this class template // Makes sure the user doesn't instantiate this class template
// with a const or reference type. // with a const or reference type.
(void)testing::StaticAssertTypeEq<Container, (void)testing::StaticAssertTypeEq<Container,
@ -2500,11 +2520,11 @@ class ContainerEqMatcher {
void DescribeTo(::std::ostream* os) const { void DescribeTo(::std::ostream* os) const {
*os << "equals "; *os << "equals ";
UniversalPrint(rhs_, os); UniversalPrint(expected_, os);
} }
void DescribeNegationTo(::std::ostream* os) const { void DescribeNegationTo(::std::ostream* os) const {
*os << "does not equal "; *os << "does not equal ";
UniversalPrint(rhs_, os); UniversalPrint(expected_, os);
} }
template <typename LhsContainer> template <typename LhsContainer>
@ -2516,7 +2536,7 @@ class ContainerEqMatcher {
LhsView; LhsView;
typedef typename LhsView::type LhsStlContainer; typedef typename LhsView::type LhsStlContainer;
StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs); StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
if (lhs_stl_container == rhs_) if (lhs_stl_container == expected_)
return true; return true;
::std::ostream* const os = listener->stream(); ::std::ostream* const os = listener->stream();
@ -2526,8 +2546,8 @@ class ContainerEqMatcher {
for (typename LhsStlContainer::const_iterator it = for (typename LhsStlContainer::const_iterator it =
lhs_stl_container.begin(); lhs_stl_container.begin();
it != lhs_stl_container.end(); ++it) { it != lhs_stl_container.end(); ++it) {
if (internal::ArrayAwareFind(rhs_.begin(), rhs_.end(), *it) == if (internal::ArrayAwareFind(expected_.begin(), expected_.end(), *it) ==
rhs_.end()) { expected_.end()) {
if (printed_header) { if (printed_header) {
*os << ", "; *os << ", ";
} else { } else {
@ -2540,8 +2560,8 @@ class ContainerEqMatcher {
// Now check for missing values. // Now check for missing values.
bool printed_header2 = false; bool printed_header2 = false;
for (typename StlContainer::const_iterator it = rhs_.begin(); for (typename StlContainer::const_iterator it = expected_.begin();
it != rhs_.end(); ++it) { it != expected_.end(); ++it) {
if (internal::ArrayAwareFind( if (internal::ArrayAwareFind(
lhs_stl_container.begin(), lhs_stl_container.end(), *it) == lhs_stl_container.begin(), lhs_stl_container.end(), *it) ==
lhs_stl_container.end()) { lhs_stl_container.end()) {
@ -2561,7 +2581,7 @@ class ContainerEqMatcher {
} }
private: private:
const StlContainer rhs_; const StlContainer expected_;
GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher); GTEST_DISALLOW_ASSIGN_(ContainerEqMatcher);
}; };

View File

@ -72,7 +72,7 @@ template <class Class, typename MethodPtr>
class InvokeMethodAction { class InvokeMethodAction {
public: public:
InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr) InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr)
: obj_ptr_(obj_ptr), method_ptr_(method_ptr) {} : method_ptr_(method_ptr), obj_ptr_(obj_ptr) {}
template <typename Result, typename ArgumentTuple> template <typename Result, typename ArgumentTuple>
Result Perform(const ArgumentTuple& args) const { Result Perform(const ArgumentTuple& args) const {
@ -81,8 +81,11 @@ class InvokeMethodAction {
} }
private: private:
Class* const obj_ptr_; // The order of these members matters. Reversing the order can trigger
// warning C4121 in MSVC (see
// http://computer-programming-forum.com/7-vc.net/6fbc30265f860ad1.htm ).
const MethodPtr method_ptr_; const MethodPtr method_ptr_;
Class* const obj_ptr_;
GTEST_DISALLOW_ASSIGN_(InvokeMethodAction); GTEST_DISALLOW_ASSIGN_(InvokeMethodAction);
}; };

View File

@ -3071,6 +3071,19 @@ TEST_F(DoubleNearTest, DoubleNearCanDescribeSelf) {
EXPECT_EQ("is anything", DescribeNegation(m3)); EXPECT_EQ("is anything", DescribeNegation(m3));
} }
TEST_F(DoubleNearTest, ExplainsResultWhenMatchFails) {
EXPECT_EQ("", Explain(DoubleNear(2.0, 0.1), 2.05));
EXPECT_EQ("which is 0.2 from 2", Explain(DoubleNear(2.0, 0.1), 2.2));
EXPECT_EQ("which is -0.3 from 2", Explain(DoubleNear(2.0, 0.1), 1.7));
const string explanation = Explain(DoubleNear(2.1, 1e-10), 2.1 + 1.2e-10);
// Different C++ implementations may print floating-point numbers
// slightly differently.
EXPECT_TRUE(explanation == "which is 1.2e-10 from 2.1" || // GCC
explanation == "which is 1.2e-010 from 2.1") // MSVC
<< " where explanation is \"" << explanation << "\".";
}
TEST_F(DoubleNearTest, NanSensitiveDoubleNearCanDescribeSelf) { TEST_F(DoubleNearTest, NanSensitiveDoubleNearCanDescribeSelf) {
Matcher<double> m1 = NanSensitiveDoubleNear(2.0, 0.5); Matcher<double> m1 = NanSensitiveDoubleNear(2.0, 0.5);
EXPECT_EQ("is approximately 2 (absolute error <= 0.5)", Describe(m1)); EXPECT_EQ("is approximately 2 (absolute error <= 0.5)", Describe(m1));