diff --git a/src/codemodder/result.py b/src/codemodder/result.py index b268c7d2..0c1743ed 100644 --- a/src/codemodder/result.py +++ b/src/codemodder/result.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from pathlib import Path +from typing import Any from .utils.abc_dataclass import ABCDataclass @@ -49,4 +50,16 @@ def all_rule_ids(self) -> list[str]: return list(self.keys()) def __or__(self, other): - return ResultSet(super().__or__(other)) + result = ResultSet(super().__or__(other)) + for k in self.keys() | other.keys(): + result[k] = list_dict_or(self[k], other[k]) + return result + + +def list_dict_or( + dictionary: dict[Any, list[Any]], other: dict[Any, list[Any]] +) -> dict[Path, list[Any]]: + result_dict = other | dictionary + for k in other.keys() | dictionary.keys(): + result_dict[k] = dictionary[k] + other[k] + return result_dict diff --git a/tests/test_results.py b/tests/test_results.py new file mode 100644 index 00000000..5f34bfd6 --- /dev/null +++ b/tests/test_results.py @@ -0,0 +1,55 @@ +import json +from pathlib import Path +from codemodder.sonar_results import SonarResultSet + + +class TestResults: + def test_or(self, tmpdir): + issues1 = { + "issues": [ + { + "rule": "rule", + "component": "code.py", + "textRange": { + "startLine": 2, + "endLine": 2, + "startOffset": 2, + "endOffset": 2, + }, + } + ] + } + issues2 = { + "issues": [ + { + "rule": "rule", + "component": "code.py", + "textRange": { + "startLine": 1, + "endLine": 1, + "startOffset": 1, + "endOffset": 1, + }, + } + ] + } + sonar_json1 = Path(tmpdir) / "sonar1.json" + sonar_json1.write_text(json.dumps(issues1)) + sonar_json2 = Path(tmpdir) / "sonar2.json" + sonar_json2.write_text(json.dumps(issues2)) + + result1 = SonarResultSet.from_json(sonar_json1) + result2 = SonarResultSet.from_json(sonar_json2) + + combined = result1 | result2 + assert len(combined["rule"][Path("code.py")]) == 2 + assert result2["rule"][Path("code.py")][0] in combined["rule"][Path("code.py")] + assert result1["rule"][Path("code.py")][0] in combined["rule"][Path("code.py")] + + def test_sonar_robustness(self, tmpdir): + sonar_json = Path(tmpdir) / "sonar1.json" + # not a valid json + sonar_json.touch() + result = SonarResultSet.from_json(sonar_json) + # did not crash and returned an empty ResultSet + assert not result