Add tracing for branches as coverage goals
@@ -16,6 +16,7 @@ from pynguin.analyses.controlflow.cfg import CFG
from pynguin.analyses.controlflow.controldependencegraph import ControlDependenceGraph
from pynguin.analyses.controlflow.dominatortree import DominatorTree
from pynguin.analyses.controlflow.programgraph import ProgramGraphNode
+from pynguin.coverage.branch.branchpool import INSTANCE
from pynguin.testcase.execution.executiontracer import (
CodeObjectMetaData,
ExecutionTracer,
@@ -54,6 +55,8 @@ class BranchDistanceInstrumentation:
def __init__(self, tracer: ExecutionTracer) -> None:
self._tracer = tracer
+ self._branch_pool = INSTANCE
+ self._branch_pool.clear()
def _instrument_inner_code_objects(
self, code: CodeType, parent_code_object_id: int
@@ -109,6 +112,11 @@ class BranchDistanceInstrumentation:
real_entry_node = cfg.get_successors(cfg.entry_node).pop() # Only one exists!
assert real_entry_node.basic_block is not None, "Basic block cannot be None."
self._add_code_object_executed(real_entry_node.basic_block, code_object_id)
+ self._track_branchless_function(
+ code,
+ cfg,
+ real_entry_node,
+ )
self._instrument_cfg(cfg, code_object_id)
return self._instrument_inner_code_objects(
@@ -243,6 +251,7 @@ class BranchDistanceInstrumentation:
Instr("CALL_METHOD", 2, lineno=lineno),
Instr("POP_TOP", lineno=lineno),
]
+ self._branch_pool.register_branch(block, code_object_id, predicate_id, lineno)
return predicate_id
def _instrument_compare_based_conditional_jump(
@@ -283,6 +292,9 @@ class BranchDistanceInstrumentation:
Instr("CALL_METHOD", 4, lineno=lineno),
Instr("POP_TOP", lineno=lineno),
]
+ self._branch_pool.register_branch(
+ block, code_object_id, predicate_id, lineno, cmp_op
+ )
return predicate_id
def _add_code_object_executed(self, block: BasicBlock, code_object_id: int) -> None:
@@ -462,3 +474,16 @@ class BranchDistanceInstrumentation:
for node in nodes:
node.clear()
return tuple(nodes)
+
+ def _track_branchless_function(
+ self,
+ code: CodeType,
+ cfg: CFG,
+ real_entry_node: ProgramGraphNode,
+ ) -> None:
+ if len(cfg.nodes) == 3 and [real_entry_node] == [
+ n for n in cfg.nodes if not n.is_artificial
+ ]:
+ self._branch_pool.register_branchless_function(
+ code.co_name, code.co_firstlineno
+ )
