StrictDoc Documentation
tests/unit/strictdoc/backend/sdoc/test_dsl_passthrough.py
Source file coverage
Path:
tests/unit/strictdoc/backend/sdoc/test_dsl_passthrough.py
Lines:
1308
Non-empty lines:
949
Non-empty lines covered with requirements:
949 / 949 (100.0%)
Functions:
46
Functions covered by requirements:
46 / 46 (100.0%)
1
"""
2
@relation(SDOC-SRS-136, scope=file)
3
"""
4
 
5
import pytest
6
 
7
from strictdoc.backend.sdoc.models.document import SDocDocument
8
from strictdoc.backend.sdoc.models.grammar_element import ReferenceType
9
from strictdoc.backend.sdoc.models.inline_link import InlineLink
10
from strictdoc.backend.sdoc.models.node import (
11
    SDocNode,
12
)
13
from strictdoc.backend.sdoc.models.reference import (
14
    FileReference,
15
)
16
from strictdoc.backend.sdoc.reader import SDReader
17
from strictdoc.backend.sdoc.writer import SDWriter
18
from strictdoc.helpers.exception import StrictDocException
19
 
20
 
21
def test_001_minimal_doc(default_project_config):
22
    input_sdoc = """\
23
[DOCUMENT]
24
TITLE: Test Doc
25
 
26
[REQUIREMENT]
27
STATEMENT: ...
28
 
29
[REQUIREMENT]
30
STATEMENT: ...
31
 
32
[REQUIREMENT]
33
STATEMENT: ...
34
"""
35
 
36
    reader = SDReader()
37
 
38
    document = reader.read(input_sdoc)
39
    assert isinstance(document, SDocDocument)
40
 
41
    writer = SDWriter(default_project_config)
42
    output = writer.write(document)
43
 
44
    assert input_sdoc == output
45
 
46
 
47
def test_002_minimal_req(default_project_config):
48
    input_sdoc = """
49
[DOCUMENT]
50
TITLE: Test Doc
51
 
52
[REQUIREMENT]
53
TITLE: Hello
54
""".lstrip()
55
 
56
    reader = SDReader()
57
 
58
    document = reader.read(input_sdoc)
59
    assert isinstance(document, SDocDocument)
60
 
61
    writer = SDWriter(default_project_config)
62
    output = writer.write(document)
63
 
64
    assert input_sdoc == output
65
 
66
 
67
def test_005_requirement_UID_supports_special_characters(
68
    default_project_config,
69
):
70
    input_sdoc = """
71
[DOCUMENT]
72
TITLE: Test Doc
73
 
74
[REQUIREMENT]
75
UID: FOO-1
76
TITLE: Hello
77
 
78
[REQUIREMENT]
79
UID: FOO.1
80
TITLE: Hello
81
 
82
[REQUIREMENT]
83
UID: FOO/1
84
TITLE: Hello
85
 
86
[REQUIREMENT]
87
UID: FOO_1
88
TITLE: Hello
89
 
90
[REQUIREMENT]
91
UID: FOO 1
92
TITLE: Hello
93
 
94
[REQUIREMENT]
95
UID: FOO::1
96
TITLE: Hello
97
""".lstrip()
98
 
99
    reader = SDReader()
100
 
101
    document = reader.read(input_sdoc)
102
    assert isinstance(document, SDocDocument)
103
 
104
    writer = SDWriter(default_project_config)
105
    output = writer.write(document)
106
 
107
    assert input_sdoc == output
108
 
109
 
110
def test_003_comments_01_several_comments(default_project_config):
111
    input_sdoc = """
112
[DOCUMENT]
113
TITLE: Test Doc
114
 
115
[REQUIREMENT]
116
TITLE: Hello
117
COMMENT: Comment #1
118
COMMENT: Comment #2
119
COMMENT: Comment #3
120
""".lstrip()
121
 
122
    reader = SDReader()
123
 
124
    document = reader.read(input_sdoc)
125
    assert isinstance(document, SDocDocument)
126
 
127
    writer = SDWriter(default_project_config)
128
    output = writer.write(document)
129
 
130
    assert input_sdoc == output
131
 
132
 
133
def test_004_several_tags(default_project_config):
134
    input_sdoc = """
135
[DOCUMENT]
136
TITLE: Test Doc
137
 
138
[REQUIREMENT]
139
TAGS: A, B, C, D
140
TITLE: Hello
141
""".lstrip()
142
 
143
    reader = SDReader()
144
 
145
    document = reader.read(input_sdoc)
146
    assert isinstance(document, SDocDocument)
147
 
148
    writer = SDWriter(default_project_config)
149
    output = writer.write(document)
150
 
151
    assert input_sdoc == output
152
 
153
 
154
def test_010_multiple_sections(default_project_config):
155
    input_sdoc = """\
156
[DOCUMENT]
157
TITLE: Test Doc
158
 
159
[[SECTION]]
160
TITLE: Test Section
161
 
162
[REQUIREMENT]
163
STATEMENT: >>>
164
This is a statement 1
165
This is a statement 2
166
This is a statement 3
167
<<<
168
 
169
[[/SECTION]]
170
 
171
[[SECTION]]
172
TITLE: Test Section
173
 
174
[REQUIREMENT]
175
STATEMENT: >>>
176
This is a statement 1
177
This is a statement 2
178
This is a statement 3
179
<<<
180
 
181
[[/SECTION]]
182
"""
183
 
