From cc24d6fcefd79e7abe1ae7524def21ec4be5adb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20St=C3=B6neberg?= Date: Sun, 19 Dec 2021 12:36:11 +0100 Subject: [PATCH] Fix #9162 (Invalid --project files do not give any error) (#3177) --- .github/workflows/CI-unixish.yml | 57 ++++--- .gitignore | 6 +- .travis_suppressions | 1 + cli/cmdlineparser.cpp | 6 +- gui/main.cpp | 8 - gui/test/filelist/filelist.pro | 2 + gui/test/projectfile/projectfile.pro | 9 +- gui/test/projectfile/testprojectfile.cpp | 1 + .../translationhandler/translationhandler.pro | 8 +- gui/test/xmlreportv2/xmlreportv2.pro | 2 +- lib/importproject.cpp | 142 ++++++++++++----- lib/importproject.h | 12 +- test/cli/test-project.py | 146 ++++++++++++++++++ test/cli/testutils.py | 30 +++- test/testimportproject.cpp | 4 + 15 files changed, 347 insertions(+), 87 deletions(-) create mode 100644 test/cli/test-project.py diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index f3a2b0de7..a79a29b26 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -156,21 +156,39 @@ jobs: if: contains(matrix.os, 'ubuntu') run: | pushd gui - qmake HAVE_QCHART=yes + qmake CONFIG+=debug HAVE_QCHART=yes make -j$(nproc) - name: Run GUI tests on ubuntu if: contains(matrix.os, 'ubuntu') run: | + pushd gui/test/cppchecklibrarydata + qmake CONFIG+=debug + make -j$(nproc) + ./test-cppchecklibrarydata + popd + pushd gui/test/filelist + qmake CONFIG+=debug + make -j$(nproc) + # TODO: requires X session + #./test-filelist + popd pushd gui/test/projectfile - qmake + qmake CONFIG+=debug make -j$(nproc) ./test-projectfile popd - pushd gui/test/cppchecklibrarydata - qmake + pushd gui/test/translationhandler + qmake CONFIG+=debug make -j$(nproc) - ./test-cppchecklibrarydata + # TODO: requires X session + #./test-translationhandler + popd + pushd gui/test/xmlreportv2 + qmake CONFIG+=debug + make -j$(nproc) + # TODO: requires X session + #./test-xmlreportv2 - name: Generate Qt help file on ubuntu if: contains(matrix.os, 'ubuntu') @@ -178,6 +196,19 @@ jobs: pushd gui/help qhelpgenerator online-help.qhcp -o online-help.qhc + - name: Build triage on ubuntu + if: matrix.os == 'ubuntu-20.04' + run: | + pushd tools/triage + qmake CONFIG+=debug + make -j$(nproc) + + - name: Build Fuzzer + if: matrix.os == 'ubuntu-20.04' + run: | + pushd oss-fuzz + make -j$(nproc) CXX=clang++ CXXFLAGS="-fsanitize=address" fuzz-client translate + - name: Self check (build) if: matrix.os == 'ubuntu-20.04' run: | @@ -196,18 +227,4 @@ jobs: mkdir b2 ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck --cppcheck-build-dir=b2 -D__CPPCHECK__ -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt --addon=naming.json -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings gui/*.cpp gui/temp/*.cpp # self check test and tools - ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings test/*.cpp tools - - - name: Build triage on ubuntu - if: matrix.os == 'ubuntu-20.04' - run: | - pushd tools/triage - qmake - make -j$(nproc) - - - name: Build Fuzzer - if: matrix.os == 'ubuntu-20.04' - run: | - pushd oss-fuzz - make -j$(nproc) CXX=clang++ CXXFLAGS="-fsanitize=address" fuzz-client translate - + ./cppcheck -q -j$(nproc) --std=c++11 --template=selfcheck -D__CPPCHECK__ -DQ_MOC_OUTPUT_REVISION=67 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings test/*.cpp tools diff --git a/.gitignore b/.gitignore index 19f081c3e..c74a0121e 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,8 @@ gui/cppcheck-gui gui/cppcheck-gui.exe gui/gui.sln gui/gui.vcproj +gui/help/online-help.qch +gui/help/online-help.qhc gui/Makefile gui/Makefile.debug gui/Makefile.release @@ -66,11 +68,11 @@ gui/test/Makefile gui/test/*/Makefile gui/test/*/*/Makefile gui/test/benchmark/simple/benchmark-simple +gui/test/cppchecklibrarydata/qrc_resources.cpp +gui/test/cppchecklibrarydata/test-cppchecklibrarydata gui/test/filelist/test-filelist gui/test/projectfile/test-projectfile gui/test/translationhandler/test-translationhandler -gui/test/xmlreport/test-xmlreport -gui/test/xmlreportv1/test-xmlreportv1 gui/test/xmlreportv2/test-xmlreportv2 # Doxygen output folder diff --git a/.travis_suppressions b/.travis_suppressions index d7c2d188d..86a138e8b 100644 --- a/.travis_suppressions +++ b/.travis_suppressions @@ -11,6 +11,7 @@ useStlAlgorithm simplifyUsing:lib/valueptr.h symbolDatabaseWarning:gui/temp/moc_*.cpp simplifyUsing:gui/temp/moc_*.cpp +symbolDatabaseWarning:tools/triage/moc_*.cpp # debug suppressions valueFlowBailout diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index de4daa963..e3970119d 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -644,13 +644,17 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) } } if (projType == ImportProject::Type::MISSING) { - printError("failed to open project '" + projectFile + "'."); + printError("failed to open project '" + projectFile + "'. The file does not exist."); return false; } if (projType == ImportProject::Type::UNKNOWN) { printError("failed to load project '" + projectFile + "'. The format is unknown."); return false; } + if (projType == ImportProject::Type::FAILURE) { + printError("failed to load project '" + projectFile + "'. An error occurred."); + return false; + } } // --project-configuration diff --git a/gui/main.cpp b/gui/main.cpp index 3cc8ec141..2e2a91c92 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -48,14 +48,6 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); -#if QT_VERSION < 0x050000 - // Set codecs so that UTF-8 strings in sources are handled correctly. - // This is ONLY needed for Qt versions 4.x. - // Qt 5.x assumes UTF-8 by default. - QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); - QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); -#endif - QCoreApplication::setOrganizationName("Cppcheck"); QCoreApplication::setApplicationName("Cppcheck-GUI"); diff --git a/gui/test/filelist/filelist.pro b/gui/test/filelist/filelist.pro index 52adfcbd2..e756f095e 100644 --- a/gui/test/filelist/filelist.pro +++ b/gui/test/filelist/filelist.pro @@ -15,10 +15,12 @@ SOURCES += testfilelist.cpp \ ../../filelist.cpp \ ../../../lib/pathmatch.cpp \ ../../../lib/path.cpp \ + ../../../lib/utils.cpp \ ../../../externals/simplecpp/simplecpp.cpp HEADERS += testfilelist.h \ ../../filelist.h \ ../../../lib/pathmatch.h \ ../../../lib/path.h \ + ../../../lib/utils.h \ ../../../externals/simplecpp/simplecpp.h diff --git a/gui/test/projectfile/projectfile.pro b/gui/test/projectfile/projectfile.pro index 5a6c7fdd5..fd1a2f44a 100644 --- a/gui/test/projectfile/projectfile.pro +++ b/gui/test/projectfile/projectfile.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = test-projectfile DEPENDPATH += . -INCLUDEPATH += . ../../../externals/simplecpp +INCLUDEPATH += . ../../../externals/simplecpp ../../../externals/tinyxml2 ../../../externals/picojson OBJECTS_DIR = ../build MOC_DIR = ../build QT -= gui @@ -14,11 +14,8 @@ DEFINES += SRCDIR=\\\"$$PWD\\\" # tests SOURCES += testprojectfile.cpp \ - ../../projectfile.cpp \ - ../../../lib/path.cpp \ - ../../../externals/simplecpp/simplecpp.cpp + ../../projectfile.cpp HEADERS += testprojectfile.h \ ../../projectfile.h \ - ../../../lib/path.h \ - ../../../externals/simplecpp/simplecpp.h + ../../../externals/picojson/picojson.h diff --git a/gui/test/projectfile/testprojectfile.cpp b/gui/test/projectfile/testprojectfile.cpp index aaf708902..0d01b4528 100644 --- a/gui/test/projectfile/testprojectfile.cpp +++ b/gui/test/projectfile/testprojectfile.cpp @@ -31,6 +31,7 @@ Settings::Settings() : maxCtuDepth(10), maxTemplateRecursion(100) {} cppcheck::Platform::Platform() {} Library::Library() {} ImportProject::ImportProject() {} +bool ImportProject::sourceFileExists(const std::string &/*file*/) { return true; } void TestProjectFile::loadInexisting() { diff --git a/gui/test/translationhandler/translationhandler.pro b/gui/test/translationhandler/translationhandler.pro index b131732df..40f2fabb5 100644 --- a/gui/test/translationhandler/translationhandler.pro +++ b/gui/test/translationhandler/translationhandler.pro @@ -9,6 +9,10 @@ QT += widgets include(../common.pri) # tests -SOURCES += testtranslationhandler.cpp +SOURCES += testtranslationhandler.cpp \ + ../../translationhandler.cpp \ + ../../common.cpp -HEADERS += testtranslationhandler.h +HEADERS += testtranslationhandler.h \ + ../../translationhandler.h \ + ../../common.h diff --git a/gui/test/xmlreportv2/xmlreportv2.pro b/gui/test/xmlreportv2/xmlreportv2.pro index 26851a356..bb9fab084 100644 --- a/gui/test/xmlreportv2/xmlreportv2.pro +++ b/gui/test/xmlreportv2/xmlreportv2.pro @@ -1,7 +1,7 @@ TEMPLATE = app TARGET = test-xmlreportv2 DEPENDPATH += . -INCLUDEPATH += . ../../../externals/simplecpp ../../../externals/tinyxml +INCLUDEPATH += . ../../../externals/simplecpp OBJECTS_DIR = ../build MOC_DIR = ../build diff --git a/lib/importproject.cpp b/lib/importproject.cpp index d86d9450f..79e3ced69 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -199,28 +199,35 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings settings ? settings->fileFilters : std::vector(); if (endsWith(filename, ".json")) { - importCompileCommands(fin); - setRelativePaths(filename); - return ImportProject::Type::COMPILE_DB; + if (importCompileCommands(fin)) { + setRelativePaths(filename); + return ImportProject::Type::COMPILE_DB; + } } else if (endsWith(filename, ".sln")) { - importSln(fin, mPath, fileFilters); - setRelativePaths(filename); - return ImportProject::Type::VS_SLN; + if (importSln(fin, mPath, fileFilters)) { + setRelativePaths(filename); + return ImportProject::Type::VS_SLN; + } } else if (endsWith(filename, ".vcxproj")) { std::map variables; - importVcxproj(filename, variables, emptyString, fileFilters); - setRelativePaths(filename); - return ImportProject::Type::VS_VCXPROJ; + if (importVcxproj(filename, variables, emptyString, fileFilters)) { + setRelativePaths(filename); + return ImportProject::Type::VS_VCXPROJ; + } } else if (endsWith(filename, ".bpr")) { - importBcb6Prj(filename); - setRelativePaths(filename); - return ImportProject::Type::BORLAND; + if (importBcb6Prj(filename)) { + setRelativePaths(filename); + return ImportProject::Type::BORLAND; + } } else if (settings && endsWith(filename, ".cppcheck")) { - const bool success = importCppcheckGuiProject(fin, settings); - setRelativePaths(filename); - return success ? ImportProject::Type::CPPCHECK_GUI : ImportProject::Type::MISSING; + if (importCppcheckGuiProject(fin, settings)) { + setRelativePaths(filename); + return ImportProject::Type::CPPCHECK_GUI; + } + } else { + return ImportProject::Type::UNKNOWN; } - return ImportProject::Type::UNKNOWN; + return ImportProject::Type::FAILURE; } static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) @@ -385,12 +392,14 @@ void ImportProject::FileSettings::parseCommand(std::string command) setDefines(defs); } -void ImportProject::importCompileCommands(std::istream &istr) +bool ImportProject::importCompileCommands(std::istream &istr) { picojson::value compileCommands; istr >> compileCommands; - if (!compileCommands.is()) - return; + if (!compileCommands.is()) { + printError("compilation database is not a JSON array"); + return false; + } for (const picojson::value &fileInfo : compileCommands.get()) { picojson::object obj = fileInfo.get(); @@ -412,14 +421,16 @@ void ImportProject::importCompileCommands(std::istream &istr) } } } else { - return; + printError("'arguments' field in compilation database entry is not a JSON array"); + return false; } } else if (obj.find("command") != obj.end()) { if (obj["command"].is()) { comm << obj["command"].get(); } } else { - return; + printError("no 'arguments' or 'command' field found in compilation database entry"); + return false; } const std::string command = comm.str(); @@ -441,19 +452,39 @@ void ImportProject::importCompileCommands(std::istream &istr) #endif else fs.filename = Path::simplifyPath(directory + file); + if (!sourceFileExists(fs.filename)) { + printError("'" + fs.filename + "' from compilation database does not exist"); + return false; + } fs.parseCommand(command); // read settings; -D, -I, -U, -std, -m*, -f* std::map variables; fs.setIncludePaths(directory, fs.includePaths, variables); fileSettings.push_back(fs); } + + return true; } -void ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters) +bool ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters) { + std::string line; + + // skip magic word + if (!std::getline(istr,line)) { + printError("Visual Studio solution file is empty"); + return false; + } + + if (!std::getline(istr, line) || line.find("Microsoft Visual Studio Solution File") != 0) { + printError("Visual Studio solution file header not found"); + return false; + } + std::map variables; variables["SolutionDir"] = path; - std::string line; + bool found = false; + while (std::getline(istr,line)) { if (line.compare(0,8,"Project(")!=0) continue; @@ -466,8 +497,19 @@ void ImportProject::importSln(std::istream &istr, const std::string &path, const std::string vcxproj(line.substr(pos1+1, pos-pos1+7)); if (!Path::isAbsolute(vcxproj)) vcxproj = path + vcxproj; - importVcxproj(Path::fromNativeSeparators(vcxproj), variables, emptyString, fileFilters); + if (!importVcxproj(Path::fromNativeSeparators(vcxproj), variables, emptyString, fileFilters)) { + printError("failed to load '" + vcxproj + "' from Visual Studio solution"); + return false; + } + found = true; } + + if (!found) { + printError("no projects found in Visual Studio solution file"); + return false; + } + + return true; } namespace { @@ -659,7 +701,7 @@ static void loadVisualStudioProperties(const std::string &props, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters) +bool ImportProject::importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters) { variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); @@ -672,11 +714,15 @@ void ImportProject::importVcxproj(const std::string &filename, std::mapFirstChildElement(); node; node = node->NextSiblingElement()) { if (std::strcmp(node->Name(), "ItemGroup") == 0) { const char *labelAttribute = node->Attribute("Label"); @@ -767,17 +813,23 @@ void ImportProject::importVcxproj(const std::string &filename, std::mapName(), CppcheckXml::ProjectElementName) != 0) + if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) { + printError("Cppcheck GUI project file has no XML root node"); return false; + } const std::string &path = mPath; @@ -1211,6 +1270,7 @@ bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *setti settings->safeChecks = temp.safeChecks; settings->bugHunting = temp.bugHunting; settings->functionContracts = temp.functionContracts; + return true; } @@ -1259,3 +1319,13 @@ void ImportProject::setRelativePaths(const std::string &filename) includePath = Path::getRelativePath(includePath, basePaths); } } + +void ImportProject::printError(const std::string &message) +{ + std::cout << "cppcheck: error: " << message << std::endl; +} + +bool ImportProject::sourceFileExists(const std::string &file) +{ + return Path::fileExists(file); +} diff --git a/lib/importproject.h b/lib/importproject.h index 13424ea1c..6a6fa4173 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -52,6 +52,7 @@ public: enum class Type { UNKNOWN, MISSING, + FAILURE, COMPILE_DB, VS_SLN, VS_VCXPROJ, @@ -106,12 +107,15 @@ public: Type import(const std::string &filename, Settings *settings=nullptr); protected: - void importCompileCommands(std::istream &istr); + bool importCompileCommands(std::istream &istr); bool importCppcheckGuiProject(std::istream &istr, Settings *settings); + virtual bool sourceFileExists(const std::string &file); private: - void importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters); - void importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters); - void importBcb6Prj(const std::string &projectFilename); + bool importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters); + bool importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters); + bool importBcb6Prj(const std::string &projectFilename); + + void printError(const std::string &message); void setRelativePaths(const std::string &filename); diff --git a/test/cli/test-project.py b/test/cli/test-project.py new file mode 100644 index 000000000..8f6e221f7 --- /dev/null +++ b/test/cli/test-project.py @@ -0,0 +1,146 @@ +# python -m pytest test-projects.py + +import pytest +import os +import json +from testutils import cppcheck + + +@pytest.mark.parametrize("project_ext", ["json", "sln", "vcxproj", "bpr", "cppcheck"]) +def test_missing_project(project_ext): + project_file = "file.{}".format(project_ext) + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) + assert 1 == ret + assert "cppcheck: error: failed to open project '{}'. The file does not exist.\n".format(project_file) == stdout + assert "" == stderr + + +def _test_project_error(tmpdir, ext, content, expected): + project_file = os.path.join(tmpdir, "file.{}".format(ext)) + + with open(project_file, 'w') as f: + if content is not None: + f.write(content) + + ret, stdout, stderr = cppcheck(['--project=' + str(project_file)]) + assert 1 == ret + assert "cppcheck: error: " + expected + "\ncppcheck: error: failed to load project '{}'. An error occurred.\n".format(project_file) == stdout + assert "" == stderr + + +@pytest.mark.parametrize("project_ext, expected", [ + ("json", "compilation database is not a JSON array"), + ("sln", "Visual Studio solution file is empty"), + ("vcxproj", "Visual Studio project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), + ("bpr", "Borland project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), + ("cppcheck", "Cppcheck GUI project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT") +]) +def test_empty_project(tmpdir, project_ext, expected): + _test_project_error(tmpdir, project_ext, None, expected) + + +def test_json_entry_file_not_found(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp", + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "'{}' from compilation database does not exist".format(os.path.join(tmpdir, "bug1.cpp")) + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_json_no_arguments(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "no 'arguments' or 'command' field found in compilation database entry" + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_json_invalid_arguments(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "arguments": "", + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "'arguments' field in compilation database entry is not a JSON array" + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_sln_invalid_file(tmpdir): + content = "some file" + + expected = "Visual Studio solution file header not found" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_no_header(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "some header" + + expected = "Visual Studio solution file header not found" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_no_projects(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" + + expected = "no projects found in Visual Studio solution file" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_project_file_not_found(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" \ + "# Visual Studio Version 16\r\n" \ + "VisualStudioVersion = 16.0.29020.237\r\n" \ + "MinimumVisualStudioVersion = 10.0.40219.1\r\n" \ + 'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}"\r\n' \ + "ProjectSection(ProjectDependencies) = postProject\r\n" \ + "{C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A}\r\n" \ + "EndProjectSection\r\n" \ + "EndProject\r\n" + + expected = "Visual Studio project file is not a valid XML - XML_ERROR_FILE_NOT_FOUND\n" \ + "cppcheck: error: failed to load '{}' from Visual Studio solution".format(os.path.join(tmpdir, "cli\\cli.vcxproj")) + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_vcxproj_no_xml_root(tmpdir): + content = '' + + expected = "Visual Studio project file has no XML root node" + + _test_project_error(tmpdir, "vcxproj", content, expected) + + +def test_bpr_no_xml_root(tmpdir): + content = '' + + expected = "Borland project file has no XML root node" + + _test_project_error(tmpdir, "bpr", content, expected) + + +def test_cppcheck_no_xml_root(tmpdir): + content = '' + + expected = "Cppcheck GUI project file has no XML root node" + + _test_project_error(tmpdir, "cppcheck", content, expected) diff --git a/test/cli/testutils.py b/test/cli/testutils.py index 599fd1865..468b05c28 100644 --- a/test/cli/testutils.py +++ b/test/cli/testutils.py @@ -4,6 +4,9 @@ import os import subprocess # Create Cppcheck project file +import sys + + def create_gui_project_file(project_file, root_path=None, import_project=None, paths=None, exclude_paths=None, suppressions=None, addon=None): cppcheck_xml = ('\n' '\n') @@ -40,15 +43,28 @@ def create_gui_project_file(project_file, root_path=None, import_project=None, p f.close() +def lookup_cppcheck_exe(): + # path the script is located in + script_path = os.path.dirname(os.path.realpath(__file__)) + + exe_name = "cppcheck" + + if sys.platform == "win32": + exe_name += ".exe" + + for base in (script_path + '/../../', './'): + for path in ('', 'bin/', 'bin/debug/'): + exe_path = base + path + exe_name + if os.path.isfile(exe_path): + return exe_path + + return None + + # Run Cppcheck with args def cppcheck(args): - exe = None - for ext in ('', '.exe'): - for base in ('../../', '../../../'): - for path in ('', 'bin/', 'bin/debug/'): - if (exe is None) and os.path.isfile(base + path + 'cppcheck' + ext): - exe = base + path + 'cppcheck' + ext - assert exe is not None + exe = lookup_cppcheck_exe() + assert exe is not None, 'no cppcheck binary found' logging.info(exe + ' ' + ' '.join(args)) p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) diff --git a/test/testimportproject.cpp b/test/testimportproject.cpp index 07f626e57..8948c75c7 100644 --- a/test/testimportproject.cpp +++ b/test/testimportproject.cpp @@ -28,6 +28,10 @@ class TestImporter : public ImportProject { public: using ImportProject::importCompileCommands; using ImportProject::importCppcheckGuiProject; + + bool sourceFileExists(const std::string & /*file*/) OVERRIDE { + return true; + } };