diff --git a/Debug/DebOut.hh b/Debug/DebOut.hh index 9a5fe2577569bd11a4994180368fd6d35c25f514..2200081dcfb2a53517d733edcf82027ee8cdd534 100644 --- a/Debug/DebOut.hh +++ b/Debug/DebOut.hh @@ -9,7 +9,9 @@ #ifndef DEB_ON #define DEB_enter_func PROGRESS_TICK; -#define DEB_only(CC) +#define DEB_only(EXPR) +#define DEB_onlyc(EXPR) +#define DEB_only_if(CC, EXPR) #define DEB_exec(LL, AA) { PROGRESS_TICK; } #define DEB_exec_if(CC, LL, AA) { PROGRESS_TICK; } #define DEB_out(LL, AA) { PROGRESS_TICK; } @@ -138,7 +140,9 @@ std::string to_string(const T& _t) static int deb_lvl = ::Debug::INVALID_LEVEL; \ ::Debug::Enter deb(__FILE__, __FUNCTION__, deb_nmbr, deb_lvl); -#define DEB_only(CC) CC +#define DEB_only(EXPR) EXPR +#define DEB_onlyc(EXPR) , EXPR // useful when comma is optionally required +#define DEB_only_if(CC, EXPR) if (CC) { (EXPR); } #define DEB_if(CC, LL, EXPR) \ { \ diff --git a/Test/TestChecksum.cc b/Test/TestChecksum.cc index ae57ad41c0917cb115c2c63610524a1c66cb7936..9b2217a85ba85518c7b33549b62fda4a827334ea 100644 --- a/Test/TestChecksum.cc +++ b/Test/TestChecksum.cc @@ -10,6 +10,7 @@ #include "TestChecksumReport.hh" #include "TestError.hh" +#include <Base/Code/Quality.hh> #include <Base/Debug/DebCallStack.hh> #include <Base/Paths/Filesystem.hh> #include <Base/Paths/PathLink.hh> @@ -190,21 +191,26 @@ std::string Object::format_checksum(const Result& _rslt, const String& _data) return strm.str; } -void Object::add(const Result& _rslt, const String& _data) +void Object::add(const Result& _rslt, const String& _data, bool _debg_call_stck) { Base::OStringStream strm; #ifdef DEB_ON - static String prev_call_stck; - String call_stck("/"); - Debug::CallStack::query().append(call_stck); - - if (prev_call_stck != call_stck) + if (_debg_call_stck) { - strm << call_stck << ::Base::ENDL; - prev_call_stck = call_stck; + static String prev_call_stck; + String call_stck("/"); + Debug::CallStack::query().append(call_stck); + + if (prev_call_stck != call_stck) + { + strm << call_stck << ::Base::ENDL; + prev_call_stck = call_stck; + } } -#endif // DEB_ON +#else + LOW_CODE_QUALITY_VARIABLE_ALLOW(_debg_call_stck); +#endif // Write checksum data and remove trailing whitespace strm << format_checksum(_rslt, _data); @@ -455,7 +461,7 @@ DifferenceDistribution Compare::Impl::run(Base::IOutputStream& _log_os) const _log_os << REMOVED_TAG << old_entr.line() << ENDL; // just print it if (!old_entr.group()) // if it is a group change, just ignore it { - if (Checksum::completion.end(old_entr.line())) + if (Checksum::completion.success(old_entr.line())) ++test_diff[Difference(Difference::FAILED, old_entr.line())]; else ++test_diff[Difference(Difference::UNKNOWN, "Removed checksum")]; @@ -467,7 +473,7 @@ DifferenceDistribution Compare::Impl::run(Base::IOutputStream& _log_os) const _log_os << ADDED_TAG << new_entr.line() << ENDL; // just print it if (!new_entr.group()) // if it is a group change, just ignore it { - if (Checksum::completion.end(new_entr.line())) + if (Checksum::completion.success(new_entr.line())) ++test_diff[Difference(Difference::WORKED, new_entr.line())]; else ++test_diff[Difference(Difference::UNKNOWN, "Added checksum")]; @@ -577,6 +583,17 @@ public: { } + void initialize() + { + if (opened_) + return; + + strm_.open(REPORT_FILENAME); + TEST_THROW_ERROR_if(!strm_.good(), TEST_CHECKSUM_FAILED_OPEN_REPORT); + opened_ = true; + strm_ << REPORT_LEVEL_TAG << LEVEL_TEXT[run_lvl] << ::Base::ENDL; + } + void operator()(const std::string& _str) { if (_str.empty()) @@ -585,12 +602,7 @@ public: static std::mutex mtx; // synchronize access to the checksum report stream std::lock_guard<std::mutex> lock(mtx); - if (!opened_) - { - strm_.open(REPORT_FILENAME); - opened_ = true; - strm_ << REPORT_LEVEL_TAG << LEVEL_TEXT[run_lvl] << ::Base::ENDL; - } + initialize(); // ensure stream is opened strm_ << _str << ::Base::ENDL; strm_.flush(); @@ -607,6 +619,8 @@ void Write::make() { impl_ = new Impl; } void Write::set(Write& _othr) { impl_ = _othr.impl_; } +void Write::initialize() { impl_->initialize(); } + void Write::operator()(const std::string& _str) { (*impl_)(_str); } Write write; diff --git a/Test/TestChecksum.hh b/Test/TestChecksum.hh index 87984e84a5e59d80ebc2371711240d2e419dce3b..c984597a2490bcf6d7f95a52a2be080268f18ffa 100644 --- a/Test/TestChecksum.hh +++ b/Test/TestChecksum.hh @@ -158,8 +158,12 @@ protected: */ Object(const char* const _name, const Level _lvl = L_ALL); - //! Add a record of the checksum (protected version) - void add(const Result& _rslt, const String& _data); + /*! + Add a record of the checksum (protected version). + \param _debg_call_stck Also output the current call stack location (only in + Debug mode) + */ + void add(const Result& _rslt, const String& _data, bool _debg_call_stck = true); /*! Compare the data, the default implementation does a string comparison. diff --git a/Test/TestChecksumCompletion.cc b/Test/TestChecksumCompletion.cc index 08485def86e6208b6421a852449a20b83bf1db01..5b986fde88970b38a39ce219f24967d24d99dc3c 100644 --- a/Test/TestChecksumCompletion.cc +++ b/Test/TestChecksumCompletion.cc @@ -15,23 +15,34 @@ namespace Checksum namespace { -const char* const END = "END"; +const char* const SUCCESS = "Success: END"; +const char* const FAILURE = "Failure: "; } // namespace Completion::Completion() : Object("Completion", L_STABLE) {} -void Completion::record_end() +void Completion::record_success() +{ + add(Result::OK, SUCCESS, false); +} + +void Completion::record_failure(const std::string& _msg) { std::stringstream mess; - mess << "Success: " << END; + mess << FAILURE << _msg; + add(Result::FAILURE, mess.str(), false); +} - add(Result::OK, mess.str()); +bool Completion::success(const std::string& _line) +{ + return _line.find(Checksum::completion.name()) != std::string::npos && + _line.find(SUCCESS) != std::string::npos; } -bool Completion::end(const std::string& _line) +bool Completion::failure(const std::string& _line) { return _line.find(Checksum::completion.name()) != std::string::npos && - _line.find(END) != std::string::npos; + _line.find(FAILURE) != std::string::npos; } // Register the checksum to check test completion. diff --git a/Test/TestChecksumCompletion.hh b/Test/TestChecksumCompletion.hh index 6e05ccafa66397d1b81c036200072dce9ed7e731..a0a08255829a5d7d6102acdc1ffe5a094201ed27 100644 --- a/Test/TestChecksumCompletion.hh +++ b/Test/TestChecksumCompletion.hh @@ -19,11 +19,20 @@ class Completion : public Object public: Completion(); - //! Record the test "end" completion checksum - void record_end(); + //! Record the test "success" completion checksum + void record_success(); - //! Get if the line contains the end completion checksum record - static bool end(const std::string& _line); + /*! + Record a test failure checksum + \param _msg The reason for the failure, e.g. an exception + */ + void record_failure(const std::string& _msg); + + //! Get if the line contains the "success" completion checksum record + static bool success(const std::string& _line); + + //! Get if the line contains the "failure" completion checksum record + static bool failure(const std::string& _line); }; // Register the checksum to check test completion. diff --git a/Test/TestChecksumNumberT.hh b/Test/TestChecksumNumberT.hh index c0433d4eddd02b34935a5ddda6e2886e69c09e6c..36b8afd15cc3e4cf0a9b6045e98a8ef4031eb9c0 100644 --- a/Test/TestChecksumNumberT.hh +++ b/Test/TestChecksumNumberT.hh @@ -15,135 +15,193 @@ namespace Test { namespace Checksum { +//! Support functionality for NumberT<> test checksums +namespace Number +{ -/*! Comparison class using opeartor== of ValueT. -*/ -template <typename ValueT> -struct EqualityCompareT +//! Standard methods to compute the change between two different checksum numbers +namespace Change +{ + +//! The change of the checksum number is absolute +struct Absolute { - bool same(const ValueT& _a, const ValueT& _b) const { return _a == _b; } + template <typename ValueT> + ValueT operator()(const ValueT& _old, const ValueT& _new) const + { + return _new - _old; + } }; -/*! Comparison class for checksums that MUST only be logged. -*/ -template <typename ValueT> -struct NoCompareT +//! Change of the number checksum is relative w.r.t. the old value +struct Relative { - bool same(const ValueT& _a, const ValueT& _b) const { return true; } + template <typename ValueT> + ValueT operator()(const ValueT& _old, const ValueT& _new) const + { + const auto chng = _new - _old; + return _old == 0 ? chng : chng / _old; + } }; +} // namespace Change -enum class ToleranceType +//! Methods for deciding if a number checksum change is negligible +namespace Negligible { - ABSOLUTE, - RELATIVE +//! No change is considered negligible, do not use for floating point numbers. +struct None +{ + template <class ValueT> bool operator()(const ValueT&) { return false; } }; -enum +//! Consider a value change <= 10^EXPONENT as neglegible +template <int EXPONENT> struct Exponent10ToleranceT { - DUMMY_EXPONENT = 1234 + template <typename ValueT> + bool operator()(const ValueT& _diff) const + { + static_assert(EXPONENT <= std::numeric_limits<ValueT>::max_exponent10, + "Exponent too large"); + static_assert(EXPONENT >= std::numeric_limits<ValueT>::min_exponent10, + "Exponent too small"); + static const auto TOLERANCE = std::pow(10, EXPONENT); + return std::abs(_diff) <= TOLERANCE; + } }; +} // namespace Negligible + +//! Qualify the change of the checksum value as a Difference::Type value +namespace Qualify +{ + +//! Default approach, qualify as unknown +struct None +{ + template <typename ValueT> + Difference::Type operator()(const ValueT&, const ValueT&) const + { + return Difference::UNKNOWN; + } +}; + +//! Qualify the new value as an improvement if it is smaller than the old one +struct SmallerIsBetter +{ + template <typename ValueT> + Difference::Type operator()(const ValueT& _old, const ValueT& _new) const + { + return _old > _new ? Difference::IMPROVED : Difference::REGRESSED; + } +}; + +// Qualify the new value as an improvement if it is larger than the old one +struct LargerIsBetter +{ + template <typename ValueT> + Difference::Type operator()(const ValueT& _old, const ValueT& _new) const + { + return _old < _new ? Difference::IMPROVED : Difference::REGRESSED; + } +}; + +// Qualify the new value as an improvement if it is closer to 0 +struct Target0 +{ + template <typename ValueT> + Difference::Type operator()(const ValueT& _old, const ValueT& _new) const + { + return std::abs(_old) > std::abs(_new) ? Difference::IMPROVED + : Difference::REGRESSED; + } +}; + +} // namespace Qualify + /*! -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. +Compare the new vs the old number checksum value. Various aspects of the +comparison can be parameterized through the template arguments. */ -template <typename ScalarT, int EXPONENT, - ToleranceType TOLERANCE_TYPE = ToleranceType::ABSOLUTE> -struct TolerantCompareT -{ - - 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 +template <class ValueT, + class ChangeT = Change::Absolute, //!< compute the values change + class NegligibleT = Negligible::None, //!< is the change negligible + class QualifyT = Qualify::None //!< qualify the change as a Difference::Type + > +struct CompareT +{ + // If we have a floating point Value type, the Negligible type trait should be + // set to something different than the Neglegible::None default. + static_assert(!std::is_floating_point<ValueT>::value || + !std::is_same<NegligibleT, Negligible::None>::value, + "For floating point checksums, please specify a NeglegibleT trait that " + "allows small changes to qualify as negligible differences, e.g., use " + "Qualify::Exponent10ToleranceT"); + + ValueT change(const ValueT& _old, const ValueT& _new) const { - static const auto TOLERANCE = std::pow(10, EXPONENT); + return ChangeT()(_old, _new); + } - // Prevent division by zero - if constexpr (TOLERANCE_TYPE == ToleranceType::RELATIVE) - { - if (_b == 0) - return _a == _b; - } + bool neglegible(const ValueT& _diff) const + { + return NegligibleT()(_diff); + } - double diff = std::fabs(_a - _b); + Difference::Type qualify(const ValueT& _old, const ValueT& _new) const + { + return QualifyT()(_old, _new); + } - if constexpr (TOLERANCE_TYPE == ToleranceType::RELATIVE) - diff /= _b; + Difference operator()(const ValueT& _old, const ValueT& _new) const + { + if (_old == _new) // bitwise equal? + return Difference::EQUAL; - return diff <= TOLERANCE; + const auto diff = change(_old, _new); + Base::OStringStream strm; + strm << diff; + return Difference( + neglegible(diff) ? Difference::NEGLIGIBLE : qualify(_old, _new), + strm.str); } }; -// 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 +} // namespace Number /*! Generic checksum class to record and compare a value of a certain type. */ -template <typename ValueT, class CompareT = DefaultCompareT<ValueT>> +template <typename ValueT, + class ChangeT = Number::Change::Absolute, //!< compute the values change + class NegligibleT = Number::Negligible::None, //!< is the change negligible + class QualifyT = + Number::Qualify::None //!< qualify the change as a Difference::Type + > class NumberT : public Object { public: - NumberT(const char* const _name, //!<[in] Checksum name - const Level _lvl = L_ALL, //!<[in] Checksum level - const CompareT& _comp = CompareT() //!<[in] Comparison function - ) - : Object(_name, _lvl), comp_(_comp) + NumberT(const char* const _name, const Level _lvl = L_ALL) + : Object(_name, _lvl) { } protected: - //! Generic implementation of number data comparison virtual Difference compare_data(const String& _old, const String& _new) const { std::istringstream strm_old(_old), strm_new(_new); ValueT val_old, val_new; strm_old >> val_old; strm_new >> val_new; - - // TODO: multiple comparisons, can we use just one? - if (val_new == val_old) // bitwise comparison - return Difference::EQUAL; - - 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); + return cmpr_(val_old, val_new); } private: - CompareT comp_; // Compare class. + Number::CompareT<ValueT, ChangeT, NegligibleT, QualifyT> cmpr_; }; -template <typename ValueT, int EXPONENT, - ToleranceType TOLERANCE_TYPE = ToleranceType::ABSOLUTE> -using TolerantNumberT = - NumberT<ValueT, TolerantCompareT<ValueT, EXPONENT, TOLERANCE_TYPE>>; - - -}//namespace Checksum -}//namespace Test +} // namespace Checksum +} // namespace Test -#endif//TEST_ON -#endif//BASE_TESTCHECKSUMNUMBERT_HH_INCLUDED +#endif // TEST_ON +#endif // BASE_TESTCHECKSUMNUMBERT_HH_INCLUDED diff --git a/Test/TestChecksumReport.hh b/Test/TestChecksumReport.hh index 3ee9d6882eb4172a873366d6283beb4690312af9..f5293865609b6511f2164d3863357587454e671e 100644 --- a/Test/TestChecksumReport.hh +++ b/Test/TestChecksumReport.hh @@ -101,7 +101,10 @@ public: //! Set an instance made in another binary void set(Write&); - // write _str to the report + //! Open the report file for writing (in the current working directory) + void initialize(); + + // Write _str to the report file void operator()(const std::string& _str); private: diff --git a/Test/TestError.hh b/Test/TestError.hh index cd8b4586db3b35c7f1cd0072d658a39a94926ce3..7107e7e06161f1bce791c2a7dec3affa7a5cc49d 100755 --- a/Test/TestError.hh +++ b/Test/TestError.hh @@ -6,6 +6,7 @@ #ifdef TEST_ON #include <Base/Utils/BaseError.hh> +#include <Base/Utils/OStringStream.hh> #include <exception> diff --git a/Test/TestErrorInc.hh b/Test/TestErrorInc.hh index bd058720652739205816b2d1f9f2817d9a1e0c2b..14692946be2507de416a3ab41a58b1b0bc9e19c7 100755 --- a/Test/TestErrorInc.hh +++ b/Test/TestErrorInc.hh @@ -24,4 +24,8 @@ DEFINE_ERROR(TEST_OUTCOME_UNEXPECTED, "Unexpected outcome.") // Error codes related to argument parsing DEFINE_ERROR(TEST_TOO_FEW_ARGS, "Too few arguments have been supplied.") +// Error codes relating to TestChecksum.(cc|hh) +DEFINE_ERROR(TEST_CHECKSUM_FAILED_OPEN_REPORT, + "Could not open report file.") + #endif // TEST_ON diff --git a/Utils/IOutputStream.hh b/Utils/IOutputStream.hh index 4fe89e6bdbbbac9fbbad578d55effb0570b05900..1ea6b1eabf865199fee7c257ff5f0ef09f76c167 100644 --- a/Utils/IOutputStream.hh +++ b/Utils/IOutputStream.hh @@ -5,7 +5,7 @@ #include <string> #include <vector> -#include <cstddef> +#include <cstddef> // for ptrdiff_t #include <stdint.h> #include <cinttypes> #include <limits.h> @@ -245,6 +245,12 @@ inline FormatT<> format(const char* const _frmt, const T& _vrbl) return FormatT<>(_frmt, _vrbl); } +template <typename T> +inline FormatT<> format_percent(const T& _frct) +{ + return format("%.2f%%", 100. * _frct); +} + namespace FormatHex { template <int byte_nmbr> struct CastT; diff --git a/Utils/OptionT.hh b/Utils/OptionT.hh index 540cbf30331a3d88385f63ff2321943c4c15051c..58b5d33f20a77aca32d0d796f5c58507704ab4e5 100644 --- a/Utils/OptionT.hh +++ b/Utils/OptionT.hh @@ -36,6 +36,12 @@ public: operator T&() { return value; } operator const T&() const { return value; } + OptionT<T>& operator=(const T& _val) + { + value = _val; + return *this; + } + #ifdef TEST_ON // define a binding mechanism for the option value to a test arg protected: void bind(const char* const _name); //!< Bind the option to a test argument diff --git a/Utils/Scope.hh b/Utils/Scope.hh index 25c59b5b1b17c1fcdb2008a1e64c5d5f0f08699e..729a7fe21336fc9330d2c5c05615d286148af91b 100644 --- a/Utils/Scope.hh +++ b/Utils/Scope.hh @@ -41,7 +41,7 @@ auto make_exit(FuncT&& _func) // Create a backup of VAR and restore it at the end of scope. #define SCOPE_VARIABLE_RESTORE(VAR) \ - SCOPE_ON_EXIT( std::bind([&VAR](const auto& _var) { VAR = _var; }, VAR)) + SCOPE_ON_EXIT(std::bind([&VAR](const auto& _var) { VAR = _var; }, VAR)) // Set NEW_VAL by using SETTER and restore OLD_VAL at the end of the scope. @@ -56,6 +56,9 @@ auto make_exit(FuncT&& _func) SCOPE_VARIABLE_RESTORE(VAR); \ VAR = NEW_VAL +// Same as above, but works for globally defined variables +#define SCOPE_VARIABLE_CHANGE_GLOBAL(VAR, NEW_VAL) \ + SCOPE_VARIABLE_CHANGE([](const auto& _var) { VAR = _var; }, NEW_VAL, VAR) } // namespace Scope diff --git a/Utils/ThrowError.hh b/Utils/ThrowError.hh index 5eaf0d08be80aeb018b5e50108cbaf44f4f9f369..fb9df395254d4e916d3609a6481b18a794bc1a28 100644 --- a/Utils/ThrowError.hh +++ b/Utils/ThrowError.hh @@ -43,9 +43,11 @@ struct ErrorExtT : public ErrorT, public ThrowInfo }//namespace Debug -#define THROW_ERROR_MODULE_VAR(MODULE, VAR) { \ - throw Debug::ErrorExtT<MODULE::Error>(VAR, \ - Debug::ThrowInfo(#MODULE, BASE_CODELINK)); } +#define THROW_ERROR_MODULE_VAR(MODULE, VAR) \ + { \ + throw ::Debug::ErrorExtT<MODULE::Error>( \ + VAR, ::Debug::ThrowInfo(#MODULE, BASE_CODELINK)); \ + } #define THROW_ERROR_MODULE(MODULE, INDEX) \ THROW_ERROR_MODULE_VAR(MODULE, MODULE::Error::INDEX)