184
    reader = SDReader()
185
 
186
    document = reader.read(input_sdoc)
187
    assert isinstance(document, SDocDocument)
188
 
189
    writer = SDWriter(default_project_config)
190
    output = writer.write(document)
191
    assert input_sdoc == output
192
 
193
 
194
def test_020_requirement_mid(default_project_config):
195
    input_sdoc = """
196
[DOCUMENT]
197
TITLE: Test Doc
198
 
199
[GRAMMAR]
200
ELEMENTS:
201
- TAG: TEXT
202
  FIELDS:
203
  - TITLE: UID
204
    TYPE: String
205
    REQUIRED: False
206
  - TITLE: STATEMENT
207
    TYPE: String
208
    REQUIRED: True
209
- TAG: REQUIREMENT
210
  FIELDS:
211
  - TITLE: MID
212
    TYPE: String
213
    REQUIRED: False
214
  - TITLE: STATEMENT
215
    TYPE: String
216
    REQUIRED: False
217
 
218
[REQUIREMENT]
219
MID: abcdef123456
220
STATEMENT: >>>
221
This is a statement 1
222
This is a statement 2
223
This is a statement 3
224
<<<
225
""".lstrip()
226
 
227
    reader = SDReader()
228
 
229
    document = reader.read(input_sdoc)
230
    assert isinstance(document, SDocDocument)
231
 
232
    writer = SDWriter(default_project_config)
233
    output = writer.write(document)
234
    assert input_sdoc == output
235
 
236
 
237
def test_021_section_and_document_mid(default_project_config):
238
    input_sdoc = """
239
[DOCUMENT]
240
MID: xyz09876
241
TITLE: Test Doc
242
OPTIONS:
243
  ENABLE_MID: True
244
 
245
[[SECTION]]
246
MID: abcdef123456
247
TITLE: Test Section
248
 
249
[[/SECTION]]
250
""".lstrip()
251
 
252
    reader = SDReader()
253
 
254
    document = reader.read(input_sdoc)
255
    assert isinstance(document, SDocDocument)
256
 
257
    writer = SDWriter(default_project_config)
258
    output = writer.write(document)
259
 
260
    assert input_sdoc == output
261
 
262
 
263
def test_022_requirement_uid_and_link(default_project_config):
264
    input_sdoc = """
265
[DOCUMENT]
266
TITLE: Test Doc
267
 
268
[REQUIREMENT]
269
UID: With spaces and (_-.) and non latin 特点
270
STATEMENT: >>>
271
This is a statement.
272
 
273
[TEXT]
274
STATEMENT: >>>
275
[LINK: With spaces and (_-.) and non latin 特点]
276
<<<
277
""".lstrip()
278
 
279
    reader = SDReader()
280
 
281
    document = reader.read(input_sdoc)
282
    assert isinstance(document, SDocDocument)
283
 
284
    writer = SDWriter(default_project_config)
285
    output = writer.write(document)
286
 
287
    assert input_sdoc == output
288
 
289
 
290
def test_030_multiline_statement(default_project_config):
291
    input_sdoc = """
292
[DOCUMENT]
293
TITLE: Test Doc
294
 
295
[[SECTION]]
296
TITLE: Test Section
297
 
298
[REQUIREMENT]
299
STATEMENT: >>>
300
This is a statement 1
301
This is a statement 2
302
This is a statement 3
303
<<<
304
 
305
[[/SECTION]]
306
""".lstrip()
307
 
308
    reader = SDReader()
309
 
310
    document = reader.read(input_sdoc)
311
    assert isinstance(document, SDocDocument)
312
 
313
    assert isinstance(
314
        document.section_contents[0].section_contents[0], SDocNode
315
    )
316
    requirement_1 = document.section_contents[0].section_contents[0]
317
    assert (
318
        requirement_1.reserved_statement
319
        == "This is a statement 1\nThis is a statement 2\nThis is a statement 3\n"
320
    )
321
 
322
    writer = SDWriter(default_project_config)
323
    output = writer.write(document)
324
 
325
    assert input_sdoc == output
326
 
327
 
328
def test_036_rationale_single_line(default_project_config):
329
    input_sdoc = """
330
[DOCUMENT]
331
TITLE: Test Doc
332
 
333
[[SECTION]]
334
TITLE: Test Section
335
 
336
[REQUIREMENT]
337
STATEMENT: Some statement
338
RATIONALE: This is a Rationale
339
 
340
[[/SECTION]]
341
""".lstrip()
342
 
343
    reader = SDReader()
344
 
345
    document = reader.read(input_sdoc)
346
    assert isinstance(document, SDocDocument)
347
 
348
    writer = SDWriter(default_project_config)
349
    output = writer.write(document)
350
 
351
    assert input_sdoc == output
352
 
