diff --git a/tests/data/test_llvm_coverage_report.py b/tests/data/test_llvm_coverage_report.py index 196993593..0e4ec580e 100644 --- a/tests/data/test_llvm_coverage_report.py +++ b/tests/data/test_llvm_coverage_report.py @@ -60,7 +60,7 @@ def setUp(self): self.root = CodeRegion.from_list([0, 0, 100, 100, 5, 0, 0, 0], "main", ["test.txt"]) - self.left = CodeRegion.from_list([0, 1, 49, 100, 5, 0, 0, 0], "main", + self.left = CodeRegion.from_list([0, 1, 50, 0, 5, 0, 0, 0], "main", ["test.txt"]) self.right = CodeRegion.from_list([50, 0, 100, 99, 5, 0, 0, 0], "main", ["test.txt"]) @@ -212,6 +212,34 @@ def test_insert_2(self): root.insert(left_child) self.assertIs(left.childs[0], left_child) + def test_insert_3(self): + + root = CodeRegion.from_list([0, 0, 100, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + left = CodeRegion.from_list([0, 1, 50, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + right = CodeRegion.from_list([50, 0, 100, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + root.insert(right) + with self.assertRaises(ValueError): + root.insert(left) + + def test_overlap(self): + + root = CodeRegion.from_list([0, 0, 100, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + left = CodeRegion.from_list([0, 1, 50, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + right = CodeRegion.from_list([50, 0, 100, 100, 5, 0, 0, 0], "main", + ["test.txt"]) + + self.assertFalse(left.overlaps(root)) + self.assertFalse(right.overlaps(root)) + self.assertFalse(root.overlaps(left)) + self.assertFalse(root.overlaps(right)) + self.assertTrue(left.overlaps(right)) + self.assertTrue(right.overlaps(left)) + def test_find_region(self): self.assertEqual( self.root.find_code_region(line=0, column=0), self.root @@ -226,11 +254,9 @@ def test_find_region(self): self.root.find_code_region(line=50, column=0), self.right ) self.assertEqual( - self.root.find_code_region(line=100, column=99), self.right - ) - self.assertEqual( - self.root.find_code_region(line=100, column=100), self.root + self.root.find_code_region(line=100, column=99), self.root ) + self.assertEqual(self.root.find_code_region(line=100, column=100), None) self.assertEqual( self.root.find_code_region(line=10, column=0), self.left_left_2 ) @@ -303,9 +329,19 @@ def test_coverage_json_parsing(self): report = CoverageReport.from_json(json_file, base_dir=tmp_dir) code_region = report.tree["foo.cc"] - for region in code_region.iter_preorder(): - print(region.count) - pass + self.assertEqual(code_region.total_count, 0) + self.assertEqual(code_region.childs[0].total_count, 20) + self.assertEqual(code_region.childs[0].childs[0].total_count, 20) + self.assertEqual(code_region.childs[0].childs[1].total_count, 2) + self.assertEqual(code_region.childs[1].total_count, 2) + self.assertEqual(code_region.childs[1].childs[0].total_count, 22) + self.assertEqual(code_region.childs[1].childs[1].total_count, 20) + self.assertEqual(code_region.childs[1].childs[2].total_count, 20) + self.assertEqual(code_region.childs[1].childs[3].total_count, 20) + self.assertEqual( + code_region.childs[1].childs[3].childs[0].total_count, 20 + ) + self.assertEqual(code_region.childs[2].total_count, 1) @run_in_test_environment( UnitTestFixtures.PAPER_CONFIGS, UnitTestFixtures.RESULT_FILES diff --git a/tests/plots/test_llvm_coverage_plot.py b/tests/plots/test_llvm_coverage_plot.py index 038b7851e..1a2a43d64 100644 --- a/tests/plots/test_llvm_coverage_plot.py +++ b/tests/plots/test_llvm_coverage_plot.py @@ -550,18 +550,18 @@ def test_confusion_matrices(self): print(result) enc = result["enc"] self.assertEqual(enc.TP, 3) - self.assertEqual(enc.TN, 38) + self.assertEqual(enc.TN, 39) self.assertEqual(enc.FP, 0) - self.assertEqual(enc.FN, 9) + self.assertEqual(enc.FN, 8) compress = result["compress"] self.assertEqual(compress.TP, 2) - self.assertEqual(compress.TN, 39) + self.assertEqual(compress.TN, 40) self.assertEqual(compress.FP, 0) - self.assertEqual(compress.FN, 9) + self.assertEqual(compress.FN, 8) all = result["__all__"] self.assertEqual(all.TP, 4) - self.assertEqual(all.TN, 35) + self.assertEqual(all.TN, 36) self.assertEqual(all.FP, 0) - self.assertEqual(all.FN, 11) + self.assertEqual(all.FN, 10) diff --git a/varats/varats/data/reports/llvm_coverage_report.py b/varats/varats/data/reports/llvm_coverage_report.py index 0fe7a9de2..45f97d154 100644 --- a/varats/varats/data/reports/llvm_coverage_report.py +++ b/varats/varats/data/reports/llvm_coverage_report.py @@ -156,7 +156,7 @@ def expr_to_str(expression: Expression) -> str: @dataclass -class CodeRegion: # pylint: disable=too-many-instance-attributes +class CodeRegion: # pylint: disable=too-many-instance-attributes, too-many-public-methods """Code region tree.""" start: RegionStart end: RegionEnd @@ -312,6 +312,23 @@ def is_subregion(self, other: CodeRegion) -> bool: return start_ok and end_ok and not (start_equal and end_equal) + def overlaps(self, other: CodeRegion) -> bool: + """ + Tests if regions overlap. + + They overlaps if they are not subregions, but one location is inside of + the other. + """ + + if self.is_subregion(other) or other.is_subregion(self): + return False + if self.is_location_inside( + other.start.line, other.start.column + ) != other.is_location_inside(self.start.line, self.start.column): + return True + + return False + def add_instantiation(self, region: CodeRegion) -> None: """If a code region already exists in a tree.""" if region != self: @@ -351,6 +368,10 @@ def insert(self, region: CodeRegion) -> None: child.parent = region node.childs.remove(child) + if any(child.overlaps(region) for child in node.childs): + raise ValueError( + "The given region overlaps with another region!" + ) node.childs.append(region) node.childs.sort() region.parent = node @@ -363,6 +384,13 @@ def combine_features(self, region: CodeRegion) -> None: raise AssertionError("CodeRegions are not identical") x.presence_conditions.extend(y.presence_conditions) + def get_code_region(self, element: CodeRegion) -> tp.Optional[CodeRegion]: + """Returns the code region if it exists already.""" + for child in self.iter_breadth_first(): + if child == element: + return child + return None + def find_code_region(self, line: int, column: int) -> tp.Optional[CodeRegion]: """ @@ -386,13 +414,13 @@ def is_location_inside(self, line: int, column: int) -> bool: # Location could be inside. Check cases. if self.start.line == line == self.end.line: # Location in same line - return self.start.column <= column <= self.end.column + return self.start.column <= column < self.end.column if self.start.line == line: # Location in start line return self.start.column <= column if self.end.line == line: # Location in end line - return column <= self.end.column + return column < self.end.column # Location neither in start line not in end line return self.start.line < line < self.end.line return False @@ -405,9 +433,8 @@ def annotate_covered(self, configuration: Configuration) -> None: """ kind = PresenceKind.BECOMES_ACTIVE for region in self.iter_breadth_first(): - if region.kind == CodeRegionKind.GAP and len( - region.vara_instrs - ) == 0: + if region.kind == CodeRegionKind.GAP: + assert len(region.vara_instrs) == 0 continue if region.is_covered(): region.presence_conditions[kind].append( @@ -497,13 +524,11 @@ def add(self, region: CodeRegion) -> None: ) return root_region = self[filename] - found_region = root_region.find_code_region( - region.start.line, region.start.column - ) - if found_region is not None and found_region == region: + if (found_region := root_region.get_code_region(region)) is not None: # Region exists already found_region.add_instantiation(region) - if not region in root_region: + else: + # Region does not exist root_region.insert(region) def sorted(self) -> FilenameRegionMapping: diff --git a/varats/varats/plots/llvm_coverage_plot.py b/varats/varats/plots/llvm_coverage_plot.py index 5669f4706..5aadcc4b6 100644 --- a/varats/varats/plots/llvm_coverage_plot.py +++ b/varats/varats/plots/llvm_coverage_plot.py @@ -418,13 +418,13 @@ def _plot_confusion_matrix(reports: CoverageReports, outdir: Path) -> None: with outfile.open("w") as output: output.write(f"{matrix}\n") tps = [str(x) for x in matrix.getTPs()] - output.write(f"True Positives:\n{chr(10).join(tps)}\n") + output.write(f"True Positives:\n{chr(10).join(sorted(tps))}\n") tns = [str(x) for x in matrix.getTNs()] - output.write(f"True Negatives:\n{chr(10).join(tns)}\n") + output.write(f"True Negatives:\n{chr(10).join(sorted(tns))}\n") fps = [str(x) for x in matrix.getFPs()] - output.write(f"False Positives:\n{chr(10).join(fps)}\n") + output.write(f"False Positives:\n{chr(10).join(sorted(fps))}\n") fns = [str(x) for x in matrix.getFNs()] - output.write(f"False Negatives:\n{chr(10).join(fns)}\n") + output.write(f"False Negatives:\n{chr(10).join(sorted(fns))}\n") class CoveragePlotGenerator( diff --git a/varats/varats/projects/c_projects/xz.py b/varats/varats/projects/c_projects/xz.py index 6f8612cbc..cfc23423d 100644 --- a/varats/varats/projects/c_projects/xz.py +++ b/varats/varats/projects/c_projects/xz.py @@ -6,29 +6,29 @@ from benchbuild.source import HTTPMultiple from benchbuild.utils.cmd import autoreconf, make from benchbuild.utils.revision_ranges import ( - block_revisions, GoodBadSubgraph, RevisionRange, + block_revisions, ) from benchbuild.utils.settings import get_number_of_jobs from plumbum import local -from varats.containers.containers import get_base_image, ImageBase +from varats.containers.containers import ImageBase, get_base_image from varats.experiment.workload_util import RSBinary, WorkloadCategory from varats.paper.paper_config import PaperConfigSpecificGit from varats.project.project_domain import ProjectDomains from varats.project.project_util import ( + BinaryType, ProjectBinaryWrapper, get_local_project_git_path, - BinaryType, verify_binaries, ) from varats.project.sources import FeatureSource from varats.project.varats_project import VProject from varats.utils.git_util import ( + RevisionBinaryMap, ShortCommitHash, get_all_revisions_between, - RevisionBinaryMap, ) from varats.utils.settings import bb_cfg