tests: Make tests aware of meson test wrapper
[mesa.git] / src / compiler / glsl / glcpp / tests / glcpp_test.py
1 # encoding=utf-8
2 # Copyright © 2018 Intel Corporation
3
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
10
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
13
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 # SOFTWARE.
21
22 """Run glcpp tests with various line endings."""
23
24 from __future__ import print_function
25 import argparse
26 import difflib
27 import errno
28 import io
29 import os
30 import subprocess
31 import sys
32 import tempfile
33
34 # The meson version handles windows paths better, but if it's not available
35 # fall back to shlex
36 try:
37 from meson.mesonlib import split_args
38 except ImportError:
39 from shlex import split as split_args
40
41
42 def arg_parser():
43 parser = argparse.ArgumentParser()
44 parser.add_argument('glcpp', help='Path to the he glcpp binary.')
45 parser.add_argument('testdir', help='Path to tests and expected output.')
46 parser.add_argument('--unix', action='store_true', help='Run tests for Unix style newlines')
47 parser.add_argument('--windows', action='store_true', help='Run tests for Windows/Dos style newlines')
48 parser.add_argument('--oldmac', action='store_true', help='Run tests for Old Mac (pre-OSX) style newlines')
49 parser.add_argument('--bizarro', action='store_true', help='Run tests for Bizarro world style newlines')
50 parser.add_argument('--valgrind', action='store_true', help='Run with valgrind for errors')
51 return parser.parse_args()
52
53
54 def parse_test_file(filename, nl_format):
55 """Check for any special arguments and return them as a list."""
56 # Disable "universal newlines" mode; we can't directly use `nl_format` as
57 # the `newline` argument, because the "bizarro" test uses something Python
58 # considers invalid.
59 with io.open(filename, newline='') as f:
60 for l in f.read().split(nl_format):
61 if 'glcpp-args:' in l:
62 return l.split('glcpp-args:')[1].strip().split()
63 return []
64
65
66 def test_output(glcpp, filename, expfile, nl_format='\n'):
67 """Test that the output of glcpp is what we expect."""
68 extra_args = parse_test_file(filename, nl_format)
69
70 with open(filename, 'rb') as f:
71 proc = subprocess.Popen(
72 glcpp + extra_args,
73 stdout=subprocess.PIPE,
74 stderr=subprocess.STDOUT,
75 stdin=subprocess.PIPE)
76 actual, _ = proc.communicate(f.read())
77 actual = actual.decode('utf-8')
78
79 with open(expfile, 'r') as f:
80 expected = f.read()
81
82 if actual == expected:
83 return (True, [])
84 return (False, difflib.unified_diff(actual.splitlines(), expected.splitlines()))
85
86
87 def _valgrind(glcpp, filename):
88 """Run valgrind and report any warnings."""
89 extra_args = parse_test_file(filename, nl_format='\n')
90
91 try:
92 fd, tmpfile = tempfile.mkstemp()
93 os.close(fd)
94 with open(filename, 'rb') as f:
95 proc = subprocess.Popen(
96 ['valgrind', '--error-exitcode=31', '--log-file', tmpfile] + glcpp + extra_args,
97 stdout=subprocess.PIPE,
98 stderr=subprocess.STDOUT,
99 stdin=subprocess.PIPE)
100 proc.communicate(f.read())
101 if proc.returncode != 31:
102 return (True, [])
103 with open(tmpfile, 'rb') as f:
104 contents = f.read()
105 return (False, contents)
106 finally:
107 os.unlink(tmpfile)
108
109
110 def test_unix(args):
111 """Test files with unix style (\n) new lines."""
112 total = 0
113 passed = 0
114
115 print('============= Testing for Correctness (Unix) =============')
116 for filename in os.listdir(args.testdir):
117 if not filename.endswith('.c'):
118 continue
119
120 print( '{}:'.format(os.path.splitext(filename)[0]), end=' ')
121 total += 1
122
123 testfile = os.path.join(args.testdir, filename)
124 valid, diff = test_output(args.glcpp, testfile, testfile + '.expected')
125 if valid:
126 passed += 1
127 print('PASS')
128 else:
129 print('FAIL')
130 for l in diff:
131 print(l, file=sys.stderr)
132
133 if not total:
134 raise Exception('Could not find any tests.')
135
136 print('{}/{}'.format(passed, total), 'tests returned correct results')
137 return total == passed
138
139
140 def _replace_test(args, replace):
141 """Test files with non-unix style line endings. Print your own header."""
142 total = 0
143 passed = 0
144
145 for filename in os.listdir(args.testdir):
146 if not filename.endswith('.c'):
147 continue
148
149 print( '{}:'.format(os.path.splitext(filename)[0]), end=' ')
150 total += 1
151 testfile = os.path.join(args.testdir, filename)
152 try:
153 fd, tmpfile = tempfile.mkstemp()
154 os.close(fd)
155 with io.open(testfile, 'rt') as f:
156 contents = f.read()
157 with io.open(tmpfile, 'wt') as f:
158 f.write(contents.replace('\n', replace))
159 valid, diff = test_output(
160 args.glcpp, tmpfile, testfile + '.expected', nl_format=replace)
161 finally:
162 os.unlink(tmpfile)
163
164 if valid:
165 passed += 1
166 print('PASS')
167 else:
168 print('FAIL')
169 for l in diff:
170 print(l, file=sys.stderr)
171
172 if not total:
173 raise Exception('Could not find any tests.')
174
175 print('{}/{}'.format(passed, total), 'tests returned correct results')
176 return total == passed
177
178
179 def test_windows(args):
180 """Test files with windows/dos style (\r\n) new lines."""
181 print('============= Testing for Correctness (Windows) =============')
182 return _replace_test(args, '\r\n')
183
184
185 def test_oldmac(args):
186 """Test files with Old Mac style (\r) new lines."""
187 print('============= Testing for Correctness (Old Mac) =============')
188 return _replace_test(args, '\r')
189
190
191 def test_bizarro(args):
192 """Test files with Bizarro world style (\n\r) new lines."""
193 # This is allowed by the spec, but why?
194 print('============= Testing for Correctness (Bizarro) =============')
195 return _replace_test(args, '\n\r')
196
197
198 def test_valgrind(args):
199 total = 0
200 passed = 0
201
202 print('============= Testing for Valgrind Warnings =============')
203 for filename in os.listdir(args.testdir):
204 if not filename.endswith('.c'):
205 continue
206
207 print( '{}:'.format(os.path.splitext(filename)[0]), end=' ')
208 total += 1
209 valid, log = _valgrind(args.glcpp, os.path.join(args.testdir, filename))
210 if valid:
211 passed += 1
212 print('PASS')
213 else:
214 print('FAIL')
215 print(log, file=sys.stderr)
216
217 if not total:
218 raise Exception('Could not find any tests.')
219
220 print('{}/{}'.format(passed, total), 'tests returned correct results')
221 return total == passed
222
223
224 def main():
225 args = arg_parser()
226
227 wrapper = os.environ.get('MESON_EXE_WRAPPER')
228 if wrapper is not None:
229 args.glcpp = split_args(wrapper) + [args.glcpp]
230 else:
231 args.glcpp = [args.glcpp]
232
233 success = True
234 try:
235 if args.unix:
236 success = success and test_unix(args)
237 if args.windows:
238 success = success and test_windows(args)
239 if args.oldmac:
240 success = success and test_oldmac(args)
241 if args.bizarro:
242 success = success and test_bizarro(args)
243 if args.valgrind:
244 success = success and test_valgrind(args)
245 except OSError as e:
246 if e.errno == errno.ENOEXEC:
247 print('Skipping due to inability to run host binaries.',
248 file=sys.stderr)
249 sys.exit(77)
250 raise
251
252 exit(0 if success else 1)
253
254
255 if __name__ == '__main__':
256 main()