353
    assert isinstance(
354
        document.section_contents[0].section_contents[0], SDocNode
355
    )
356
    requirement_1 = document.section_contents[0].section_contents[0]
357
    assert requirement_1.rationale == "This is a Rationale"
358
 
359
 
360
def test_037_rationale_multi_line(default_project_config):
361
    input_sdoc = """
362
[DOCUMENT]
363
TITLE: Test Doc
364
 
365
[[SECTION]]
366
TITLE: Test Section
367
 
368
[REQUIREMENT]
369
STATEMENT: Some statement
370
RATIONALE: >>>
371
This is a Rationale line 1
372
This is a Rationale line 2
373
This is a Rationale line 3
374
<<<
375
 
376
[[/SECTION]]
377
""".lstrip()
378
 
379
    reader = SDReader()
380
 
381
    document = reader.read(input_sdoc)
382
    assert isinstance(document, SDocDocument)
383
 
384
    writer = SDWriter(default_project_config)
385
    output = writer.write(document)
386
 
387
    assert input_sdoc == output
388
 
389
    assert isinstance(
390
        document.section_contents[0].section_contents[0], SDocNode
391
    )
392
    requirement_1 = document.section_contents[0].section_contents[0]
393
    assert requirement_1.rationale == (
394
        "This is a Rationale line 1\n"
395
        "This is a Rationale line 2\n"
396
        "This is a Rationale line 3\n"
397
    )
398
 
399
 
400
# This test is needed to make sure that the grammar details related
401
# to the difference of parting single vs multiline strings are covered.
402
def test_050_requirement_single_line_statement_one_symbol(
403
    default_project_config,
404
):
405
    input_sdoc = """
406
[DOCUMENT]
407
TITLE: Test Doc
408
 
409
[REQUIREMENT]
410
STATEMENT: 1
411
""".lstrip()
412
 
413
    reader = SDReader()
414
 
415
    document = reader.read(input_sdoc)
416
    assert isinstance(document, SDocDocument)
417
 
418
    document: SDocDocument = reader.read(input_sdoc)
419
    requirement = document.section_contents[0]
420
    assert requirement.reserved_statement == "1"
421
 
422
    writer = SDWriter(default_project_config)
423
    output = writer.write(document)
424
 
425
    assert input_sdoc == output
426
 
427
 
428
def test_060_file_ref(default_project_config):
429
    input_sdoc = """
430
[DOCUMENT]
431
TITLE: Test Doc
432
 
433
[REQUIREMENT]
434
STATEMENT: ...
435
RELATIONS:
436
- TYPE: File
437
  VALUE: /tmp/sample.cpp
438
""".lstrip()
439
 
440
    reader = SDReader()
441
 
442
    document = reader.read(input_sdoc)
443
    assert isinstance(document, SDocDocument)
444
 
445
    requirement: SDocNode = document.section_contents[0]
446
    relations = requirement.relations
447
    assert len(relations) == 1
448
 
449
    reference: FileReference = relations[0]
450
    assert isinstance(reference, FileReference)
451
    assert reference.ref_type == ReferenceType.FILE
452
    assert reference.g_file_entry.g_file_path == "/tmp/sample.cpp"
453
 
454
    writer = SDWriter(default_project_config)
455
    output = writer.write(document)
456
 
457
    assert input_sdoc == output
458
 
459
 
460
def test_070_document_config_version(default_project_config):
461
    input_sdoc = """
462
[DOCUMENT]
463
TITLE: Test Doc
464
VERSION: 0.0.1
465
 
466
[REQUIREMENT]
467
STATEMENT: ...
468
""".lstrip()
469
 
470
    reader = SDReader()
471
 
472
    document = reader.read(input_sdoc)
473
    assert isinstance(document, SDocDocument)
474
 
475
    document: SDocDocument = reader.read(input_sdoc)
476
    assert document.config.version == "0.0.1"
477
 
478
    writer = SDWriter(default_project_config)
479
    output = writer.write(document)
480
 
481
    assert input_sdoc == output
482
 
483
 
484
def test_071_document_config_number(default_project_config):
485
    input_sdoc = """
486
[DOCUMENT]
487
TITLE: Test Doc
488
UID: SDOC-01
489
 
490
[REQUIREMENT]
491
STATEMENT: ...
492
""".lstrip()
493
 
494
    reader = SDReader()
495
 
496
    document = reader.read(input_sdoc)
497
    assert isinstance(document, SDocDocument)
498
 
499
    document: SDocDocument = reader.read(input_sdoc)
500
    assert document.config.uid == "SDOC-01"
501
 
502
    writer = SDWriter(default_project_config)
503
    output = writer.write(document)
504
 
505
    assert input_sdoc == output
506
 
507
 
508
def test_072_document_config_classification(default_project_config):
509
    input_sdoc = """
510
[DOCUMENT]
511
TITLE: Test Doc
512
UID: SDOC-01
513
VERSION: 0.0.1
514
CLASSIFICATION: Restricted
515
 
516
[REQUIREMENT]
517
STATEMENT: ...
518
""".lstrip()
519
 
520
    reader = SDReader()
