Path:
tests/unit/strictdoc/core/test_traceability_index.py
Lines:
486
Non-empty lines:
427
Non-empty lines covered with requirements:
427 / 427 (100.0%)
Functions:
12
Functions covered by requirements:
12 / 12 (100.0%)
1
"""2
@relation(SDOC-SRS-28, scope=file)3
"""4
5
from strictdoc.core.document_tree import DocumentTree
6
from strictdoc.core.traceability_index import TraceabilityIndex
7
from strictdoc.core.traceability_index_builder import TraceabilityIndexBuilder
8
from strictdoc.helpers.mid import MID
9
from tests.unit.helpers.document_builder import DocumentBuilder
10
11
12
def test_valid_01_one_document_with_1req():
13
document_builder = DocumentBuilder()
14
requirement = document_builder.add_requirement("REQ-001")
15
document_1 = document_builder.build()
16
17
file_tree = []
18
document_list = [document_1]
19
map_docs_by_paths = {}
20
document_tree = DocumentTree(
21
file_tree=file_tree,
22
document_list=document_list,
23
map_docs_by_paths=map_docs_by_paths,
24
map_docs_by_rel_paths={},
25
map_grammars_by_filenames={},
26
)27
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
28
document_tree, project_config=document_builder.project_config
29
)30
parent_requirements = traceability_index.get_parent_requirements(
31
requirement32
)33
assert parent_requirements == []
34
35
36
def test_valid_02_one_document_with_1req():
37
document_builder = DocumentBuilder()
38
requirement1 = document_builder.add_requirement("REQ-001")
39
requirement2 = document_builder.add_requirement("REQ-002")
40
requirement3 = document_builder.add_requirement("REQ-003")
41
requirement4 = document_builder.add_requirement("REQ-004")
42
document_builder.add_requirement_relation(
43
relation_type="Parent",
44
source_requirement_id="REQ-002",
45
target_requirement_id="REQ-001",
46
role=None,
47
)48
document_builder.add_requirement_relation(
49
relation_type="Parent",
50
source_requirement_id="REQ-003",
51
target_requirement_id="REQ-001",
52
role=None,
53
)54
document_builder.add_requirement_relation(
55
relation_type="Parent",
56
source_requirement_id="REQ-003",
57
target_requirement_id="REQ-002",
58
role=None,
59
)60
document_builder.add_requirement_relation(
61
relation_type="Parent",
62
source_requirement_id="REQ-004",
63
target_requirement_id="REQ-003",
64
role=None,
65
)66
67
document_1 = document_builder.build()
68
69
file_tree = []
70
document_list = [document_1]
71
map_docs_by_paths = {}
72
document_tree = DocumentTree(
73
file_tree=file_tree,
74
document_list=document_list,
75
map_docs_by_paths=map_docs_by_paths,
76
map_docs_by_rel_paths={},
77
map_grammars_by_filenames={},
78
)79
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
80
document_tree, project_config=document_builder.project_config
81
)82
requirement1_parents = traceability_index.get_parent_requirements(
83
requirement184
)85
assert requirement1_parents == []
86
87
requirement2_parents = traceability_index.get_parent_requirements(
88
requirement289
)90
assert requirement2_parents == [requirement1]
91
92
requirement3_parents = traceability_index.get_parent_requirements(
93
requirement394
)95
assert requirement3_parents == [requirement1, requirement2]
96
97
requirement4_parents = traceability_index.get_parent_requirements(
98
requirement499
)100
assert requirement4_parents == [requirement3]
101
102
103
def test_reverse_role_display_label_for_parent_relation():
104
document_builder = DocumentBuilder()
105
parent_requirement = document_builder.add_requirement("REQ-001")
106
child_requirement = document_builder.add_requirement("REQ-002")
107
document_builder.add_requirement_relation(
108
relation_type="Parent",
109
source_requirement_id="REQ-002",
110
target_requirement_id="REQ-001",
111
role="Refines",
112
reverse_role="Refined by",
113
)114
115
document = document_builder.build()
116
document_tree = DocumentTree(
117
file_tree=[],
118
document_list=[document],
119
map_docs_by_paths={},
120
map_docs_by_rel_paths={},
121
map_grammars_by_filenames={},
122
)123
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
124
document_tree, project_config=document_builder.project_config
125
)126
127
assert (
128
traceability_index.get_display_role_for_parent_relation(
129
child_requirement, parent_requirement, "Refines"
130
)131
== "Refines"
132
)133
assert (
134
traceability_index.get_display_role_for_child_relation(
135
parent_requirement, child_requirement, "Refines"
136
)137
== "Refined by"
138
)139
140
141
def test_reverse_role_display_label_for_child_relation():
142
document_builder = DocumentBuilder()
143
parent_requirement = document_builder.add_requirement("REQ-001")
144
child_requirement = document_builder.add_requirement("REQ-002")
145
document_builder.add_requirement_relation(
146
relation_type="Child",
147
source_requirement_id="REQ-001",
148
target_requirement_id="REQ-002",
149
role="Verifies",
150
reverse_role="Verified by",
151
)152
153
document = document_builder.build()
154
document_tree = DocumentTree(
155
file_tree=[],
156
document_list=[document],
157
map_docs_by_paths={},
158
map_docs_by_rel_paths={},
159
map_grammars_by_filenames={},
160
)161
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
162
document_tree, project_config=document_builder.project_config
163
)164
165
assert (
166
traceability_index.get_display_role_for_child_relation(
167
parent_requirement, child_requirement, "Verifies"
168
)169
== "Verifies"
170
)171
assert (
172
traceability_index.get_display_role_for_parent_relation(
173
child_requirement, parent_requirement, "Verifies"
174
)175
== "Verified by"
176
)177
178
179
def test_relation_display_label_falls_back_to_role_for_parent_relation():
180
document_builder = DocumentBuilder()
181
parent_requirement = document_builder.add_requirement("REQ-001")
182
child_requirement = document_builder.add_requirement("REQ-002")
183
document_builder.add_requirement_relation(
184
relation_type="Parent",
185
source_requirement_id="REQ-002",
186
target_requirement_id="REQ-001",
187
role="Refines",
188
)189
190
document = document_builder.build()
191
document_tree = DocumentTree(
192
file_tree=[],
193
document_list=[document],
194
map_docs_by_paths={},
195
map_docs_by_rel_paths={},
196
map_grammars_by_filenames={},
197
)198
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
199
document_tree, project_config=document_builder.project_config
200
)201
202
assert (
203
traceability_index.get_display_role_for_parent_relation(
204
child_requirement, parent_requirement, "Refines"
205
)206
== "Refines"
207
)208
assert (
209
traceability_index.get_display_role_for_child_relation(
210
parent_requirement, child_requirement, "Refines"
211
)212
== "Refines"
213
)214
215
216
def test_relation_display_label_falls_back_to_role_for_child_relation():
217
document_builder = DocumentBuilder()
218
parent_requirement = document_builder.add_requirement("REQ-001")
219
child_requirement = document_builder.add_requirement("REQ-002")
220
document_builder.add_requirement_relation(
221
relation_type="Child",
222
source_requirement_id="REQ-001",
223
target_requirement_id="REQ-002",
224
role="Verifies",
225
)226
227
document = document_builder.build()
228
document_tree = DocumentTree(
229
file_tree=[],
230
document_list=[document],
231
map_docs_by_paths={},
232
map_docs_by_rel_paths={},
233
map_grammars_by_filenames={},
234
)235
traceability_index = TraceabilityIndexBuilder.create_from_document_tree(
236
document_tree, project_config=document_builder.project_config
237
)238
239
assert (
240
traceability_index.get_display_role_for_child_relation(
241
parent_requirement, child_requirement, "Verifies"
242
)243
== "Verifies"
244
)245
assert (
246
traceability_index.get_display_role_for_parent_relation(
247
child_requirement, parent_requirement, "Verifies"
248
)249
== "Verifies"
250
)251
252
253
def test__adding_parent_link__01__two_requirements_in_one_document():
254
document_builder = DocumentBuilder()
255
requirement1 = document_builder.add_requirement("REQ-001")
256
requirement2 = document_builder.add_requirement("REQ-002")
257
document_1 = document_builder.build()
258
259
file_tree = []
260
document_list = [document_1]
261
map_docs_by_paths = {}
262
document_tree = DocumentTree(
263
file_tree=file_tree,
264
document_list=document_list,
265
map_docs_by_paths=map_docs_by_paths,
266
map_docs_by_rel_paths={},
267
map_grammars_by_filenames={},
268
)269
traceability_index: TraceabilityIndex = (
270
TraceabilityIndexBuilder.create_from_document_tree(
271
document_tree, project_config=document_builder.project_config
272
)273
)274
traceability_index.update_requirement_parent_uid(
275
requirement2, "REQ-001", None
276
)277
278
# REQ2 has REQ1 as its parent.279
req2_parent_requirements = traceability_index.get_parent_requirements(
280
requirement2281
)282
assert req2_parent_requirements == [requirement1]
283
284
# REQ1 has REQ2 as its child.285
req1_child_requirements = traceability_index.get_children_requirements(
286
requirement1287
)288
assert req1_child_requirements == [requirement2]
289
290
291
def test__adding_parent_link__02__two_requirements_in_two_documents():
292
document_builder_1 = DocumentBuilder("DOC-1")
293
requirement1 = document_builder_1.add_requirement("REQ-001")
294
document_1 = document_builder_1.build()
295
296
document_builder_2 = DocumentBuilder("DOC-2")
297
requirement2 = document_builder_2.add_requirement("REQ-002")
298
document_2 = document_builder_2.build()
299
300
file_tree = []
301
document_list = [document_1, document_2]
302
map_docs_by_paths = {}
303
document_tree = DocumentTree(
304
file_tree=file_tree,
305
document_list=document_list,
306
map_docs_by_paths=map_docs_by_paths,
307
map_docs_by_rel_paths={},
308
map_grammars_by_filenames={},
309
)310
traceability_index: TraceabilityIndex = (
311
TraceabilityIndexBuilder.create_from_document_tree(
312
document_tree, project_config=document_builder_1.project_config
313
)314
)315
traceability_index.update_requirement_parent_uid(
316
requirement2, "REQ-001", None
317
)318
319
# REQ2 has REQ1 as its parent.320
req2_parent_requirements = traceability_index.get_parent_requirements(
321
requirement2322
)323
assert req2_parent_requirements == [requirement1]
324
325
# REQ1 has REQ2 as its child.326
req1_child_requirements = traceability_index.get_children_requirements(
327
requirement1328
)329
assert req1_child_requirements == [requirement2]
330
331
332
def test__adding_parent_link__04__two_requirements_remove_parent_link():
333
document_builder = DocumentBuilder()
334
requirement1 = document_builder.add_requirement("REQ-001")
335
requirement2 = document_builder.add_requirement("REQ-002")
336
document_builder.add_requirement_relation(
337
relation_type="Parent",
338
source_requirement_id="REQ-002",
339
target_requirement_id="REQ-001",
340
role=None,
341
)342
document_1 = document_builder.build()
343
344
file_tree = []
345
document_list = [document_1]
346
map_docs_by_paths = {}
347
document_tree = DocumentTree(
348
file_tree=file_tree,
349
document_list=document_list,
350
map_docs_by_paths=map_docs_by_paths,
351
map_docs_by_rel_paths={},
352
map_grammars_by_filenames={},
353
)354
traceability_index: TraceabilityIndex = (
355
TraceabilityIndexBuilder.create_from_document_tree(
356
document_tree, project_config=document_builder.project_config
357
)358
)359
traceability_index.remove_requirement_parent_uid(
360
requirement2, "REQ-001", role=None
361
)362
363
req2_parent_requirements = traceability_index.get_parent_requirements(
364
requirement2365
)366
assert req2_parent_requirements == []
367
368
req1_child_requirements = traceability_index.get_children_requirements(
369
requirement1370
)371
assert req1_child_requirements == []
372
373
374
def test_get_node_by_mid():
375
document_builder = DocumentBuilder()
376
document_1 = document_builder.build()
377
378
file_tree = []
379
document_list = [document_1]
380
map_docs_by_paths = {}
381
document_tree = DocumentTree(
382
file_tree=file_tree,
383
document_list=document_list,
384
map_docs_by_paths=map_docs_by_paths,
385
map_docs_by_rel_paths={},
386
map_grammars_by_filenames={},
387
)388
traceability_index: TraceabilityIndex = (
389
TraceabilityIndexBuilder.create_from_document_tree(
390
document_tree, project_config=document_builder.project_config
391
)392
)393
assert (
394
traceability_index.get_node_by_mid(MID(document_1.reserved_mid))
395
== document_1
396
)397
398
399
# Regression test: update_disconnect_two_documents_if_no_links_left used to400
# iterate children as (node, _) tuples and access .document instead of401
# .get_document(), crashing when a requirement in doc-A has a child in doc-B.402
def test__disconnect_two_documents__child_in_other_document_does_not_crash():
403
document_builder_1 = DocumentBuilder("DOC-1")
404
requirement1 = document_builder_1.add_requirement("REQ-001")
405
document_1 = document_builder_1.build()
406
407
document_builder_2 = DocumentBuilder("DOC-2")
408
_ = document_builder_2.add_requirement("REQ-002")
409
document_builder_2.add_requirement_relation(
410
relation_type="Parent",
411
source_requirement_id="REQ-002",
412
target_requirement_id="REQ-001",
413
role=None,
414
)415
document_2 = document_builder_2.build()
416
417
document_tree = DocumentTree(
418
file_tree=[],
419
document_list=[document_1, document_2],
420
map_docs_by_paths={},
421
map_docs_by_rel_paths={},
422
map_grammars_by_filenames={},
423
)424
traceability_index: TraceabilityIndex = (
425
TraceabilityIndexBuilder.create_from_document_tree(
426
document_tree, project_config=document_builder_1.project_config
427
)428
)429
430
# REQ-001 in DOC-1 has REQ-002 (in DOC-2) as its child.431
# This must not crash (previously crashed due to tuple-unpacking bug).432
# The method should detect the cross-document child link and return early433
# without deleting the document-to-document connection.434
children_before = traceability_index.get_children_requirements(requirement1)
435
assert len(children_before) == 1
436
437
traceability_index.update_disconnect_two_documents_if_no_links_left(
438
document_1, document_2
439
)440
441
# The link must still be intact because a cross-document child exists.442
children_after = traceability_index.get_children_requirements(requirement1)
443
assert len(children_after) == 1
444
445
446
# Regression test: delete_requirement previously left stale NODE_TO_CHILD_NODES447
# entries on parent nodes, so after deletion the parent still reported the448
# deleted requirement as one of its children.449
def test__delete_requirement__parent_child_links_cleaned_up_symmetrically():
450
document_builder = DocumentBuilder()
451
requirement1 = document_builder.add_requirement("REQ-001")
452
requirement2 = document_builder.add_requirement("REQ-002")
453
document_builder.add_requirement_relation(
454
relation_type="Parent",
455
source_requirement_id="REQ-002",
456
target_requirement_id="REQ-001",
457
role=None,
458
)459
document_1 = document_builder.build()
460
461
document_tree = DocumentTree(
462
file_tree=[],
463
document_list=[document_1],
464
map_docs_by_paths={},
465
map_docs_by_rel_paths={},
466
map_grammars_by_filenames={},
467
)468
traceability_index: TraceabilityIndex = (
469
TraceabilityIndexBuilder.create_from_document_tree(
470
document_tree, project_config=document_builder.project_config
471
)472
)473
474
# Precondition: REQ-001 is parent of REQ-002.475
assert traceability_index.get_children_requirements(requirement1) == [
476
requirement2477
]478
assert traceability_index.get_parent_requirements(requirement2) == [
479
requirement1480
]481
482
traceability_index.delete_requirement(requirement2)
483
484
# After deletion the stale NODE_TO_CHILD_NODES entry on REQ-001 must be485
# gone; previously it was left in place.486
assert traceability_index.get_children_requirements(requirement1) == []