/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2015-2019 Danny Robson */ #pragma once #include "except.hpp" #include "maths.hpp" #include "debug/validate.hpp" #include #include #include #include namespace cruft::TAP { /// A simple TAP (Test Anything Protocol) test case output class logger { public: enum { PASS, FAIL, SKIP, TODO }; //--------------------------------------------------------------------- logger (); logger (std::ostream&); ~logger (); // NOTE: explicitly disable copy constructors and all assignment // operators so that we don't accidentally overwrite the contained // status code in any instance. This value is required in all // instances so that we can return the correct status code from a test // binary. logger (logger&&) = default; logger (logger const &) = delete; logger& operator= (logger const &) = delete; logger& operator= (logger &&) = delete; //--------------------------------------------------------------------- template bool expect (const bool test, fmt::format_string format, Args&&... args) { m_output << (test ? "ok " : "not ok ") << ++m_size << " - "; fmt::print (m_output, format, std::forward (args)...); m_output << std::endl; if (!test) m_status = EXIT_FAILURE; return test; } //--------------------------------------------------------------------- template decltype(auto) expect (const std::function &test, Args&&...args, fmt::format_string fmt) { try { return expect (test (std::forward (args)...), std::move (fmt)); } catch (...) { return expect (false, fmt); } } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_valid (ValueT &&value, fmt::format_string fmt, ArgsT &&...args) { return expect ( debug::is_valid ( std::forward (value) ), std::move (fmt), std::forward (args)... ); } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_mod (ValueA &&a, ValueB &&b, fmt::format_string fmt, Args &&...args) { return expect ( a % b == 0, std::move (fmt), std::forward (args)... ); } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_eq (const T &a, const U &b, fmt::format_string fmt, Args&&...args) { #if 1 return expect (almost_equal (a, b), std::move (fmt), std::forward (args)...); #else if (almost_equal (a, b)) return expect (true, fmt, std::forward (args)...); else return expect (false, "{} # {} != {}", format::printf (fmt)(std::forward (args)...), a, b); #endif } //--------------------------------------------------------------------- template decltype(auto) expect_neq (const T &a, const U &b, fmt::format_string fmt, Args&&...args) { return expect (!almost_equal (a, b), std::move (fmt), std::forward (args)...); } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_gt (const ValueA &a, const ValueB &b, fmt::format_string fmt, Args&&...args) { return expect (a > b, std::move (fmt), std::forward (args)...); } //--------------------------------------------------------------------- template decltype(auto) expect_ge (const T &a, const U &b, fmt::format_string fmt, Args&&...args) { return expect (a >= b, std::move (fmt), std::forward (args)...); } //--------------------------------------------------------------------- template decltype(auto) expect_lt (const T &a, const U &b, fmt::format_string fmt, Args&&...args) { return expect (a < b, std::move (fmt), std::forward (args)...); } //--------------------------------------------------------------------- template decltype(auto) expect_le (const T &a, const U &b, fmt::format_string fmt, Args&&...args) { return expect (a <= b, std::move (fmt), std::forward (args)...); } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_nan (const T &t, fmt::format_string fmt, Args&&...args) { return expect (std::isnan (t), std::move (fmt), std::forward (args)...); } /////////////////////////////////////////////////////////////////////// template decltype(auto) expect_nothrow (T &&t, fmt::format_string fmt, Args&&...args) { bool success = true; try { t (); } catch (...) { success = false; } return expect (success, std::move (fmt), std::forward (args)...); } //--------------------------------------------------------------------- template decltype(auto) expect_throw (T &&t, fmt::format_string fmt, Args&&...args) { bool success = false; try { t (); } catch (const E&) { success = true; } catch (...) { success = false; } return expect (success, std::move (fmt), std::forward (args)...); } /////////////////////////////////////////////////////////////////////// template decltype(auto) fail (fmt::format_string fmt, Args &&...args) { return expect (false, fmt, std::forward (args)...); } //--------------------------------------------------------------------- void skip (const std::string &msg); void todo (const std::string &msg); void noop (void); /////////////////////////////////////////////////////////////////////// int status [[nodiscard]] (void) const; /// Invoke a FunctionT with a TAP::logger as the first argument, and /// the remainder forwarded from the provided parameter pack. /// /// If an exception escapes from the FunctionT the logger will be /// marked as failed, using a message that attempts to use the raised /// exception value (else a static generic message if the type isn't /// easily discoverable). /// /// Returns the status of the logger provided to the FunctionT. template static auto run [[nodiscard]] (FunctionT &&function, Args&&...args) noexcept { logger tap; try { function (tap, args...); return tap.status (); } catch (std::exception const &err) { tap.fail ("no exceptions: {:s}", err.what ()); } catch (cruft::error const &err) { tap.fail ("no exceptions: {}", err); } catch (...) { tap.fail ("no exceptions"); } return EXIT_FAILURE; } private: #if !defined(NDEBUG) mutable int m_reported = -1; #endif std::ostream &m_output; int m_status; size_t m_size; }; }