521
 
522
    document = reader.read(input_sdoc)
523
    assert isinstance(document, SDocDocument)
524
 
525
    document: SDocDocument = reader.read(input_sdoc)
526
    assert document.config.classification == "Restricted"
527
 
528
    writer = SDWriter(default_project_config)
529
    output = writer.write(document)
530
 
531
    assert input_sdoc == output
532
 
533
 
534
def test_073_document_config_requirement_prefix(default_project_config):
535
    input_sdoc = """
536
[DOCUMENT]
537
TITLE: Test Doc
538
UID: SDOC-01
539
VERSION: 0.0.1
540
PREFIX: DOC-
541
 
542
[REQUIREMENT]
543
STATEMENT: ...
544
""".lstrip()
545
 
546
    reader = SDReader()
547
 
548
    document = reader.read(input_sdoc)
549
    assert isinstance(document, SDocDocument)
550
 
551
    document: SDocDocument = reader.read(input_sdoc)
552
    assert document.config.requirement_prefix == "DOC-"
553
 
554
    writer = SDWriter(default_project_config)
555
    output = writer.write(document)
556
 
557
    assert input_sdoc == output
558
 
559
 
560
def test_074_document_config_metadata(default_project_config):
561
    input_sdoc = """
562
[DOCUMENT]
563
TITLE: Test Doc
564
UID: SDOC-01
565
VERSION: 0.0.1
566
METADATA:
567
  AUTHOR: James T. Kirk
568
  CHECKED-BY: Chuck Norris
569
  APPROVED-BY: Wile E. Coyote
570
  OWNER: [LINK: FOO]
571
 
572
[REQUIREMENT]
573
UID: FOO
574
""".lstrip()
575
 
576
    reader = SDReader()
577
 
578
    document = reader.read(input_sdoc)
579
    assert isinstance(document, SDocDocument)
580
 
581
    custom_metadata = document.config.custom_metadata
582
    assert custom_metadata is not None
583
    owner_entry = custom_metadata.entries[-1]
584
    assert owner_entry.key == "OWNER"
585
    assert len(owner_entry.parts) == 1
586
    assert isinstance(owner_entry.parts[0], InlineLink)
587
    assert owner_entry.parts[0].link == "FOO"
588
 
589
    writer = SDWriter(default_project_config)
590
    output = writer.write(document)
591
 
592
    assert input_sdoc == output
593
 
594
 
595
def test_090_document_config_all_fields(default_project_config):
596
    input_sdoc = """
597
[DOCUMENT]
598
TITLE: Test Doc
599
UID: SDOC-01
600
VERSION: 0.0.1
601
CLASSIFICATION: Restricted
602
ROOT: True
603
OPTIONS:
604
  MARKUP: Text
605
  AUTO_LEVELS: Off
606
  LAYOUT: Website
607
  VIEW_STYLE: Table
608
  NODE_IN_TOC: True
609
 
610
[[SECTION]]
611
LEVEL: 123
612
TITLE: "Section"
613
 
614
[REQUIREMENT]
615
LEVEL: 456
616
STATEMENT: ABC
617
 
618
[[/SECTION]]
619
""".lstrip()
620
 
621
    reader = SDReader()
622
 
623
    document = reader.read(input_sdoc)
624
    assert isinstance(document, SDocDocument)
625
 
626
    document: SDocDocument = reader.read(input_sdoc)
627
    assert document.title == "Test Doc"
628
    assert document.config.version == "0.0.1"
629
    assert document.config.uid == "SDOC-01"
630
    assert document.config.classification == "Restricted"
631
    assert document.config.root is True
632
    assert document.config.markup == "Text"
633
    assert document.config.auto_levels is False
634
    assert document.config.requirement_style == "Table"
635
    assert document.config.requirement_in_toc == "True"
636
 
637
    section = document.section_contents[0]
638
    assert isinstance(section, SDocNode)
639
    assert section.custom_level == "123"
640
 
641
    requirement = section.section_contents[0]
642
    assert isinstance(requirement, SDocNode)
643
    assert requirement.custom_level == "456"
644
 
645
    writer = SDWriter(default_project_config)
646
    output = writer.write(document)
647
 
648
    assert input_sdoc == output
649
 
650
 
651
def test_100_basic_test(default_project_config):
652
    input_sdoc = """
653
[DOCUMENT]
654
TITLE: Test Doc
655
 
656
[[SECTION]]
657
TITLE: Test Section
658
 
659
[REQUIREMENT]
660
TAGS: Tag 1, Tag 2, Tag 3
661
STATEMENT: System shall do X
662
COMMENT: This requirement is very important
663
RELATIONS:
664
- TYPE: File
665
  VALUE: /usr/local/bin/hexe
666
- TYPE: File
667
  VALUE: /usr/local/bin/hexe
668
- TYPE: File
669
  VALUE: /usr/local/bin/hexe
670
 
671
[REQUIREMENT]
672
UID: REQ-001
673
STATUS: Draft
674
TITLE: Optional title B
675
STATEMENT: System shall do Y
676
COMMENT: This requirement is very important
677
 
678
[[/SECTION]]
679
""".lstrip()
680
 
