Path:
strictdoc/backend/gcov/reader.py
Lines:
318
Non-empty lines:
292
Non-empty lines covered with requirements:
292 / 292 (100.0%)
Functions:
5
Functions covered by requirements:
5 / 5 (100.0%)
1
"""2
@relation(SDOC-SRS-144, scope=file)3
"""4
5
import hashlib
6
import json
7
from dataclasses import dataclass
8
from pathlib import Path
9
from typing import Type
10
11
from strictdoc.backend.gcov.helpers import normalize_gcovr_file_path
12
from strictdoc.backend.sdoc.document_reference import DocumentReference
13
from strictdoc.backend.sdoc.models.document import SDocDocument
14
from strictdoc.backend.sdoc.models.document_grammar import DocumentGrammar
15
from strictdoc.backend.sdoc.models.node import SDocNode, SDocNodeField
16
from strictdoc.backend.sdoc.models.reference import (
17
FileEntry,
18
FileEntryFormat,
19
FileReference,
20
)21
from strictdoc.core.file_system.file_tree import File
22
from strictdoc.core.project_config import ProjectConfig
23
from strictdoc.helpers.file_system import file_open_read_utf8
24
25
26
@dataclass27
class GCovStatsObject:
28
total_number_of_covered_functions: int = 0
29
total_number_of_non_covered_functions: int = 0
30
31
def add_function(self, covered: bool) -> None:
32
if covered:
33
self.total_number_of_covered_functions += 1
34
else:
35
self.total_number_of_non_covered_functions += 1
36
37
38
class GCovJSONReader:
39
@classmethod40
def read_from_file(
41
cls: Type["GCovJSONReader"],
42
doc_file: File,
43
project_config: ProjectConfig,
44
) -> SDocDocument:
45
with file_open_read_utf8(doc_file.full_path) as file:
46
content = file.read()
47
return cls.read_from_string(content, doc_file, project_config)
48
49
@classmethod50
def read_from_string(
51
cls: Type["GCovJSONReader"],
52
content: str,
53
doc_file: File, # noqa: ARG003
54
project_config: ProjectConfig,
55
) -> SDocDocument:
56
if len(content) == 0:
57
raise RuntimeError("Document is empty")
58
try:
59
json_content = json.loads(content)
60
except Exception as exception: # pylint: disable=broad-except
61
raise RuntimeError(str(exception)) from None
62
63
document = SDocDocument(
64
mid=None,
65
title="Code coverage report",
66
config=None,
67
view=None,
68
grammar=None,
69
section_contents=[],
70
autogen=True,
71
)72
document.ng_including_document_reference = DocumentReference()
73
grammar = DocumentGrammar.create_for_test_report(document)
74
document.grammar = grammar
75
document.config.requirement_style = "Table"
76
77
document_stats = GCovStatsObject()
78
79
"""
80
Parse individual <testcase> elements.81
"""82
83
source_root_path = (
84
project_config.source_root_path
85
if project_config is not None
86
else None
87
)88
89
json_files = json_content["files"]
90
for json_file_ in json_files:
91
stats = GCovStatsObject()
92
93
json_file_name = normalize_gcovr_file_path(
94
json_file_["file"], source_root_path=source_root_path
95
)96
97
file_section = SDocNode.create_section(
98
parent=document,
99
document=document,
100
title=json_file_name,
101
)102
file_section.ng_including_document_reference = DocumentReference()
103
file_section.ng_document_reference = DocumentReference()
104
file_section.ng_document_reference.set_document(document)
105
document.section_contents.append(file_section)
106
107
covered_functions_section = SDocNode.create_section(
108
parent=document,
109
document=document,
110
title="Covered functions",
111
)112
covered_functions_section.ng_including_document_reference = (
113
DocumentReference()
114
)115
covered_functions_section.ng_document_reference = (
116
DocumentReference()
117
)118
covered_functions_section.ng_document_reference.set_document(
119
document120
)121
file_section.section_contents.append(covered_functions_section)
122
123
non_covered_functions_section = SDocNode.create_section(
124
parent=document,
125
document=document,
126
title="Non-covered functions",
127
)128
non_covered_functions_section.ng_including_document_reference = (
129
DocumentReference()
130
)131
non_covered_functions_section.ng_document_reference = (
132
DocumentReference()
133
)134
non_covered_functions_section.ng_document_reference.set_document(
135
document136
)137
file_section.section_contents.append(non_covered_functions_section)
138
139
for json_function_ in json_file_["functions"]:
140
json_function_name = json_function_.get(
141
"demangled_name", json_function_.get("name")
142
)143
assert json_function_name is not None, json_function_
144
is_function_covered = json_function_["execution_count"] > 0
145
stats.add_function(is_function_covered)
146
147
testcase_node = SDocNode(
148
parent=document,
149
node_type="TEST_RESULT",
150
fields=[],
151
relations=[],
152
)153
testcase_node.ng_document_reference = DocumentReference()
154
testcase_node.ng_document_reference.set_document(document)
155
testcase_node.ng_including_document_reference = (
156
DocumentReference()
157
)158
testcase_node.set_field_value(
159
field_name="UID",
160
form_field_index=0,
161
value=SDocNodeField(
162
parent=testcase_node,
163
field_name="UID",
164
parts=[
165
"GCOV:" + json_file_name + ":" + json_function_name
166
],167
multiline__=None,
168
),169
)170
# FIXME: Remove?171
testcase_node.set_field_value(
172
field_name="STATUS",
173
form_field_index=0,
174
value=SDocNodeField(
175
parent=testcase_node,
176
field_name="STATUS",
177
parts=[
178
"Covered" if is_function_covered else "Non-covered"
179
],180
multiline__=None,
181
),182
)183
184
gcov_path_hash = hashlib.md5(
185
json_file_name.encode("utf-8")
186
).hexdigest()
187
link = (
188
"../../../../" # noqa: ISC003
189
+ "coverage."
190
+ Path(json_file_name).name
191
+ ". "
192
+ gcov_path_hash
193
+ ".html"
194
)195
testcase_node.set_field_value(
196
field_name="STATEMENT",
197
form_field_index=0,
198
value=SDocNodeField(
199
parent=testcase_node,
200
field_name="STATEMENT",
201
parts=[
202
f"""
203
`LINK <{link}>`_
204
"""205
],206
multiline__=None,
207
),208
)209
testcase_node.set_field_value(
210
field_name="TITLE",
211
form_field_index=0,
212
value=SDocNodeField(
213
parent=testcase_node,
214
field_name="TITLE",
215
parts=[json_function_name],
216
multiline__=None,
217
),218
)219
testcase_node.relations.append(
220
FileReference(
221
parent=testcase_node,
222
g_file_entry=FileEntry(
223
parent=None,
224
g_file_format=FileEntryFormat.SOURCECODE,
225
g_file_path=json_file_name,
226
g_line_range=None,
227
element="function",
228
id=json_function_name,
229
),230
)231
)232
if is_function_covered:
233
covered_functions_section.section_contents.append(
234
testcase_node235
)236
else:
237
non_covered_functions_section.section_contents.append(
238
testcase_node239
)240
241
summary_table = f"""\
242
.. list-table:: Coverage summary for {json_file_name}
243
:widths: 25 10244
:header-rows: 0245
246
* - **Total covered functions:**247
- {stats.total_number_of_covered_functions}
248
* - **Total non-covered functions:**249
- {stats.total_number_of_non_covered_functions}
250
"""251
252
testcase_node = SDocNode(
253
parent=document,
254
node_type="TEXT",
255
fields=[],
256
relations=[],
257
)258
testcase_node.ng_document_reference = DocumentReference()
259
testcase_node.ng_document_reference.set_document(document)
260
testcase_node.ng_including_document_reference = DocumentReference()
261
testcase_node.set_field_value(
262
field_name="STATEMENT",
263
form_field_index=0,
264
value=SDocNodeField(
265
parent=file_section,
266
field_name="STATEMENT",
267
parts=[summary_table],
268
multiline__="True",
269
),270
)271
file_section.section_contents.insert(0, testcase_node)
272
document_stats.total_number_of_covered_functions += (
273
stats.total_number_of_covered_functions
274
)275
document_stats.total_number_of_non_covered_functions += (
276
stats.total_number_of_non_covered_functions
277
)278
279
summary_table = f"""\
280
.. list-table:: Coverage summary281
:widths: 25 10282
:header-rows: 0283
284
* - **Total number of files :**285
- {len(json_files)}
286
* - **Total covered functions:**287
- {document_stats.total_number_of_covered_functions}
288
* - **Total non-covered functions:**289
- {document_stats.total_number_of_non_covered_functions}
290
"""291
292
testcase_node = SDocNode(
293
parent=document,
294
node_type="TEXT",
295
fields=[],
296
relations=[],
297
)298
testcase_node.ng_document_reference = DocumentReference()
299
testcase_node.ng_document_reference.set_document(document)
300
testcase_node.ng_including_document_reference = DocumentReference()
301
testcase_node.set_field_value(
302
field_name="STATEMENT",
303
form_field_index=0,
304
value=SDocNodeField(
305
parent=testcase_node,
306
field_name="STATEMENT",
307
parts=[summary_table],
308
multiline__="True",
309
),310
)311
document.section_contents.insert(0, testcase_node)
312
document_stats.total_number_of_covered_functions += (
313
stats.total_number_of_covered_functions
314
)315
document_stats.total_number_of_non_covered_functions += (
316
stats.total_number_of_non_covered_functions
317
)318
return document