Skip to content

Commit

Permalink
Report unfixed when parsing/transforming errors (#557)
Browse files Browse the repository at this point in the history
* refactor transformer test

* report unfixed

* report unfixed within add failures
  • Loading branch information
clavedeluna authored May 13, 2024
1 parent a5a227d commit 51ab4bd
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 23 deletions.
9 changes: 4 additions & 5 deletions src/codemodder/codemods/libcst_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,19 +263,18 @@ def apply(
with open(file_path, "r", encoding="utf-8") as f:
source_tree = cst.parse_module(f.read())
except Exception:
file_context.add_failure(file_path)
logger.exception("error parsing file %s", file_path)
file_context.add_failure(file_path, reason := "Failed to parse file")
logger.exception("%s %s", reason, file_path)
return None

tree = source_tree

try:
with file_context.timer.measure("transform"):
for transformer in self.transformers:
tree = transformer.transform(tree, results, file_context)
except Exception:
file_context.add_failure(file_path)
logger.exception("error transforming file %s", file_path)
file_context.add_failure(file_path, reason := "Failed to transform file")
logger.exception("%s %s", reason, file_path)
return None

if not file_context.codemod_changes:
Expand Down
10 changes: 9 additions & 1 deletion src/codemodder/file_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ def add_dependency(self, dependency: Dependency):
def add_changeset(self, result: ChangeSet):
self.changesets.append(result)

def add_failure(self, filename: Path):
def add_failure(self, filename: Path, reason: str):
self.failures.append(filename)
self.add_unfixed_findings(self.get_all_findings(), reason, 0)

def add_unfixed_findings(
self, findings: list[Finding], reason: str, line_number: int | None = None
Expand All @@ -58,3 +59,10 @@ def get_findings_for_location(self, line_number: int):
)
and result.finding is not None
]

def get_all_findings(self):
return [
result.finding
for result in (self.results or [])
if result.finding is not None
]
93 changes: 76 additions & 17 deletions tests/test_libcst_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,98 @@
LibcstResultTransformer,
LibcstTransformerPipeline,
)
from codemodder.context import CodemodExecutionContext
from codemodder.file_context import FileContext
from core_codemods.defectdojo.results import DefectDojoResult


def test_parse_error(mocker, caplog):
mocker.patch(
"codemodder.codemods.libcst_transformer.cst.parse_module",
side_effect=ParserSyntaxError,
def _apply_and_assert(mocker, transformer):
file_context = FileContext("home", mocker.MagicMock())
execution_context = CodemodExecutionContext(
directory=mocker.MagicMock(),
dry_run=True,
verbose=False,
registry=mocker.MagicMock(),
repo_manager=mocker.MagicMock(),
path_include=[],
path_exclude=[],
)
pipeline = LibcstTransformerPipeline(transformer)
pipeline.apply(
context=execution_context,
file_context=file_context,
results=None,
)
assert len(file_context.failures) == 1
assert file_context.unfixed_findings == []

transformer = mocker.MagicMock(spec=LibcstResultTransformer)
file_context = mocker.MagicMock()

def _apply_and_assert_with_tool(mocker, transformer, reason):
file_path = mocker.MagicMock()
file_context = FileContext(
"home",
file_path,
results=[
DefectDojoResult.from_finding(
{
"id": 1,
"title": "python.django.security.audit.secure-cookies.django-secure-set-cookie",
"file_path": file_path,
"line": 2,
},
)
],
)
execution_context = CodemodExecutionContext(
directory=mocker.MagicMock(),
dry_run=True,
verbose=False,
registry=mocker.MagicMock(),
repo_manager=mocker.MagicMock(),
path_include=[],
path_exclude=[],
tool_result_files_map={"sonar": ["results.json"]},
)
pipeline = LibcstTransformerPipeline(transformer)
pipeline.apply(
context=mocker.MagicMock(),
context=execution_context,
file_context=file_context,
results=None,
)
assert len(file_context.failures) == 1
assert len(file_context.unfixed_findings) == 1
assert file_context.unfixed_findings[0].reason == reason

file_context.add_failure.assert_called_once()
assert "error parsing file" in caplog.text

def test_parse_error(mocker, caplog):
mocker.patch(
"codemodder.codemods.libcst_transformer.cst.parse_module",
side_effect=ParserSyntaxError,
)
transformer = mocker.MagicMock(spec=LibcstResultTransformer)
_apply_and_assert(mocker, transformer)
assert "Failed to parse file" in caplog.text


def test_transformer_error(mocker, caplog):
transformer = mocker.MagicMock(spec=LibcstResultTransformer)
transformer.transform.side_effect = ParserSyntaxError
file_context = mocker.MagicMock()
_apply_and_assert(mocker, transformer)
assert "Failed to transform file" in caplog.text

pipeline = LibcstTransformerPipeline(transformer)
pipeline.apply(
context=mocker.MagicMock(),
file_context=file_context,
results=None,

def test_parse_error_with_tool(mocker, caplog):
mocker.patch(
"codemodder.codemods.libcst_transformer.cst.parse_module",
side_effect=ParserSyntaxError,
)
transformer = mocker.MagicMock(spec=LibcstResultTransformer)
_apply_and_assert_with_tool(mocker, transformer, "Failed to parse file")
assert "Failed to parse file" in caplog.text

file_context.add_failure.assert_called_once()
assert "error transforming file" in caplog.text

def test_transformer_error_with_tool(mocker, caplog):
transformer = mocker.MagicMock(spec=LibcstResultTransformer)
transformer.transform.side_effect = ParserSyntaxError
_apply_and_assert_with_tool(mocker, transformer, "Failed to transform file")
assert "Failed to transform file" in caplog.text

0 comments on commit 51ab4bd

Please sign in to comment.