681
    reader = SDReader()
682
 
683
    document = reader.read(input_sdoc)
684
    assert isinstance(document, SDocDocument)
685
 
686
    assert isinstance(
687
        document.section_contents[0].section_contents[0], SDocNode
688
    )
689
    requirement_1 = document.section_contents[0].section_contents[0]
690
    assert requirement_1.reserved_tags[0] == "Tag 1"
691
    assert requirement_1.reserved_tags[1] == "Tag 2"
692
    assert requirement_1.reserved_tags[2] == "Tag 3"
693
 
694
    writer = SDWriter(default_project_config)
695
    output = writer.write(document)
696
 
697
    assert input_sdoc == output
698
 
699
 
700
def test_081_document_config_markup_not_specified(default_project_config):
701
    input_sdoc = """
702
[DOCUMENT]
703
TITLE: Test Doc
704
VERSION: 0.0.1
705
 
706
[REQUIREMENT]
707
TITLE: ...
708
""".lstrip()
709
 
710
    reader = SDReader()
711
 
712
    document = reader.read(input_sdoc)
713
    assert isinstance(document, SDocDocument)
714
 
715
    document: SDocDocument = reader.read(input_sdoc)
716
    assert document.config.version == "0.0.1"
717
    assert document.config.markup is None
718
 
719
    writer = SDWriter(default_project_config)
720
    output = writer.write(document)
721
 
722
    assert input_sdoc == output
723
 
724
 
725
def test_081_document_config_markup_specified(default_project_config):
726
    input_sdoc = """
727
[DOCUMENT]
728
TITLE: Test Doc
729
VERSION: 0.0.1
730
OPTIONS:
731
  MARKUP: Text
732
 
733
[REQUIREMENT]
734
STATEMENT: ...
735
""".lstrip()
736
 
737
    reader = SDReader()
738
 
739
    document = reader.read(input_sdoc)
740
    assert isinstance(document, SDocDocument)
741
 
742
    document: SDocDocument = reader.read(input_sdoc)
743
    assert document.config.version == "0.0.1"
744
 
745
    writer = SDWriter(default_project_config)
746
    output = writer.write(document)
747
 
748
    assert input_sdoc == output
749
 
750
 
751
def test_082_document_config_auto_levels_specified_to_false(
752
    default_project_config,
753
):
754
    input_sdoc = """
755
[DOCUMENT]
756
TITLE: Test Doc
757
VERSION: 0.0.1
758
OPTIONS:
759
  AUTO_LEVELS: Off
760
""".lstrip()
761
 
762
    reader = SDReader()
763
 
764
    document = reader.read(input_sdoc)
765
    assert isinstance(document, SDocDocument)
766
 
767
    document: SDocDocument = reader.read(input_sdoc)
768
    assert document.config.auto_levels is False
769
 
770
    writer = SDWriter(default_project_config)
771
    output = writer.write(document)
772
 
773
    assert input_sdoc == output
774
 
775
 
776
def test_083_requirement_level(default_project_config):
777
    input_sdoc = """
778
[DOCUMENT]
779
TITLE: Test Doc
780
VERSION: 0.0.1
781
OPTIONS:
782
  AUTO_LEVELS: Off
783
 
784
[[SECTION]]
785
LEVEL: 123
786
TITLE: "Section"
787
 
788
[REQUIREMENT]
789
LEVEL: 456
790
STATEMENT: ABC
791
 
792
[[/SECTION]]
793
""".lstrip()
794
 
795
    reader = SDReader()
796
 
797
    document = reader.read(input_sdoc)
798
    assert isinstance(document, SDocDocument)
799
 
800
    document: SDocDocument = reader.read(input_sdoc)
801
    assert document.config.auto_levels is False
802
    section: SDocNode = document.section_contents[0]
803
    assert section.custom_level == "123"
804
 
805
    requirement = section.section_contents[0]
806
    assert isinstance(requirement, SDocNode)
807
    assert requirement.custom_level == "456"
808
 
809
    writer = SDWriter(default_project_config)
810
    output = writer.write(document)
811
 
812
    assert input_sdoc == output
813
 
814
 
815
def test_085_options_requirement_style(default_project_config):
816
    input_sdoc = """
817
[DOCUMENT]
818
TITLE: Test Doc
819
VERSION: 0.0.1
820
OPTIONS:
821
  VIEW_STYLE: Table
822
""".lstrip()
823
 
824
    reader = SDReader()
825
 
826
    document = reader.read(input_sdoc)
827
    assert isinstance(document, SDocDocument)
828
 
829
    document: SDocDocument = reader.read(input_sdoc)
830
    assert document.config.requirement_style == "Table"
831
 
832
    writer = SDWriter(default_project_config)
833
    output = writer.write(document)
834
 
835
    assert input_sdoc == output
836
 
837
 
838
def test_087_options_requirement_in_toc(default_project_config):
839
    input_sdoc = """
840
[DOCUMENT]
841
TITLE: Test Doc
842
VERSION: 0.0.1
843
OPTIONS:
844
  NODE_IN_TOC: True
845
""".lstrip()
846
 
