| #!/usr/bin/env python |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2019, Data61, CSIRO (ABN 41 687 119 230) |
| # |
| # SPDX-License-Identifier: BSD-2-Clause |
| # |
| # |
| |
| ''' |
| Test harness for running jinja_pylint.py across the templates. |
| ''' |
| |
| from __future__ import absolute_import, division, print_function, \ |
| unicode_literals |
| |
| import os, re, subprocess, sys, unittest |
| |
| ME = os.path.abspath(__file__) |
| |
| # Make CAmkES importable |
| sys.path.append(os.path.join(os.path.dirname(ME), '../../..')) |
| |
| from camkes.internal.tests.utils import CAmkESTest |
| |
| class TestPyLint(CAmkESTest): |
| def setUp(self): |
| super(TestPyLint, self).setUp() |
| self.lint = os.path.join(os.path.dirname(ME), |
| '../../../tools/jinja_pylint.py') |
| self.assertTrue(os.access(self.lint, os.X_OK), |
| 'jinja_pylint.py not found or not executable') |
| |
| # Pylint always generates errors when run on Jinja files. Some of these are |
| # spurious. Regexes to suppress spurious errors are given here. |
| to_ignore = frozenset([ |
| |
| # Pylint header. |
| re.compile(r'\*+\sModule\s\w+$'), |
| |
| # Pylint always warns about missing Jinja supports. |
| re.compile(r'E:\s*\d+,\s*\d+:\s+Undefined variable\s+\'(environment|dummy)\'\s+\(undefined-variable\)$'), |
| |
| # Jinja sometimes re-uses internal function names. |
| re.compile(r'E:\s*\d+,\s*\d+:\s+function already defined line \d+ \(function-redefined\)$'), |
| |
| # Output from jinja_pylint.py (the other one, not us) itself. |
| re.compile('compiling to [^\s]+?\.\.\.$'), |
| re.compile('running pylint on [^\s]+?\.\.\.$'), |
| |
| # Blank lines. |
| re.compile('$'), |
| ]) |
| |
| def _lint(self, path): |
| ''' |
| Generic lint invoker that we'll curry below. |
| ''' |
| p = subprocess.Popen([self.lint, path, '--errors-only'], |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) |
| # Pylint errors come out on stdout. Why? Who knows. |
| stdout, _ = p.communicate() |
| errors = [] |
| for line in [x.strip() for x in stdout.split('\n')]: |
| if any((x.match(line) for x in to_ignore)): |
| continue |
| errors.append(line) |
| self.assertListEqual(errors, [], '\n'.join(['%s:' % path] + errors)) |
| |
| regex = re.compile(r'[^\w]') |
| template_dir = os.path.abspath(os.path.join(os.path.dirname(ME), '..')) |
| tests_dir = os.path.dirname(ME) |
| |
| # Find all the templates. |
| for root, _, filenames in os.walk(template_dir): |
| |
| if root.startswith(tests_dir): |
| # Don't analyse the test files. |
| continue |
| |
| # For each template, monkey patch a test for it onto the test class. |
| for f in filenames: |
| if f.lower().endswith('.py'): |
| # Skip Python sources. |
| continue |
| name = 'test_%s' % regex.sub('_', f) |
| path = os.path.join(root, f) |
| setattr(TestPyLint, name, lambda self, path=path: _lint(self, path)) |
| |
| if __name__ == '__main__': |
| unittest.main() |