OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright 2007 The Closure Linter Authors. All Rights Reserved. | |
3 # | |
4 # Licensed under the Apache License, Version 2.0 (the "License"); | |
5 # you may not use this file except in compliance with the License. | |
6 # You may obtain a copy of the License at | |
7 # | |
8 # http://www.apache.org/licenses/LICENSE-2.0 | |
9 # | |
10 # Unless required by applicable law or agreed to in writing, software | |
11 # distributed under the License is distributed on an "AS-IS" BASIS, | |
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 # See the License for the specific language governing permissions and | |
14 # limitations under the License. | |
15 | |
16 """Test case that runs a checker on a file, matching errors against annotations. | |
17 | |
18 Runs the given checker on the given file, accumulating all errors. The list | |
19 of errors is then matched against those annotated in the file. Based heavily | |
20 on devtools/javascript/gpylint/full_test.py. | |
21 """ | |
22 | |
23 __author__ = ('robbyw@google.com (Robert Walker)', | |
24 'ajp@google.com (Andy Perelson)') | |
25 | |
26 import re | |
27 | |
28 import gflags as flags | |
29 import unittest as googletest | |
30 from closure_linter.common import erroraccumulator | |
31 | |
32 | |
33 class AnnotatedFileTestCase(googletest.TestCase): | |
34 """Test case to run a linter against a single file.""" | |
35 | |
36 # Matches an all caps letters + underscores error identifer | |
37 _MESSAGE = {'msg': '[A-Z][A-Z_]+'} | |
38 # Matches a //, followed by an optional line number with a +/-, followed by a | |
39 # list of message IDs. Used to extract expected messages from testdata files. | |
40 # TODO(robbyw): Generalize to use different commenting patterns. | |
41 _EXPECTED_RE = re.compile(r'\s*//\s*(?:(?P<line>[+-]?[0-9]+):)?' | |
42 r'\s*(?P<msgs>%(msg)s(?:,\s*%(msg)s)*)' % _MESSAGE) | |
43 | |
44 def __init__(self, filename, lint_callable, converter): | |
45 """Create a single file lint test case. | |
46 | |
47 Args: | |
48 filename: Filename to test. | |
49 lint_callable: Callable that lints a file. This is usually runner.Run(). | |
50 converter: Function taking an error string and returning an error code. | |
51 """ | |
52 | |
53 googletest.TestCase.__init__(self, 'runTest') | |
54 self._filename = filename | |
55 self._messages = [] | |
56 self._lint_callable = lint_callable | |
57 self._converter = converter | |
58 | |
59 def setUp(self): | |
60 flags.FLAGS.dot_on_next_line = True | |
61 | |
62 def tearDown(self): | |
63 flags.FLAGS.dot_on_next_line = False | |
64 | |
65 def shortDescription(self): | |
66 """Provides a description for the test.""" | |
67 return 'Run linter on %s' % self._filename | |
68 | |
69 def runTest(self): | |
70 """Runs the test.""" | |
71 try: | |
72 filename = self._filename | |
73 stream = open(filename) | |
74 except IOError as ex: | |
75 raise IOError('Could not find testdata resource for %s: %s' % | |
76 (self._filename, ex)) | |
77 | |
78 expected = self._GetExpectedMessages(stream) | |
79 got = self._ProcessFileAndGetMessages(filename) | |
80 self.assertEqual(expected, got) | |
81 | |
82 def _GetExpectedMessages(self, stream): | |
83 """Parse a file and get a sorted list of expected messages.""" | |
84 messages = [] | |
85 for i, line in enumerate(stream): | |
86 match = self._EXPECTED_RE.search(line) | |
87 if match: | |
88 line = match.group('line') | |
89 msg_ids = match.group('msgs') | |
90 if line is None: | |
91 line = i + 1 | |
92 elif line.startswith('+') or line.startswith('-'): | |
93 line = i + 1 + int(line) | |
94 else: | |
95 line = int(line) | |
96 for msg_id in msg_ids.split(','): | |
97 # Ignore a spurious message from the license preamble. | |
98 if msg_id != 'WITHOUT': | |
99 messages.append((line, self._converter(msg_id.strip()))) | |
100 stream.seek(0) | |
101 messages.sort() | |
102 return messages | |
103 | |
104 def _ProcessFileAndGetMessages(self, filename): | |
105 """Trap gjslint's output parse it to get messages added.""" | |
106 error_accumulator = erroraccumulator.ErrorAccumulator() | |
107 self._lint_callable(filename, error_accumulator) | |
108 | |
109 errors = error_accumulator.GetErrors() | |
110 | |
111 # Convert to expected tuple format. | |
112 | |
113 error_msgs = [(error.token.line_number, error.code) for error in errors] | |
114 error_msgs.sort() | |
115 return error_msgs | |
OLD | NEW |