847
    reader = SDReader()
848
 
849
    document = reader.read(input_sdoc)
850
    assert isinstance(document, SDocDocument)
851
 
852
    document: SDocDocument = reader.read(input_sdoc)
853
    assert document.config.requirement_in_toc == "True"
854
 
855
    writer = SDWriter(default_project_config)
856
    output = writer.write(document)
857
 
858
    assert input_sdoc == output
859
 
860
 
861
def test_089_document_config_use_mid(default_project_config):
862
    input_sdoc = """
863
[DOCUMENT]
864
MID: foobar
865
TITLE: Test Doc
866
OPTIONS:
867
  ENABLE_MID: True
868
""".lstrip()
869
 
870
    reader = SDReader()
871
 
872
    document = reader.read(input_sdoc)
873
    assert isinstance(document, SDocDocument)
874
 
875
    document: SDocDocument = reader.read(input_sdoc)
876
    assert document.config.enable_mid is True
877
 
878
    writer = SDWriter(default_project_config)
879
    output = writer.write(document)
880
 
881
    assert input_sdoc == output
882
 
883
 
884
def test_090_byte_order_mark_symbol_does_not_cause_parsing_errors():
885
    input_sdoc = (
886
        "\ufeff"
887
        """\
888
[DOCUMENT]
889
TITLE: Test Doc
890
"""
891
    )
892
 
893
    reader = SDReader()
894
 
895
    document = reader.read(input_sdoc)
896
    assert isinstance(document, SDocDocument)
897
 
898
 
899
def test__validation__30__composite_node_start_end_tags_do_not_match():
900
    input_sdoc = """\
901
[DOCUMENT]
902
TITLE: Test Doc
903
 
904
[[REQUIREMENT]]
905
UID: TITLE
906
 
907
[[/FOOBAR]]
908
""".lstrip()
909
 
910
    reader = SDReader()
911
 
912
    with pytest.raises(Exception) as exc_info:
913
        _ = reader.read(input_sdoc)
914
 
915
    assert exc_info.type is StrictDocException
916
    assert exc_info.value.args[0] == (
917
        "[[NODE]] syntax error: "
918
        "Opening and closing tags must match: "
919
        "opening: REQUIREMENT, closing: FOOBAR."
920
    )
921
 
922
 
923
def test_edge_case_01_minimal_requirement(default_project_config):
924
    input_sdoc = """
925
[DOCUMENT]
926
TITLE: Test Doc
927
 
928
[REQUIREMENT]
929
STATEMENT: ...
930
""".lstrip()
931
 
932
    reader = SDReader()
933
 
934
    document: SDocDocument = reader.read(input_sdoc)
935
    assert isinstance(document, SDocDocument)
936
 
937
    requirement: SDocNode = document.section_contents[0]
938
    assert requirement.reserved_uid is None
939
    assert requirement.reserved_title is None
940
    assert requirement.reserved_statement == "..."
941
 
942
    writer = SDWriter(default_project_config)
943
    output = writer.write(document)
944
 
945
    assert input_sdoc == output
946
 
947
 
948
def test_edge_case_02_uid_present_but_empty_with_no_space_character():
949
    # Note: There is no whitespace character after "UID:".
950
    input_sdoc = """
951
[DOCUMENT]
952
TITLE: Test Doc
953
 
954
[REQUIREMENT]
955
UID:
956
""".lstrip()
957
 
958
    reader = SDReader()
959
 
960
    with pytest.raises(Exception) as exc_info:
961
        _ = reader.read(input_sdoc)
962
 
963
    assert exc_info.type is StrictDocException
964
    assert "Expected ': '" in exc_info.value.args[0]
965
 
966
 
967
def test_edge_case_03_uid_present_but_empty_with_space_character():
968
    # Note: There is a whitespace character after "UID:".
969
    input_sdoc = """
970
[DOCUMENT]
971
TITLE: Test Doc
972
 
973
[REQUIREMENT]
974
UID: 
975
""".lstrip()  # noqa: W291
976
 
977
    reader = SDReader()
978
 
979
    with pytest.raises(Exception) as exc_info:
980
        _ = reader.read(input_sdoc)
981
 
982
    assert exc_info.type is StrictDocException
983
    assert "Expected '([\\w]+[\\w()\\-\\/.: ]*)'" in exc_info.value.args[0]
984
 
985
 
986
def test_edge_case_04_uid_present_but_empty_with_two_space_characters():
987
    # Note: There are two whitespace characters after "UID:".
988
    input_sdoc = """
989
[DOCUMENT]
990
TITLE: Test Doc
991
 
992
[REQUIREMENT]
993
UID:  
994
""".lstrip()  # noqa: W291
995
 
996
    reader = SDReader()
997
 
998
    with pytest.raises(Exception) as exc_info:
999
        _ = reader.read(input_sdoc)
1000
 
1001
    assert exc_info.type is StrictDocException
1002
    assert "Expected '([\\w]+[\\w()\\-\\/.: ]*)'" in exc_info.value.args[0]
1003
 
1004
 
