diff --git a/Test/TestChecksum.hh b/Test/TestChecksum.hh index e0b2d90268a3d40e3ed9dd400c0a1ba6205a5d35..87984e84a5e59d80ebc2371711240d2e419dce3b 100644 --- a/Test/TestChecksum.hh +++ b/Test/TestChecksum.hh @@ -1,4 +1,4 @@ -// (C) Copyright 2021 by Autodesk, Inc. +// (C) Copyright 2022 by Autodesk, Inc. #ifndef BASE_TESTCHECKSUM_HH_INCLUDED #define BASE_TESTCHECKSUM_HH_INCLUDED @@ -44,9 +44,9 @@ public: enum Type { EQUAL, // result is bitwise identical + NEGLIGIBLE, // result is negligibly different UNKNOWN, // non-negligible difference, but of unknown quality IMPROVED, // result is better - NEGLIGIBLE, // result is negligibly different SUSPICIOUS, // result is different, and the new result might be worse REGRESSED, // result is worse WORKED, // result works now, but used to fail @@ -55,8 +55,8 @@ public: static const char* type_text(const Type _type) { - static const char dscr[][32] = {"EQUAL", "UNKNOWN", "IMPROVED", - "NEGLIGIBLE", "SUSPICIOUS", "REGRESSED", "WORKED", "FAILED"}; + static const char dscr[][32] = {"EQUAL", "NEGLIGIBLE", "UNKNOWN", + "IMPROVED", "SUSPICIOUS", "REGRESSED", "WORKED", "FAILED"}; return dscr[_type]; } diff --git a/Test/TestChecksumNumberT.hh b/Test/TestChecksumNumberT.hh index a556ab995bac4a29685a240b7338da016e33362b..c0433d4eddd02b34935a5ddda6e2886e69c09e6c 100644 --- a/Test/TestChecksumNumberT.hh +++ b/Test/TestChecksumNumberT.hh @@ -1,21 +1,25 @@ -// (C) Copyright 2021 by Autodesk, Inc. +// (C) Copyright 2022 by Autodesk, Inc. -#ifndef BASE_CHECKSUMLOGVALUET_HH_INCLUDE -#define BASE_CHECKSUMLOGVALUET_HH_INCLUDE +#ifndef BASE_TESTCHECKSUMNUMBERT_HH_INCLUDED +#define BASE_TESTCHECKSUMNUMBERT_HH_INCLUDED #include <Base/Test/TestChecksum.hh> #ifdef TEST_ON + #include <cmath> #include <sstream> +#include <type_traits> -namespace Test { -namespace Checksum { +namespace Test +{ +namespace Checksum +{ -/*! Default implementation of a comparison class for ValueT. +/*! Comparison class using opeartor== of ValueT. */ template <typename ValueT> -struct DefaultCompareT +struct EqualityCompareT { bool same(const ValueT& _a, const ValueT& _b) const { return _a == _b; } }; @@ -29,21 +33,68 @@ struct NoCompareT }; +enum class ToleranceType +{ + ABSOLUTE, + RELATIVE +}; + +enum +{ + DUMMY_EXPONENT = 1234 +}; + /*! -Utility class to compare double with a tolerance. Can be used to redefine the -default compare class in class ValueT. +Utility class to compare values with a tolerance. Tolerance is specified via the +template argument as 10^EXPONENT. If RELATIVE is true, the difference is divided +by the right (new) value before comparison. */ -struct DoubleCompare +template <typename ScalarT, int EXPONENT, + ToleranceType TOLERANCE_TYPE = ToleranceType::ABSOLUTE> +struct TolerantCompareT { - DoubleCompare(double _tol = 1e-12) : tol_(_tol) {} - bool same(const double& _a, const double& _b) const + + static_assert(EXPONENT != DUMMY_EXPONENT, + "For floating point checksums, please explicitly define a comparison " + "class, e.g. this TolerantCompareT with an exponent suitable for your " + "checksum."); + static_assert( + EXPONENT <= std::numeric_limits<ScalarT>::max_exponent10 || + EXPONENT == DUMMY_EXPONENT, // disable assertion for default exponent + "Exponent too large"); + static_assert(EXPONENT >= std::numeric_limits<ScalarT>::min_exponent10, + "Exponent too small"); + + bool same(const ScalarT& _a, const ScalarT& _b) const { - return std::fabs(_a - _b) <= tol_; + static const auto TOLERANCE = std::pow(10, EXPONENT); + + // Prevent division by zero + if constexpr (TOLERANCE_TYPE == ToleranceType::RELATIVE) + { + if (_b == 0) + return _a == _b; + } + + double diff = std::fabs(_a - _b); + + if constexpr (TOLERANCE_TYPE == ToleranceType::RELATIVE) + diff /= _b; + + return diff <= TOLERANCE; } -private: - double tol_; }; +// The default Compare type for ValueT. For integral types exact equality is +// checked. For floating point type TolerantCompare is used which actually +// generates a compile error and asks the developer to choose a meaningful +// threshold. +template <typename ValueT> +using DefaultCompareT = std::conditional_t< // if + std::is_floating_point_v<ValueT>, // ValueT is floating point + TolerantCompareT<ValueT, DUMMY_EXPONENT>, // use TolerantCompareT + EqualityCompareT<ValueT>>; // else use EqualityCompare + /*! Generic checksum class to record and compare a value of a certain type. */ @@ -71,11 +122,13 @@ protected: // TODO: multiple comparisons, can we use just one? if (val_new == val_old) // bitwise comparison return Difference::EQUAL; - if (comp_.same(val_old, val_new)) // tolerance comparison - return Difference::NEGLIGIBLE; Base::OStringStream diff; diff << (val_new - val_old); + + if (comp_.same(val_old, val_new)) // tolerance comparison + return Difference(Difference::NEGLIGIBLE, diff.str); + return Difference(Difference::UNKNOWN, diff.str); } @@ -83,8 +136,14 @@ private: CompareT comp_; // Compare class. }; +template <typename ValueT, int EXPONENT, + ToleranceType TOLERANCE_TYPE = ToleranceType::ABSOLUTE> +using TolerantNumberT = + NumberT<ValueT, TolerantCompareT<ValueT, EXPONENT, TOLERANCE_TYPE>>; + + }//namespace Checksum }//namespace Test #endif//TEST_ON -#endif//BASE_CHECKSUMLOGVALUET_HH_INCLUDE +#endif//BASE_TESTCHECKSUMNUMBERT_HH_INCLUDED diff --git a/Test/TestReport.cc b/Test/TestReport.cc index a7fec103b963deded059e5a10d79da13506c5448..9586e332066069fae3e7d42f0597cf3f1dec43df 100644 --- a/Test/TestReport.cc +++ b/Test/TestReport.cc @@ -1,4 +1,4 @@ -// (C) Copyright 2021 by Autodesk, Inc. +// (C) Copyright 2022 by Autodesk, Inc. #ifdef TEST_ON @@ -373,18 +373,35 @@ fs::path construct_diff_path(const fs::path& _root_left, return _root_right / diff_flnm; } -// Add a border of asterisks around a string -std::string add_border(const std::string& _message) + +// Add a border of asterisks around multiple strings. Each string is put on a +// new line. +std::string add_border(const std::vector<std::string>& _messages) { - const char ENDL = Base::ENDL; - std::string border = std::string(_message.size() + 4, '*'); + using Base::ENDL; + const auto max_msg_size = std::max_element(_messages.begin(), _messages.end(), + [](const std::string& _a, const std::string& _b) + { return _a.size() < _b.size(); })->size(); + + const std::string border(max_msg_size + 4, '*'); Base::OStringStream strm; - strm << ENDL << border << ENDL << "* " << _message << " *" << ENDL << border - << ENDL; + strm << ENDL << border << ENDL; + for (const auto& message : _messages) + { + const std::string padding(max_msg_size - message.size(), ' '); + strm << "* " << message << padding << " *" << ENDL; + } + strm << border << ENDL; return strm.str; } +// Add a border of asterisks around a string +std::string add_border(const std::string& _message) +{ + return add_border(std::vector<std::string>(1, _message)); +} + // Namespace for functions that deal with updating and mirroring test // directories namespace Sync @@ -474,7 +491,7 @@ ExitStatus make_comparison(const char* const _dir_left, // Compare the checksums produced by each test in the common set. size_t common_tests_nmbr = common_tests.size(); - size_t diff_test_nmbr = 0, test_idx = 0; + size_t diff_test_nmbr = 0, negl_diff_test_nmbr = 0, test_idx = 0; if (!_show_progress) std::cout << "Comparing tests..."; @@ -508,6 +525,12 @@ ExitStatus make_comparison(const char* const _dir_left, // Count the number of tests for which differences were found ++diff_test_nmbr; + if (diff_stats.size() == 1 && + diff_stats.find(Checksum::Difference::NEGLIGIBLE) != diff_stats.end()) + { + ++negl_diff_test_nmbr; // all differences are negligible + } + // If set to update or mirror the tests, empty the left-suite test output // directory and copy over the checksum report from the right-suite test // output directory. @@ -526,11 +549,22 @@ ExitStatus make_comparison(const char* const _dir_left, } std::cout << "complete." << std::endl; + const auto test_string = [](size_t _nmbr) + { + auto res = std::to_string(_nmbr) + " TEST"; + if (_nmbr != 1) + res += "S"; + return res; + }; + // Print the comparison summary. - std::cout << add_border("COMPARISON HAS DETECTED DIFFERENCES IN " + - std::to_string(diff_test_nmbr) + " TEST" + - (diff_test_nmbr == 1 ? "" : "S") + ".") - << std::endl; + std::vector<std::string> messages; + messages.push_back("COMPARISON HAS DETECTED DIFFERENCES IN " + + test_string(diff_test_nmbr) + "."); + messages.push_back(std::string("THE DIFFERENCES ARE NEGLIGIBLE IN ") + + ((diff_test_nmbr == negl_diff_test_nmbr) ? "ALL " : "") + + test_string(negl_diff_test_nmbr) + "."); + std::cout << add_border(messages) << std::endl; // Are we creating diffs? const bool create_diffs = _cot == COT_SHORT_DIFF || _cot == COT_FULL_DIFF;