2019-09-08 18:04:41 +03:00
|
|
|
# Copyright (c) 2019-present PlatformIO <contact@platformio.org>
|
|
|
|
#
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
#
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
#
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
# limitations under the License.
|
|
|
|
|
2020-08-17 12:56:57 +03:00
|
|
|
# pylint: disable=redefined-outer-name
|
|
|
|
|
2019-09-08 18:04:41 +03:00
|
|
|
import json
|
2022-04-27 20:45:21 +03:00
|
|
|
import sys
|
2020-04-26 12:58:05 +03:00
|
|
|
from os.path import isfile, join
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
import pytest
|
|
|
|
|
2020-04-26 01:38:25 +03:00
|
|
|
from platformio import fs
|
2019-10-23 16:05:27 +03:00
|
|
|
from platformio.commands.check.command import cli as cmd_check
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
DEFAULT_CONFIG = """
|
|
|
|
[env:native]
|
|
|
|
platform = native
|
|
|
|
"""
|
|
|
|
|
|
|
|
TEST_CODE = """
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
void run_defects() {
|
|
|
|
/* Freeing a pointer twice */
|
|
|
|
int* doubleFreePi = (int*)malloc(sizeof(int));
|
|
|
|
*doubleFreePi=2;
|
|
|
|
free(doubleFreePi);
|
2019-10-30 13:38:46 +02:00
|
|
|
free(doubleFreePi); /* High */
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
/* Reading uninitialized memory */
|
|
|
|
int* uninitializedPi = (int*)malloc(sizeof(int));
|
2019-10-30 13:38:46 +02:00
|
|
|
*uninitializedPi++; /* High + Medium*/
|
2019-09-08 18:04:41 +03:00
|
|
|
free(uninitializedPi);
|
|
|
|
|
|
|
|
/* Delete instead of delete [] */
|
|
|
|
int* wrongDeletePi = new int[10];
|
|
|
|
wrongDeletePi++;
|
2019-10-30 13:38:46 +02:00
|
|
|
delete wrongDeletePi; /* High */
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
/* Index out of bounds */
|
|
|
|
int arr[10];
|
|
|
|
for(int i=0; i < 11; i++) {
|
2019-10-30 13:38:46 +02:00
|
|
|
arr[i] = 0; /* High */
|
2019-09-08 18:04:41 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
2019-11-04 21:34:39 +02:00
|
|
|
int uninitializedVar; /* Low */
|
2019-09-08 18:04:41 +03:00
|
|
|
run_defects();
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
|
|
|
|
PVS_STUDIO_FREE_LICENSE_HEADER = """
|
|
|
|
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
|
|
|
|
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
|
|
|
|
"""
|
|
|
|
|
2021-09-10 15:32:30 +03:00
|
|
|
EXPECTED_ERRORS = 5
|
2019-09-08 18:04:41 +03:00
|
|
|
EXPECTED_WARNINGS = 1
|
2021-09-10 15:32:30 +03:00
|
|
|
EXPECTED_STYLE = 2
|
2019-09-08 18:04:41 +03:00
|
|
|
EXPECTED_DEFECTS = EXPECTED_ERRORS + EXPECTED_WARNINGS + EXPECTED_STYLE
|
|
|
|
|
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
@pytest.fixture(scope="module")
|
|
|
|
def check_dir(tmpdir_factory):
|
|
|
|
tmpdir = tmpdir_factory.mktemp("project")
|
|
|
|
tmpdir.join("platformio.ini").write(DEFAULT_CONFIG)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(TEST_CODE)
|
|
|
|
return tmpdir
|
|
|
|
|
|
|
|
|
2019-09-08 18:04:41 +03:00
|
|
|
def count_defects(output):
|
|
|
|
error, warning, style = 0, 0, 0
|
|
|
|
for l in output.split("\n"):
|
|
|
|
if "[high:error]" in l:
|
|
|
|
error += 1
|
|
|
|
elif "[medium:warning]" in l:
|
|
|
|
warning += 1
|
|
|
|
elif "[low:style]" in l:
|
|
|
|
style += 1
|
|
|
|
return error, warning, style
|
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_cli_output(clirunner, validate_cliresult, check_dir):
|
2019-09-08 23:33:25 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(check_dir)])
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
2019-09-23 23:13:48 +03:00
|
|
|
assert errors + warnings + style == EXPECTED_DEFECTS
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_json_output(clirunner, validate_cliresult, check_dir):
|
2019-09-08 18:04:41 +03:00
|
|
|
result = clirunner.invoke(
|
2019-09-23 23:13:48 +03:00
|
|
|
cmd_check, ["--project-dir", str(check_dir), "--json-output"]
|
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
|
|
|
|
2019-09-08 18:04:41 +03:00
|
|
|
output = json.loads(result.stdout.strip())
|
|
|
|
|
|
|
|
assert isinstance(output, list)
|
2019-09-23 23:13:48 +03:00
|
|
|
assert len(output[0].get("defects", [])) == EXPECTED_DEFECTS
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
def test_check_tool_defines_passed(clirunner, check_dir):
|
2019-09-23 23:13:48 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(check_dir), "--verbose"])
|
2019-09-08 18:04:41 +03:00
|
|
|
output = result.output
|
|
|
|
|
2019-09-23 23:13:48 +03:00
|
|
|
assert "PLATFORMIO=" in output
|
|
|
|
assert "__GNUC__" in output
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2022-04-27 20:45:21 +03:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_language_standard_definition_passed(clirunner, tmpdir):
|
|
|
|
config = DEFAULT_CONFIG + "\nbuild_flags = -std=c++17"
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(TEST_CODE)
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"])
|
|
|
|
|
|
|
|
assert "__cplusplus=201703L" in result.output
|
|
|
|
assert "--std=c++17" in result.output
|
|
|
|
|
|
|
|
|
2021-09-29 14:46:02 +03:00
|
|
|
def test_check_language_standard_option_is_converted(clirunner, tmpdir):
|
|
|
|
config = (
|
|
|
|
DEFAULT_CONFIG
|
|
|
|
+ """
|
|
|
|
build_flags = -std=gnu++1y
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(TEST_CODE)
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"])
|
|
|
|
|
|
|
|
assert "--std=c++14" in result.output
|
|
|
|
|
|
|
|
|
|
|
|
def test_check_language_standard_is_prioritized_over_build_flags(clirunner, tmpdir):
|
|
|
|
config = (
|
|
|
|
DEFAULT_CONFIG
|
|
|
|
+ """
|
|
|
|
check_flags = --std=c++03
|
|
|
|
build_flags = -std=c++17
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(TEST_CODE)
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"])
|
|
|
|
|
|
|
|
assert "--std=c++03" in result.output
|
|
|
|
assert "--std=c++17" not in result.output
|
|
|
|
|
|
|
|
|
|
|
|
def test_check_language_standard_for_c_language(clirunner, tmpdir):
|
|
|
|
config = DEFAULT_CONFIG + "\nbuild_flags = -std=c11"
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.c").write(TEST_CODE)
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"])
|
|
|
|
|
|
|
|
assert "--std=c11" in result.output
|
|
|
|
assert "__STDC_VERSION__=201112L" in result.output
|
|
|
|
assert "__cplusplus" not in result.output
|
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_severity_threshold(clirunner, validate_cliresult, check_dir):
|
2019-09-08 18:04:41 +03:00
|
|
|
result = clirunner.invoke(
|
2019-09-23 23:13:48 +03:00
|
|
|
cmd_check, ["--project-dir", str(check_dir), "--severity=high"]
|
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
2019-09-23 23:13:48 +03:00
|
|
|
assert errors == EXPECTED_ERRORS
|
|
|
|
assert warnings == 0
|
|
|
|
assert style == 0
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
def test_check_includes_passed(clirunner, check_dir):
|
2019-09-23 23:13:48 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(check_dir), "--verbose"])
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
inc_count = 0
|
2020-08-25 21:19:21 +03:00
|
|
|
for l in result.output.split("\n"):
|
2019-09-08 18:04:41 +03:00
|
|
|
if l.startswith("Includes:"):
|
|
|
|
inc_count = l.count("-I")
|
|
|
|
|
|
|
|
# at least 1 include path for default mode
|
2021-01-20 15:14:45 +02:00
|
|
|
assert inc_count > 0
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_silent_mode(clirunner, validate_cliresult, check_dir):
|
2019-09-23 23:13:48 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(check_dir), "--silent"])
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert errors == EXPECTED_ERRORS
|
|
|
|
assert warnings == 0
|
|
|
|
assert style == 0
|
|
|
|
|
|
|
|
|
2020-08-25 22:01:08 +03:00
|
|
|
def test_check_custom_pattern_absolute_path(
|
|
|
|
clirunner, validate_cliresult, tmpdir_factory
|
|
|
|
):
|
2019-11-04 21:34:39 +02:00
|
|
|
project_dir = tmpdir_factory.mktemp("project")
|
|
|
|
project_dir.join("platformio.ini").write(DEFAULT_CONFIG)
|
|
|
|
|
|
|
|
check_dir = tmpdir_factory.mktemp("custom_src_dir")
|
|
|
|
check_dir.join("main.cpp").write(TEST_CODE)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
result = clirunner.invoke(
|
2019-11-04 21:34:39 +02:00
|
|
|
cmd_check, ["--project-dir", str(project_dir), "--pattern=" + str(check_dir)]
|
2019-09-23 23:13:48 +03:00
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert errors == EXPECTED_ERRORS
|
|
|
|
assert warnings == EXPECTED_WARNINGS
|
|
|
|
assert style == EXPECTED_STYLE
|
|
|
|
|
|
|
|
|
2020-08-25 22:01:08 +03:00
|
|
|
def test_check_custom_pattern_relative_path(
|
|
|
|
clirunner, validate_cliresult, tmpdir_factory
|
|
|
|
):
|
2019-11-04 21:34:39 +02:00
|
|
|
tmpdir = tmpdir_factory.mktemp("project")
|
|
|
|
tmpdir.join("platformio.ini").write(DEFAULT_CONFIG)
|
|
|
|
|
|
|
|
tmpdir.mkdir("app").join("main.cpp").write(TEST_CODE)
|
|
|
|
tmpdir.mkdir("prj").join("test.cpp").write(TEST_CODE)
|
|
|
|
|
|
|
|
result = clirunner.invoke(
|
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--pattern=app", "--pattern=prj"]
|
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-11-04 21:34:39 +02:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert errors + warnings + style == EXPECTED_DEFECTS * 2
|
|
|
|
|
|
|
|
|
2019-09-27 14:13:53 +03:00
|
|
|
def test_check_no_source_files(clirunner, tmpdir):
|
2019-09-08 18:04:41 +03:00
|
|
|
tmpdir.join("platformio.ini").write(DEFAULT_CONFIG)
|
|
|
|
tmpdir.mkdir("src")
|
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert result.exit_code != 0
|
|
|
|
assert errors == 0
|
|
|
|
assert warnings == 0
|
|
|
|
assert style == 0
|
|
|
|
|
|
|
|
|
2019-09-27 14:13:53 +03:00
|
|
|
def test_check_bad_flag_passed(clirunner, check_dir):
|
2019-09-08 18:04:41 +03:00
|
|
|
result = clirunner.invoke(
|
2019-09-23 23:13:48 +03:00
|
|
|
cmd_check, ["--project-dir", str(check_dir), '"--flags=--UNKNOWN"']
|
|
|
|
)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert result.exit_code != 0
|
|
|
|
assert errors == 0
|
|
|
|
assert warnings == 0
|
|
|
|
assert style == 0
|
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_success_if_no_errors(clirunner, validate_cliresult, tmpdir):
|
2019-09-08 18:04:41 +03:00
|
|
|
tmpdir.join("platformio.ini").write(DEFAULT_CONFIG)
|
2019-09-23 23:13:48 +03:00
|
|
|
tmpdir.mkdir("src").join("main.c").write(
|
|
|
|
"""
|
2019-09-08 18:04:41 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2019-10-30 13:38:46 +02:00
|
|
|
void unused_function(){
|
2019-09-08 18:04:41 +03:00
|
|
|
int unusedVar = 0;
|
|
|
|
int* iP = &unusedVar;
|
|
|
|
*iP++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
}
|
2019-09-23 23:13:48 +03:00
|
|
|
"""
|
|
|
|
)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert "[PASSED]" in result.output
|
|
|
|
assert errors == 0
|
|
|
|
assert warnings == 1
|
|
|
|
assert style == 1
|
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_individual_flags_passed(clirunner, validate_cliresult, tmpdir):
|
2020-01-23 12:57:54 +02:00
|
|
|
config = DEFAULT_CONFIG + "\ncheck_tool = cppcheck, clangtidy, pvs-studio"
|
|
|
|
config += """\ncheck_flags =
|
|
|
|
cppcheck: --std=c++11
|
|
|
|
clangtidy: --fix-errors
|
|
|
|
pvs-studio: --analysis-mode=4
|
|
|
|
"""
|
2020-08-25 21:19:21 +03:00
|
|
|
|
2019-09-08 18:04:41 +03:00
|
|
|
tmpdir.join("platformio.ini").write(config)
|
2020-08-25 22:01:08 +03:00
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(
|
|
|
|
PVS_STUDIO_FREE_LICENSE_HEADER + TEST_CODE
|
|
|
|
)
|
2019-09-08 23:33:25 +03:00
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir), "-v"])
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2020-01-23 12:57:54 +02:00
|
|
|
clang_flags_found = cppcheck_flags_found = pvs_flags_found = False
|
2019-09-08 18:04:41 +03:00
|
|
|
for l in result.output.split("\n"):
|
|
|
|
if "--fix" in l and "clang-tidy" in l and "--std=c++11" not in l:
|
|
|
|
clang_flags_found = True
|
|
|
|
elif "--std=c++11" in l and "cppcheck" in l and "--fix" not in l:
|
|
|
|
cppcheck_flags_found = True
|
2020-01-23 12:57:54 +02:00
|
|
|
elif (
|
|
|
|
"--analysis-mode=4" in l and "pvs-studio" in l.lower() and "--fix" not in l
|
|
|
|
):
|
|
|
|
pvs_flags_found = True
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
assert clang_flags_found
|
|
|
|
assert cppcheck_flags_found
|
2020-01-23 12:57:54 +02:00
|
|
|
assert pvs_flags_found
|
2019-09-08 18:04:41 +03:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_cppcheck_misra_addon(clirunner, validate_cliresult, check_dir):
|
2019-09-23 23:13:48 +03:00
|
|
|
check_dir.join("misra.json").write(
|
|
|
|
"""
|
2019-09-08 18:04:41 +03:00
|
|
|
{
|
|
|
|
"script": "addons/misra.py",
|
|
|
|
"args": ["--rule-texts=rules.txt"]
|
|
|
|
}
|
2019-09-23 23:13:48 +03:00
|
|
|
"""
|
|
|
|
)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2019-09-23 23:13:48 +03:00
|
|
|
check_dir.join("rules.txt").write(
|
|
|
|
"""
|
2019-09-08 18:04:41 +03:00
|
|
|
Appendix A Summary of guidelines
|
|
|
|
Rule 3.1 Required
|
|
|
|
R3.1 text.
|
|
|
|
Rule 4.1 Required
|
|
|
|
R4.1 text.
|
|
|
|
Rule 10.4 Mandatory
|
|
|
|
R10.4 text.
|
|
|
|
Rule 11.5 Advisory
|
|
|
|
R11.5 text.
|
|
|
|
Rule 15.5 Advisory
|
|
|
|
R15.5 text.
|
|
|
|
Rule 15.6 Required
|
|
|
|
R15.6 text.
|
|
|
|
Rule 17.7 Required
|
|
|
|
R17.7 text.
|
|
|
|
Rule 20.1 Advisory
|
|
|
|
R20.1 text.
|
|
|
|
Rule 21.3 Required
|
|
|
|
R21.3 Found MISRA defect
|
|
|
|
Rule 21.4
|
|
|
|
R21.4 text.
|
2019-09-23 23:13:48 +03:00
|
|
|
"""
|
|
|
|
)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2019-09-08 23:33:25 +03:00
|
|
|
result = clirunner.invoke(
|
2019-09-23 23:13:48 +03:00
|
|
|
cmd_check, ["--project-dir", str(check_dir), "--flags=--addon=misra.json"]
|
|
|
|
)
|
2019-09-08 18:04:41 +03:00
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2019-09-08 18:04:41 +03:00
|
|
|
assert "R21.3 Found MISRA defect" in result.output
|
2019-09-08 23:33:25 +03:00
|
|
|
assert not isfile(join(str(check_dir), "src", "main.cpp.dump"))
|
2019-10-25 15:40:50 +03:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_fails_on_defects_only_with_flag(clirunner, validate_cliresult, tmpdir):
|
2019-10-25 15:40:50 +03:00
|
|
|
config = DEFAULT_CONFIG + "\ncheck_tool = cppcheck, clangtidy"
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(TEST_CODE)
|
|
|
|
|
2019-10-25 17:43:52 +03:00
|
|
|
default_result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
|
2019-10-25 15:40:50 +03:00
|
|
|
|
|
|
|
result_with_flag = clirunner.invoke(
|
2019-10-30 13:38:46 +02:00
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--fail-on-defect=high"]
|
2019-10-25 17:43:52 +03:00
|
|
|
)
|
2019-10-25 15:40:50 +03:00
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(default_result)
|
2019-10-25 15:40:50 +03:00
|
|
|
assert result_with_flag.exit_code != 0
|
2019-10-30 13:38:46 +02:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_fails_on_defects_only_on_specified_level(
|
|
|
|
clirunner, validate_cliresult, tmpdir
|
|
|
|
):
|
2019-10-30 13:38:46 +02:00
|
|
|
config = DEFAULT_CONFIG + "\ncheck_tool = cppcheck, clangtidy"
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.c").write(
|
|
|
|
"""
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
void unused_function(){
|
|
|
|
int unusedVar = 0;
|
|
|
|
int* iP = &unusedVar;
|
|
|
|
*iP++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
|
|
|
high_result = clirunner.invoke(
|
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--fail-on-defect=high"]
|
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(high_result)
|
2019-10-30 13:38:46 +02:00
|
|
|
|
|
|
|
low_result = clirunner.invoke(
|
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--fail-on-defect=low"]
|
|
|
|
)
|
|
|
|
|
|
|
|
assert low_result.exit_code != 0
|
2020-01-23 12:57:54 +02:00
|
|
|
|
|
|
|
|
2020-07-08 21:52:34 +03:00
|
|
|
def test_check_pvs_studio_free_license(clirunner, tmpdir):
|
2020-01-23 12:57:54 +02:00
|
|
|
config = """
|
|
|
|
[env:test]
|
|
|
|
platform = teensy
|
|
|
|
board = teensy35
|
|
|
|
framework = arduino
|
|
|
|
check_tool = pvs-studio
|
|
|
|
"""
|
|
|
|
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
2020-08-25 21:19:21 +03:00
|
|
|
tmpdir.mkdir("src").join("main.c").write(PVS_STUDIO_FREE_LICENSE_HEADER + TEST_CODE)
|
2020-01-23 12:57:54 +02:00
|
|
|
|
|
|
|
result = clirunner.invoke(
|
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--fail-on-defect=high", "-v"]
|
|
|
|
)
|
|
|
|
|
|
|
|
errors, warnings, style = count_defects(result.output)
|
|
|
|
|
|
|
|
assert result.exit_code != 0
|
|
|
|
assert errors != 0
|
|
|
|
assert warnings != 0
|
|
|
|
assert style == 0
|
2020-04-26 00:10:41 +03:00
|
|
|
|
|
|
|
|
2020-12-22 00:21:32 +02:00
|
|
|
def test_check_pvs_studio_fails_without_license(clirunner, tmpdir):
|
|
|
|
config = DEFAULT_CONFIG + "\ncheck_tool = pvs-studio"
|
|
|
|
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.c").write(TEST_CODE)
|
|
|
|
|
|
|
|
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 was not entered" in verbose_result.output.lower()
|
2022-04-27 20:45:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
@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()
|
2020-12-22 00:21:32 +02:00
|
|
|
|
|
|
|
|
2020-08-22 17:48:49 +03:00
|
|
|
def test_check_embedded_platform_all_tools(clirunner, validate_cliresult, tmpdir):
|
2020-04-26 00:10:41 +03:00
|
|
|
config = """
|
|
|
|
[env:test]
|
|
|
|
platform = ststm32
|
|
|
|
board = nucleo_f401re
|
|
|
|
framework = %s
|
|
|
|
check_tool = %s
|
|
|
|
"""
|
|
|
|
# tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.c").write(
|
2020-08-25 22:01:08 +03:00
|
|
|
PVS_STUDIO_FREE_LICENSE_HEADER
|
|
|
|
+ """
|
2020-04-26 00:10:41 +03:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
void unused_function(int val){
|
|
|
|
int unusedVar = 0;
|
|
|
|
int* iP = &unusedVar;
|
|
|
|
*iP++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
2021-09-29 14:46:02 +03:00
|
|
|
for framework in (
|
|
|
|
"arduino",
|
|
|
|
"stm32cube",
|
|
|
|
"zephyr",
|
|
|
|
):
|
2020-04-26 00:10:41 +03:00
|
|
|
for tool in ("cppcheck", "clangtidy", "pvs-studio"):
|
|
|
|
tmpdir.join("platformio.ini").write(config % (framework, tool))
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
|
2020-08-22 17:48:49 +03:00
|
|
|
validate_cliresult(result)
|
2020-04-26 00:10:41 +03:00
|
|
|
defects = sum(count_defects(result.output))
|
2020-09-09 16:27:36 +03:00
|
|
|
assert defects > 0, "Failed %s with %s" % (
|
|
|
|
framework,
|
|
|
|
tool,
|
|
|
|
)
|
2020-04-26 01:38:25 +03:00
|
|
|
|
|
|
|
|
2020-08-25 21:19:21 +03:00
|
|
|
def test_check_skip_includes_from_packages(clirunner, validate_cliresult, tmpdir):
|
2020-04-26 01:38:25 +03:00
|
|
|
config = """
|
|
|
|
[env:test]
|
|
|
|
platform = nordicnrf52
|
|
|
|
board = nrf52_dk
|
|
|
|
framework = arduino
|
|
|
|
"""
|
|
|
|
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.c").write(TEST_CODE)
|
|
|
|
|
|
|
|
result = clirunner.invoke(
|
|
|
|
cmd_check, ["--project-dir", str(tmpdir), "--skip-packages", "-v"]
|
|
|
|
)
|
2020-08-25 21:19:21 +03:00
|
|
|
validate_cliresult(result)
|
2020-04-26 01:38:25 +03:00
|
|
|
|
|
|
|
project_path = fs.to_unix_path(str(tmpdir))
|
2020-08-25 21:19:21 +03:00
|
|
|
for l in result.output.split("\n"):
|
2020-04-26 01:38:25 +03:00
|
|
|
if not l.startswith("Includes:"):
|
|
|
|
continue
|
|
|
|
for inc in l.split(" "):
|
|
|
|
if inc.startswith("-I") and project_path not in inc:
|
|
|
|
pytest.fail("Detected an include path from packages: " + inc)
|
2020-08-25 21:19:21 +03:00
|
|
|
|
|
|
|
|
|
|
|
def test_check_multiline_error(clirunner, tmpdir_factory):
|
|
|
|
project_dir = tmpdir_factory.mktemp("project")
|
|
|
|
project_dir.join("platformio.ini").write(DEFAULT_CONFIG)
|
|
|
|
|
|
|
|
project_dir.mkdir("include").join("main.h").write(
|
|
|
|
"""
|
|
|
|
#error This is a multiline error message \\
|
|
|
|
that should be correctly reported \\
|
|
|
|
in both default and verbose modes.
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
|
|
|
project_dir.mkdir("src").join("main.c").write(
|
|
|
|
"""
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "main.h"
|
|
|
|
|
|
|
|
int main() {}
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(project_dir)])
|
|
|
|
errors, _, _ = count_defects(result.output)
|
|
|
|
|
|
|
|
result = clirunner.invoke(cmd_check, ["--project-dir", str(project_dir), "-v"])
|
|
|
|
verbose_errors, _, _ = count_defects(result.output)
|
|
|
|
|
|
|
|
assert verbose_errors == errors == 1
|
2021-03-04 22:27:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
def test_check_handles_spaces_in_paths(clirunner, validate_cliresult, tmpdir_factory):
|
|
|
|
tmpdir = tmpdir_factory.mktemp("project dir")
|
|
|
|
config = DEFAULT_CONFIG + "\ncheck_tool = cppcheck, clangtidy, pvs-studio"
|
|
|
|
tmpdir.join("platformio.ini").write(config)
|
|
|
|
tmpdir.mkdir("src").join("main.cpp").write(
|
|
|
|
PVS_STUDIO_FREE_LICENSE_HEADER + TEST_CODE
|
|
|
|
)
|
|
|
|
|
|
|
|
default_result = clirunner.invoke(cmd_check, ["--project-dir", str(tmpdir)])
|
|
|
|
|
|
|
|
validate_cliresult(default_result)
|