greatly improved `Settings::loadCppcheckCfg()` error handling (#5712)

This also fixes the issue that `cppcheck.cfg` is no longer being loaded
from executable path. That was introduced by #5704.
This commit is contained in:
Oliver Stöneberg 2023-12-01 16:34:14 +01:00 committed by GitHub
parent 4182f943aa
commit 3272a2bbe7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 278 additions and 44 deletions

View File

@ -40,4 +40,4 @@ jobs:
- name: Check - name: Check
run: | run: |
cppcheckpremium-${{ env.PREMIUM_VERSION }}/premiumaddon --check-loc-license cppcheck.lic > cppcheck-premium-loc cppcheckpremium-${{ env.PREMIUM_VERSION }}/premiumaddon --check-loc-license cppcheck.lic > cppcheck-premium-loc
cppcheckpremium-${{ env.PREMIUM_VERSION }}/cppcheck -j$(nproc) -D__GNUC__ -D__CPPCHECK__ --suppressions-list=cppcheckpremium-suppressions --platform=unix64 --enable=style --premium=misra-c++-2008 --premium=cert-c++-2016 --error-exitcode=1 lib cppcheckpremium-${{ env.PREMIUM_VERSION }}/cppcheck -j$(nproc) -D__GNUC__ -D__CPPCHECK__ --suppressions-list=cppcheckpremium-suppressions --platform=unix64 --enable=style --premium=misra-c++-2008 --premium=cert-c++-2016 --inline-suppr --error-exitcode=1 lib

View File

@ -728,7 +728,7 @@ test/testclangimport.o: test/testclangimport.cpp lib/addoninfo.h lib/check.h lib
test/testclass.o: test/testclass.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h test/testclass.o: test/testclass.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testclass.cpp $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testclass.cpp
test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.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/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcmdlineparser.cpp $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcmdlineparser.cpp
test/testcolor.o: test/testcolor.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/testcolor.o: test/testcolor.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h
@ -809,7 +809,7 @@ test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp
test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testprocessexecutor.cpp $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testprocessexecutor.cpp
test/testsettings.o: test/testsettings.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/testsettings.o: test/testsettings.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsettings.cpp $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsettings.cpp
test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.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 test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.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

View File

@ -282,6 +282,8 @@ bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[])
// TODO: error out on all missing given files/paths // TODO: error out on all missing given files/paths
CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const argv[]) CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const argv[])
{ {
mSettings.exename = Path::getCurrentExecutablePath(argv[0]);
if (argc <= 1) { if (argc <= 1) {
printHelp(); printHelp();
return Result::Exit; return Result::Exit;
@ -307,7 +309,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
// print all possible error messages.. // print all possible error messages..
if (std::strcmp(argv[i], "--errorlist") == 0) { if (std::strcmp(argv[i], "--errorlist") == 0) {
mSettings.loadCppcheckCfg(); if (!loadCppcheckCfg())
return Result::Fail;
{ {
XMLErrorMessagesLogger xmlLogger; XMLErrorMessagesLogger xmlLogger;
std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName); std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName);
@ -324,7 +327,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
} }
if (std::strcmp(argv[i], "--version") == 0) { if (std::strcmp(argv[i], "--version") == 0) {
mSettings.loadCppcheckCfg(); if (!loadCppcheckCfg())
return Result::Fail;
if (!mSettings.cppcheckCfgProductName.empty()) { if (!mSettings.cppcheckCfgProductName.empty()) {
mLogger.printRaw(mSettings.cppcheckCfgProductName); mLogger.printRaw(mSettings.cppcheckCfgProductName);
} else { } else {
@ -343,8 +347,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
ImportProject project; ImportProject project;
mSettings.exename = Path::getCurrentExecutablePath(argv[0]);
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') { if (argv[i][0] == '-') {
// User define // User define
@ -1203,7 +1205,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
} }
} }
mSettings.loadCppcheckCfg(); if (!loadCppcheckCfg())
return Result::Fail;
// Default template format.. // Default template format..
if (mSettings.templateFormat.empty()) { if (mSettings.templateFormat.empty()) {
@ -1609,10 +1612,10 @@ void CmdLineParser::printHelp() const
mLogger.printRaw(oss.str()); mLogger.printRaw(oss.str());
} }
bool CmdLineParser::isCppcheckPremium() const { bool CmdLineParser::isCppcheckPremium() {
if (mSettings.cppcheckCfgProductName.empty()) Settings settings;
mSettings.loadCppcheckCfg(); settings.loadCppcheckCfg(); // TODO: how to handle errors?
return startsWith(mSettings.cppcheckCfgProductName, "Cppcheck Premium"); return startsWith(settings.cppcheckCfgProductName, "Cppcheck Premium");
} }
bool CmdLineParser::tryLoadLibrary(Library& destination, const std::string& basepath, const char* filename) bool CmdLineParser::tryLoadLibrary(Library& destination, const std::string& basepath, const char* filename)
@ -1701,3 +1704,14 @@ bool CmdLineParser::loadAddons(Settings& settings)
} }
return result; return result;
} }
bool CmdLineParser::loadCppcheckCfg()
{
const std::string cfgErr = mSettings.loadCppcheckCfg();
if (!cfgErr.empty()) {
mLogger.printError("could not load cppcheck.cfg - " + cfgErr);
return false;
}
return true;
}

