[docs] 1# @Test suite for one-line comment parser functionality, TEST_OLP_1, test, [IMPL_OLP_1]
2import pytest
3
4from sphinx_codelinks.analyse.oneline_parser import (
5 OnelineParserInvalidWarning,
6 WarningSubTypeEnum,
7 oneline_parser,
8)
9from sphinx_codelinks.config import ESCAPE, UNIX_NEWLINE, OneLineCommentStyle
10
11from .conftest import ONELINE_COMMENT_STYLE, ONELINE_COMMENT_STYLE_DEFAULT
12
13
14@pytest.mark.parametrize(
15 "oneline, result",
16 [
17 (
18 f"@title 1, IMPL_1 {UNIX_NEWLINE}",
19 {
20 "title": "title 1",
21 "id": "IMPL_1",
22 "type": "impl",
23 "links": [],
24 "start_column": 1,
25 "end_column": 17,
26 },
27 ),
28 # Test case for leading space after start sequence (bug fix)
29 (
30 f"@ title 1, IMPL_1 {UNIX_NEWLINE}",
31 {
32 "title": "title 1",
33 "id": "IMPL_1",
34 "type": "impl",
35 "links": [],
36 "start_column": 1,
37 "end_column": 18,
38 },
39 ),
40 # Test case for multiple leading spaces after start sequence
41 (
42 f"@ title 1, IMPL_1 {UNIX_NEWLINE}",
43 {
44 "title": "title 1",
45 "id": "IMPL_1",
46 "type": "impl",
47 "links": [],
48 "start_column": 1,
49 "end_column": 20,
50 },
51 ),
52 # Test case for trailing space before end sequence
53 (
54 f"@title 1, IMPL_1 {UNIX_NEWLINE}",
55 {
56 "title": "title 1",
57 "id": "IMPL_1",
58 "type": "impl",
59 "links": [],
60 "start_column": 1,
61 "end_column": 18,
62 },
63 ),
64 # Test case for both leading and trailing spaces
65 (
66 f"@ title 1, IMPL_1 {UNIX_NEWLINE}",
67 {
68 "title": "title 1",
69 "id": "IMPL_1",
70 "type": "impl",
71 "links": [],
72 "start_column": 1,
73 "end_column": 21,
74 },
75 ),
76 ],
77)
78def test_oneline_parser_default_config_positive(
79 oneline: str, result: dict[str, str | list[str]]
80) -> None:
81 oneline_need = oneline_parser(oneline, ONELINE_COMMENT_STYLE_DEFAULT)
82 assert oneline_need == result
83
84
85# Test case for space as field separator (as mentioned by Kilian)
86# Example: @Implementation <field1> <field2>
87ONELINE_COMMENT_STYLE_SPACE_SEPARATOR = OneLineCommentStyle(
88 start_sequence="@",
89 end_sequence=UNIX_NEWLINE,
90 field_split_char=" ",
91 needs_fields=[
92 {"name": "title"},
93 {"name": "id"},
94 {"name": "type", "default": "impl"},
95 ],
96)
97
98
99@pytest.mark.parametrize(
100 "oneline, result",
101 [
102 # Basic space-separated fields
103 (
104 f"@Implementation IMPL_1{UNIX_NEWLINE}",
105 {
106 "title": "Implementation",
107 "id": "IMPL_1",
108 "type": "impl",
109 "start_column": 1,
110 "end_column": 22,
111 },
112 ),
113 # Space separator with explicit type
114 (
115 f"@MyFeature FEAT_001 feature{UNIX_NEWLINE}",
116 {
117 "title": "MyFeature",
118 "id": "FEAT_001",
119 "type": "feature",
120 "start_column": 1,
121 "end_column": 27,
122 },
123 ),
124 # Leading space after @ (the bug Kilian reported)
125 (
126 f"@ Implementation IMPL_2{UNIX_NEWLINE}",
127 {
128 "title": "Implementation",
129 "id": "IMPL_2",
130 "type": "impl",
131 "start_column": 1,
132 "end_column": 23,
133 },
134 ),
135 # Trailing space before newline
136 (
137 f"@Implementation IMPL_3 {UNIX_NEWLINE}",
138 {
139 "title": "Implementation",
140 "id": "IMPL_3",
141 "type": "impl",
142 "start_column": 1,
143 "end_column": 23,
144 },
145 ),
146 # Multiple leading spaces after @
147 (
148 f"@ Title ID_456{UNIX_NEWLINE}",
149 {
150 "title": "Title",
151 "id": "ID_456",
152 "type": "impl",
153 "start_column": 1,
154 "end_column": 15,
155 },
156 ),
157 # Title contain spaces
158 (
159 f"@ Title\ escape\ space ID_456{UNIX_NEWLINE}",
160 {
161 "title": "Title escape space",
162 "id": "ID_456",
163 "type": "impl",
164 "start_column": 1,
165 "end_column": 30,
166 },
167 ),
168 ],
169)
170def test_oneline_parser_space_separator(
171 oneline: str, result: dict[str, str | list[str]]
172) -> None:
173 """Test oneline parser with space as field separator."""
174 oneline_need = oneline_parser(oneline, ONELINE_COMMENT_STYLE_SPACE_SEPARATOR)
175 assert oneline_need == result
176
177
178@pytest.mark.parametrize(
179 "oneline, result",
180 [
181 (
182 "[[IMPL_1, title 1]]",
183 {
184 "id": "IMPL_1",
185 "title": "title 1",
186 "type": "impl",
187 "links": [],
188 "status": "open",
189 "priority": "low",
190 "start_column": 2,
191 "end_column": 17,
192 },
193 ),
194 (
195 "[[IMPL_2, title 2, impl, [], closed]]",
196 {
197 "id": "IMPL_2",
198 "title": "title 2",
199 "type": "impl",
200 "links": [],
201 "status": "closed",
202 "priority": "low",
203 "start_column": 2,
204 "end_column": 35,
205 },
206 ),
207 (
208 "[[IMPL_3, title\, 3, impl, [], closed]]",
209 {
210 "id": "IMPL_3",
211 "title": "title, 3",
212 "type": "impl",
213 "links": [],
214 "status": "closed",
215 "priority": "low",
216 "start_column": 2,
217 "end_column": 37,
218 },
219 ),
220 (
221 "[[IMPL_5, title 5, impl, [SPEC_1, SPEC_2], open]]",
222 {
223 "id": "IMPL_5",
224 "title": "title 5",
225 "type": "impl",
226 "links": ["SPEC_1", "SPEC_2"],
227 "status": "open",
228 "priority": "low",
229 "start_column": 2,
230 "end_column": 47,
231 },
232 ),
233 (
234 "[[IMPL_7, Function has a, in the title]]",
235 {
236 "id": "IMPL_7",
237 "title": "Function has a",
238 "type": "in the title",
239 "links": [],
240 "status": "open",
241 "priority": "low",
242 "start_column": 2,
243 "end_column": 38,
244 },
245 ),
246 (
247 "[[IMPL_8, [Title starts with a bracket], impl]]",
248 {
249 "id": "IMPL_8",
250 "title": "[Title starts with a bracket]",
251 "type": "impl",
252 "links": [],
253 "status": "open",
254 "priority": "low",
255 "start_column": 2,
256 "end_column": 45,
257 },
258 ),
259 (
260 "[[IMPL_9, Function Baz, impl, [SPEC_1, SPEC_2[text], SPEC_3], open]]",
261 {
262 "id": "IMPL_9",
263 "title": "Function Baz",
264 "type": "impl",
265 "links": ["SPEC_1", "SPEC_2[text"],
266 "status": "SPEC_3]",
267 "priority": "open",
268 "start_column": 2,
269 "end_column": 66,
270 },
271 ),
272 (
273 "[[IMPL_10, title 10, impl, [SPEC_1], open]]",
274 {
275 "id": "IMPL_10",
276 "title": "title 10",
277 "type": "impl",
278 "links": ["SPEC_1"],
279 "status": "open",
280 "priority": "low",
281 "start_column": 2,
282 "end_column": 41,
283 },
284 ),
285 (
286 "[[IMPL_11, title 11, impl, [SPEC\,_1], open]]",
287 {
288 "id": "IMPL_11",
289 "title": "title 11",
290 "type": "impl",
291 "links": ["SPEC,_1"],
292 "status": "open",
293 "priority": "low",
294 "start_column": 2,
295 "end_column": 43,
296 },
297 ),
298 (
299 "[[IMPL_12, title 12, impl, [\[SPEC\,_1\]], open]]",
300 {
301 "id": "IMPL_12",
302 "title": "title 12",
303 "type": "impl",
304 "links": ["[SPEC,_1]"],
305 "status": "open",
306 "priority": "low",
307 "start_column": 2,
308 "end_column": 47,
309 },
310 ),
311 (
312 "[[IMPL_13, title\\ 13, impl, [\[SPEC\,_1\]], open]]",
313 {
314 "id": "IMPL_13",
315 "title": "title\ 13",
316 "type": "impl",
317 "links": ["[SPEC,_1]"],
318 "status": "open",
319 "priority": "low",
320 "start_column": 2,
321 "end_column": 48,
322 },
323 ),
324 ],
325)
326def test_oneline_parser_custom_config_positive(
327 oneline: str, result: dict[str, str | list[str]]
328) -> None:
329 oneline_need = oneline_parser(oneline, ONELINE_COMMENT_STYLE)
330 assert oneline_need == result
331
332
333@pytest.mark.parametrize(
334 "oneline, result",
335 [
336 (
337 f"[[IMPL_4, title{ESCAPE}{ESCAPE}, 4, impl, [], closed]]",
338 OnelineParserInvalidWarning(
339 sub_type=WarningSubTypeEnum.missing_square_brackets,
340 msg="Field links with 'type': 'list[str]' must be given with '[]' brackets",
341 ),
342 ),
343 (
344 "[[IMPL_2, Function Bar, impl, [SPEC_1, SPEC_2, open]]",
345 OnelineParserInvalidWarning(
346 sub_type=WarningSubTypeEnum.missing_square_brackets,
347 msg="Field links with 'type': 'list[str]' must be given with '[]' brackets",
348 ),
349 ),
350 (
351 "[[IMPL_13, title 13, impl, 13[\[SPEC\,_1\]], open]]",
352 OnelineParserInvalidWarning(
353 sub_type=WarningSubTypeEnum.not_start_or_end_with_square_brackets,
354 msg="Field links with 'type': 'list[str]' must start with '[' and end with ']'",
355 ),
356 ),
357 (
358 "[[IMPL_14, title 13, impl, 13[\[SPEC\,_1\]], open, low, high]]",
359 OnelineParserInvalidWarning(
360 sub_type=WarningSubTypeEnum.too_many_fields,
361 msg="7 given fields. They shall be less than 6",
362 ),
363 ),
364 (
365 "[[IMPL_15]]",
366 OnelineParserInvalidWarning(
367 sub_type=WarningSubTypeEnum.too_few_fields,
368 msg="1 given fields. They shall be more than 2",
369 ),
370 ),
371 (
372 f"[[IMPL_16]]{UNIX_NEWLINE}, title 16]]",
373 OnelineParserInvalidWarning(
374 sub_type=WarningSubTypeEnum.newline_in_field,
375 msg="Field id has newline character. It is not allowed",
376 ),
377 ),
378 ],
379)
380def test_oneline_parser_custom_config_negative(
381 oneline: str, result: OnelineParserInvalidWarning
382) -> None:
383 res = oneline_parser(oneline, ONELINE_COMMENT_STYLE)
384 assert res == result
385
386
387@pytest.mark.parametrize(
388 "oneline, result",
389 [
390 (
391 f"@title 17]]{UNIX_NEWLINE}, IMPL_17 {UNIX_NEWLINE}",
392 OnelineParserInvalidWarning(
393 sub_type=WarningSubTypeEnum.newline_in_field,
394 msg="Field title has newline character. It is not allowed",
395 ),
396 ),
397 (
398 f"@title 17]], IMPL_17, impl, [SPEC_3, SPEC_4{UNIX_NEWLINE} ] {UNIX_NEWLINE}",
399 OnelineParserInvalidWarning(
400 sub_type=WarningSubTypeEnum.newline_in_field,
401 msg="Field links has newline character. It is not allowed",
402 ),
403 ),
404 ],
405)
406def test_oneline_parser_default_config_negative(
407 oneline: str, result: OnelineParserInvalidWarning
408) -> None:
409 assert oneline_parser(oneline, ONELINE_COMMENT_STYLE_DEFAULT) == result
410
411
412@pytest.mark.parametrize(
413 "oneline_config, result",
414 [
415 (
416 OneLineCommentStyle(
417 start_sequence="[[",
418 end_sequence="]]",
419 field_split_char=",",
420 needs_fields=[
421 {"name": "title"},
422 {"name": "id"},
423 {"name": "type", "default": "impl"},
424 {"name": "links", "type": "list[]", "default": []}, # wrong type
425 ],
426 ),
427 [
428 "Schema validation error in need_fields 'links': 'list[]' is not one of ['str', 'list[str]']"
429 ],
430 ),
431 (
432 OneLineCommentStyle(
433 start_sequence="[[",
434 end_sequence="]]",
435 field_split_char=",",
436 needs_fields=[
437 {"name": "title"},
438 {"name": "id"},
439 {"name": "type", "default": 123}, # int is invalid
440 {"name": "links", "type": "list[str]", "default": []},
441 ],
442 ),
443 [
444 "Schema validation error in need_fields 'type': 123 is not of type 'string'"
445 ],
446 ),
447 (
448 OneLineCommentStyle(
449 start_sequence="[[",
450 end_sequence="]]",
451 field_split_char=",",
452 needs_fields=[
453 {"name": "title", "qwe": "qwe"}, # invalid qwe filed
454 {"name": "id"},
455 {"name": "type", "default": "impl"},
456 {"name": "links", "type": "list[str]", "default": []},
457 ],
458 ),
459 [
460 "Schema validation error in need_fields 'title': Additional properties are not allowed ('qwe' was unexpected)"
461 ],
462 ),
463 (
464 OneLineCommentStyle(
465 start_sequence="[[",
466 end_sequence="]]",
467 field_split_char=",",
468 needs_fields=[
469 {"name": "title"},
470 {"name": "id"},
471 {
472 "name": "type",
473 "type: ": "list[str]",
474 "default": "impl",
475 }, # wring combination of type and default
476 {"name": "links", "type": "list[str]", "default": []},
477 ],
478 ),
479 [
480 "Schema validation error in need_fields 'type': Additional properties are not allowed ('type: ' was unexpected)"
481 ],
482 ),
483 (
484 OneLineCommentStyle(
485 start_sequence="[[",
486 end_sequence="]]",
487 field_split_char=",",
488 needs_fields=[
489 {"name": "id"} # "title" and "type" are not given
490 ],
491 ),
492 ["Missing required fields: ['title', 'type']"],
493 ),
494 (
495 OneLineCommentStyle(
496 start_sequence="[[",
497 end_sequence="]]",
498 field_split_char=",",
499 needs_fields=[
500 {"name": "id"},
501 {"name": "id"}, # duplicate
502 ],
503 ),
504 [
505 "Missing required fields: ['title', 'type']",
506 "Field 'id' is defined multiple times.",
507 ],
508 ),
509 (
510 OneLineCommentStyle(
511 start_sequence=1234, # wrong type
512 end_sequence=5678,
513 field_split_char=2222,
514 needs_fields=[
515 {"name": "id"},
516 ],
517 ),
518 [
519 "Schema validation error in field 'field_split_char': 2222 is not of type 'string'",
520 "Schema validation error in field 'end_sequence': 5678 is not of type 'string'",
521 "Schema validation error in field 'start_sequence': 1234 is not of type 'string'",
522 "Missing required fields: ['title', 'type']",
523 ],
524 ),
525 ],
526)
527def test_oneline_schema_validator_negative(oneline_config, result):
528 errors = oneline_config.check_fields_configuration()
529 assert sorted(errors) == sorted(result)