Commit | Line | Data |
---|---|---|
88004776 PP |
1 | # The MIT License (MIT) |
2 | # | |
3 | # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.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 pytest | |
25 | import os | |
26 | import os.path | |
27 | import barectf | |
28 | import shutil | |
29 | import subprocess | |
816fefd3 PP |
30 | import tempfile |
31 | ||
32 | ||
33 | def pytest_collect_file(parent, path): | |
34 | yaml_ext = '.yaml' | |
35 | ||
36 | if path.ext != yaml_ext: | |
37 | # not a YAML file: cancel | |
38 | return | |
39 | ||
40 | # At the end of this loop, if `path` is | |
41 | # `/home/jo/barectf/tests/tracing/configs/succeed/static-array/of-str.yaml`, | |
42 | # for example, then `elems` is: | |
43 | # | |
44 | # * `of-str.yaml` | |
45 | # * `static-array` | |
46 | # * `succeed` | |
47 | path_str = str(path) | |
48 | elems = [] | |
49 | ||
50 | while True: | |
51 | elem = os.path.basename(path_str) | |
52 | ||
53 | if elem == 'configs': | |
54 | break | |
55 | ||
56 | elems.append(elem) | |
57 | path_str = os.path.dirname(path_str) | |
58 | ||
59 | # create C source, expectation file, and support directory paths | |
60 | base_dir = os.path.dirname(path_str) | |
61 | base_name = elems[0].replace(yaml_ext, '') | |
62 | rel_dir = os.path.join(*list(reversed(elems[1:]))) | |
63 | src_path = os.path.join(*[base_dir, 'src', rel_dir, f'{base_name}.c']) | |
64 | data_expect_path = os.path.join(*([base_dir, 'expect', rel_dir, f'{base_name}.data.expect'])) | |
65 | metadata_expect_path = os.path.join(*([base_dir, 'expect', rel_dir, f'{base_name}.metadata.expect'])) | |
66 | support_dir_path = os.path.join(base_dir, 'support') | |
67 | ||
68 | # create a unique test name | |
69 | name = f'test-{"-".join(reversed(elems))}'.replace(yaml_ext, '') | |
70 | ||
71 | # create the file node | |
72 | return _YamlFile.from_parent(parent, fspath=path, src_path=src_path, | |
73 | data_expect_path=data_expect_path, | |
74 | metadata_expect_path=metadata_expect_path, | |
75 | support_dir_path=support_dir_path, name=name) | |
76 | ||
77 | ||
78 | class _YamlFile(pytest.File): | |
79 | def __init__(self, parent, fspath, src_path, data_expect_path, metadata_expect_path, | |
80 | support_dir_path, name): | |
81 | super().__init__(parent=parent, fspath=fspath) | |
82 | self._name = name | |
83 | self._src_path = src_path | |
84 | self._data_expect_path = data_expect_path | |
85 | self._metadata_expect_path = metadata_expect_path | |
86 | self._support_dir_path = support_dir_path | |
87 | ||
88 | def collect(self): | |
89 | # yield a single item | |
90 | yield _YamlItem.from_parent(self, name=self._name, src_path=self._src_path, | |
91 | data_expect_path=self._data_expect_path, | |
92 | metadata_expect_path=self._metadata_expect_path, | |
93 | support_dir_path=self._support_dir_path) | |
94 | ||
95 | ||
96 | class _YamlItem(pytest.Item): | |
97 | def __init__(self, parent, name, src_path, data_expect_path, metadata_expect_path, | |
98 | support_dir_path): | |
99 | super().__init__(parent=parent, name=name) | |
100 | self._src_path = src_path | |
101 | self._data_expect_path = data_expect_path | |
102 | self._metadata_expect_path = metadata_expect_path | |
103 | self._support_dir_path = support_dir_path | |
104 | ||
105 | def runtest(self): | |
106 | # create a temporary directory | |
107 | tmpdir = tempfile.TemporaryDirectory(prefix='pytest-barectf') | |
88004776 PP |
108 | |
109 | # create barectf configuration | |
816fefd3 | 110 | with open(self.fspath) as f: |
88004776 PP |
111 | cfg = barectf.configuration_from_file(f) |
112 | ||
113 | # generate and write C code files | |
114 | cg = barectf.CodeGenerator(cfg) | |
115 | files = cg.generate_c_headers() | |
116 | files += cg.generate_c_sources() | |
117 | ||
118 | for file in files: | |
816fefd3 | 119 | with open(os.path.join(tmpdir.name, file.name), 'w') as f: |
88004776 PP |
120 | f.write(file.contents) |
121 | ||
122 | # generate metadata stream, stripping the version and date | |
123 | file = cg.generate_metadata_stream() | |
124 | lines = file.contents.split('\n') | |
125 | new_lines = [] | |
126 | discard_patterns = [ | |
127 | 'Copyright (c)', | |
128 | 'The following code was generated', | |
129 | '* on ', | |
130 | 'barectf_gen_date =', | |
131 | 'tracer_major =', | |
132 | 'tracer_minor =', | |
133 | 'tracer_patch =', | |
134 | ] | |
135 | ||
136 | for line in lines: | |
137 | skip = False | |
138 | ||
139 | for pattern in discard_patterns: | |
140 | if pattern in line: | |
141 | skip = True | |
142 | ||
143 | if skip: | |
144 | continue | |
145 | ||
146 | new_lines.append(line) | |
147 | ||
148 | actual_metadata = '\n'.join(new_lines) | |
149 | ||
150 | # copy Makefile to build directory | |
816fefd3 | 151 | shutil.copy(os.path.join(self._support_dir_path, 'Makefile'), tmpdir.name) |
88004776 PP |
152 | |
153 | # copy platform files to build directory | |
816fefd3 PP |
154 | shutil.copy(os.path.join(self._support_dir_path, 'test-platform.c'), tmpdir.name) |
155 | shutil.copy(os.path.join(self._support_dir_path, 'test-platform.h'), tmpdir.name) | |
88004776 PP |
156 | |
157 | # copy specific source code file to build directory | |
816fefd3 | 158 | shutil.copy(self._src_path, os.path.join(tmpdir.name, 'test.c')) |
88004776 PP |
159 | |
160 | # build the test | |
816fefd3 | 161 | subprocess.check_output(['make'], cwd=tmpdir.name) |
88004776 PP |
162 | |
163 | # run the test (produce the data stream) | |
816fefd3 | 164 | subprocess.check_output(['./test'], cwd=tmpdir.name) |
88004776 PP |
165 | |
166 | # read actual stream | |
816fefd3 | 167 | with open(os.path.join(tmpdir.name, 'stream'), 'rb') as f: |
88004776 PP |
168 | actual_stream = f.read() |
169 | ||
170 | # read data stream expectation file | |
816fefd3 | 171 | with open(self._data_expect_path, 'rb') as f: |
88004776 PP |
172 | expected_stream = f.read() |
173 | ||
174 | # read metadata stream expectation file | |
816fefd3 | 175 | with open(self._metadata_expect_path, 'r') as f: |
88004776 PP |
176 | expected_metadata = f.read() |
177 | ||
178 | # validate streams | |
179 | assert actual_metadata == expected_metadata | |
180 | assert actual_stream == expected_stream | |
181 | ||
816fefd3 PP |
182 | # delete temporary directory |
183 | tmpdir.cleanup() | |
184 | ||
185 | def repr_failure(self, excinfo, style=None): | |
186 | return f'`{self.fspath}` failed: {excinfo}.' | |
187 | ||
188 | def reportinfo(self): | |
189 | return self.fspath, None, self.name |