View File

@ -111,7 +111,7 @@ protected:
void printHelp() const; void printHelp() const;
private: private:
bool isCppcheckPremium() const; static bool isCppcheckPremium();
template<typename T> template<typename T>
bool parseNumberArg(const char* const arg, std::size_t offset, T& num, bool mustBePositive = false) bool parseNumberArg(const char* const arg, std::size_t offset, T& num, bool mustBePositive = false)
@ -150,6 +150,8 @@ private:
*/ */
bool loadAddons(Settings& settings); bool loadAddons(Settings& settings);
bool loadCppcheckCfg();
CmdLineLogger &mLogger; CmdLineLogger &mLogger;
std::vector<std::string> mPathNames; std::vector<std::string> mPathNames;

View File

@ -119,7 +119,7 @@ MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) :
{ {
Settings tempSettings; Settings tempSettings;
tempSettings.exename = QCoreApplication::applicationFilePath().toStdString(); tempSettings.exename = QCoreApplication::applicationFilePath().toStdString();
tempSettings.loadCppcheckCfg(); tempSettings.loadCppcheckCfg(); // TODO: how to handle error?
mCppcheckCfgProductName = QString::fromStdString(tempSettings.cppcheckCfgProductName); mCppcheckCfgProductName = QString::fromStdString(tempSettings.cppcheckCfgProductName);
mCppcheckCfgAbout = QString::fromStdString(tempSettings.cppcheckCfgAbout); mCppcheckCfgAbout = QString::fromStdString(tempSettings.cppcheckCfgAbout);
} }
@ -903,8 +903,7 @@ bool MainWindow::tryLoadLibrary(Library *library, const QString& filename)
return true; return true;
} }
Settings MainWindow::getCppcheckSettings() Settings MainWindow::getCppcheckSettings() {
{
saveSettings(); // Save settings saveSettings(); // Save settings
Settings result; Settings result;
@ -915,7 +914,11 @@ Settings MainWindow::getCppcheckSettings()
if (!std) if (!std)
QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured.").arg("std.cfg")); QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured.").arg("std.cfg"));
result.loadCppcheckCfg(); {
const QString cfgErr = QString::fromStdString(result.loadCppcheckCfg());
if (!cfgErr.isEmpty())
QMessageBox::critical(this, tr("Error"), tr("Failed to load %1 - %2").arg("cppcheck.cfg").arg(cfgErr));
}
// If project file loaded, read settings from it // If project file loaded, read settings from it
if (mProjectFile) { if (mProjectFile) {

View File

@ -40,40 +40,87 @@ Settings::Settings()
setCheckLevelNormal(); setCheckLevelNormal();
} }
// TODO: report error when the config is invalid std::string Settings::loadCppcheckCfg()
void Settings::loadCppcheckCfg()
{ {
std::string fileName = Path::getPathFromFilename(exename) + "cppcheck.cfg"; static const std::string cfgFilename = "cppcheck.cfg";
std::string fileName;
#ifdef FILESDIR #ifdef FILESDIR
if (Path::isFile(FILESDIR "/cppcheck.cfg")) if (Path::isFile(Path::join(FILESDIR, cfgFilename)))
fileName = FILESDIR "/cppcheck.cfg"; fileName = Path::join(FILESDIR, cfgFilename);
#endif #endif
// cppcheck-suppress knownConditionTrueFalse
if (fileName.empty()) {
fileName = Path::getPathFromFilename(exename) + cfgFilename;
if (!Path::isFile(fileName))
return "";
}
std::ifstream fin(fileName); std::ifstream fin(fileName);
if (!fin.is_open()) if (!fin.is_open())
return; return "could not open file";
picojson::value json; picojson::value json;
fin >> json; fin >> json;
if (!picojson::get_last_error().empty()) {
return; const std::string& lastErr = picojson::get_last_error();
picojson::object obj = json.get<picojson::object>(); if (!lastErr.empty())
if (obj.count("productName") && obj["productName"].is<std::string>()) return "not a valid JSON - " + lastErr;
cppcheckCfgProductName = obj["productName"].get<std::string>(); }
if (obj.count("about") && obj["about"].is<std::string>()) const picojson::object& obj = json.get<picojson::object>();
cppcheckCfgAbout = obj["about"].get<std::string>(); {
if (obj.count("addons") && obj["addons"].is<picojson::array>()) { const picojson::object::const_iterator it = obj.find("productName");
for (const picojson::value &v : obj["addons"].get<picojson::array>()) { if (it != obj.cend()) {
const std::string &s = v.get<std::string>(); const auto& v = it->second;
if (!Path::isAbsolute(s)) if (!v.is<std::string>())
addons.emplace(Path::getPathFromFilename(fileName) + s); return "'productName' is not a string";
else cppcheckCfgProductName = v.get<std::string>();
addons.emplace(s);
} }
} }
if (obj.count("suppressions") && obj["suppressions"].is<picojson::array>()) { {
for (const picojson::value &v : obj["suppressions"].get<picojson::array>()) const picojson::object::const_iterator it = obj.find("about");
nomsg.addSuppressionLine(v.get<std::string>()); if (it != obj.cend()) {
const auto& v = it->second;
if (!v.is<std::string>())
return "'about' is not a string";
cppcheckCfgAbout = v.get<std::string>();
}
} }
{
const picojson::object::const_iterator it = obj.find("addons");
if (it != obj.cend()) {
const auto& entry = it->second;
if (!entry.is<picojson::array>())
return "'addons' is not an array";
for (const picojson::value &v : entry.get<picojson::array>())
{
if (!v.is<std::string>())
return "'addons' array entry is not a string";
const std::string &s = v.get<std::string>();
if (!Path::isAbsolute(s))
addons.emplace(Path::join(Path::getPathFromFilename(fileName), s));
else
addons.emplace(s);
}
}
}
{
const picojson::object::const_iterator it = obj.find("suppressions");
if (it != obj.cend()) {
const auto& entry = it->second;
if (!entry.is<picojson::array>())
return "'suppressions' is not an array";
for (const picojson::value &v : entry.get<picojson::array>())
{
if (!v.is<std::string>())
return "'suppressions' array entry is not a string";
const std::string &s = v.get<std::string>();
const std::string err = nomsg.addSuppressionLine(s);
if (!err.empty())
return "could not parse suppression '" + s + "' - " + err;
}
}
}
return "";
} }
std::string Settings::parseEnabled(const std::string &str, std::tuple<SimpleEnableGroup<Severity>, SimpleEnableGroup<Checks>> &groups) std::string Settings::parseEnabled(const std::string &str, std::tuple<SimpleEnableGroup<Severity>, SimpleEnableGroup<Checks>> &groups)

