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