1005
def test_edge_case_10_empty_multiline_field():
1006
    input_sdoc = """
1007
[DOCUMENT]
1008
TITLE: Test Doc
1009
 
1010
[REQUIREMENT]
1011
COMMENT: >>>
1012
<<<
1013
""".lstrip()
1014
 
1015
    reader = SDReader()
1016
 
1017
    with pytest.raises(Exception) as exc_info:
1018
        _ = reader.read(input_sdoc)
1019
 
1020
    assert exc_info.type is StrictDocException
1021
    assert (
1022
        "Expected '^\\[ANCHOR: ' or '[LINK: ' or "
1023
        "'(?ms)(?!^<<<)(?!\\[LINK: "
1024
        "([\\w]+[\\w()\\-\\/.: ]*))(?!^\\[ANCHOR: "
1025
        "([\\w]+[\\w()\\-\\/.: ]*)).'" in exc_info.value.args[0]
1026
    )
1027
 
1028
 
1029
def test_edge_case_11_empty_multiline_field_with_one_newline():
1030
    input_sdoc = """
1031
[DOCUMENT]
1032
TITLE: Test Doc
1033
 
1034
[REQUIREMENT]
1035
COMMENT: >>>
1036
 
1037
<<<
1038
""".lstrip()
1039
 
1040
    reader = SDReader()
1041
 
1042
    with pytest.raises(Exception) as exc_info:
1043
        _ = reader.read(input_sdoc)
1044
 
1045
    assert exc_info.type is StrictDocException
1046
    assert "Node statement cannot be empty." in exc_info.value.args[0]
1047
 
1048
 
1049
def test_edge_case_19_empty_section_title():
1050
    input_sdoc = """
1051
[DOCUMENT]
1052
TITLE: Test Doc
1053
 
1054
[[SECTION]]
1055
TITLE:
1056
 
1057
[[/SECTION]]
1058
""".lstrip()
1059
 
1060
    reader = SDReader()
1061
 
1062
    with pytest.raises(StrictDocException) as exc_info:
1063
        _ = reader.read(input_sdoc)
1064
 
1065
    assert exc_info.type is StrictDocException
1066
    assert "TITLE:*" in exc_info.value.args[0]
1067
    assert "Expected ' '" in exc_info.value.args[0]
1068
 
1069
 
1070
def test_edge_case_20_empty_section_title():
1071
    input_sdoc = """
1072
[DOCUMENT]
1073
TITLE: Test Doc
1074
 
1075
[[SECTION]]
1076
TITLE: 
1077
 
1078
[[/SECTION]]
1079
""".lstrip()  # noqa: W291
1080
 
1081
    reader = SDReader()
1082
 
1083
    with pytest.raises(Exception) as exc_info:
1084
        _ = reader.read(input_sdoc)
1085
 
1086
    assert exc_info.type is StrictDocException
1087
    assert "TITLE: *" in exc_info.value.args[0]
1088
 
1089
 
1090
def test_edge_case_21_section_title_with_empty_space():
1091
    # Empty space after TITLE:
1092
    input_sdoc = """
1093
[DOCUMENT]
1094
TITLE: Test Doc
1095
 
1096
[[SECTION]]
1097
TITLE: 
1098
 
1099
[[/SECTION]]
1100
""".lstrip()  # noqa: W291
1101
 
1102
    reader = SDReader()
1103
 
1104
    with pytest.raises(Exception) as exc_info:
1105
        _ = reader.read(input_sdoc)
1106
 
1107
    assert exc_info.type is StrictDocException
1108
    assert "TITLE: *" in exc_info.value.args[0]
1109
 
1110
 
1111
def test_edge_case_22_section_title_with_two_empty_spaces():
1112
    # Two empty spaces after TITLE:
1113
    input_sdoc = """
1114
[DOCUMENT]
1115
TITLE: Test Doc
1116
 
1117
[[SECTION]]
1118
TITLE:  
1119
 
1120
[[/SECTION]]
1121
""".lstrip()  # noqa: W291
1122
 
1123
    reader = SDReader()
1124
 
1125
    with pytest.raises(Exception) as exc_info:
1126
        _ = reader.read(input_sdoc)
1127
 
1128
    assert exc_info.type is StrictDocException
1129
    assert "TITLE: *" in exc_info.value.args[0]
1130
 
1131
 
1132
def test_edge_case_23_leading_spaces_do_not_imply_empy_field(
1133
    default_project_config,
1134
):
1135
    input_sdoc = """
1136
[DOCUMENT]
1137
TITLE: Test Doc
1138
 
1139
[GRAMMAR]
1140
ELEMENTS:
1141
- TAG: TEXT
1142
  FIELDS:
1143
  - TITLE: STATEMENT
1144
    TYPE: String
1145
    REQUIRED: True
1146
- TAG: REQUIREMENT
1147
  FIELDS:
1148
  - TITLE: STATEMENT
1149
    TYPE: String
1150
    REQUIRED: False
1151
  - TITLE: MY_FIELD
1152
    TYPE: String
1153
    REQUIRED: True
1154
  RELATIONS:
1155
  - TYPE: Parent
1156
 
1157
[REQUIREMENT]
1158
MY_FIELD: >>>
1159
    Some text here...
1160
    Some text here...
1161
    Some text here...
1162
<<<
1163
""".lstrip()
1164
 
