Skip to content

Commit 11a57e4

Browse files
committed
feat(backend/sdoc): support SingleChoice values with parentheses
Based on feedback from a user who generates `SingleChoice` values through ReqIF from Polarion, the reported examples are: `A(B)`, `B(C)`, `C(D)`, etc.
1 parent 6587f3f commit 11a57e4

5 files changed

Lines changed: 94 additions & 7 deletions

File tree

strictdoc/backend/sdoc/grammar/type_system.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
;
55
66
ChoiceOption[noskipws]:
7-
/[\w\/|-]+( *[\w\/|-]+)*/
7+
/(["])[^,]+\1|[^,()"]+/
88
;
99
1010
ChoiceOptionXs[noskipws]:

strictdoc/backend/sdoc/models/grammar_element.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,24 @@ def __init__(
8383
self.title: str = title
8484
self.human_title: Optional[str] = human_title
8585
self.gef_type = RequirementFieldType.SINGLE_CHOICE
86-
self.options: List[str] = options
86+
87+
processed_options = []
88+
for option_ in options:
89+
processed_options.append(option_.strip('"'))
90+
self.options: List[str] = processed_options
91+
8792
self.required: bool = required == "True"
8893
self.mid: MID = MID.create()
8994

95+
def get_unprocessed_options(self) -> List[str]:
96+
unprocessed_options = []
97+
for option_ in self.options:
98+
if any(char_ in option_ for char_ in ["(", ")"]):
99+
unprocessed_options.append('"' + option_ + '"')
100+
else:
101+
unprocessed_options.append(option_)
102+
return unprocessed_options
103+
90104

91105
@auto_described
92106
class GrammarElementFieldMultipleChoice(GrammarElementField):

strictdoc/backend/sdoc/writer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ def _print_grammar_field_type(
458458
elif isinstance(grammar_field, GrammarElementFieldSingleChoice):
459459
output += RequirementFieldType.SINGLE_CHOICE
460460
output += "("
461-
output += ", ".join(grammar_field.options)
461+
output += ", ".join(grammar_field.get_unprocessed_options())
462462
output += ")"
463463
elif isinstance(grammar_field, GrammarElementFieldMultipleChoice):
464464
output += RequirementFieldType.MULTIPLE_CHOICE

tests/integration/features/reqif/profiles/p01_sdoc/end_to_end/31_custom_grammar_single_choice_type/sample.sdoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ ELEMENTS:
88
- TITLE: CHOICE_FIELD
99
TYPE: SingleChoice(A, B, C, D)
1010
REQUIRED: False
11+
- TITLE: CHOICE_FIELD_2
12+
TYPE: SingleChoice("A(B)", "B(C)", "C(D)")
13+
REQUIRED: False
1114
- TITLE: STATEMENT
1215
TYPE: String
1316
REQUIRED: False
1417

1518
[REQUIREMENT]
1619
CHOICE_FIELD: A
20+
CHOICE_FIELD_2: A(B)

tests/unit/strictdoc/backend/sdoc/test_dsl_passthrough_grammar.py

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22
@relation(SDOC-SRS-136, scope=file)
33
"""
44

5+
import pytest
6+
57
from strictdoc.backend.sdoc.models.document import SDocDocument
68
from strictdoc.backend.sdoc.models.node import SDocNode
79
from strictdoc.backend.sdoc.reader import SDReader
810
from strictdoc.backend.sdoc.validations.sdoc_validator import SDocValidator
911
from strictdoc.backend.sdoc.writer import SDWriter
1012
from strictdoc.helpers.cast import assert_cast
13+
from strictdoc.helpers.exception import StrictDocException
1114
from tests.unit.helpers.fake_document_meta import create_fake_document_meta
1215

1316

14-
def test_150_grammar_minimal_doc(default_project_config):
17+
def test_01_grammar_minimal_doc(default_project_config):
1518
input_sdoc = """
1619
[DOCUMENT]
1720
TITLE: Test Doc
@@ -58,7 +61,7 @@ def test_150_grammar_minimal_doc(default_project_config):
5861
assert expected_sdoc == output
5962

6063

61-
def test_151_grammar_single_choice(default_project_config):
64+
def test_10_grammar_single_choice(default_project_config):
6265
input_sdoc = """
6366
[DOCUMENT]
6467
TITLE: Test Doc
@@ -81,27 +84,93 @@ def test_151_grammar_single_choice(default_project_config):
8184
- TITLE: SINGLE_CHOICE_FIELD_3
8285
TYPE: SingleChoice(Hardware test, Software test)
8386
REQUIRED: True
87+
- TITLE: SINGLE_CHOICE_FIELD_4
88+
TYPE: SingleChoice("A(B)", "B(C)")
89+
REQUIRED: True
8490
- TITLE: STATEMENT
8591
TYPE: String
8692
REQUIRED: False
8793
8894
[LOW_LEVEL_REQUIREMENT]
8995
SINGLE_CHOICE_FIELD: A
90-
SINGLE_CHOICE_FIELD: Test/Hardware
91-
SINGLE_CHOICE_FIELD: Hardware test
96+
SINGLE_CHOICE_FIELD_2: Test/Hardware
97+
SINGLE_CHOICE_FIELD_3: Hardware test
98+
SINGLE_CHOICE_FIELD_4: A(B)
9299
""".lstrip()
93100

94101
reader = SDReader()
95102

96103
document = reader.read(input_sdoc)
97104
assert isinstance(document, SDocDocument)
98105

106+
assert len(document.section_contents) == 1
107+
field_4 = document.grammar.elements_by_type["LOW_LEVEL_REQUIREMENT"].fields[
108+
3
109+
]
110+
assert field_4.options == ["A(B)", "B(C)"]
111+
99112
writer = SDWriter(default_project_config)
100113
output = writer.write(document)
101114

102115
assert input_sdoc == output
103116

104117

118+
def test_15_grammar_single_choice_with_branches_but_no_quotes_raises_exception():
119+
input_sdoc = """
120+
[DOCUMENT]
121+
TITLE: Test Doc
122+
123+
[GRAMMAR]
124+
ELEMENTS:
125+
- TAG: TEXT
126+
FIELDS:
127+
- TITLE: STATEMENT
128+
TYPE: String
129+
REQUIRED: True
130+
- TAG: LOW_LEVEL_REQUIREMENT
131+
FIELDS:
132+
- TITLE: SINGLE_CHOICE_FIELD
133+
TYPE: SingleChoice(A(B))
134+
REQUIRED: True
135+
- TITLE: STATEMENT
136+
TYPE: String
137+
REQUIRED: False
138+
""".lstrip()
139+
140+
reader = SDReader()
141+
142+
with pytest.raises(StrictDocException):
143+
_ = reader.read(input_sdoc)
144+
145+
146+
def test_16_grammar_single_choice_with_unmatched_quotes_raises_exception():
147+
input_sdoc = """
148+
[DOCUMENT]
149+
TITLE: Test Doc
150+
151+
[GRAMMAR]
152+
ELEMENTS:
153+
- TAG: TEXT
154+
FIELDS:
155+
- TITLE: STATEMENT
156+
TYPE: String
157+
REQUIRED: True
158+
- TAG: LOW_LEVEL_REQUIREMENT
159+
FIELDS:
160+
- TITLE: SINGLE_CHOICE_FIELD
161+
TYPE: SingleChoice("A)
162+
REQUIRED: True
163+
- TITLE: STATEMENT
164+
TYPE: String
165+
REQUIRED: False
166+
""".lstrip()
167+
168+
reader = SDReader()
169+
170+
with pytest.raises(StrictDocException):
171+
_ = reader.read(input_sdoc)
172+
173+
105174
def test_152_grammar_multiple_choice(default_project_config):
106175
input_sdoc = """\
107176
[DOCUMENT]

0 commit comments

Comments
 (0)