checker: allow physical checks to return multiple results, add tests

This commit is contained in:
Tom Milligan 2018-12-05 22:04:32 +00:00 committed by Tom Milligan
parent 93ad9617b0
commit 2803d0a810
No known key found for this signature in database
GPG key ID: BFF10895C45328D2
2 changed files with 109 additions and 18 deletions

View file

@ -541,21 +541,38 @@ class FileChecker(object):
self.processor.next_logical_line()
def run_physical_checks(self, physical_line, override_error_line=None):
"""Run all checks for a given physical line."""
"""Run all checks for a given physical line.
A single physical check may return multiple errors.
"""
for plugin in self.checks["physical_line_plugins"]:
self.processor.update_checker_state_for(plugin)
result = self.run_check(plugin, physical_line=physical_line)
if result is not None:
column_offset, text = result
error_code = self.report(
error_code=None,
line_number=self.processor.line_number,
column=column_offset,
text=text,
line=(override_error_line or physical_line),
)
self.processor.check_physical_error(error_code, physical_line)
if result is not None:
# This is a single result if first element is an int
column_offset = None
try:
column_offset = result[0]
except (IndexError, TypeError):
pass
if isinstance(column_offset, int):
# If we only have a single result, convert to a collection
result = (result,)
for result_single in result:
column_offset, text = result_single
error_code = self.report(
error_code=None,
line_number=self.processor.line_number,
column=column_offset,
text=text,
line=(override_error_line or physical_line),
)
self.processor.check_physical_error(
error_code, physical_line
)
def process_tokens(self):
"""Process tokens and trigger checks.

View file

@ -5,8 +5,17 @@ import pytest
from flake8 import checker
from flake8.plugins import manager
PHYSICAL_LINE = "# Physical line content"
EXPECTED_REPORT = (1, 1, 'T000 Expected Message')
EXPECTED_REPORT_PHYSICAL_LINE = (1, 'T000 Expected Message')
EXPECTED_RESULT_PHYSICAL_LINE = (
'T000',
0,
1,
'Expected Message',
PHYSICAL_LINE,
)
class PluginClass(object):
@ -43,13 +52,48 @@ def plugin_func_list(tree):
return [EXPECTED_REPORT + (type(plugin_func_list), )]
@pytest.mark.parametrize('plugin_target', [
PluginClass,
plugin_func_gen,
plugin_func_list,
])
def test_handle_file_plugins(plugin_target):
"""Test the FileChecker class handling different file plugin types."""
@plugin_func
def plugin_func_physical_ret(physical_line):
"""Expect report from a physical_line. Single return."""
return EXPECTED_REPORT_PHYSICAL_LINE
@plugin_func
def plugin_func_physical_none(physical_line):
"""Expect report from a physical_line. No results."""
return None
@plugin_func
def plugin_func_physical_list_single(physical_line):
"""Expect report from a physical_line. List of single result."""
return [EXPECTED_REPORT_PHYSICAL_LINE]
@plugin_func
def plugin_func_physical_list_multiple(physical_line):
"""Expect report from a physical_line. List of multiple results."""
return [EXPECTED_REPORT_PHYSICAL_LINE] * 2
@plugin_func
def plugin_func_physical_gen_single(physical_line):
"""Expect report from a physical_line. Generator of single result."""
yield EXPECTED_REPORT_PHYSICAL_LINE
@plugin_func
def plugin_func_physical_gen_multiple(physical_line):
"""Expect report from a physical_line. Generator of multiple results."""
for _ in range(3):
yield EXPECTED_REPORT_PHYSICAL_LINE
def mock_file_checker_with_plugin(plugin_target):
"""Get a mock FileChecker class with plugin_target registered.
Useful as a starting point for mocking reports/results.
"""
# Mock an entry point returning the plugin target
entry_point = mock.Mock(spec=['load'])
entry_point.name = plugin_target.name
@ -67,6 +111,17 @@ def test_handle_file_plugins(plugin_target):
checks.to_dictionary(),
mock.MagicMock()
)
return file_checker
@pytest.mark.parametrize('plugin_target', [
PluginClass,
plugin_func_gen,
plugin_func_list,
])
def test_handle_file_plugins(plugin_target):
"""Test the FileChecker class handling different file plugin types."""
file_checker = mock_file_checker_with_plugin(plugin_target)
# Do not actually build an AST
file_checker.processor.build_ast = lambda: True
@ -81,6 +136,25 @@ def test_handle_file_plugins(plugin_target):
text=EXPECTED_REPORT[2])
@pytest.mark.parametrize('plugin_target,len_results', [
(plugin_func_physical_ret, 1),
(plugin_func_physical_none, 0),
(plugin_func_physical_list_single, 1),
(plugin_func_physical_list_multiple, 2),
(plugin_func_physical_gen_single, 1),
(plugin_func_physical_gen_multiple, 3),
])
def test_line_check_results(plugin_target, len_results):
"""Test the FileChecker class handling results from line checks."""
file_checker = mock_file_checker_with_plugin(plugin_target)
# Results will be store in an internal array
file_checker.run_physical_checks(PHYSICAL_LINE)
assert file_checker.results == [
EXPECTED_RESULT_PHYSICAL_LINE
] * len_results
PLACEHOLDER_CODE = 'some_line = "of" * code'