View File

@ -99,7 +99,7 @@ private:
public: public:
Settings(); Settings();
void loadCppcheckCfg(); std::string loadCppcheckCfg();
/** @brief addons, either filename of python/json file or json data */ /** @brief addons, either filename of python/json file or json data */
std::unordered_set<std::string> addons; std::unordered_set<std::string> addons;

View File

@ -32,3 +32,4 @@ Other:
- You can suppress all warnings where macro is used using "-macro" - You can suppress all warnings where macro is used using "-macro"
- fixed CMake build with UBSAN and GCC - fixed CMake build with UBSAN and GCC
- Added command-line options "--fsigned-char" and "--funsigned-char" to control the signess of the "char" type. This overrides previously specified "--platform" options and is overrides by following ones. - Added command-line options "--fsigned-char" and "--funsigned-char" to control the signess of the "char" type. This overrides previously specified "--platform" options and is overrides by following ones.
- An error is now reported when the "cppcheck.cfg" is invalid. The CLI version will also exit with a failure in that case.

View File

@ -22,6 +22,7 @@
#include "cppcheckexecutor.h" #include "cppcheckexecutor.h"
#include "errortypes.h" #include "errortypes.h"
#include "helpers.h" #include "helpers.h"
#include "path.h"
#include "platform.h" #include "platform.h"
#include "redirect.h" #include "redirect.h"
#include "settings.h" #include "settings.h"
@ -125,6 +126,7 @@ private:
TEST_CASE(version); TEST_CASE(version);
TEST_CASE(versionWithCfg); TEST_CASE(versionWithCfg);
TEST_CASE(versionExclusive); TEST_CASE(versionExclusive);
TEST_CASE(versionWithInvalidCfg);
TEST_CASE(onefile); TEST_CASE(onefile);
TEST_CASE(onepath); TEST_CASE(onepath);
TEST_CASE(optionwithoutfile); TEST_CASE(optionwithoutfile);
@ -268,7 +270,9 @@ private:
TEST_CASE(showtimeEmpty); TEST_CASE(showtimeEmpty);
TEST_CASE(showtimeInvalid); TEST_CASE(showtimeInvalid);
TEST_CASE(errorlist); TEST_CASE(errorlist);
TEST_CASE(errorlistWithCfg);
TEST_CASE(errorlistExclusive); TEST_CASE(errorlistExclusive);
TEST_CASE(errorlistWithInvalidCfg);
TEST_CASE(ignorepathsnopath); TEST_CASE(ignorepathsnopath);
#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) #if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING)
TEST_CASE(exceptionhandling); TEST_CASE(exceptionhandling);
@ -360,6 +364,8 @@ private:
TEST_CASE(cppcheckBuildDirExistent); TEST_CASE(cppcheckBuildDirExistent);
TEST_CASE(cppcheckBuildDirNonExistent); TEST_CASE(cppcheckBuildDirNonExistent);
TEST_CASE(cppcheckBuildDirEmpty); TEST_CASE(cppcheckBuildDirEmpty);
TEST_CASE(invalidCppcheckCfg);
} }
void nooptions() { void nooptions() {
@ -412,7 +418,7 @@ private:
void versionWithCfg() { void versionWithCfg() {
REDIRECT; REDIRECT;
ScopedFile file("cppcheck.cfg", ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"),
"{\n" "{\n"
"\"productName\": \"The Product\"" "\"productName\": \"The Product\""
"}\n"); "}\n");
@ -433,6 +439,16 @@ private:
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT); ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
} }
void versionWithInvalidCfg() {
REDIRECT;
ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"),
"{\n");
const char * const argv[] = {"cppcheck", "--version"};
ASSERT_EQUALS(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 1 near: \n", logger->str());
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
void onefile() { void onefile() {
REDIRECT; REDIRECT;
const char * const argv[] = {"cppcheck", "file.cpp"}; const char * const argv[] = {"cppcheck", "file.cpp"};
@ -1690,11 +1706,19 @@ private:
const char * const argv[] = {"cppcheck", "--errorlist"}; const char * const argv[] = {"cppcheck", "--errorlist"};
ASSERT_EQUALS(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); ASSERT_EQUALS(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger
ASSERT(startsWith(GET_REDIRECT_OUTPUT, "<?xml")); ASSERT(startsWith(GET_REDIRECT_OUTPUT, ErrorMessage::getXMLHeader("")));
ASSERT(endsWith(GET_REDIRECT_OUTPUT, "</results>\n")); ASSERT(endsWith(GET_REDIRECT_OUTPUT, "</results>\n"));
} }
// TODO: test --errorlist with product name void errorlistWithCfg() {
REDIRECT;
ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"),
R"({"productName": "The Product"}\n)");
const char * const argv[] = {"cppcheck", "--errorlist"};
ASSERT_EQUALS(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger
ASSERT(startsWith(GET_REDIRECT_OUTPUT, ErrorMessage::getXMLHeader("The Product")));
}
void errorlistExclusive() { void errorlistExclusive() {
REDIRECT; REDIRECT;
@ -1705,6 +1729,16 @@ private:
ASSERT(endsWith(GET_REDIRECT_OUTPUT, "</results>\n")); ASSERT(endsWith(GET_REDIRECT_OUTPUT, "</results>\n"));
} }
void errorlistWithInvalidCfg() {
REDIRECT;
ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"),
"{\n");
const char * const argv[] = {"cppcheck", "--errorlist"};
ASSERT_EQUALS(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 1 near: \n", logger->str());
ASSERT_EQUALS("", GET_REDIRECT_OUTPUT);
}
void ignorepathsnopath() { void ignorepathsnopath() {
REDIRECT; REDIRECT;
const char * const argv[] = {"cppcheck", "-i"}; const char * const argv[] = {"cppcheck", "-i"};
@ -2309,6 +2343,15 @@ private:
ASSERT_EQUALS(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); ASSERT_EQUALS(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("cppcheck: error: Directory '' specified by --cppcheck-build-dir argument has to be existent.\n", logger->str()); ASSERT_EQUALS("cppcheck: error: Directory '' specified by --cppcheck-build-dir argument has to be existent.\n", logger->str());
} }
void invalidCppcheckCfg() {
REDIRECT;
ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"),
"{\n");
const char * const argv[] = {"cppcheck", "test.cpp"};
ASSERT_EQUALS(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv));
ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 1 near: \n", logger->str());
}
}; };
REGISTER_TEST(TestCmdlineParser) REGISTER_TEST(TestCmdlineParser)

