diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index fa9d180af00456b476960f2555d843be3637b05d..11f42ad642b408f2810a03bb7bc921f46c70c635 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -16,7 +16,6 @@ set(my_headers ${CMAKE_CURRENT_SOURCE_DIR}/TestPaths.hh ${CMAKE_CURRENT_SOURCE_DIR}/TestReport.hh ${CMAKE_CURRENT_SOURCE_DIR}/TestResult.hh - ${CMAKE_CURRENT_SOURCE_DIR}/TestResultAnalysis.hh PARENT_SCOPE ) @@ -24,6 +23,7 @@ set(my_headers set(my_sources ${CMAKE_CURRENT_SOURCE_DIR}/TestArgs.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestChecksum.cc + ${CMAKE_CURRENT_SOURCE_DIR}/TestChecksumCompare.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestChecksumCompletion.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestChecksumCondition.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestChecksumDebugEvent.cc @@ -33,6 +33,5 @@ set(my_sources ${CMAKE_CURRENT_SOURCE_DIR}/TestOutcome.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestPaths.cc ${CMAKE_CURRENT_SOURCE_DIR}/TestReport.cc - ${CMAKE_CURRENT_SOURCE_DIR}/TestResultAnalysis.cc PARENT_SCOPE ) diff --git a/Test/TestCase.hh b/Test/TestCase.hh index 6a1dea61d1fc27f674f4f2c56516aff1f5cc514d..473caaad3bb657a4d92c092e8eff2ffe87c2393a 100755 --- a/Test/TestCase.hh +++ b/Test/TestCase.hh @@ -5,26 +5,21 @@ #ifdef TEST_ON +#include <Base/Test/TestChecksumCompare.hh> + /* The BASE_TEST() macro wraps around an existing test declaration macro (e.g. TEST_CASE() for Catch2), augmenting it with a checksum comparison. For now, the existing test declaration macro must define a void function. -In order to use BASE_TEST(), the following macros *must* be defined (before this +In order to use BASE_TEST(), the following macro *must* be defined (before this file is included): -- TEST_DECLARATION: a placeholder for the existing test declaration macro; - -- TEST_COMPARE_CHECKSUMS: a placeholder for a function that compares checksums - output by the currently-running test against the corresponding baseline. + TEST_DECLARATION: a placeholder for the existing test declaration macro; As an example, to use this with Catch2, one would write: - #define TEST_DECLARATION TEST_CASE - -And to compare the checksums using Test::compare_checksums(), one would write: - - #define TEST_COMPARE_CHECKSUMS Test::compare_checksums() + #define TEST_DECLARATION TEST_CASE It is not required to use BASE_TEST(), and it is straightforward to define a custom macro with a similar function. The following macros are available for @@ -51,30 +46,29 @@ use: // Both TEST_DECLARATION and TEST_COMPARE_CHECKSUMS must be defined in order to // use the BASE_TEST() macro. -#if defined(TEST_DECLARATION) && defined(TEST_COMPARE_CHECKSUMS) +#ifdef TEST_DECLARATION #define INTERNAL_BASE_TEST_VOID(TEST_FUNCTION, ...) \ static void TEST_FUNCTION(); \ TEST_DECLARATION(__VA_ARGS__) \ { \ TEST_FUNCTION(); \ - TEST_COMPARE_CHECKSUMS; \ + ::Test::Checksum::compare.check_equal(); \ } \ void TEST_FUNCTION() #define BASE_TEST(...) \ INTERNAL_BASE_TEST_VOID(INTERNAL_UNIQUE_BASE_TEST_FUNCTION_NAME, __VA_ARGS__) -#else // defined(TEST_DECLARATION) && defined(TEST_COMPARE_CHECKSUMS) +#else // TEST_DECLARATION #define BASE_TEST(...) \ static_assert(false, \ - "Both TEST_DECLARATION and TEST_COMPARE_CHECKSUMS must be " \ - "defined in order to use the BASE_TEST() macro. Please ensure " \ - "they are defined before TestCase.hh is included."); \ + "TEST_DECLARATION must be defined in order to use the BASE_TEST() " \ + "macro. Please ensure it is defined before TestCase.hh is included."); \ static void INTERNAL_UNIQUE_BASE_TEST_FUNCTION_NAME() -#endif // defined(TEST_DECLARATION) && defined(TEST_COMPARE_CHECKSUMS) +#endif // TEST_DECLARATION #endif // TEST_ON #endif // BASE_TESTCASE_HH_INCLUDED diff --git a/Test/TestResultAnalysis.cc b/Test/TestChecksumCompare.cc old mode 100644 new mode 100755 similarity index 51% rename from Test/TestResultAnalysis.cc rename to Test/TestChecksumCompare.cc index bb3abebdf788e2fbeecf572629c61b05150b71c9..732cd8d57cb313c3844acacbcfe2258a6534d429 --- a/Test/TestResultAnalysis.cc +++ b/Test/TestChecksumCompare.cc @@ -3,22 +3,28 @@ #ifdef TEST_ON #include <Base/Security/Mandatory.hh> -#include <Base/Paths/Filesystem.hh> -#include "TestResultAnalysis.hh" -#include "LongestCommonSubsequenceT.hh" -#include "TestChecksum.hh" +#include "TestChecksumCompare.hh" #include "TestChecksumCompletion.hh" -#include "TestChecksumFile.hh" -#include <algorithm> -#include <cctype> +#include "LongestCommonSubsequenceT.hh" + +#include <Base/Paths/Filesystem.hh> +#include <Base/Utils/NullOutputStream.hh> +#include <Base/Paths/PathLink.hh> + +#include <exception> #include <fstream> #include <iostream> #include <sstream> +#include <string> namespace Test { +using PathLink = Base::PathLink; + +namespace Checksum +{ namespace { @@ -33,8 +39,8 @@ void erase_code_links(std::string& _str) const char* const CODE_LINK_END = "]"; for (auto bgn_pstn = _str.find(CODE_LINK_BEGIN); - bgn_pstn != std::string::npos && !_str.empty(); - bgn_pstn = _str.find(CODE_LINK_BEGIN, bgn_pstn)) + bgn_pstn != std::string::npos && !_str.empty(); + bgn_pstn = _str.find(CODE_LINK_BEGIN, bgn_pstn)) { const auto end_pstn = _str.find(CODE_LINK_END, bgn_pstn); if (end_pstn == std::string::npos) @@ -52,7 +58,8 @@ inline void rtrim(std::string& s) s.end()); } -// Read and parse lines of a report to get the checksum Result, name and content. +// Read and parse lines of a report to get the checksum Result, name and +// content. class Report { public: @@ -61,8 +68,7 @@ public: { public: Entry() {} - explicit Entry(const std::string& _line) - : line_(_line) + explicit Entry(const std::string& _line) : line_(_line) { // Ignore lines that represent (debug) callstack groups if (group()) @@ -106,8 +112,8 @@ public: const Checksum::Record& record() const { return rcrd_; } private: - std::string line_; // Current line - std::string name_; // Extracted checksum name + std::string line_; // Current line + std::string name_; // Extracted checksum name Checksum::Record rcrd_; // Extracted checksum record private: @@ -156,7 +162,7 @@ public: size_t size() const { return entrs_.size(); } - const Entry& operator[] (const size_t _i) const { return entrs_[_i]; } + const Entry& operator[](const size_t _i) const { return entrs_[_i]; } private: Checksum::Level lvl_; @@ -165,9 +171,49 @@ private: } // namespace -DifferenceDistribution compare_reports(const fs::path& _rprt_path0, - const fs::path& _rprt_path1, Base::IOutputStream& _log, - const Checksum::Compare& _chks_cmpr, const bool _shrt_frmt) +// "Hidden" import of this function from TestChecksum.cc +Difference compare_from_registry( + const String& _name, const Record& _old_rcrd, const Record& _new_rcrd); + +class Compare::Impl +{ +public: + //! Set the report pair to compare + void set_reports(const fs::path& _left_path, const fs::path& _right_path); + + //! Set the short format, i.e., remove identical checksums, true by default. + void set_short_format(const bool _short_format = true); + + /*! + Run the comparison and generate a difference report. Throws a std error if the + report paths are not set. + */ + DifferenceDistribution run(Base::IOutputStream& _log_os) const; + + /*! + Run the comparison and throw a std error if both reports exist but the + checksums are not equal. + */ + void check_equal() const; + +private: + fs::path left_path_; + fs::path right_path_; + bool short_format_ = true; +}; + +void Compare::Impl::set_reports(const fs::path& _left_path, const fs::path& _right_path) +{ + left_path_ = _left_path; + right_path_ = _right_path; +} + +void Compare::Impl::set_short_format(const bool _short_format) +{ + short_format_ = _short_format; +} + +DifferenceDistribution Compare::Impl::run(Base::IOutputStream& _log_os) const { using Base::ENDL; @@ -182,7 +228,15 @@ DifferenceDistribution compare_reports(const fs::path& _rprt_path0, const char* const DIFF_TAG = "! "; const char* const RESULT_TAG = " "; - const Report rprts[2] = {Report(_rprt_path0), Report(_rprt_path1)}; + // Throw an error if the report paths have not been set + if (left_path_.empty() || right_path_.empty()) + { + throw std::runtime_error( + "Unable to compare checksum reports: Report paths have not been set. " + "This should be done using Test::Checksum::Compare::set_reports()."); + } + + const Report rprts[2] = {Report(left_path_), Report(right_path_)}; LongestCommonSubsequenceT<Report> rprt_lcs(rprts[0], rprts[1]); rprt_lcs.trace(); @@ -200,32 +254,32 @@ DifferenceDistribution compare_reports(const fs::path& _rprt_path0, if (old_entr.group()) // this match entry is a group? { - if (!_shrt_frmt) - _log << GROUP_TAG << old_entr.line() << ENDL; // just print it + if (!short_format_) + _log_os << GROUP_TAG << old_entr.line() << ENDL; // just print it } else // checksum case { - const auto diff = - _chks_cmpr(old_entr.name(), old_entr.record(), new_entr.record()); + const auto diff = compare_from_registry( + old_entr.name(), old_entr.record(), new_entr.record()); if (diff.equal()) // no difference? { - if (!_shrt_frmt) - _log << SAME_TAG << old_entr.line() << ENDL; // just print it + if (!short_format_) + _log_os << SAME_TAG << old_entr.line() << ENDL; // just print it } else { // print difference - _log << NAME_TAG << old_entr.name() << ": " << diff.type_text() - << ENDL; - _log << OLD_TAG << old_entr.record().rslt << RESULT_TAG - << old_entr.record().data << ENDL; - _log << NEW_TAG << new_entr.record().rslt << RESULT_TAG - << new_entr.record().data << ENDL; + _log_os << NAME_TAG << old_entr.name() << ": " << diff.type_text() + << ENDL; + _log_os << OLD_TAG << old_entr.record().rslt << RESULT_TAG + << old_entr.record().data << ENDL; + _log_os << NEW_TAG << new_entr.record().rslt << RESULT_TAG + << new_entr.record().data << ENDL; const char rslt_mark = old_entr.record().rslt.type() == new_entr.record().rslt.type() ? ' ' : '!'; - _log << DIFF_TAG << rslt_mark << RESULT_TAG << diff.description() - << ENDL; + _log_os << DIFF_TAG << rslt_mark << RESULT_TAG << diff.description() + << ENDL; } ++test_diff[diff]; } @@ -233,7 +287,7 @@ DifferenceDistribution compare_reports(const fs::path& _rprt_path0, else if (ij.i_valid() && rprts[0].level() <= rprts[1].level()) { // old entry removed and the old checksum level is equal or stricter const auto& old_entr = rprts[0][ij.i]; - _log << REMOVED_TAG << old_entr.line() << ENDL; // just print it + _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())) @@ -245,7 +299,7 @@ DifferenceDistribution compare_reports(const fs::path& _rprt_path0, else if (ij.j_valid() && rprts[0].level() >= rprts[1].level()) { // new entry added and the new checksum level is stricter or equal const auto& new_entr = rprts[1][ij.j]; - _log << ADDED_TAG << new_entr.line() << ENDL; // just print it + _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())) @@ -259,6 +313,127 @@ DifferenceDistribution compare_reports(const fs::path& _rprt_path0, return test_diff; } +void Compare::Impl::check_equal() const +{ + std::cout << "Info: Comparing checksums..."; + + // Throw an error if the report paths have not been set + if (left_path_.empty() || right_path_.empty()) + { + throw std::runtime_error( + "Unable to compare checksum reports: Report paths have not been set. " + "This should be done using Test::Checksum::Compare::set_reports()."); + } + + // Skip the comparison if one of these files doesn't exist + if (!fs::exists(left_path_)) + { + std::cout << "no baseline report found (expected at " + << left_path_.generic_string() << ")." << std::endl; + return; + } + + if (!fs::exists(right_path_)) + { + std::cout << "no checksum report found for the current test (expected at " + << right_path_.generic_string() << ")." << std::endl; + return; + } + + // Compare the test report against the baseline report file + Base::OStringStream log_os; + auto diffs = run(log_os); + + std::cout << "complete. "; + + if (diffs.empty()) + { + // No differences found + std::cout << "No differences found." << std::endl; + } + else + { + // Differences found: display short diff and throw error + std::cout << "Differences found!" << std::endl << std::endl; + std::cout << log_os.str << std::endl; + + std::cout << "Compare " << PathLink(left_path_) << " with " + << PathLink(right_path_) << "." << std::endl; + throw std::runtime_error("Checksum comparison failed--differences found!"); + } +} + +Compare::Impl* Compare::impl_ = nullptr; +bool Compare::owned_ = false; + +void Compare::make() +{ + if (impl_ != nullptr) + return; + impl_ = new Impl; + owned_ = true; +} + +void Compare::set(Compare& _othr) +{ + if (_othr.impl_ != nullptr) + { + throw std::runtime_error( + "Unable to set implementation: incoming implementation is null."); + } + + if (impl_ != nullptr) + { + throw std::runtime_error( + "Unable to set implementation: implementation has already been set."); + } + + impl_ = _othr.impl_; +} + +Compare::~Compare() +{ + if (owned_) + delete impl_; +} + +void Compare::set_reports( + const fs::path& _left_path, const fs::path& _right_path) +{ + check_implementation(); + impl_->set_reports(_left_path, _right_path); +} + +void Compare::set_short_format(const bool _short_format) +{ + check_implementation(); + impl_->set_short_format(_short_format); +} + +DifferenceDistribution Compare::run(Base::IOutputStream& _log_os) const +{ + check_implementation(); + return impl_->run(_log_os); +} + +void Compare::check_equal() const +{ + check_implementation(); + impl_->check_equal(); +} + +void Compare::check_implementation() const +{ + if (impl_ == nullptr) + { + throw std::runtime_error("Implementation is null: Implementation should be " + "set using Test::Checksum::Compare::make() or " + "Test::Checksum::Compare::set()."); + } +} + +Compare compare; +} // namespace Checksum } // namespace Test #endif // TEST_ON diff --git a/Test/TestChecksumCompare.hh b/Test/TestChecksumCompare.hh index bc0c2f0e0e65598a1257fb13cc4cc8fa0c2da340..9c627df7c3ecaab66530037efa6a4203b9a6251d 100644 --- a/Test/TestChecksumCompare.hh +++ b/Test/TestChecksumCompare.hh @@ -6,31 +6,84 @@ #ifdef TEST_ON #include <Base/Test/TestChecksum.hh> +#include <Base/Paths/Filesystem.hh> +#include <Base/Utils/NullOutputStream.hh> -#include <functional> +#include <map> namespace Test { +namespace fs = Base::filesystem; + namespace Checksum { + +typedef std::map<Difference, size_t> DifferenceDistribution; + /*! -Function type to compare two records of the same checksum identified by name. -The compare function should return \ref Difference value to describe the -difference of the old vs the new value. -*/ -typedef std::function<Difference(const String& /*!<[in] checksum name */, - const Record& /*!<[in] checksum old value */, - const Record& /*!<[in] checksum new value */)> - Compare; - -/*! -Compare function that uses the compare method of registered checksums. -\note Make sure to wrap this appropriately for shared object export when using -in shared binary components in order to use the registry in the shared binary! +Compare two test checksum reports. + +This class should be instantiated uniquely in a specific way to ensure it is +shared across different instances of Base in separate binary modules in the same +process. + +As the checksums reside in the binary that implements the tested algorithms, the +class instance should be created there with \ref make(). The instance is owned +by that binary and must be deleted by it, as it's stored on its heap. + +Once created, the instance should be \ref set() in the test binary (usually an +executable). \ref set() will not take ownership of the pointer. With this +approach all class methods will return the same instance throughout the process. + +\note These considerations are irrelevant if the tested and test executable are +linked statically, i.e., they share the same binary. */ -Difference compare_from_registry( - const String& _name, const Record& _old_rcrd, const Record& _new_rcrd); +class Compare +{ +public: + //! Make and store the unique instance + static void make(); + + //! Set an instance made in another binary + static void set(Compare&); +public: + //! Destroy the owned instance + ~Compare(); + + //! Set the report pair to compare + void set_reports(const fs::path& _left_path, const fs::path& _right_path); + + //! Set the short format, i.e., remove identical checksums, true by default. + void set_short_format(const bool _short_format = true); + + /*! + Run the comparison and generate a difference report. Throws a std error if the + report paths are not set. + */ + DifferenceDistribution run( + Base::IOutputStream& _log_os = Base::null_os) const; + + /*! + Run the comparison and throw a std error if both reports exist but the + checksums are not equal. + */ + void check_equal() const; + +private: + //! Throw std error if impl_ is nullptr + void check_implementation() const; + +private: + class Impl; + static Impl* impl_; // shared implementation pointer + static bool owned_; // own implementation or not +}; + +/*! +Shared instance per binary, make sure to call make() and set() as appropriate. +*/ +extern Compare compare; } // namespace Checksum } // namespace Test diff --git a/Test/TestReport.cc b/Test/TestReport.cc index 74738d02acfcdd5cf049e28e72fc3ae14449cf7d..df9cb04454a78f180bb954f9ab904af5afd0084b 100644 --- a/Test/TestReport.cc +++ b/Test/TestReport.cc @@ -6,10 +6,10 @@ #include <Base/Paths/Filesystem.hh> #include <Base/Paths/PathLink.hh> #include "TestChecksum.hh" +#include "TestChecksumCompare.hh" #include "TestChecksumCompletion.hh" #include "TestReport.hh" #include "TestResult.hh" -#include "TestResultAnalysis.hh" #include <cstring> #include <fstream> @@ -236,7 +236,8 @@ typedef std::stringstream TestStream; struct TestDiffSummary { TestDiffSummary(const fs::path& _test_path, - const Test::DifferenceDistribution& _diff, const TestStream& _descr) + const Test::Checksum::DifferenceDistribution& _diff, + const TestStream& _descr) : path_(_test_path), diffs_(_diff), descr_(std::move(_descr.str())) { } @@ -274,7 +275,7 @@ struct TestDiffSummary bool equivalent() const { return diffs_.empty(); } fs::path path_; - Test::DifferenceDistribution diffs_; + Test::Checksum::DifferenceDistribution diffs_; std::string descr_; // Test report of test differences description. }; @@ -457,7 +458,7 @@ void replace(const fs::path& _root_source, const fs::path& _root_target, // right. ExitStatus make_comparison(const char* const _dir_left, const char* const _dir_right, const CompareOutputType _cot, - const Checksum::Compare& _chks_cmpr, const bool _show_progress) + const bool _show_progress) { // Find the executed tests in the two test suites. TestTree test_trees[2]{TestTree(_dir_left), TestTree(_dir_right)}; @@ -488,15 +489,15 @@ ExitStatus make_comparison(const char* const _dir_left, // Create stream to store the differences for the current test Base::OutputStreamAdaptT<TestStream> test_diff_log; - // Get the paths to the reports for both suites - const auto report_path_left = - test_trees[0].root_dir() / test_path / Test::REPORT_FILENAME; - const auto report_path_right = - test_trees[1].root_dir() / test_path / Test::REPORT_FILENAME; + // Set comparison parameters + Test::Checksum::compare.set_reports( + test_trees[0].root_dir() / test_path / Test::REPORT_FILENAME, + test_trees[1].root_dir() / test_path / Test::REPORT_FILENAME); + + Test::Checksum::compare.set_short_format(_cot == COT_SHORT_DIFF); // Compare the checksums for the test between the two suites - const auto diff_stats = compare_reports(report_path_left, report_path_right, - test_diff_log, _chks_cmpr, _cot == COT_SHORT_DIFF); + const auto diff_stats = Test::Checksum::compare.run(test_diff_log); if (!diff_stats.empty()) { // Differences were found @@ -648,8 +649,7 @@ ExitStatus make_comparison(const char* const _dir_left, continue; \ } -int report(const int _argc, const char* const _argv[], - const Checksum::Compare& _chks_cmpr) +int report(const int _argc, const char* const _argv[]) { // Set usage text const auto usage = @@ -712,7 +712,7 @@ int report(const int _argc, const char* const _argv[], // Perform the appropriate comparison and catch any errors try { - rslt = make_comparison(dir_left, dir_right, cot, _chks_cmpr, show_progress); + rslt = make_comparison(dir_left, dir_right, cot, show_progress); } catch (const std::exception& excpt) { diff --git a/Test/TestReport.hh b/Test/TestReport.hh index 7b660b0541b00647e5ef82e59b5b5a652f2b4a5d..39e411f209582bebcf0e5172b987529af24443e6 100644 --- a/Test/TestReport.hh +++ b/Test/TestReport.hh @@ -54,7 +54,7 @@ of progress-related output from the function. */ ExitStatus make_comparison(const char* const _dir_left, const char* const _dir_right, const CompareOutputType _cot, - const Checksum::Compare& _chks_cmpr, const bool _show_progress = true); + const bool _show_progress = true); /*! This is a wrapper to make_comparison() that parses command-line arguments. The @@ -66,8 +66,7 @@ following command-line format is expected: * --no-progress will set _show_progress = false. */ -int report(const int _argc, const char* const _argv[], - const Checksum::Compare& _chks_cmpr); +int report(const int _argc, const char* const _argv[]); } // namespace Test diff --git a/Test/TestResultAnalysis.hh b/Test/TestResultAnalysis.hh deleted file mode 100644 index 65ceec1935ad48f58e1045cc3c8f82c67cab8dc0..0000000000000000000000000000000000000000 --- a/Test/TestResultAnalysis.hh +++ /dev/null @@ -1,42 +0,0 @@ -// (C) Copyright 2021 by Autodesk, Inc. - -#ifndef BASE_TESTRESULTANALYSIS_HH_INCLUDE -#define BASE_TESTRESULTANALYSIS_HH_INCLUDE - -#ifdef TEST_ON - -#include <Base/Test/TestChecksumCompare.hh> -#include <Base/Paths/Filesystem.hh> - -#include <map> -#include <string> - -namespace Test -{ -namespace fs = Base::filesystem; - -typedef Checksum::Difference Difference; -typedef std::map<Difference, size_t> DifferenceDistribution; - -/*! -Compares the checksums listed in two test reports. Returns a map with the -difference statistics (counts for each type of difference). Equal checksums are -ignored if _short_frmt = true. - -Arguments: - -* [in] _rprt_path0: Path to the left-suite report file. -* [in] _rprt_path1: Path to the right-suite report file. -* [out] _log: Description of the differences. -* [in] _chks_cmpr: Checksum compare function. -* [in] _short_frmt: Remove identical checksums (short format) [default = false]. - -*/ -DifferenceDistribution compare_reports(const fs::path& _rprt_path0, - const fs::path& _rprt_path1, Base::IOutputStream& _log, - const Checksum::Compare& _chks_cmpr, const bool _short_frmt = false); - -} // namespace Test - -#endif // TEST_ON -#endif // BASE_TESTRESULTANALYSIS_HH_INCLUDE diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt index cf4fb2d068e741d23c28324a1dc80b8908e5189c..331c336486ce22dfa254d159a6a8ecc300ac37be 100644 --- a/Utils/CMakeLists.txt +++ b/Utils/CMakeLists.txt @@ -4,6 +4,7 @@ set(my_headers ${CMAKE_CURRENT_SOURCE_DIR}/Environment.hh ${CMAKE_CURRENT_SOURCE_DIR}/FileOutput.hh ${CMAKE_CURRENT_SOURCE_DIR}/IOutputStream.hh + ${CMAKE_CURRENT_SOURCE_DIR}/NullOutputStream.hh ${CMAKE_CURRENT_SOURCE_DIR}/OStringStream.hh ${CMAKE_CURRENT_SOURCE_DIR}/RedirectStream.hh ${CMAKE_CURRENT_SOURCE_DIR}/StopWatch.hh @@ -17,6 +18,7 @@ set(my_sources ${CMAKE_CURRENT_SOURCE_DIR}/Environment.cc ${CMAKE_CURRENT_SOURCE_DIR}/FileOutput.cc ${CMAKE_CURRENT_SOURCE_DIR}/IOutputStream.cc + ${CMAKE_CURRENT_SOURCE_DIR}/NullOutputStream.cc ${CMAKE_CURRENT_SOURCE_DIR}/OStringStream.cc ${CMAKE_CURRENT_SOURCE_DIR}/RedirectStream.cc ${CMAKE_CURRENT_SOURCE_DIR}/StopWatch.cc diff --git a/Utils/NullOutputStream.cc b/Utils/NullOutputStream.cc new file mode 100755 index 0000000000000000000000000000000000000000..2f522ef995e052a6a66390e15f919f93ebfac566 --- /dev/null +++ b/Utils/NullOutputStream.cc @@ -0,0 +1,11 @@ +// (C) Copyright 2021 by Autodesk, Inc. + +#include "Base/Security/Mandatory.hh" +#include "NullOutputStream.hh" + +namespace Base +{ + +NullOutputStream null_os; + +} // namespace Base diff --git a/Utils/NullOutputStream.hh b/Utils/NullOutputStream.hh new file mode 100755 index 0000000000000000000000000000000000000000..3f9dce6128b792896d9424f971dae8f6233693fe --- /dev/null +++ b/Utils/NullOutputStream.hh @@ -0,0 +1,27 @@ +// (C) Copyright 2021 by Autodesk, Inc. + +#ifndef BASE_NULLOUTPUTSTREAM_HH_INCLUDED +#define BASE_NULLOUTPUTSTREAM_HH_INCLUDED + +#include <Base/Utils/IOutputStream.hh> + +namespace Base +{ + +// An output stream that ignores data streamed to it +class BASEDLLEXPORT NullOutputStream : public IOutputStream +{ +public: + IOutputStream& print(const char) override { return *this; } + IOutputStream& print(const int) override { return *this; } + IOutputStream& print(const size_t) override { return *this; } + IOutputStream& print(const float) override { return *this; } + IOutputStream& print(const double) override { return *this; } + IOutputStream& print(const char* const) override { return *this; } +}; + +extern NullOutputStream null_os; + +} // namespace Base + +#endif // BASE_NULLOUTPUTSTREAM_HH_INCLUDED