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