View File

@ -20,6 +20,7 @@
#include "errortypes.h" #include "errortypes.h"
#include "settings.h" #include "settings.h"
#include "fixture.h" #include "fixture.h"
#include "helpers.h"
class TestSettings : public TestFixture { class TestSettings : public TestFixture {
public: public:
@ -28,6 +29,7 @@ public:
private: private:
void run() override { void run() override {
TEST_CASE(simpleEnableGroup); TEST_CASE(simpleEnableGroup);
TEST_CASE(loadCppcheckCfg);
} }
void simpleEnableGroup() const { void simpleEnableGroup() const {
@ -85,6 +87,128 @@ private:
ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude)); ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude));
ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck));
} }
void loadCppcheckCfg()
{
{
Settings s;
ASSERT_EQUALS("", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
"{}\n");
ASSERT_EQUALS("", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
"{\n");
ASSERT_EQUALS("not a valid JSON - syntax error at line 1 near: ", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"productName": ""}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS("", s.cppcheckCfgProductName);
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"productName": "product"}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS("product", s.cppcheckCfgProductName);
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"productName": 1}\n)");
ASSERT_EQUALS("'productName' is not a string", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"about": ""}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS("", s.cppcheckCfgAbout);
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"about": "about"}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS("about", s.cppcheckCfgAbout);
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"about": 1}\n)");
ASSERT_EQUALS("'about' is not a string", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"addons": []}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS(0, s.addons.size());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"addons": 1}\n)");
ASSERT_EQUALS("'addons' is not an array", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"addons": ["addon"]}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS(1, s.addons.size());
ASSERT_EQUALS("addon", *s.addons.cbegin());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"addons": [1]}\n)");
ASSERT_EQUALS("'addons' array entry is not a string", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"addons": []}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS(0, s.addons.size());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"suppressions": 1}\n)");
ASSERT_EQUALS("'suppressions' is not an array", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"suppressions": ["id"]}\n)");
ASSERT_EQUALS("", s.loadCppcheckCfg());
ASSERT_EQUALS(1, s.nomsg.getSuppressions().size());
ASSERT_EQUALS("id", s.nomsg.getSuppressions().cbegin()->errorId);
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"suppressions": [""]}\n)");
ASSERT_EQUALS("could not parse suppression '' - Failed to add suppression. No id.", s.loadCppcheckCfg());
}
{
Settings s;
ScopedFile file("cppcheck.cfg",
R"({"suppressions": [1]}\n)");
ASSERT_EQUALS("'suppressions' array entry is not a string", s.loadCppcheckCfg());
}
// TODO: test with FILESDIR
}
}; };
REGISTER_TEST(TestSettings) REGISTER_TEST(TestSettings)