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