1165
    expected_sdoc = """
1166
[DOCUMENT]
1167
TITLE: Test Doc
1168
 
1169
[GRAMMAR]
1170
ELEMENTS:
1171
- TAG: TEXT
1172
  FIELDS:
1173
  - TITLE: STATEMENT
1174
    TYPE: String
1175
    REQUIRED: True
1176
- TAG: REQUIREMENT
1177
  FIELDS:
1178
  - TITLE: STATEMENT
1179
    TYPE: String
1180
    REQUIRED: False
1181
  - TITLE: MY_FIELD
1182
    TYPE: String
1183
    REQUIRED: True
1184
  RELATIONS:
1185
  - TYPE: Parent
1186
 
1187
[REQUIREMENT]
1188
MY_FIELD: >>>
1189
    Some text here...
1190
    Some text here...
1191
    Some text here...
1192
<<<
1193
""".lstrip()
1194
 
1195
    reader = SDReader()
1196
 
1197
    document = reader.read(input_sdoc)
1198
    assert isinstance(document, SDocDocument)
1199
 
1200
    writer = SDWriter(default_project_config)
1201
    output = writer.write(document)
1202
    assert expected_sdoc == output
1203
 
1204
 
1205
def test_edge_case_30_single_vs_multiline_disallow_multiline_reserved_single():
1206
    input_sdoc = """\
1207
[DOCUMENT]
1208
TITLE: Test Doc
1209
 
1210
[REQUIREMENT]
1211
STATUS: >>>
1212
Multiline status is not allowed.
1213
<<<
1214
""".lstrip()  # noqa: W291
1215
 
1216
    reader = SDReader()
1217
 
1218
    with pytest.raises(Exception) as exc_info:
1219
        _ = reader.read(input_sdoc)
1220
 
1221
    assert exc_info.type is StrictDocException
1222
    assert exc_info.value.args[0] == (
1223
        "The node field STATUS is a reserved field and can only be written as "
1224
        "a single-line, not multiline, field."
1225
    )
1226
 
1227
 
1228
def test_crlf_single_line_field():
1229
    input_sdoc = (
1230
        "[DOCUMENT]\r\n"
1231
        "TITLE: Test Doc\r\n"
1232
        "\r\n"
1233
        "[REQUIREMENT]\r\n"
1234
        "STATEMENT: Test statement.\r\n"
1235
    )
1236
 
1237
    reader = SDReader()
1238
 
1239
    document = reader.read(input_sdoc)
1240
    assert isinstance(document, SDocDocument)
1241
    assert document.title == "Test Doc"
1242
    node = document.section_contents[0]
1243
    assert isinstance(node, SDocNode)
1244
    assert node.reserved_statement == "Test statement."
1245
 
1246
 
1247
def test_crlf_multiline_field():
1248
    input_sdoc = (
1249
        "[DOCUMENT]\r\n"
1250
        "TITLE: Test Doc\r\n"
1251
        "\r\n"
1252
        "[REQUIREMENT]\r\n"
1253
        "STATEMENT: >>>\r\n"
1254
        "Multiline content.\r\n"
1255
        "<<<\r\n"
1256
    )
1257
 
1258
    reader = SDReader()
1259
 
1260
    document = reader.read(input_sdoc)
1261
    assert isinstance(document, SDocDocument)
1262
    node = document.section_contents[0]
1263
    assert isinstance(node, SDocNode)
1264
    assert node.reserved_statement == "Multiline content.\r\n"
1265
 
1266
 
1267
def test_anchor_title_with_quoted_comma_roundtrip(default_project_config):
1268
    input_sdoc = """
1269
[DOCUMENT]
1270
TITLE: Test Doc
1271
 
1272
[TEXT]
1273
STATEMENT: >>>
1274
[ANCHOR: A1, "Title with, a comma"]
1275
<<<
1276
""".lstrip()
1277
 
1278
    reader = SDReader()
1279
 
1280
    document = reader.read(input_sdoc)
1281
    assert isinstance(document, SDocDocument)
1282
 
1283
    writer = SDWriter(default_project_config)
1284
    output = writer.write(document)
1285
 
1286
    assert input_sdoc == output
1287
 
1288
 
1289
def test_anchor_title_with_quoted_bracket_roundtrip(default_project_config):
1290
    input_sdoc = """
1291
[DOCUMENT]
1292
TITLE: Test Doc
1293
 
1294
[TEXT]
1295
STATEMENT: >>>
1296
[ANCHOR: A1, "Title [with] brackets"]
1297
<<<
1298
""".lstrip()
1299
 
1300
    reader = SDReader()
1301
 
1302
    document = reader.read(input_sdoc)
1303
    assert isinstance(document, SDocDocument)
1304
 
1305
    writer = SDWriter(default_project_config)
1306
    output = writer.write(document)
1307
 
1308
    assert input_sdoc == output