StrictDoc Documentation
tests/unit/strictdoc/backend/sdoc_source_code/test_reports/test_junit_xml_reader_ctest.py
Source file coverage
Path:
tests/unit/strictdoc/backend/sdoc_source_code/test_reports/test_junit_xml_reader_ctest.py
Lines:
258
Non-empty lines:
231
Non-empty lines covered with requirements:
231 / 231 (100.0%)
Functions:
6
Functions covered by requirements:
6 / 6 (100.0%)
1
"""
2
@relation(SDOC-SRS-143, scope=file)
3
"""
4
 
5
import os
6
import tempfile
7
 
8
import pytest
9
 
10
from strictdoc.backend.sdoc.models.document import SDocDocument
11
from strictdoc.backend.sdoc.models.node import SDocNode
12
from strictdoc.backend.sdoc_source_code.test_reports.junit_xml_reader import (
13
    JUnitXMLReader,
14
)
15
from strictdoc.core.file_system.file_tree import File
16
from strictdoc.core.project_config import ProjectConfig
17
from strictdoc.helpers.cast import assert_cast
18
from strictdoc.helpers.paths import SDocRelativePath
19
 
20
 
21
def test_01_ctest():
22
    source_input = """
23
<?xml version="1.0" encoding="UTF-8"?>
24
<testsuite name="(empty)"
25
	tests="8"
26
	failures="0"
27
	disabled="0"
28
	skipped="0"
29
	hostname=""
30
	time="0"
31
	timestamp="2025-03-10T10:30:39"
32
	>
33
	<testcase name="TestPrtMath.TransitionDistance" classname="TestPrtMath.TransitionDistance" time="0.023645" status="run">
34
		<properties/>
35
		<system-out>xxxxx
36
</system-out>
37
	</testcase>
38
</testsuite>
39
""".lstrip()
40
 
41
    project_config: ProjectConfig = ProjectConfig.default_config()
42
 
43
    with tempfile.NamedTemporaryFile(
44
        mode="w+", delete=True, suffix=".ctest.junit.xml"
45
    ) as temp_file:
46
        doc_file: File = File(
47
            0,
48
            temp_file.name,
49
            SDocRelativePath(os.path.basename(temp_file.name)),
50
        )
51
        document: SDocDocument = JUnitXMLReader.read_from_string(
52
            source_input, doc_file, project_config
53
        )
54
        assert len(document.section_contents) == 1
55
        assert len(document.section_contents[0].section_contents) == 2
56
 
57
        test_result_node: SDocNode = assert_cast(
58
            document.section_contents[0].section_contents[1], SDocNode
59
        )
60
        assert (
61
            test_result_node.get_meta_field_value_by_title("TEST_FUNCTION")
62
            == "#GTEST#TestPrtMath.TransitionDistance"
63
        )
64
 
65
 
66
def test_02_testcase_properties_are_mapped_to_fields():
67
    source_input = """\
68
<?xml version="1.0" encoding="UTF-8"?>
69
<testsuite name="Test suite" tests="1" failures="0" skipped="0">
70
  <testcase name="TestSuite.Test1"
71
            classname="TestSuite.Test1"
72
            time="0.1">
73
    <properties>
74
      <property name="requirement" value="REQ1"/>
75
      <property name="strictdoc:image" value="test1.png"/>
76
    </properties>
77
  </testcase>
78
</testsuite>
79
"""
80
 
81
    project_config: ProjectConfig = ProjectConfig.default_config()
82
 
83
    with tempfile.NamedTemporaryFile(
84
        mode="w+", delete=True, suffix=".ctest.junit.xml"
85
    ) as temp_file:
86
        doc_file: File = File(
87
            0,
88
            temp_file.name,
89
            SDocRelativePath(os.path.basename(temp_file.name)),
90
        )
91
        document: SDocDocument = JUnitXMLReader.read_from_string(
92
            source_input, doc_file, project_config
93
        )
94
 
95
        test_result_grammar = document.grammar.elements_by_type["TEST_RESULT"]
96
        assert (
97
            test_result_grammar.fields_map["REQUIREMENT"].human_title
98
            == "requirement"
99
        )
100
        assert (
101
            test_result_grammar.fields_map["STRICTDOC_IMAGE"].human_title
102
            == "strictdoc:image"
103
        )
104
        assert not test_result_grammar.is_field_multiline("REQUIREMENT")
105
        assert not test_result_grammar.is_field_multiline("STRICTDOC_IMAGE")
106
 
107
        test_result_node: SDocNode = assert_cast(
108
            document.section_contents[0].section_contents[1], SDocNode
109
        )
110
        assert (
111
            test_result_node.get_meta_field_value_by_title("REQUIREMENT")
112
            == "REQ1"
113
        )
114
        assert (
115
            test_result_node.get_meta_field_value_by_title("STRICTDOC_IMAGE")
116
            == "test1.png"
117
        )
118
 
