Commit | Line | Data |
---|---|---|
bf8f3b38 PP |
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 | |
ee724c95 | 80 | expected_msg = "" |
f5dcb24c PP |
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() | |
bf8f3b38 PP |
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 |