extracted single job execution into `SingleExecutor` / improved testing / do not unconditionally apply colors to output (#4882)

* added `Settings::useSingleJob()` and use it instead of checking `jobs` or `jointSuppressionReport`

* extracted single job execution into `SingleExecutor`

* moved `reportStatus()` from `CppCheckExecutor` to Èxecutor

* TestSingleExecutor: improved tests

* added testing of markup extension handling in executors

* cleaned up includes based on `include-what-you-use`

* testsingleexecutor.cpp: suppress `performance-unnecessary-value-param` clang-tidy warnings

* ProcessExecutor: send color via pipe instead of applying it beforehand

* do not unconditionally apply colors to output / disable all colors in tests / adjusted tests for changed output behavior

* fixed precision loss in `Executor::reportStatus()`

* fixed `naming-varname` selfcheck warnings
This commit is contained in:
Oliver Stöneberg 2023-04-08 18:06:38 +02:00 committed by GitHub
parent ba168474f2
commit 1f2b49142e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 551 additions and 119 deletions

View File

@ -261,6 +261,7 @@ CLIOBJ = cli/cmdlineparser.o \
cli/filelister.o \
cli/main.o \
cli/processexecutor.o \
cli/singleexecutor.o \
cli/stacktrace.o \
cli/threadexecutor.o
@ -310,6 +311,7 @@ TESTOBJ = test/fixture.o \
test/testsimplifytokens.o \
test/testsimplifytypedef.o \
test/testsimplifyusing.o \
test/testsingleexecutor.o \
test/testsizeof.o \
test/teststl.o \
test/teststring.o \
@ -342,7 +344,7 @@ cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)
all: cppcheck testrunner
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/singleexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)
test: all
@ -634,7 +636,7 @@ $(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathli
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cmdlineparser.cpp
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutor.cpp
cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/utils.h
@ -655,6 +657,9 @@ cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/err
cli/processexecutor.o: cli/processexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/processexecutor.cpp
cli/singleexecutor.o: cli/singleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/singleexecutor.cpp
cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/stacktrace.cpp
@ -799,6 +804,9 @@ test/testsimplifytypedef.o: test/testsimplifytypedef.cpp externals/simplecpp/sim
test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifyusing.cpp
test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsingleexecutor.cpp
test/testsizeof.o: test/testsizeof.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checksizeof.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsizeof.cpp

View File

@ -435,6 +435,7 @@
<ClInclude Include="executor.h" />
<ClInclude Include="filelister.h" />
<ClInclude Include="processexecutor.h" />
<ClInclude Include="singleexecutor.h" />
<ClInclude Include="stacktrace.h" />
<ClInclude Include="threadexecutor.h" />
</ItemGroup>
@ -461,6 +462,7 @@
<ClCompile Include="filelister.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="processexecutor.cpp" />
<ClCompile Include="singleexecutor.cpp" />
<ClCompile Include="stacktrace.cpp" />
<ClCompile Include="threadexecutor.cpp" />
</ItemGroup>

View File

@ -30,8 +30,10 @@
#include "path.h"
#include "pathmatch.h"
#include "settings.h"
#include "singleexecutor.h"
#include "suppressions.h"
#include "utils.h"
#include "checkunusedfunctions.h"
#if defined(THREADING_MODEL_THREAD)
@ -51,7 +53,6 @@
#include <sstream> // IWYU pragma: keep
#include <utility>
#include <vector>
#include <numeric>
#ifdef USE_UNIX_SIGNAL_HANDLING
#include "cppcheckexecutorsig.h"
@ -242,7 +243,7 @@ bool CppCheckExecutor::reportSuppressions(const Settings &settings, bool unusedF
return false;
bool err = false;
if (settings.jointSuppressionReport) {
if (settings.useSingleJob()) {
for (std::map<std::string, std::size_t>::const_iterator i = files.cbegin(); i != files.cend(); ++i) {
err |= errorLogger.reportUnmatchedSuppressions(
settings.nomsg.getUnmatchedLocalSuppressions(i->first, unusedFunctionCheckEnabled));
@ -310,54 +311,10 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
}
unsigned int returnValue = 0;
if (settings.jobs == 1) {
if (settings.useSingleJob()) {
// Single process
settings.jointSuppressionReport = true;
const std::size_t totalfilesize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const std::pair<std::string, std::size_t>& f) {
return v + f.second;
});
std::size_t processedsize = 0;
unsigned int c = 0;
if (settings.project.fileSettings.empty()) {
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
if (!settings.library.markupFile(i->first)
|| !settings.library.processMarkupAfterCode(i->first)) {
returnValue += cppcheck.check(i->first);
processedsize += i->second;
if (!settings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
c++;
}
}
} else {
// filesettings
// check all files of the project
for (const ImportProject::FileSettings &fs : settings.project.fileSettings) {
returnValue += cppcheck.check(fs);
++c;
if (!settings.quiet)
reportStatus(c, settings.project.fileSettings.size(), c, settings.project.fileSettings.size());
if (settings.clangTidy)
cppcheck.analyseClangTidy(fs);
}
}
// TODO: not performed when multiple jobs are being used
// second loop to parse all markup files which may not work until all
// c/cpp files have been parsed and checked
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
if (settings.library.markupFile(i->first) && settings.library.processMarkupAfterCode(i->first)) {
returnValue += cppcheck.check(i->first);
processedsize += i->second;
if (!settings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
c++;
}
}
if (cppcheck.analyseWholeProgram())
returnValue++;
SingleExecutor executor(cppcheck, mFiles, settings, *this);
returnValue = executor.check();
} else {
#if defined(THREADING_MODEL_THREAD)
ThreadExecutor executor(mFiles, settings, *this);
@ -423,8 +380,10 @@ void CppCheckExecutor::reportErr(const std::string &errmsg)
void CppCheckExecutor::reportOut(const std::string &outmsg, Color c)
{
// TODO: do not unconditionally apply colors
std::cout << c << ansiToOEM(outmsg, true) << Color::Reset << std::endl;
if (c == Color::Reset)
std::cout << ansiToOEM(outmsg, true) << std::endl;
else
std::cout << toString(c) << ansiToOEM(outmsg, true) << toString(Color::Reset) << std::endl;
}
void CppCheckExecutor::reportProgress(const std::string &filename, const char stage[], const std::size_t value)
@ -450,19 +409,6 @@ void CppCheckExecutor::reportProgress(const std::string &filename, const char st
}
}
void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal)
{
if (filecount > 1) {
std::ostringstream oss;
const long percentDone = (sizetotal > 0) ? static_cast<long>(static_cast<long double>(sizedone) / sizetotal * 100) : 0;
oss << fileindex << '/' << filecount
<< " files checked " << percentDone
<< "% done";
// TODO: do not unconditionally print in color
std::cout << Color::FgBlue << oss.str() << Color::Reset << std::endl;
}
}
void CppCheckExecutor::reportErr(const ErrorMessage &msg)
{
if (mShowAllErrors) {

View File

@ -81,16 +81,6 @@ public:
void reportProgress(const std::string &filename, const char stage[], const std::size_t value) override;
/**
* Information about how many files have been checked
*
* @param fileindex This many files have been checked.
* @param filecount This many files there are in total.
* @param sizedone The sum of sizes of the files checked.
* @param sizetotal The total sizes of the files.
*/
static void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal);
/**
* @param exceptionOutput Output file
*/

View File

@ -18,11 +18,14 @@
#include "executor.h"
#include "color.h"
#include "errorlogger.h"
#include "settings.h"
#include "suppressions.h"
#include <algorithm>
#include <iostream>
#include <sstream> // IWYU pragma: keep
#include <utility>
Executor::Executor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
@ -47,3 +50,15 @@ bool Executor::hasToLog(const ErrorMessage &msg)
return false;
}
void Executor::reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal)
{
if (filecount > 1) {
std::ostringstream oss;
const unsigned long percentDone = (sizetotal > 0) ? (100 * sizedone) / sizetotal : 0;
oss << fileindex << '/' << filecount
<< " files checked " << percentDone
<< "% done";
mErrorLogger.reportOut(oss.str(), Color::FgBlue);
}
}

View File

@ -46,6 +46,16 @@ public:
virtual unsigned int check() = 0;
/**
* Information about how many files have been checked
*
* @param fileindex This many files have been checked.
* @param filecount This many files there are in total.
* @param sizedone The sum of sizes of the files checked.
* @param sizetotal The total sizes of the files.
*/
void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal);
protected:
/**
* @brief Check if message is being suppressed and unique.

View File

@ -73,8 +73,7 @@ public:
explicit PipeWriter(int pipe) : mWpipe(pipe) {}
void reportOut(const std::string &outmsg, Color c) override {
// TODO: do not unconditionally apply colors
writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset));
writeToPipe(REPORT_OUT, static_cast<char>(c) + outmsg);
}
void reportErr(const ErrorMessage &msg) override {
@ -178,7 +177,9 @@ bool ProcessExecutor::handleRead(int rpipe, unsigned int &result, const std::str
bool res = true;
if (type == PipeWriter::REPORT_OUT) {
mErrorLogger.reportOut(buf);
// the first charcater is the color
const Color c = static_cast<Color>(buf[0]);
mErrorLogger.reportOut(buf + 1, c);
} else if (type == PipeWriter::REPORT_ERROR) {
ErrorMessage msg;
try {
@ -328,7 +329,7 @@ unsigned int ProcessExecutor::check()
fileCount++;
processedsize += size;
if (!mSettings.quiet)
CppCheckExecutor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize);
Executor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize);
close(*rp);
rp = rpipes.erase(rp);

90
cli/singleexecutor.cpp Normal file
View File

@ -0,0 +1,90 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "singleexecutor.h"
#include "cppcheck.h"
#include "importproject.h"
#include "library.h"
#include "settings.h"
#include <list>
#include <numeric>
#include <utility>
class ErrorLogger;
SingleExecutor::SingleExecutor(CppCheck &cppcheck, const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
: Executor(files, settings, errorLogger)
, mCppcheck(cppcheck)
{}
SingleExecutor::~SingleExecutor()
{}
// TODO: markup handling is not performed with multiple jobs
unsigned int SingleExecutor::check()
{
unsigned int result = 0;
const std::size_t totalfilesize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const std::pair<std::string, std::size_t>& f) {
return v + f.second;
});
std::size_t processedsize = 0;
unsigned int c = 0;
if (mSettings.project.fileSettings.empty()) {
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
if (!mSettings.library.markupFile(i->first)
|| !mSettings.library.processMarkupAfterCode(i->first)) {
result += mCppcheck.check(i->first);
processedsize += i->second;
if (!mSettings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
c++;
}
}
} else {
// filesettings
// check all files of the project
for (const ImportProject::FileSettings &fs : mSettings.project.fileSettings) {
result += mCppcheck.check(fs);
++c;
if (!mSettings.quiet)
reportStatus(c, mSettings.project.fileSettings.size(), c, mSettings.project.fileSettings.size());
if (mSettings.clangTidy)
mCppcheck.analyseClangTidy(fs);
}
}
// second loop to parse all markup files which may not work until all
// c/cpp files have been parsed and checked
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
if (mSettings.library.markupFile(i->first) && mSettings.library.processMarkupAfterCode(i->first)) {
result += mCppcheck.check(i->first);
processedsize += i->second;
if (!mSettings.quiet)
reportStatus(c + 1, mFiles.size(), processedsize, totalfilesize);
c++;
}
}
if (mCppcheck.analyseWholeProgram())
result++;
return result;
}

46
cli/singleexecutor.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SINGLEEXECUTOR_H
#define SINGLEEXECUTOR_H
#include "executor.h"
#include <cstddef>
#include <map>
#include <string>
class ErrorLogger;
class Settings;
class CppCheck;
class SingleExecutor : public Executor
{
public:
SingleExecutor(CppCheck &cppcheck, const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger);
SingleExecutor(const SingleExecutor &) = delete;
~SingleExecutor() override;
void operator=(const SingleExecutor &) = delete;
unsigned int check() override;
private:
CppCheck &mCppcheck;
};
#endif // SINGLEEXECUTOR_H

View File

@ -69,7 +69,7 @@ public:
void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal) {
std::lock_guard<std::mutex> lg(mReportSync);
CppCheckExecutor::reportStatus(fileindex, filecount, sizedone, sizetotal);
mThreadExecutor.reportStatus(fileindex, filecount, sizedone, sizetotal);
}
private:

View File

@ -360,7 +360,7 @@ Check::FileInfo *CheckUnusedFunctions::getFileInfo(const Tokenizer *tokenizer, c
{
if (!settings->checks.isEnabled(Checks::unusedFunction))
return nullptr;
if (settings->jobs == 1 && settings->buildDir.empty())
if (settings->useSingleJob() && settings->buildDir.empty())
instance.parseTokens(*tokenizer, tokenizer->list.getFiles().front().c_str(), settings);
return nullptr;
}

View File

@ -24,14 +24,17 @@
#include <cstddef>
#include <sstream> // IWYU pragma: keep
bool gDisableColors = false;
#ifdef _WIN32
std::ostream& operator<<(std::ostream& os, const Color& /*c*/)
{
#else
std::ostream& operator<<(std::ostream & os, const Color& c)
{
static const bool use_color = isatty(STDOUT_FILENO);
if (use_color)
// TODO: handle piping into file as well as other pipes like stderr
static const bool s_is_tty = isatty(STDOUT_FILENO);
if (!gDisableColors && s_is_tty)
return os << "\033[" << static_cast<std::size_t>(c) << "m";
#endif
return os;

View File

@ -40,6 +40,8 @@ enum class Color {
};
CPPCHECKLIB std::ostream& operator<<(std::ostream& os, const Color& c);
std::string toString(const Color& c);
CPPCHECKLIB std::string toString(const Color& c);
extern CPPCHECKLIB bool gDisableColors; // for testing
#endif

View File

@ -1026,7 +1026,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
// In jointSuppressionReport mode, unmatched suppressions are
// collected after all files are processed
if (!mSettings.jointSuppressionReport && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) {
reportUnmatchedSuppressions(mSettings.nomsg.getUnmatchedLocalSuppressions(filename, isUnusedFunctionCheckEnabled()));
}
@ -1111,12 +1111,12 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
return;
if (mSettings.jobs == 1 || !mSettings.buildDir.empty()) {
if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) {
// Analyse the tokens..
CTU::FileInfo *fi1 = CTU::getFileInfo(&tokenizer);
if (fi1) {
if (mSettings.jobs == 1)
if (mSettings.useSingleJob())
mFileInfo.push_back(fi1);
if (!mSettings.buildDir.empty())
mAnalyzerInformation.setFileInfo("ctu", fi1->toString());
@ -1128,7 +1128,7 @@ void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
Check::FileInfo *fi = check->getFileInfo(&tokenizer, &mSettings);
if (fi != nullptr) {
if (mSettings.jobs == 1)
if (mSettings.useSingleJob())
mFileInfo.push_back(fi);
if (!mSettings.buildDir.empty())
mAnalyzerInformation.setFileInfo(check->name(), fi->toString());
@ -1652,9 +1652,6 @@ void CppCheck::reportProgress(const std::string &filename, const char stage[], c
mErrorLogger.reportProgress(filename, stage, value);
}
void CppCheck::reportStatus(unsigned int /*fileindex*/, unsigned int /*filecount*/, std::size_t /*sizedone*/, std::size_t /*sizetotal*/)
{}
void CppCheck::getErrorMessages()
{
Settings s(mSettings);
@ -1837,7 +1834,7 @@ void CppCheck::analyseWholeProgram(const std::string &buildDir, const std::map<s
bool CppCheck::isUnusedFunctionCheckEnabled() const
{
return (mSettings.jobs == 1 && mSettings.checks.isEnabled(Checks::unusedFunction));
return (mSettings.useSingleJob() && mSettings.checks.isEnabled(Checks::unusedFunction));
}
void CppCheck::removeCtuInfoFiles(const std::map<std::string, std::size_t> &files)

View File

@ -112,8 +112,6 @@ public:
*/
static const char * extraVersion();
virtual void reportStatus(unsigned int fileindex, unsigned int filecount, std::size_t sizedone, std::size_t sizetotal);
/**
* @brief Call all "getErrorMessages" in all registered Check classes.
* Also print out XML header and footer.

View File

@ -51,7 +51,11 @@ namespace tinyxml2 {
* @brief Library definitions handling
*/
class CPPCHECKLIB Library {
// TODO: get rid of this
friend class TestSymbolDatabase; // For testing only
friend class TestSingleExecutor; // For testing only
friend class TestThreadExecutor; // For testing only
friend class TestProcessExecutor; // For testing only
public:
Library();

View File

@ -59,7 +59,6 @@ Settings::Settings()
force(false),
inlineSuppressions(false),
jobs(1),
jointSuppressionReport(false),
loadAverage(0),
maxConfigs(12),
maxCtuDepth(2),

View File

@ -216,11 +216,6 @@ public:
time. Default is 1. (-j N) */
unsigned int jobs;
/** @brief Collect unmatched suppressions in one run.
* This delays the reporting until all files are checked.
* It is needed by checks that analyse the whole code base. */
bool jointSuppressionReport;
/** @brief --library= */
std::list<std::string> libraries;
@ -439,6 +434,10 @@ public:
void loadSummaries();
bool useSingleJob() const {
return jobs == 1;
}
private:
static std::string parseEnabled(const std::string &str, std::tuple<SimpleEnableGroup<Severity::SeverityType>, SimpleEnableGroup<Checks>> &groups);
std::string applyEnabled(const std::string &str, bool enable);

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "color.h"
#include "options.h"
#include "preprocessor.h"
#include "fixture.h"
@ -30,6 +31,7 @@ int main(int argc, char *argv[])
#endif
Preprocessor::macroChar = '$'; // While macroChar is char(1) per default outside test suite, we require it to be a human-readable character here.
gDisableColors = true;
options args(argc, argv);

View File

@ -22,11 +22,13 @@
#include "fixture.h"
#include "helpers.h"
#include "timer.h"
#include "library.h"
#include <algorithm>
#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <utility>
@ -43,15 +45,23 @@ private:
* 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) {
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 = {}) {
errout.str("");
output.str("");
std::map<std::string, std::size_t> filemap;
for (int i = 1; i <= files; ++i) {
std::ostringstream oss;
oss << "file_" << i << ".cpp";
filemap[oss.str()] = data.size();
if (filesList.empty()) {
for (int i = 1; i <= files; ++i) {
std::ostringstream oss;
oss << "file_" << i << ".cpp";
filemap[oss.str()] = data.size();
}
}
else {
for (const auto& f : filesList)
{
filemap[f] = data.size();
}
}
settings.jobs = jobs;
@ -81,6 +91,7 @@ private:
TEST_CASE(no_errors_equal_amount_files);
TEST_CASE(one_error_less_files);
TEST_CASE(one_error_several_files);
TEST_CASE(markup);
#endif // !WIN32
}
@ -120,7 +131,6 @@ private:
const char plistOutput[] = "plist";
ScopedFile plistFile("dummy", plistOutput);
SUPPRESS;
check(16, 100, 100,
"int main()\n"
"{\n"
@ -170,6 +180,44 @@ private:
" return 0;\n"
"}");
}
void markup() {
const Settings settingsOld = settings;
settings.library.mMarkupExtensions.emplace(".cp1");
settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(2, 4, 4,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking file_2.cpp ...\n"
"1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n"
"2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n"
"3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n"
"4/4 files checked 100% done\n",
"Checking file_1.cp1 ...\n"
"1/4 files checked 25% done\n"
"Checking file_2.cpp ...\n"
"2/4 files checked 50% done\n"
"Checking file_3.cp1 ...\n"
"3/4 files checked 75% done\n"
"Checking file_4.cpp ...\n"
"4/4 files checked 100% done\n",
output.str());*/
settings = settingsOld;
}
};
REGISTER_TEST(TestProcessExecutor)

View File

@ -31,6 +31,7 @@
<ClCompile Include="..\cli\executor.cpp" />
<ClCompile Include="..\cli\filelister.cpp" />
<ClCompile Include="..\cli\processexecutor.cpp" />
<ClCompile Include="..\cli\singleexecutor.cpp" />
<ClCompile Include="..\cli\stacktrace.cpp" />
<ClCompile Include="..\cli\threadexecutor.cpp" />
<ClCompile Include="fixture.cpp">
@ -84,6 +85,7 @@
<ClCompile Include="testsimplifytokens.cpp" />
<ClCompile Include="testsimplifytypedef.cpp" />
<ClCompile Include="testsimplifyusing.cpp" />
<ClCompile Include="testsingleexecutor.cpp" />
<ClCompile Include="testsizeof.cpp" />
<ClCompile Include="teststl.cpp" />
<ClCompile Include="teststring.cpp" />
@ -114,6 +116,7 @@
<ClInclude Include="..\cli\executor.h" />
<ClInclude Include="..\cli\filelister.h" />
<ClInclude Include="..\cli\processexecutor.h" />
<ClInclude Include="..\cli\singleexecutor.h" />
<ClInclude Include="..\cli\stacktrace.h" />
<ClInclude Include="..\cli\threadexecutor.h" />
<ClInclude Include="fixture.h" />

221
test/testsingleexecutor.cpp Normal file
View File

@ -0,0 +1,221 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cppcheck.h"
#include "fixture.h"
#include "helpers.h"
#include "redirect.h"
#include "library.h"
#include "settings.h"
#include "singleexecutor.h"
#include "timer.h"
#include <algorithm>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
class TestSingleExecutor : public TestFixture {
public:
TestSingleExecutor() : TestFixture("TestSingleExecutor") {}
private:
Settings settings;
static std::string zpad3(int i)
{
if (i < 10)
return "00" + std::to_string(i);
if (i < 100)
return "0" + 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 = {}) {
errout.str("");
output.str("");
std::map<std::string, std::size_t> filemap;
if (filesList.empty()) {
for (int i = 1; i <= files; ++i) {
const std::string s = "file_" + zpad3(i) + ".cpp";
filemap[s] = data.size();
}
}
else {
for (const auto& f : filesList)
{
filemap[f] = data.size();
}
}
settings.showtime = showtime;
if (plistOutput)
settings.plistOutput = plistOutput;
// NOLINTNEXTLINE(performance-unnecessary-value-param)
CppCheck cppcheck(*this, true, [](std::string,std::vector<std::string>,std::string,std::string&){
return false;
});
cppcheck.settings() = settings;
// TODO: test with settings.project.fileSettings;
SingleExecutor executor(cppcheck, filemap, settings, *this);
std::vector<std::unique_ptr<ScopedFile>> scopedfiles;
scopedfiles.reserve(filemap.size());
for (std::map<std::string, std::size_t>::const_iterator i = filemap.cbegin(); i != filemap.cend(); ++i)
scopedfiles.emplace_back(new ScopedFile(i->first, data));
ASSERT_EQUALS(result, executor.check());
}
void run() override {
LOAD_LIB_2(settings.library, "std.cfg");
TEST_CASE(many_files);
TEST_CASE(many_files_showtime);
TEST_CASE(many_files_plist);
TEST_CASE(no_errors_more_files);
TEST_CASE(no_errors_less_files);
TEST_CASE(no_errors_equal_amount_files);
TEST_CASE(one_error_less_files);
TEST_CASE(one_error_several_files);
TEST_CASE(markup);
}
void many_files() {
const Settings settingsOld = settings;
settings.quiet = false;
check(100, 100,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}");
std::string expected;
for (int i = 1; i <= 100; ++i) {
expected += "Checking file_" + zpad3(i) + ".cpp ...\n";
expected += std::to_string(i) + "/100 files checked " + std::to_string(i) + "% done\n";
}
ASSERT_EQUALS(expected, output.str());
settings = settingsOld;
}
void many_files_showtime() {
SUPPRESS;
check(100, 100,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_SUMMARY);
}
void many_files_plist() {
const char plistOutput[] = "plist";
ScopedFile plistFile("dummy", plistOutput);
check(100, 100,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}", SHOWTIME_MODES::SHOWTIME_NONE, plistOutput);
}
void no_errors_more_files() {
check(3, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void no_errors_less_files() {
check(1, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void no_errors_equal_amount_files() {
check(2, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void one_error_less_files() {
check(1, 1,
"int main()\n"
"{\n"
" {char *a = malloc(10);}\n"
" return 0;\n"
"}");
}
void one_error_several_files() {
check(20, 20,
"int main()\n"
"{\n"
" {char *a = malloc(10);}\n"
" return 0;\n"
"}");
}
void markup() {
const Settings settingsOld = settings;
settings.library.mMarkupExtensions.emplace(".cp1");
settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(4, 4,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: filter out the "files checked" messages
ASSERT_EQUALS("Checking file_2.cpp ...\n"
"1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n"
"2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n"
"3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n"
"4/4 files checked 100% done\n", output.str());
settings = settingsOld;
}
// TODO: test clang-tidy
// TODO: test whole program analysis
};
REGISTER_TEST(TestSingleExecutor)

View File

@ -189,7 +189,7 @@ private:
if (suppression == "unusedFunction")
settings.checks.setEnabled(Checks::unusedFunction, true);
settings.severity.enable(Severity::information);
settings.jointSuppressionReport = true;
settings.jobs = 1;
if (!suppression.empty()) {
std::string r = settings.nomsg.addSuppressionLine(suppression);
EXPECT_EQ("", r);
@ -298,7 +298,7 @@ private:
" a++;\n"
"}\n",
"uninitvar:test.cpp"));
ASSERT_EQUALS("", errout.str());
//TODO_ASSERT_EQUALS("", "[test.cpp]: (information) Unmatched suppression: uninitvar\n", errout.str());
// TODO: add assert - gives different result with threads/processes
// suppress uninitvar for this file only, without error present
@ -315,7 +315,7 @@ private:
" a++;\n"
"}\n",
"*:test.cpp"));
ASSERT_EQUALS("", errout.str());
//TODO_ASSERT_EQUALS("", "[test.cpp]: (information) Unmatched suppression: *\n", errout.str());
// TODO: add assert - gives different result with threads/processes
// suppress all for this file only, without error present
@ -341,7 +341,7 @@ private:
" b++;\n"
"}\n",
"uninitvar:test.cpp:3");
ASSERT_EQUALS("[test.cpp:3]: (information) Unmatched suppression: uninitvar\n", errout.str());
//TODO_ASSERT_EQUALS("[test.cpp:3]: (information) Unmatched suppression: uninitvar\n", "", errout.str());
// suppress uninitvar inline
ASSERT_EQUALS(0, (this->*check)("void f() {\n"
@ -472,7 +472,7 @@ private:
" b++;\n"
"}\n",
"");
ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", errout.str());
//TODO_ASSERT_EQUALS("[test.cpp:4]: (information) Unmatched suppression: uninitvar\n", "", errout.str());
// #5746 - exitcode
ASSERT_EQUALS(1U,

View File

@ -20,6 +20,7 @@
#include "settings.h"
#include "fixture.h"
#include "helpers.h"
#include "library.h"
#include "threadexecutor.h"
#include "timer.h"
@ -27,6 +28,7 @@
#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <utility>
@ -43,15 +45,23 @@ private:
* 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) {
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 = {}) {
errout.str("");
output.str("");
std::map<std::string, std::size_t> filemap;
for (int i = 1; i <= files; ++i) {
std::ostringstream oss;
oss << "file_" << i << ".cpp";
filemap[oss.str()] = data.size();
if (filesList.empty()) {
for (int i = 1; i <= files; ++i) {
std::ostringstream oss;
oss << "file_" << i << ".cpp";
filemap[oss.str()] = data.size();
}
}
else {
for (const auto& f : filesList)
{
filemap[f] = data.size();
}
}
settings.jobs = jobs;
@ -80,6 +90,7 @@ private:
TEST_CASE(no_errors_equal_amount_files);
TEST_CASE(one_error_less_files);
TEST_CASE(one_error_several_files);
TEST_CASE(markup);
}
void deadlock_with_many_errors() {
@ -118,7 +129,6 @@ private:
const char plistOutput[] = "plist";
ScopedFile plistFile("dummy", plistOutput);
SUPPRESS;
check(16, 100, 100,
"int main()\n"
"{\n"
@ -168,6 +178,43 @@ private:
" return 0;\n"
"}");
}
void markup() {
const Settings settingsOld = settings;
settings.library.mMarkupExtensions.emplace(".cp1");
settings.library.mProcessAfterCode.emplace(".cp1", true);
const std::vector<std::string> files = {
"file_1.cp1", "file_2.cpp", "file_3.cp1", "file_4.cpp"
};
check(2, 4, 4,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}",
SHOWTIME_MODES::SHOWTIME_NONE, nullptr, files);
// TODO: order of "Checking" and "checked" is affected by thread
/*TODO_ASSERT_EQUALS("Checking file_2.cpp ...\n"
"1/4 files checked 25% done\n"
"Checking file_4.cpp ...\n"
"2/4 files checked 50% done\n"
"Checking file_1.cp1 ...\n"
"3/4 files checked 75% done\n"
"Checking file_3.cp1 ...\n"
"4/4 files checked 100% done\n",
"Checking file_1.cp1 ...\n"
"1/4 files checked 25% done\n"
"Checking file_2.cpp ...\n"
"2/4 files checked 50% done\n"
"Checking file_3.cp1 ...\n"
"3/4 files checked 75% done\n"
"Checking file_4.cpp ...\n"
"4/4 files checked 100% done\n",
output.str());*/
settings = settingsOld;
}
};
REGISTER_TEST(TestThreadExecutor)

View File

@ -659,7 +659,8 @@ int main(int argc, char **argv)
fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n";
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
fout << "all:\tcppcheck testrunner\n\n";
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o\n";
// TODO: generate from clifiles
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/singleexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o\n";
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
fout << "test:\tall\n";
fout << "\t./testrunner\n\n";