Path:
tests/unit/strictdoc/backend/sdoc_source_code/readers/test_reader_general.py
Lines:
500
Non-empty lines:
392
Non-empty lines covered with requirements:
392 / 392 (100.0%)
Functions:
21
Functions covered by requirements:
21 / 21 (100.0%)
1
"""2
@relation(SDOC-SRS-142, scope=file)3
"""4
5
from typing import List
6
7
import pytest
8
9
from strictdoc.backend.sdoc.error_handling import StrictDocSemanticError
10
from strictdoc.backend.sdoc_source_code.reader import (
11
RangeMarker,
12
SourceFileTraceabilityReader,
13
)14
15
16
def test_001_one_range_marker():
17
source_input = """
18
# @relation(REQ-001, scope=range_start)19
CONTENT 120
CONTENT 221
CONTENT 322
# @relation(REQ-001, scope=range_end)23
""".lstrip()
24
25
reader = SourceFileTraceabilityReader()
26
27
document = reader.read(source_input)
28
markers = document.markers
29
assert markers[0].reqs == ["REQ-001"]
30
assert markers[0].is_begin()
31
assert markers[0].ng_source_line_begin == 1
32
assert markers[0].ng_range_line_begin == 1
33
assert markers[0].ng_range_line_end == 5
34
35
assert markers[1].reqs == ["REQ-001"]
36
assert markers[1].is_end()
37
assert markers[1].ng_source_line_begin == 5
38
assert markers[1].ng_range_line_begin == 1
39
assert markers[1].ng_range_line_end == 5
40
41
42
def test_002_two_range_markers():
43
source_input = """
44
# @relation(REQ-001, scope=range_start)45
CONTENT 146
CONTENT 247
CONTENT 348
# @relation(REQ-001, scope=range_end)49
# @relation(REQ-002, scope=range_start)50
CONTENT 451
CONTENT 552
CONTENT 653
# @relation(REQ-002, scope=range_end)54
""".lstrip()
55
56
reader = SourceFileTraceabilityReader()
57
58
document = reader.read(source_input)
59
markers = document.markers
60
assert len(markers) == 4
61
marker_1 = markers[0]
62
marker_2 = markers[1]
63
marker_3 = markers[2]
64
marker_4 = markers[3]
65
assert marker_1.reqs == ["REQ-001"]
66
assert marker_2.reqs == ["REQ-001"]
67
assert marker_3.reqs == ["REQ-002"]
68
assert marker_4.reqs == ["REQ-002"]
69
70
assert marker_1.ng_source_line_begin == 1
71
assert marker_2.ng_source_line_begin == 5
72
assert marker_3.ng_source_line_begin == 6
73
assert marker_4.ng_source_line_begin == 10
74
75
assert marker_1.ng_range_line_begin == 1
76
assert marker_2.ng_range_line_begin == 1
77
assert marker_3.ng_range_line_begin == 6
78
assert marker_4.ng_range_line_begin == 6
79
80
81
def test_003_marker_with_dashes_and_underscores():
82
"""
83
Verifies that SF_REQ-001 markers can be parsed (identifiers with mixed _ and -).84
85
Bug report: https://github.com/strictdoc-project/strictdoc/discussions/2568.86
"""87
88
source_input = """\
89
# @relation(SF_REQ-001, scope=file)90
CONTENT 191
CONTENT 292
CONTENT 393
""".lstrip()
94
95
reader = SourceFileTraceabilityReader()
96
97
document = reader.read(source_input)
98
markers = document.markers
99
assert markers[0].reqs == ["SF_REQ-001"]
100
assert markers[0].is_begin()
101
assert markers[0].ng_source_line_begin == 1
102
assert markers[0].ng_range_line_begin == 1
103
assert markers[0].ng_range_line_end == 4
104
105
106
def test_005_no_markers():
107
source_input = """
108
def hello_world_2():109
110
print("hello world") # noqa: T201111
""".lstrip()
112
113
reader = SourceFileTraceabilityReader()
114
_ = reader.read(source_input)
115
116
117
def test_006_empty_file():
118
source_input = ""
119
120
reader = SourceFileTraceabilityReader()
121
traceability_info = reader.read(source_input)
122
123
assert traceability_info.markers == []
124
125
126
def test_007_single_line_with_no_newline():
127
source_input = "Single line"
128
129
reader = SourceFileTraceabilityReader()
130
traceability_info = reader.read(source_input)
131
132
assert traceability_info.markers == []
133
134
135
def test_008_three_nested_range_markers():
136
source_input = """
137
CONTENT 1138
# @relation(REQ-001, scope=range_start)139
CONTENT 2140
# @relation(REQ-002, scope=range_start)141
CONTENT 3142
# @relation(REQ-003, scope=range_start)143
CONTENT 4144
# @relation(REQ-003, scope=range_end)145
CONTENT 5146
# @relation(REQ-002, scope=range_end)147
CONTENT 6148
# @relation(REQ-001, scope=range_end)149
CONTENT 7150
# @relation(REQ-001, scope=range_start)151
CONTENT 8152
# @relation(REQ-001, scope=range_end)153
CONTENT 9154
""".lstrip()
155
156
reader = SourceFileTraceabilityReader()
157
158
document = reader.read(source_input)
159
markers = document.markers
160
assert len(markers) == 8
161
marker_1 = markers[0]
162
marker_2 = markers[1]
163
marker_3 = markers[2]
164
marker_4 = markers[3]
165
marker_5 = markers[4]
166
marker_6 = markers[5]
167
marker_7 = markers[6]
168
marker_8 = markers[7]
169
assert marker_1.reqs == ["REQ-001"]
170
assert marker_2.reqs == ["REQ-002"]
171
assert marker_3.reqs == ["REQ-003"]
172
assert marker_4.reqs == ["REQ-003"]
173
assert marker_5.reqs == ["REQ-002"]
174
assert marker_6.reqs == ["REQ-001"]
175
assert marker_7.reqs == ["REQ-001"]
176
assert marker_8.reqs == ["REQ-001"]
177
178
assert marker_1.ng_source_line_begin == 2
179
assert marker_2.ng_source_line_begin == 4
180
assert marker_3.ng_source_line_begin == 6
181
assert marker_4.ng_source_line_begin == 8
182
assert marker_5.ng_source_line_begin == 10
183
assert marker_6.ng_source_line_begin == 12
184
assert marker_7.ng_source_line_begin == 14
185
assert marker_8.ng_source_line_begin == 16
186
187
assert marker_1.ng_range_line_begin == 2
188
assert marker_2.ng_range_line_begin == 4
189
assert marker_3.ng_range_line_begin == 6
190
assert marker_4.ng_range_line_begin == 6
191
assert marker_5.ng_range_line_begin == 4
192
assert marker_6.ng_range_line_begin == 2
193
assert marker_7.ng_range_line_begin == 14
194
assert marker_8.ng_range_line_begin == 14
195
196
197
def test_009_two_requirements_in_one_marker():
198
source_input = """
199
# @relation(REQ-001, REQ-002, scope=range_start)200
CONTENT 1201
CONTENT 2202
CONTENT 3203
# @relation(REQ-001, REQ-002, scope=range_end)204
""".lstrip()
205
206
reader = SourceFileTraceabilityReader()
207
208
document = reader.read(source_input)
209
markers = document.markers
210
assert markers[0].reqs == ["REQ-001", "REQ-002"]
211
assert markers[0].is_begin()
212
assert markers[0].ng_source_line_begin == 1
213
assert markers[0].ng_range_line_begin == 1
214
assert markers[0].ng_range_line_end == 5
215
216
assert markers[1].reqs == ["REQ-001", "REQ-002"]
217
assert markers[1].is_end()
218
assert markers[1].ng_source_line_begin == 5
219
assert markers[1].ng_range_line_begin == 1
220
assert markers[1].ng_range_line_end == 5
221
222
223
def test_010_relation_skip_keyword():
224
source_input = """
225
# @relation(skip, scope=range_start)226
# @relation(REQ-001, scope=range_start)227
CONTENT 1228
CONTENT 2229
CONTENT 3230
# @relation(REQ-001, scope=range_end)231
# @relation(skip, scope=range_end)232
""".lstrip()
233
234
reader = SourceFileTraceabilityReader()
235
236
document = reader.read(source_input)
237
assert len(document.g_parts) == 7
238
assert len(document.markers) == 0
239
240
241
def test_011_relation_skip_keyword_then_normal_marker():
242
source_input = """
243
# @relation(skip, scope=range_start)244
# @relation(REQ-001, scope=range_start)245
CONTENT 1246
CONTENT 2247
CONTENT 3248
# @relation(REQ-001, scope=range_end)249
# @relation(skip, scope=range_end)250
# @relation(REQ-001, scope=range_start)251
CONTENT 1252
CONTENT 2253
CONTENT 3254
# @relation(REQ-001, scope=range_end)255
""".lstrip()
256
257
reader = SourceFileTraceabilityReader()
258
259
document = reader.read(source_input)
260
assert len(document.g_parts) == 12
261
assert len(document.markers) == 2
262
263
264
def test_012_relation_skip_keyword_then_normal_marker_4spaces_indent():
265
source_input = """
266
# @relation(skip, scope=range_start)267
# @relation(REQ-001, scope=range_start)268
CONTENT 1269
CONTENT 2270
CONTENT 3271
# @relation(REQ-001, scope=range_end)272
# @relation(skip, scope=range_end)273
# @relation(REQ-001, scope=range_start)274
CONTENT 1275
CONTENT 2276
CONTENT 3277
# @relation(REQ-001, scope=range_end)278
""".lstrip()
279
280
reader = SourceFileTraceabilityReader()
281
282
document = reader.read(source_input)
283
assert len(document.g_parts) == 12
284
assert len(document.markers) == 2
285
286
287
def test_013_relation_skip_entire_file():
288
source_input = """\
289
# @relation(skip, scope=file)290
# @relation(REQ-001, scope=range_start)291
CONTENT 1292
CONTENT 2293
CONTENT 3294
# @relation(REQ-001, scope=range_end)295
# @relation(REQ-001, scope=range_start)296
CONTENT 1297
CONTENT 2298
CONTENT 3299
# @relation(REQ-001, scope=range_end)300
"""301
302
reader = SourceFileTraceabilityReader()
303
304
document = reader.read(source_input)
305
assert len(document.g_parts) == 11
306
assert len(document.markers) == 0
307
308
309
# Testing that textx assigns correct line location when the marker is not on the310
# first line.311
def test_020_marker_not_first_line():
312
source_input = """
313
314
315
# @relation(REQ-001, scope=range_start)316
CONTENT 1317
CONTENT 2318
CONTENT 3319
# @relation(REQ-001, scope=range_end)320
321
322
323
"""324
325
reader = SourceFileTraceabilityReader()
326
327
document = reader.read(source_input)
328
329
markers: List[RangeMarker] = document.markers
330
assert markers[0].reqs == ["REQ-001"]
331
assert markers[0].is_begin()
332
assert markers[0].ng_source_line_begin == 4
333
assert markers[0].ng_range_line_begin == 4
334
assert markers[0].ng_range_line_end == 8
335
336
assert markers[1].reqs == ["REQ-001"]
337
assert markers[1].is_end()
338
assert markers[1].ng_source_line_begin == 8
339
assert markers[1].ng_range_line_begin == 4
340
assert markers[1].ng_range_line_end == 8
341
342
343
def test_031_one_range_marker():
344
source_input = """
345
@relation(REQ-001, scope=range_start)346
CONTENT 1347
CONTENT 2348
CONTENT 3349
@relation(REQ-001, scope=range_end)350
""".lstrip()
351
352
reader = SourceFileTraceabilityReader()
353
354
document = reader.read(source_input)
355
markers = document.markers
356
assert markers[0].reqs == ["REQ-001"]
357
assert markers[0].is_begin()
358
assert markers[0].ng_source_line_begin == 1
359
assert markers[0].ng_range_line_begin == 1
360
assert markers[0].ng_range_line_end == 5
361
362
assert markers[1].reqs == ["REQ-001"]
363
assert markers[1].is_end()
364
assert markers[1].ng_source_line_begin == 5
365
assert markers[1].ng_range_line_begin == 1
366
assert markers[1].ng_range_line_end == 5
367
368
369
def test_050_line_marker():
370
source_input = """
371
# @relation(REQ-001, scope=line)372
CONTENT 1373
# @relation(REQ-002, scope=line)374
CONTENT 2375
# @relation(REQ-003, scope=line)376
CONTENT 3377
""".lstrip()
378
379
reader = SourceFileTraceabilityReader()
380
381
document = reader.read(source_input)
382
markers = document.markers
383
assert markers[0].reqs == ["REQ-001"]
384
assert markers[0].ng_source_line_begin == 1
385
assert markers[0].ng_range_line_begin == 1
386
assert markers[0].ng_range_line_end == 2
387
assert markers[1].reqs == ["REQ-002"]
388
assert markers[1].ng_source_line_begin == 3
389
assert markers[1].ng_range_line_begin == 3
390
assert markers[1].ng_range_line_end == 4
391
assert markers[2].reqs == ["REQ-003"]
392
assert markers[2].ng_source_line_begin == 5
393
assert markers[2].ng_range_line_begin == 5
394
assert markers[2].ng_range_line_end == 6
395
396
397
def test_060_file_level_marker():
398
source_input = """\
399
\"\"\"400
@relation(REQ-001, scope=file)401
\"\"\"402
403
def hello_world():404
pass405
""".lstrip()
406
407
reader = SourceFileTraceabilityReader()
408
409
document = reader.read(source_input)
410
assert len(document.markers) == 1
411
markers = document.markers
412
assert markers[0].reqs == ["REQ-001"]
413
assert markers[0].ng_source_line_begin == 2
414
assert markers[0].ng_source_column_begin == 1
415
assert markers[0].ng_range_line_begin == 1
416
assert markers[0].ng_range_line_end == 6
417
418
419
def test_validation_01_one_range_marker_begin_req_not_equal_to_end_req():
420
source_input = """
421
# @relation(REQ-001, scope=range_start)422
CONTENT 1423
CONTENT 2424
CONTENT 3425
# @relation(REQ-002, scope=range_end)426
""".lstrip()
427
428
reader = SourceFileTraceabilityReader()
429
430
with pytest.raises(Exception) as exc_info:
431
_ = reader.read(source_input)
432
433
assert exc_info.type is StrictDocSemanticError
434
assert (
435
exc_info.value.args[0]
436
== "STRICTDOC RANGE: BEGIN and END requirements mismatch"
437
)438
439
440
def test_validation_02_one_range_marker_end_without_begin():
441
source_input = """
442
# @relation(REQ-002, scope=range_end)443
""".lstrip()
444
445
reader = SourceFileTraceabilityReader()
446
447
with pytest.raises(Exception) as exc_info:
448
_ = reader.read(source_input)
449
450
assert exc_info.type is StrictDocSemanticError
451
assert (
452
exc_info.value.args[0]
453
== "STRICTDOC RANGE: END marker without preceding BEGIN marker"
454
)455
456
457
def test_validation_03_range_start_without_range_end():
458
source_input = """
459
# @relation(REQ-001, scope=range_start)460
CONTENT 1461
CONTENT 2462
CONTENT 3463
""".lstrip()
464
465
reader = SourceFileTraceabilityReader()
466
467
with pytest.raises(Exception) as exc_info:
468
_ = reader.read(source_input)
469
470
assert exc_info.type is StrictDocSemanticError
471
assert (
472
exc_info.value.args[0]
473
== "Unmatched @relation keyword found in source file."
474
)475
476
477
def test_validation_04_consecutive_line_markers():
478
source_input = """
479
# @relation(REQ-001, scope=line)480
# @relation(REQ-002, scope=line)481
"""482
reader = SourceFileTraceabilityReader()
483
484
with pytest.raises(Exception) as exc_info:
485
_ = reader.read(source_input)
486
487
assert exc_info.type is StrictDocSemanticError
488
assert exc_info.value.args[0] == "Consecutive LineMarkers are not allowed"
489
490
491
def test_validation_05_line_marker_followed_by_eof():
492
source_input = "# @relation(REQ-001, scope=line)"
493
494
reader = SourceFileTraceabilityReader()
495
496
with pytest.raises(Exception) as exc_info:
497
_ = reader.read(source_input)
498
499
assert exc_info.type is StrictDocSemanticError
500
assert exc_info.value.args[0] == "LineMarker cannot be followed by EOF"