diff --git a/platformio/commands/check/tools/cppcheck.py b/platformio/commands/check/tools/cppcheck.py index 1c702b60..ec2b96d3 100644 --- a/platformio/commands/check/tools/cppcheck.py +++ b/platformio/commands/check/tools/cppcheck.py @@ -49,6 +49,7 @@ class CppcheckCheckTool(CheckToolBase): for msg in ( "No C or C++ source files found", "unrecognized command line option", + "there was an internal error", ) ): self._bad_input = True diff --git a/platformio/commands/check/tools/pvsstudio.py b/platformio/commands/check/tools/pvsstudio.py index e59281f9..e671773e 100644 --- a/platformio/commands/check/tools/pvsstudio.py +++ b/platformio/commands/check/tools/pvsstudio.py @@ -53,7 +53,15 @@ class PvsStudioCheckTool(CheckToolBase): # pylint: disable=too-many-instance-at ) def tool_output_filter(self, line): # pylint: disable=arguments-differ - if "license was not entered" in line.lower(): + if any( + [ + err_msg in line.lower() + for err_msg in ( + "license was not entered", + "license information is incorrect", + ) + ] + ): self._bad_input = True return line @@ -193,7 +201,7 @@ class PvsStudioCheckTool(CheckToolBase): # pylint: disable=too-many-instance-at '"%s"' % self._tmp_preprocessed_file, ] cmd.extend([f for f in flags if f]) - cmd.extend(["-D%s" % d for d in self.cpp_defines]) + cmd.extend(['"-D%s"' % d.replace('"', '\\"') for d in self.cpp_defines]) cmd.append('@"%s"' % self._tmp_cmd_file) # Explicitly specify C++ as the language used in .ino files diff --git a/tests/commands/test_check.py b/tests/commands/test_check.py index d4b3b79b..746a81dd 100644 --- a/tests/commands/test_check.py +++ b/tests/commands/test_check.py @@ -15,6 +15,7 @@ # pylint: disable=redefined-outer-name import json +import sys from os.path import isfile, join import pytest @@ -121,6 +122,56 @@ def test_check_tool_defines_passed(clirunner, check_dir): assert "__GNUC__" in output +def test_check_tool_complex_defines_handled( + clirunner, validate_cliresult, tmpdir_factory +): + project_dir = tmpdir_factory.mktemp("project_dir") + + project_dir.join("platformio.ini").write( + DEFAULT_CONFIG + + R""" +check_tool = cppcheck, clangtidy, pvs-studio +build_flags = + -DEXTERNAL_INCLUDE_FILE=\"test.h\" + "-DDEFINE_WITH_SPACE="Hello World!"" +""" + ) + + src_dir = project_dir.mkdir("src") + src_dir.join("test.h").write( + """ +#ifndef TEST_H +#define TEST_H +#define ARBITRARY_CONST_VALUE 10 +#endif +""" + ) + + src_dir.join("main.c").write( + PVS_STUDIO_FREE_LICENSE_HEADER + + """ +#if !defined(EXTERNAL_INCLUDE_FILE) +#error "EXTERNAL_INCLUDE_FILE is not declared!" +#else +#include EXTERNAL_INCLUDE_FILE +#endif + +int main() +{ + /* Index out of bounds */ + int arr[ARBITRARY_CONST_VALUE]; + for(int i=0; i < ARBITRARY_CONST_VALUE+1; i++) { + arr[i] = 0; /* High */ + } + return 0; +} +""" + ) + + default_result = clirunner.invoke(cmd_check, ["--project-dir", str(project_dir)]) + validate_cliresult(default_result) + + def test_check_language_standard_definition_passed(clirunner, tmpdir): config = DEFAULT_CONFIG + "\nbuild_flags = -std=c++17" tmpdir.join("platformio.ini").write(config) @@ -466,6 +517,38 @@ def test_check_pvs_studio_fails_without_license(clirunner, tmpdir): assert "license was not entered" in verbose_result.output.lower() +@pytest.mark.skipif( + sys.platform != "win32", + reason="For some reason the error message is different on Windows", +) +def test_check_pvs_studio_fails_broken_license(clirunner, tmpdir): + config = ( + DEFAULT_CONFIG + + """ +check_tool = pvs-studio +check_flags = --lic-file=./pvs-studio.lic +""" + ) + + tmpdir.join("platformio.ini").write(config) + tmpdir.mkdir("src").join("main.c").write(TEST_CODE) + tmpdir.join("pvs-studio.lic").write( + """ +TEST +TEST-TEST-TEST-TEST +""" + ) + + default_result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)]) + verbose_result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"]) + + assert default_result.exit_code != 0 + assert "failed to perform check" in default_result.output.lower() + + assert verbose_result.exit_code != 0 + assert "license information is incorrect" in verbose_result.output.lower() + + def test_check_embedded_platform_all_tools(clirunner, validate_cliresult, tmpdir): config = """ [env:test]