From 5d201c4e871585d11474d129df483ec201eae316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Fri, 4 Aug 2023 13:56:18 +0200 Subject: [PATCH] testrunner: use structs with designated initialization to pass options (#4975) I need to add parameters to some `check()` functions in the tests and things are already pretty messy with having to specify all the default values - readability aside. I found this on https://stackoverflow.com/a/49572324/532627 - apparently the CC BY-SA license by StackOverflow allows the usage within GPL. --- test/CMakeLists.txt | 4 ++++ test/helpers.h | 27 +++++++++++++++++++++++++++ test/testprocessexecutor.cpp | 27 +++++++++++++++++---------- test/testsingleexecutor.cpp | 26 +++++++++++++++++--------- test/testthreadexecutor.cpp | 26 +++++++++++++++++--------- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 371654bb5..93d8b513d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -35,6 +35,10 @@ if (BUILD_TESTS) target_compile_definitions(testrunner PRIVATE CPPCHECKLIB_IMPORT SIMPLECPP_IMPORT) target_link_libraries(testrunner cppcheck-core) endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # $ is used in dinit() dessignated initialization helper + target_compile_options_safe(testrunner -Wno-dollar-in-identifier-extension) + endif() if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) target_precompile_headers(testrunner PRIVATE precompiled.h) diff --git a/test/helpers.h b/test/helpers.h index 621544cf2..9699d6b1f 100644 --- a/test/helpers.h +++ b/test/helpers.h @@ -92,4 +92,31 @@ public: static std::string getcode(Preprocessor &preprocessor, const std::string &filedata, const std::string &cfg, const std::string &filename, Suppressions *inlineSuppression = nullptr); }; +/* designated initialization helper + Usage: + struct S + { + int i; + }; + + const auto s = dinit(S, + $.i = 1 + ); + */ +#define dinit(T, ...) \ + ([&] { T ${}; __VA_ARGS__; return $; }()) + +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || defined(__clang__) +// work around Clang compilation error +// error: default member initializer for 'y' needed within definition of enclosing class 'X' outside of member functions +// work around GCC compilation error +// error: default member initializer for ‘x::y::z’ required before the end of its enclosing class +// see https://stackoverflow.com/questions/53408962 +#define DINIT_NOEXCEPT noexcept +#else +// work around GCC 4.8 compilation error +// error: function 'x()' defaulted on its first declaration with an exception-specification that differs from the implicit declaration 'x()' +#define DINIT_NOEXCEPT +#endif + #endif // helpersH diff --git a/test/testprocessexecutor.cpp b/test/testprocessexecutor.cpp index cef576b5e..100aaa580 100644 --- a/test/testprocessexecutor.cpp +++ b/test/testprocessexecutor.cpp @@ -46,16 +46,24 @@ private: return "process"; } + struct CheckOptions + { + CheckOptions() DINIT_NOEXCEPT = default; + SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE; + const char* plistOutput = nullptr; + std::vector filesList; + }; + /** * Execute check using n jobs for y files which are have * identical data, given within data. */ - void check(unsigned int jobs, int files, int result, const std::string &data, SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE, const char* const plistOutput = nullptr, const std::vector& filesList = {}) { + void check(unsigned int jobs, int files, int result, const std::string &data, const CheckOptions &opt = {}) { errout.str(""); output.str(""); std::map filemap; - if (filesList.empty()) { + if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { std::ostringstream oss; oss << fprefix() << "_" << i << ".cpp"; @@ -63,16 +71,16 @@ private: } } else { - for (const auto& f : filesList) + for (const auto& f : opt.filesList) { filemap[f] = data.size(); } } settings.jobs = jobs; - settings.showtime = showtime; - if (plistOutput) - settings.plistOutput = plistOutput; + settings.showtime = opt.showtime; + if (opt.plistOutput) + settings.plistOutput = opt.plistOutput; // TODO: test with settings.project.fileSettings; ProcessExecutor executor(filemap, settings, *this); std::vector> scopedfiles; @@ -127,7 +135,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_SUMMARY); + "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY)); } void many_threads_plist() { @@ -139,7 +147,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); + "}", dinit(CheckOptions, $.plistOutput = plistOutput)); } void no_errors_more_files() { @@ -184,7 +192,6 @@ private: "}"); } - void markup() { const Settings settingsOld = settings; settings.library.mMarkupExtensions.emplace(".cp1"); @@ -201,7 +208,7 @@ private: " char *a = malloc(10);\n" " return 0;\n" "}", - SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); + dinit(CheckOptions, $.filesList = files)); // TODO: order of "Checking" and "checked" is affected by thread /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" "1/4 files checked 25% done\n" diff --git a/test/testsingleexecutor.cpp b/test/testsingleexecutor.cpp index 2bdf94c86..beb1dd78f 100644 --- a/test/testsingleexecutor.cpp +++ b/test/testsingleexecutor.cpp @@ -61,13 +61,21 @@ private: return std::to_string(i); } - void check(int files, int result, const std::string &data, SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE, const char* const plistOutput = nullptr, const std::vector& filesList = {}) { + struct CheckOptions + { + CheckOptions() DINIT_NOEXCEPT = default; + SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE; + const char* plistOutput = nullptr; + std::vector filesList; + }; + + void check(int files, int result, const std::string &data, const CheckOptions &opt = {}) { errout.str(""); output.str(""); settings.project.fileSettings.clear(); std::map filemap; - if (filesList.empty()) { + if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { const std::string s = fprefix() + "_" + zpad3(i) + ".cpp"; filemap[s] = data.size(); @@ -79,7 +87,7 @@ private: } } else { - for (const auto& f : filesList) + for (const auto& f : opt.filesList) { filemap[f] = data.size(); if (useFS) { @@ -90,9 +98,9 @@ private: } } - settings.showtime = showtime; - if (plistOutput) - settings.plistOutput = plistOutput; + settings.showtime = opt.showtime; + if (opt.plistOutput) + settings.plistOutput = opt.plistOutput; // NOLINTNEXTLINE(performance-unnecessary-value-param) CppCheck cppcheck(*this, true, [](std::string,std::vector,std::string,std::string&){ return false; @@ -152,7 +160,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_SUMMARY); + "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY)); } void many_files_plist() { @@ -164,7 +172,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); + "}", dinit(CheckOptions, $.plistOutput = plistOutput)); } void no_errors_more_files() { @@ -225,7 +233,7 @@ private: " char *a = malloc(10);\n" " return 0;\n" "}", - SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); + dinit(CheckOptions, $.filesList = files)); // TODO: filter out the "files checked" messages ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" "1/4 files checked 25% done\n" diff --git a/test/testthreadexecutor.cpp b/test/testthreadexecutor.cpp index 69b529a26..d438dfa13 100644 --- a/test/testthreadexecutor.cpp +++ b/test/testthreadexecutor.cpp @@ -46,16 +46,24 @@ private: return "thread"; } + struct CheckOptions + { + CheckOptions() DINIT_NOEXCEPT = default; + SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE; + const char* plistOutput = nullptr; + std::vector filesList; + }; + /** * Execute check using n jobs for y files which are have * identical data, given within data. */ - void check(unsigned int jobs, int files, int result, const std::string &data, SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE, const char* const plistOutput = nullptr, const std::vector& filesList = {}) { + void check(unsigned int jobs, int files, int result, const std::string &data, const CheckOptions &opt = {}) { errout.str(""); output.str(""); std::map filemap; - if (filesList.empty()) { + if (opt.filesList.empty()) { for (int i = 1; i <= files; ++i) { std::ostringstream oss; oss << fprefix() << "_" << i << ".cpp"; @@ -63,7 +71,7 @@ private: } } else { - for (const auto& f : filesList) + for (const auto& f : opt.filesList) { filemap[f] = data.size(); } @@ -71,9 +79,9 @@ private: Settings settings1 = settings; settings1.jobs = jobs; - settings1.showtime = showtime; - if (plistOutput) - settings1.plistOutput = plistOutput; + settings1.showtime = opt.showtime; + if (opt.plistOutput) + settings1.plistOutput = opt.plistOutput; // TODO: test with settings.project.fileSettings; ThreadExecutor executor(filemap, settings1, *this); std::vector> scopedfiles; @@ -126,7 +134,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_SUMMARY); + "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY)); } void many_threads_plist() { @@ -138,7 +146,7 @@ private: "{\n" " char *a = malloc(10);\n" " return 0;\n" - "}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); + "}", dinit(CheckOptions, $.plistOutput = plistOutput)); } void no_errors_more_files() { @@ -199,7 +207,7 @@ private: " char *a = malloc(10);\n" " return 0;\n" "}", - SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); + dinit(CheckOptions, $.filesList = files)); // TODO: order of "Checking" and "checked" is affected by thread /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" "1/4 files checked 25% done\n"