1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# 5# Copyright 2017, Data61 6# Commonwealth Scientific and Industrial Research Organisation (CSIRO) 7# ABN 41 687 119 230. 8# 9# This software may be distributed and modified according to the terms of 10# the BSD 2-Clause license. Note that NO WARRANTY is provided. 11# See "LICENSE_BSD2.txt" for details. 12# 13# @TAG(DATA61_BSD) 14# 15 16''' 17Test harness for running jinja_pylint.py across the templates. 18''' 19 20from __future__ import absolute_import, division, print_function, \ 21 unicode_literals 22 23import os, re, subprocess, sys, unittest 24 25ME = os.path.abspath(__file__) 26 27# Make CAmkES importable 28sys.path.append(os.path.join(os.path.dirname(ME), '../../..')) 29 30from camkes.internal.tests.utils import CAmkESTest 31 32class TestPyLint(CAmkESTest): 33 def setUp(self): 34 super(TestPyLint, self).setUp() 35 self.lint = os.path.join(os.path.dirname(ME), 36 '../../../tools/jinja_pylint.py') 37 self.assertTrue(os.access(self.lint, os.X_OK), 38 'jinja_pylint.py not found or not executable') 39 40# Pylint always generates errors when run on Jinja files. Some of these are 41# spurious. Regexes to suppress spurious errors are given here. 42to_ignore = frozenset([ 43 44 # Pylint header. 45 re.compile(r'\*+\sModule\s\w+$'), 46 47 # Pylint always warns about missing Jinja supports. 48 re.compile(r'E:\s*\d+,\s*\d+:\s+Undefined variable\s+\'(environment|dummy)\'\s+\(undefined-variable\)$'), 49 50 # Jinja sometimes re-uses internal function names. 51 re.compile(r'E:\s*\d+,\s*\d+:\s+function already defined line \d+ \(function-redefined\)$'), 52 53 # Output from jinja_pylint.py (the other one, not us) itself. 54 re.compile('compiling to [^\s]+?\.\.\.$'), 55 re.compile('running pylint on [^\s]+?\.\.\.$'), 56 57 # Blank lines. 58 re.compile('$'), 59]) 60 61def _lint(self, path): 62 ''' 63 Generic lint invoker that we'll curry below. 64 ''' 65 p = subprocess.Popen([self.lint, path, '--errors-only'], 66 stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) 67 # Pylint errors come out on stdout. Why? Who knows. 68 stdout, _ = p.communicate() 69 errors = [] 70 for line in [x.strip() for x in stdout.split('\n')]: 71 if any((x.match(line) for x in to_ignore)): 72 continue 73 errors.append(line) 74 self.assertListEqual(errors, [], '\n'.join(['%s:' % path] + errors)) 75 76regex = re.compile(r'[^\w]') 77template_dir = os.path.abspath(os.path.join(os.path.dirname(ME), '..')) 78tests_dir = os.path.dirname(ME) 79 80# Find all the templates. 81for root, _, filenames in os.walk(template_dir): 82 83 if root.startswith(tests_dir): 84 # Don't analyse the test files. 85 continue 86 87 # For each template, monkey patch a test for it onto the test class. 88 for f in filenames: 89 if f.lower().endswith('.py'): 90 # Skip Python sources. 91 continue 92 name = 'test_%s' % regex.sub('_', f) 93 path = os.path.join(root, f) 94 setattr(TestPyLint, name, lambda self, path=path: _lint(self, path)) 95 96if __name__ == '__main__': 97 unittest.main() 98