0c90e2a6c8b3e7a57f4136ef0e06127f7d567a53
[normand.git] / tests / conftest.py
1 # The MIT License (MIT)
2 #
3 # Copyright (c) 2023 Philippe Proulx <eeppeliteloop@gmail.com>
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
12 #
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 import re
25
26 import pytest
27
28 import normand
29
30
31 def pytest_collect_file(parent, file_path):
32 ext = ".nt"
33
34 if file_path.suffix != ext:
35 # Not a Normand test file: cancel
36 return
37
38 test_name = "test-{}".format(file_path.name.replace(ext, ""))
39
40 # Create the file node
41 if file_path.name.startswith("fail-"):
42 return _NormandTestFileFail.from_parent(parent, path=file_path, name=test_name)
43 elif file_path.name.startswith("pass-"):
44 return _NormandTestFilePass.from_parent(parent, path=file_path, name=test_name)
45 else:
46 # `.nt` file isn't a test case
47 return
48
49
50 def _split_nt_file(path):
51 normand_lines = []
52 output_lines = []
53 cur_lines = normand_lines
54
55 with open(path) as f:
56 for line in f:
57 if line.rstrip() == "---" and len(output_lines) == 0:
58 cur_lines = output_lines
59 continue
60
61 cur_lines.append(line)
62
63 return "".join(normand_lines), "".join(output_lines).strip()
64
65
66 class _NormandTestItem(pytest.Item):
67 def runtest(self):
68 self._runtest(*_split_nt_file(self.path))
69
70 def reportinfo(self):
71 return self.path, None, self.name
72
73
74 class _NormandTestItemFail(_NormandTestItem):
75 def _runtest(self, normand_text, output):
76 with pytest.raises(normand.ParseError) as exc_info:
77 normand.parse(normand_text)
78
79 exc = exc_info.value
80 expected_msg = ''
81
82 for msg in reversed(exc.messages):
83 expected_msg += "{}:{} - {}\n".format(
84 msg.text_location.line_no, msg.text_location.col_no, msg.text
85 )
86
87 assert output.strip() == expected_msg.strip()
88
89
90 class _NormandTestItemPass(_NormandTestItem):
91 @staticmethod
92 def _data_from_output(output):
93 hex_bytes = re.split(r"\s+", output.strip())
94 return bytearray([int(b, 16) for b in hex_bytes])
95
96 def _runtest(self, normand_text, output):
97 assert normand.parse(normand_text).data == self._data_from_output(output)
98
99
100 class _NormandTestFile(pytest.File):
101 def __init__(self, name, **kwargs):
102 super().__init__(**kwargs)
103 self._name = name
104
105 def collect(self):
106 # Yield a single item
107 yield self._item_cls.from_parent(self, name=self._name)
108
109
110 class _NormandTestFileFail(_NormandTestFile):
111 _item_cls = _NormandTestItemFail
112
113
114 class _NormandTestFilePass(_NormandTestFile):
115 _item_cls = _NormandTestItemPass
This page took 0.038194 seconds and 3 git commands to generate.