StrictDoc Documentation
strictdoc/backend/sdoc_source_code/models/source_file_info.py
Source file coverage
Path:
strictdoc/backend/sdoc_source_code/models/source_file_info.py
Lines:
101
Non-empty lines:
83
Non-empty lines covered with requirements:
83 / 83 (100.0%)
Functions:
8
Functions covered by requirements:
8 / 8 (100.0%)
1
from typing import Any, Dict, List, Optional, Union
2
 
3
from typing_extensions import TypeAlias
4
 
5
from strictdoc.backend.sdoc_source_code.models.language import LanguageItem
6
from strictdoc.backend.sdoc_source_code.models.language_item_marker import (
7
    LanguageItemMarker,
8
)
9
from strictdoc.backend.sdoc_source_code.models.line_marker import LineMarker
10
from strictdoc.backend.sdoc_source_code.models.range_marker import (
11
    ForwardRangeMarker,
12
    RangeMarker,
13
)
14
from strictdoc.backend.sdoc_source_code.models.source_node import SourceNode
15
from strictdoc.core.file_system.source_tree import SourceFile
16
from strictdoc.helpers.auto_described import auto_described
17
from strictdoc.helpers.file_stats import SourceFileStats
18
 
19
RelationMarkerType: TypeAlias = Union[
20
    LanguageItemMarker, LineMarker, RangeMarker, ForwardRangeMarker
21
]
22
 
23
 
24
@auto_described
25
class SourceFileTraceabilityInfo:
26
    """
27
    Class that keeps all traceability info related to a single source file.
28
 
29
    At the init time, only the backward RangeMarkers are available from
30
    a source file. At runtime, the ForwardRangeMarkers are mixed in
31
    from the Requirement/FileReference links. This is why the .markers
32
    is a union.
33
 
34
    Note that g_parts is only used by the textX grammar to allow the
35
    definition of the child grammar elements. All other instance variables
36
    of this class are not using or based on g_parts. Instead, they are
37
    populated during the textX processing step, e.g., via
38
    general_language_marker_processors.py.
39
    """
40
 
41
    def __init__(self, g_parts: List[Any]):  # noqa: ARG002
42
        self.source_file: Optional[SourceFile] = None
43
        self.source_nodes: List[SourceNode] = []
44
        self.functions: List[LanguageItem] = []
45
 
46
        #
47
        # {                                              # noqa: ERA001
48
        #  "REQ-001": [RangeMarker(...), ...],           # noqa: ERA001
49
        #  "REQ-002": [RangeMarker(...), ...],           # noqa: ERA001
50
        # }                                              # noqa: ERA001
51
        self.ng_map_reqs_to_markers: Dict[str, List[RelationMarkerType]] = {}
52
 
53
        self.ng_map_names_to_markers: Dict[str, List[RelationMarkerType]] = {}
54
        self.ng_map_names_to_definition_functions: Dict[str, LanguageItem] = {}
55
 
56
        #
57
        # Merged ranges contain ranges that are fully covered by one or more
58
        # forward or backward relations. If a range is part of a larger range,
59
        # it gets merged into the larger range. The merged ranges are tracked so
60
        # that for each source code function it can be answered whether the
61
        # function is covered by any requirement or not.
62
        #
63
        self.merged_ranges: List[List[int]] = []
64
        self.file_stats: SourceFileStats = SourceFileStats()
65
        self.ng_lines_covered = 0
66
        self.lines_info: Dict[int, bool] = {}
67
        self._coverage: float = 0
68
        self.covered_functions: int = 0
69
 
70
        self.markers: List[RelationMarkerType] = []
71
 
72
    def is_document(self) -> bool:
73
        return False
74
 
75
    # FIXME: is_requirement() will go away.
76
    def is_requirement(self) -> bool:
77
        return False
78
 
79
    # FIXME: is_section() will go away.
80
    def is_section(self) -> bool:
81
        return False
82
 
83
    # FIXME: is_section() will go away.
84
    def is_source_file(self) -> bool:
85
        return True
86
 
87
    def get_coverage(self) -> float:
88
        return self._coverage
89
 
90
    def set_coverage_stats(
91
        self,
92
        merged_ranges: List[List[int]],
93
        lines_covered: int,
94
    ) -> None:
95
        self.merged_ranges = merged_ranges
96
        self.ng_lines_covered = lines_covered
97
        self._coverage = (
98
            round(lines_covered / self.file_stats.lines_non_empty * 100, 1)
99
            if self.file_stats.lines_non_empty != 0
100
            else 0
101
        )