| # encoding=utf-8 |
| # Copyright © 2018 Intel Corporation |
| |
| # Permission is hereby granted, free of charge, to any person obtaining a copy |
| # of this software and associated documentation files (the "Software"), to deal |
| # in the Software without restriction, including without limitation the rights |
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| # copies of the Software, and to permit persons to whom the Software is |
| # furnished to do so, subject to the following conditions: |
| |
| # The above copyright notice and this permission notice shall be included in |
| # all copies or substantial portions of the Software. |
| |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| # SOFTWARE. |
| |
| """Run glcpp tests with various line endings.""" |
| |
| from __future__ import print_function |
| import argparse |
| import difflib |
| import errno |
| import io |
| import os |
| import subprocess |
| import sys |
| import tempfile |
| |
| # The meson version handles windows paths better, but if it's not available |
| # fall back to shlex |
| try: |
| from meson.mesonlib import split_args |
| except ImportError: |
| from shlex import split as split_args |
| |
| |
| def arg_parser(): |
| parser = argparse.ArgumentParser() |
| parser.add_argument('glcpp', help='Path to the he glcpp binary.') |
| parser.add_argument('testdir', help='Path to tests and expected output.') |
| parser.add_argument('--unix', action='store_true', help='Run tests for Unix style newlines') |
| parser.add_argument('--windows', action='store_true', help='Run tests for Windows/Dos style newlines') |
| parser.add_argument('--oldmac', action='store_true', help='Run tests for Old Mac (pre-OSX) style newlines') |
| parser.add_argument('--bizarro', action='store_true', help='Run tests for Bizarro world style newlines') |
| parser.add_argument('--valgrind', action='store_true', help='Run with valgrind for errors') |
| return parser.parse_args() |
| |
| |
| def parse_test_file(filename, nl_format): |
| """Check for any special arguments and return them as a list.""" |
| # Disable "universal newlines" mode; we can't directly use `nl_format` as |
| # the `newline` argument, because the "bizarro" test uses something Python |
| # considers invalid. |
| with io.open(filename, newline='') as f: |
| for l in f.read().split(nl_format): |
| if 'glcpp-args:' in l: |
| return l.split('glcpp-args:')[1].strip().split() |
| return [] |
| |
| |
| def test_output(glcpp, filename, expfile, nl_format='\n'): |
| """Test that the output of glcpp is what we expect.""" |
| extra_args = parse_test_file(filename, nl_format) |
| |
| with open(filename, 'rb') as f: |
| proc = subprocess.Popen( |
| glcpp + extra_args, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| stdin=subprocess.PIPE) |
| actual, _ = proc.communicate(f.read()) |
| actual = actual.decode('utf-8') |
| |
| if proc.returncode == 255: |
| print("Test returned general error, possibly missing linker") |
| sys.exit(77) |
| |
| with open(expfile, 'r') as f: |
| expected = f.read() |
| |
| # Bison 3.6 changed '$end' to 'end of file' in its error messages |
| # See: https://gitlab.freedesktop.org/mesa/mesa/-/issues/3181 |
| actual = actual.replace('$end', 'end of file') |
| |
| if actual == expected: |
| return (True, []) |
| return (False, difflib.unified_diff(actual.splitlines(), expected.splitlines())) |
| |
| |
| def _valgrind(glcpp, filename): |
| """Run valgrind and report any warnings.""" |
| extra_args = parse_test_file(filename, nl_format='\n') |
| |
| try: |
| fd, tmpfile = tempfile.mkstemp() |
| os.close(fd) |
| with open(filename, 'rb') as f: |
| proc = subprocess.Popen( |
| ['valgrind', '--error-exitcode=31', '--log-file', tmpfile] + glcpp + extra_args, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| stdin=subprocess.PIPE) |
| proc.communicate(f.read()) |
| if proc.returncode != 31: |
| return (True, []) |
| with open(tmpfile, 'rb') as f: |
| contents = f.read() |
| return (False, contents) |
| finally: |
| os.unlink(tmpfile) |
| |
| |
| def test_unix(args): |
| """Test files with unix style (\n) new lines.""" |
| total = 0 |
| passed = 0 |
| |
| print('============= Testing for Correctness (Unix) =============') |
| for filename in os.listdir(args.testdir): |
| if not filename.endswith('.c'): |
| continue |
| |
| print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') |
| total += 1 |
| |
| testfile = os.path.join(args.testdir, filename) |
| valid, diff = test_output(args.glcpp, testfile, testfile + '.expected') |
| if valid: |
| passed += 1 |
| print('PASS') |
| else: |
| print('FAIL') |
| for l in diff: |
| print(l, file=sys.stderr) |
| |
| if not total: |
| raise Exception('Could not find any tests.') |
| |
| print('{}/{}'.format(passed, total), 'tests returned correct results') |
| return total == passed |
| |
| |
| def _replace_test(args, replace): |
| """Test files with non-unix style line endings. Print your own header.""" |
| total = 0 |
| passed = 0 |
| |
| for filename in os.listdir(args.testdir): |
| if not filename.endswith('.c'): |
| continue |
| |
| print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') |
| total += 1 |
| testfile = os.path.join(args.testdir, filename) |
| try: |
| fd, tmpfile = tempfile.mkstemp() |
| os.close(fd) |
| with io.open(testfile, 'rt') as f: |
| contents = f.read() |
| with io.open(tmpfile, 'wt') as f: |
| f.write(contents.replace('\n', replace)) |
| valid, diff = test_output( |
| args.glcpp, tmpfile, testfile + '.expected', nl_format=replace) |
| finally: |
| os.unlink(tmpfile) |
| |
| if valid: |
| passed += 1 |
| print('PASS') |
| else: |
| print('FAIL') |
| for l in diff: |
| print(l, file=sys.stderr) |
| |
| if not total: |
| raise Exception('Could not find any tests.') |
| |
| print('{}/{}'.format(passed, total), 'tests returned correct results') |
| return total == passed |
| |
| |
| def test_windows(args): |
| """Test files with windows/dos style (\r\n) new lines.""" |
| print('============= Testing for Correctness (Windows) =============') |
| return _replace_test(args, '\r\n') |
| |
| |
| def test_oldmac(args): |
| """Test files with Old Mac style (\r) new lines.""" |
| print('============= Testing for Correctness (Old Mac) =============') |
| return _replace_test(args, '\r') |
| |
| |
| def test_bizarro(args): |
| """Test files with Bizarro world style (\n\r) new lines.""" |
| # This is allowed by the spec, but why? |
| print('============= Testing for Correctness (Bizarro) =============') |
| return _replace_test(args, '\n\r') |
| |
| |
| def test_valgrind(args): |
| total = 0 |
| passed = 0 |
| |
| print('============= Testing for Valgrind Warnings =============') |
| for filename in os.listdir(args.testdir): |
| if not filename.endswith('.c'): |
| continue |
| |
| print( '{}:'.format(os.path.splitext(filename)[0]), end=' ') |
| total += 1 |
| valid, log = _valgrind(args.glcpp, os.path.join(args.testdir, filename)) |
| if valid: |
| passed += 1 |
| print('PASS') |
| else: |
| print('FAIL') |
| print(log, file=sys.stderr) |
| |
| if not total: |
| raise Exception('Could not find any tests.') |
| |
| print('{}/{}'.format(passed, total), 'tests returned correct results') |
| return total == passed |
| |
| |
| def main(): |
| args = arg_parser() |
| |
| wrapper = os.environ.get('MESON_EXE_WRAPPER') |
| if wrapper is not None: |
| args.glcpp = split_args(wrapper) + [args.glcpp] |
| else: |
| args.glcpp = [args.glcpp] |
| |
| success = True |
| try: |
| if args.unix: |
| success = success and test_unix(args) |
| if args.windows: |
| success = success and test_windows(args) |
| if args.oldmac: |
| success = success and test_oldmac(args) |
| if args.bizarro: |
| success = success and test_bizarro(args) |
| if args.valgrind: |
| success = success and test_valgrind(args) |
| except OSError as e: |
| if e.errno == errno.ENOEXEC: |
| print('Skipping due to inability to run host binaries.', |
| file=sys.stderr) |
| sys.exit(77) |
| raise |
| |
| exit(0 if success else 1) |
| |
| |
| if __name__ == '__main__': |
| main() |