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.
This commit is contained in:
Oliver Stöneberg 2023-08-04 13:56:18 +02:00 committed by GitHub
parent 4aae670b45
commit 5d201c4e87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 82 additions and 28 deletions

View File

@ -35,6 +35,10 @@ if (BUILD_TESTS)
target_compile_definitions(testrunner PRIVATE CPPCHECKLIB_IMPORT SIMPLECPP_IMPORT) target_compile_definitions(testrunner PRIVATE CPPCHECKLIB_IMPORT SIMPLECPP_IMPORT)
target_link_libraries(testrunner cppcheck-core) target_link_libraries(testrunner cppcheck-core)
endif() 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) if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS)
target_precompile_headers(testrunner PRIVATE precompiled.h) target_precompile_headers(testrunner PRIVATE precompiled.h)

View File

@ -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); 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 #endif // helpersH

View File

@ -46,16 +46,24 @@ private:
return "process"; return "process";
} }
struct CheckOptions
{
CheckOptions() DINIT_NOEXCEPT = default;
SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE;
const char* plistOutput = nullptr;
std::vector<std::string> filesList;
};
/** /**
* Execute check using n jobs for y files which are have * Execute check using n jobs for y files which are have
* identical data, given within data. * 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<std::string>& filesList = {}) { void check(unsigned int jobs, int files, int result, const std::string &data, const CheckOptions &opt = {}) {
errout.str(""); errout.str("");
output.str(""); output.str("");
std::map<std::string, std::size_t> filemap; std::map<std::string, std::size_t> filemap;
if (filesList.empty()) { if (opt.filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
std::ostringstream oss; std::ostringstream oss;
oss << fprefix() << "_" << i << ".cpp"; oss << fprefix() << "_" << i << ".cpp";
@ -63,16 +71,16 @@ private:
} }
} }
else { else {
for (const auto& f : filesList) for (const auto& f : opt.filesList)
{ {
filemap[f] = data.size(); filemap[f] = data.size();
} }
} }
settings.jobs = jobs; settings.jobs = jobs;
settings.showtime = showtime; settings.showtime = opt.showtime;
if (plistOutput) if (opt.plistOutput)
settings.plistOutput = plistOutput; settings.plistOutput = opt.plistOutput;
// TODO: test with settings.project.fileSettings; // TODO: test with settings.project.fileSettings;
ProcessExecutor executor(filemap, settings, *this); ProcessExecutor executor(filemap, settings, *this);
std::vector<std::unique_ptr<ScopedFile>> scopedfiles; std::vector<std::unique_ptr<ScopedFile>> scopedfiles;
@ -127,7 +135,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_SUMMARY); "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY));
} }
void many_threads_plist() { void many_threads_plist() {
@ -139,7 +147,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); "}", dinit(CheckOptions, $.plistOutput = plistOutput));
} }
void no_errors_more_files() { void no_errors_more_files() {
@ -184,7 +192,6 @@ private:
"}"); "}");
} }
void markup() { void markup() {
const Settings settingsOld = settings; const Settings settingsOld = settings;
settings.library.mMarkupExtensions.emplace(".cp1"); settings.library.mMarkupExtensions.emplace(".cp1");
@ -201,7 +208,7 @@ private:
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\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: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"

View File

@ -61,13 +61,21 @@ private:
return std::to_string(i); 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<std::string>& filesList = {}) { struct CheckOptions
{
CheckOptions() DINIT_NOEXCEPT = default;
SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE;
const char* plistOutput = nullptr;
std::vector<std::string> filesList;
};
void check(int files, int result, const std::string &data, const CheckOptions &opt = {}) {
errout.str(""); errout.str("");
output.str(""); output.str("");
settings.project.fileSettings.clear(); settings.project.fileSettings.clear();
std::map<std::string, std::size_t> filemap; std::map<std::string, std::size_t> filemap;
if (filesList.empty()) { if (opt.filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
const std::string s = fprefix() + "_" + zpad3(i) + ".cpp"; const std::string s = fprefix() + "_" + zpad3(i) + ".cpp";
filemap[s] = data.size(); filemap[s] = data.size();
@ -79,7 +87,7 @@ private:
} }
} }
else { else {
for (const auto& f : filesList) for (const auto& f : opt.filesList)
{ {
filemap[f] = data.size(); filemap[f] = data.size();
if (useFS) { if (useFS) {
@ -90,9 +98,9 @@ private:
} }
} }
settings.showtime = showtime; settings.showtime = opt.showtime;
if (plistOutput) if (opt.plistOutput)
settings.plistOutput = plistOutput; settings.plistOutput = opt.plistOutput;
// NOLINTNEXTLINE(performance-unnecessary-value-param) // NOLINTNEXTLINE(performance-unnecessary-value-param)
CppCheck cppcheck(*this, true, [](std::string,std::vector<std::string>,std::string,std::string&){ CppCheck cppcheck(*this, true, [](std::string,std::vector<std::string>,std::string,std::string&){
return false; return false;
@ -152,7 +160,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_SUMMARY); "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY));
} }
void many_files_plist() { void many_files_plist() {
@ -164,7 +172,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); "}", dinit(CheckOptions, $.plistOutput = plistOutput));
} }
void no_errors_more_files() { void no_errors_more_files() {
@ -225,7 +233,7 @@ private:
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", "}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files); dinit(CheckOptions, $.filesList = files));
// TODO: filter out the "files checked" messages // TODO: filter out the "files checked" messages
ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"

View File

@ -46,16 +46,24 @@ private:
return "thread"; return "thread";
} }
struct CheckOptions
{
CheckOptions() DINIT_NOEXCEPT = default;
SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE;
const char* plistOutput = nullptr;
std::vector<std::string> filesList;
};
/** /**
* Execute check using n jobs for y files which are have * Execute check using n jobs for y files which are have
* identical data, given within data. * 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<std::string>& filesList = {}) { void check(unsigned int jobs, int files, int result, const std::string &data, const CheckOptions &opt = {}) {
errout.str(""); errout.str("");
output.str(""); output.str("");
std::map<std::string, std::size_t> filemap; std::map<std::string, std::size_t> filemap;
if (filesList.empty()) { if (opt.filesList.empty()) {
for (int i = 1; i <= files; ++i) { for (int i = 1; i <= files; ++i) {
std::ostringstream oss; std::ostringstream oss;
oss << fprefix() << "_" << i << ".cpp"; oss << fprefix() << "_" << i << ".cpp";
@ -63,7 +71,7 @@ private:
} }
} }
else { else {
for (const auto& f : filesList) for (const auto& f : opt.filesList)
{ {
filemap[f] = data.size(); filemap[f] = data.size();
} }
@ -71,9 +79,9 @@ private:
Settings settings1 = settings; Settings settings1 = settings;
settings1.jobs = jobs; settings1.jobs = jobs;
settings1.showtime = showtime; settings1.showtime = opt.showtime;
if (plistOutput) if (opt.plistOutput)
settings1.plistOutput = plistOutput; settings1.plistOutput = opt.plistOutput;
// TODO: test with settings.project.fileSettings; // TODO: test with settings.project.fileSettings;
ThreadExecutor executor(filemap, settings1, *this); ThreadExecutor executor(filemap, settings1, *this);
std::vector<std::unique_ptr<ScopedFile>> scopedfiles; std::vector<std::unique_ptr<ScopedFile>> scopedfiles;
@ -126,7 +134,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_SUMMARY); "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY));
} }
void many_threads_plist() { void many_threads_plist() {
@ -138,7 +146,7 @@ private:
"{\n" "{\n"
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\n" " return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput); "}", dinit(CheckOptions, $.plistOutput = plistOutput));
} }
void no_errors_more_files() { void no_errors_more_files() {
@ -199,7 +207,7 @@ private:
" char *a = malloc(10);\n" " char *a = malloc(10);\n"
" return 0;\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: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n" /*TODO_ASSERT_EQUALS("Checking " + fprefix() + "_2.cpp ...\n"
"1/4 files checked 25% done\n" "1/4 files checked 25% done\n"