119
 
120
def test_03_property_names_may_repeat_across_testcases():
121
    source_input = """\
122
<?xml version="1.0" encoding="UTF-8"?>
123
<testsuite name="Test suite" tests="2" failures="0" skipped="0">
124
  <testcase name="TestSuite.Test1"
125
            classname="TestSuite.Test1"
126
            time="0.1">
127
    <properties>
128
      <property name="requirement" value="REQ1"/>
129
    </properties>
130
  </testcase>
131
  <testcase name="TestSuite.Test2"
132
            classname="TestSuite.Test2"
133
            time="0.1">
134
    <properties>
135
      <property name="requirement" value="REQ2"/>
136
    </properties>
137
  </testcase>
138
</testsuite>
139
"""
140
 
141
    document: SDocDocument = _read_ctest_source(source_input)
142
    first_test_result: SDocNode = assert_cast(
143
        document.section_contents[0].section_contents[1], SDocNode
144
    )
145
    second_test_result: SDocNode = assert_cast(
146
        document.section_contents[0].section_contents[2], SDocNode
147
    )
148
    assert (
149
        first_test_result.get_meta_field_value_by_title("REQUIREMENT") == "REQ1"
150
    )
151
    assert (
152
        second_test_result.get_meta_field_value_by_title("REQUIREMENT")
153
        == "REQ2"
154
    )
155
 
156
 
157
@pytest.mark.parametrize(
158
    ("property_xml", "expected_error"),
159
    [
160
        (
161
            '<property name="requirement--id" value="REQ1"/>',
162
            (
163
                "JUnit property name cannot be mapped to a StrictDoc field "
164
                "name: 'requirement--id'."
165
            ),
166
        ),
167
        (
168
            '<property name="status" value="ignored"/>',
169
            (
170
                "JUnit property name maps to a reserved TEST_RESULT field: "
171
                "'status' -> 'STATUS'."
172
            ),
173
        ),
174
        (
175
            """\
176
<property name="strictdoc:image" value="test1.png"/>
177
<property name="strictdoc-image" value="test2.png"/>\
178
""",
179
            (
180
                "JUnit property names map to the same StrictDoc field: "
181
                "'strictdoc:image', 'strictdoc-image' -> "
182
                "'STRICTDOC_IMAGE'."
183
            ),
184
        ),
185
        (
186
            """\
187
<property name="requirement" value="REQ1"/>
188
<property name="requirement" value="REQ2"/>\
189
""",
190
            ("JUnit testcase contains duplicate property name: 'requirement'."),
191
        ),
192
        (
193
            '<property value="REQ1"/>',
194
            "JUnit property is missing required 'name' attribute.",
195
        ),
196
        (
197
            '<property name="requirement"/>',
198
            "JUnit property is missing required 'value' attribute.",
199
        ),
200
    ],
201
)
202
def test_04_invalid_testcase_properties_are_rejected(
203
    property_xml: str, expected_error: str
204
):
205
    source_input = f"""\
206
<?xml version="1.0" encoding="UTF-8"?>
207
<testsuite name="Test suite" tests="1" failures="0" skipped="0">
208
  <testcase name="TestSuite.Test1"
209
            classname="TestSuite.Test1"
210
            time="0.1">
211
    <properties>
212
      {property_xml}
213
    </properties>
214
  </testcase>
215
</testsuite>
216
"""
217
 
218
    with pytest.raises(RuntimeError) as exc_info:
219
        _ = _read_ctest_source(source_input)
220
    assert expected_error == str(exc_info.value)
221
 
222
 
223
def test__90__error_handling__empty_xml():
224
    source_input = ""
225
 
226
    project_config: ProjectConfig = ProjectConfig.default_config()
227
 
228
    with tempfile.NamedTemporaryFile(
229
        mode="w+", delete=True, suffix=".ctest.junit.xml"
230
    ) as temp_file:
231
        doc_file: File = File(
232
            0,
233
            temp_file.name,
234
            SDocRelativePath(os.path.basename(temp_file.name)),
235
        )
236
        with pytest.raises(RuntimeError) as exc_info:
237
            _ = JUnitXMLReader.read_from_string(
238
                source_input, doc_file, project_config
239
            )
240
        assert """\
241
Document is empty, line 1, column 1 (<string>, line 1)\
242
""" == str(exc_info.value)
243
 
244
 
245
def _read_ctest_source(source_input: str) -> SDocDocument:
246
    project_config: ProjectConfig = ProjectConfig.default_config()
247
 
248
    with tempfile.NamedTemporaryFile(
249
        mode="w+", delete=True, suffix=".ctest.junit.xml"
250
    ) as temp_file:
251
        doc_file: File = File(
252
            0,
253
            temp_file.name,
254
            SDocRelativePath(os.path.basename(temp_file.name)),
255
        )
256
        return JUnitXMLReader.read_from_string(
257
            source_input, doc_file, project_config
258
        )