OLD | NEW |
| (Empty) |
1 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 # for details. All rights reserved. Use of this source code is governed by a | |
3 # BSD-style license that can be found in the LICENSE file. | |
4 | |
5 """Common Testconfiguration subclasses used to define a class of tests.""" | |
6 | |
7 import atexit | |
8 import fileinput | |
9 import os | |
10 import re | |
11 import shutil | |
12 | |
13 | |
14 import test | |
15 from testing import test_case | |
16 import utils | |
17 | |
18 | |
19 # Patterns for matching test options in .dart files. | |
20 VM_OPTIONS_PATTERN = re.compile(r"// VMOptions=(.*)") | |
21 | |
22 class Error(Exception): | |
23 pass | |
24 | |
25 | |
26 class TestConfigurationError(Error): | |
27 pass | |
28 | |
29 | |
30 class StandardTestConfiguration(test.TestConfiguration): | |
31 """Configuration that looks for .dart files in the tests/*/src dirs.""" | |
32 LEGAL_KINDS = set(['compile-time error', | |
33 'runtime error', | |
34 'static type error', | |
35 'dynamic type error']) | |
36 | |
37 def __init__(self, context, root, flags = []): | |
38 super(StandardTestConfiguration, self).__init__(context, root, flags) | |
39 | |
40 def _Cleanup(self, tests): | |
41 """Remove any temporary files created by running the test.""" | |
42 if self.context.keep_temporary_files: | |
43 return | |
44 | |
45 dirs = [] | |
46 for t in tests: | |
47 if t.run_arch: | |
48 temp_dir = t.run_arch.temp_dir | |
49 if temp_dir: | |
50 dirs.append(temp_dir) | |
51 if not dirs: | |
52 return | |
53 if not utils.Daemonize(): | |
54 return | |
55 os.execlp('rm', *(['rm', '-rf'] + dirs)) | |
56 | |
57 def CreateTestCases(self, test_path, path, filename, mode, arch, component): | |
58 """Given a .dart filename, create a StandardTestCase from it.""" | |
59 # Look for VM specified as comments in the source file. If | |
60 # several sets of VM options are specified create a separate | |
61 # test for each set. | |
62 source = file(filename).read() | |
63 vm_options_list = utils.ParseTestOptionsMultiple(VM_OPTIONS_PATTERN, | |
64 source, | |
65 test_path) | |
66 tags = {} | |
67 if filename.endswith('.dart'): | |
68 tags = self.SplitMultiTest(test_path, filename) | |
69 if component in ['dartium', 'chromium', 'frogium', 'webdriver']: | |
70 if tags: | |
71 return [] | |
72 else: | |
73 if vm_options_list: | |
74 tests = [] | |
75 for options in vm_options_list: | |
76 tests.append(test_case.BrowserTestCase( | |
77 self.context, test_path, filename, False, mode, arch, component, | |
78 options + self.flags)) | |
79 return tests | |
80 else: | |
81 return [test_case.BrowserTestCase( | |
82 self.context, test_path, filename, False, mode, arch, component, | |
83 self.flags)] | |
84 else: | |
85 tests = [] | |
86 if tags: | |
87 for tag in sorted(tags): | |
88 kind, test_source = tags[tag] | |
89 if not self.Contains(path, test_path + [tag]): | |
90 continue | |
91 if vm_options_list: | |
92 for options in vm_options_list: | |
93 tests.append(test_case.MultiTestCase(self.context, | |
94 test_path + [tag], | |
95 test_source, | |
96 kind, | |
97 mode, arch, component, | |
98 options + self.flags)) | |
99 else: | |
100 tests.append(test_case.MultiTestCase(self.context, | |
101 test_path + [tag], | |
102 test_source, | |
103 kind, | |
104 mode, arch, component, | |
105 self.flags)) | |
106 else: | |
107 if vm_options_list: | |
108 for options in vm_options_list: | |
109 tests.append(test_case.StandardTestCase(self.context, | |
110 test_path, filename, mode, arch, component, | |
111 options + self.flags)) | |
112 else: | |
113 tests.append(test_case.StandardTestCase(self.context, | |
114 test_path, filename, mode, arch, component, self.flags)) | |
115 return tests | |
116 | |
117 def ListTests(self, current_path, path, mode, arch, component): | |
118 """Searches for *Test.dart files and returns list of TestCases.""" | |
119 tests = [] | |
120 for root, unused_dirs, files in os.walk(os.path.join(self.root, 'src')): | |
121 for f in [x for x in files if self.IsTest(x)]: | |
122 if f.endswith('.dart'): | |
123 test_path = current_path + [f[:-5]] # Remove .dart suffix. | |
124 elif f.endswith('.app'): | |
125 # TODO(zundel): .app files are used only the dromaeo test | |
126 # and should be removed. | |
127 test_path = current_path + [f[:-4]] # Remove .app suffix. | |
128 if not self.Contains(path, test_path): | |
129 continue | |
130 tests.extend(self.CreateTestCases(test_path, path, | |
131 os.path.join(root, f), | |
132 mode, arch, component)) | |
133 atexit.register(lambda: self._Cleanup(tests)) | |
134 return tests | |
135 | |
136 def IsTest(self, name): | |
137 """Returns True if the file name is a test file.""" | |
138 return name.endswith('Test.dart') or name.endswith('Test.app') | |
139 | |
140 def GetTestStatus(self, sections, defs): | |
141 """Reads the .status file of the TestSuite.""" | |
142 basename = os.path.basename(self.root) | |
143 for component in ['%s.status', '%s-leg.status']: | |
144 status = os.path.join(self.root, component % basename) | |
145 if os.path.exists(status): | |
146 test.ReadConfigurationInto(status, sections, defs) | |
147 | |
148 def FindReferencedFiles(self, lines): | |
149 """Scours the lines containing source code for include directives.""" | |
150 referenced_files = [] | |
151 for line in lines: | |
152 m = re.match("#(source|import)\(['\"](.*)['\"]\);", line) | |
153 if m: | |
154 file_name = m.group(2) | |
155 if not file_name.startswith('dart:'): | |
156 referenced_files.append(file_name) | |
157 return referenced_files | |
158 | |
159 def SplitMultiTest(self, test_path, filename): | |
160 """Takes a file with multiple test case defined. | |
161 | |
162 Splits the file into multiple TestCase instances. | |
163 | |
164 Args: | |
165 test_path: temporary dir to write split test case data. | |
166 filename: name of the file to split. | |
167 | |
168 Returns: | |
169 sequence of test cases split from file. | |
170 | |
171 Raises: | |
172 TestConfigurationError: when a problem with the multi-test-case | |
173 syntax is encountered. | |
174 """ | |
175 (name, extension) = os.path.splitext(os.path.basename(filename)) | |
176 with open(filename, 'r') as s: | |
177 source = s.read() | |
178 lines = source.splitlines() | |
179 tags = {} | |
180 for line in lines: | |
181 (unused_code, sep, info) = line.partition(' /// ') | |
182 if sep: | |
183 (tag, sep, kind) = info.partition(': ') | |
184 if tag in tags: | |
185 if kind != 'continued': | |
186 raise TestConfigurationError('duplicated tag %s' % tag) | |
187 elif kind not in StandardTestConfiguration.LEGAL_KINDS: | |
188 raise TestConfigurationError('unrecognized kind %s' % kind) | |
189 else: | |
190 tags[tag] = kind | |
191 if not tags: | |
192 return {} | |
193 # Prepare directory for generated tests. | |
194 tests = {} | |
195 generated_test_dir = os.path.join(utils.GetBuildRoot(utils.GuessOS()), | |
196 'generated_tests') | |
197 generated_test_dir = os.path.join(generated_test_dir, *test_path[:-1]) | |
198 if not os.path.exists(generated_test_dir): | |
199 os.makedirs(generated_test_dir) | |
200 # Copy referenced files to generated tests directory. | |
201 referenced_files = self.FindReferencedFiles(lines) | |
202 for referenced_file in referenced_files: | |
203 shutil.copy(os.path.join(os.path.dirname(filename), referenced_file), | |
204 os.path.join(generated_test_dir, referenced_file)) | |
205 # Generate test for each tag found in the main test file. | |
206 for tag in tags: | |
207 test_lines = [] | |
208 for line in lines: | |
209 if ' /// ' in line: | |
210 if ' /// %s:' % tag in line: | |
211 test_lines.append(line) | |
212 else: | |
213 test_lines.append('// %s' % line) | |
214 else: | |
215 test_lines.append(line) | |
216 test_filename = os.path.join(generated_test_dir, | |
217 '%s_%s%s' % (name, tag, extension)) | |
218 with open(test_filename, 'w') as test_file: | |
219 for line in test_lines: | |
220 print >> test_file, line | |
221 tests[tag] = (tags[tag], test_filename) | |
222 test_filename = os.path.join(generated_test_dir, | |
223 '%s%s' % (name, extension)) | |
224 with open(test_filename, 'w') as test_file: | |
225 for line in lines: | |
226 if ' /// ' not in line: | |
227 print >> test_file, line | |
228 else: | |
229 print >> test_file, '//', line | |
230 tests['none'] = ('', test_filename) | |
231 return tests | |
232 | |
233 | |
234 class BrowserTestConfiguration(StandardTestConfiguration): | |
235 """A configuration used to run tests inside a browser.""" | |
236 | |
237 def __init__(self, context, root, fatal_static_type_errors=False): | |
238 super(BrowserTestConfiguration, self).__init__(context, root) | |
239 self.fatal_static_type_errors = fatal_static_type_errors | |
240 | |
241 def ListTests(self, current_path, path, mode, arch, component): | |
242 """Searches for *Test .dart files and returns list of TestCases.""" | |
243 tests = [] | |
244 for root, unused_dirs, files in os.walk(self.root): | |
245 for f in [x for x in files if self.IsTest(x)]: | |
246 relative = os.path.relpath(root, self.root).split(os.path.sep) | |
247 test_path = current_path + relative + [os.path.splitext(f)[0]] | |
248 if not self.Contains(path, test_path): | |
249 continue | |
250 tests.append(test_case.BrowserTestCase(self.context, | |
251 test_path, | |
252 os.path.join(root, f), | |
253 self.fatal_static_type_errors, | |
254 mode, arch, component)) | |
255 atexit.register(lambda: self._Cleanup(tests)) | |
256 return tests | |
257 | |
258 def IsTest(self, name): | |
259 return name.endswith('_tests.dart') or name.endswith('Test.dart') | |
260 | |
261 | |
262 class CompilationTestConfiguration(test.TestConfiguration): | |
263 """Configuration that searches specific directories for apps to compile. | |
264 | |
265 Expects a status file named dartc.status | |
266 """ | |
267 | |
268 def __init__(self, context, root): | |
269 super(CompilationTestConfiguration, self).__init__(context, root) | |
270 | |
271 def ListTests(self, current_path, path, mode, arch, component): | |
272 """Searches for files satisfying IsTest() and returns list of TestCases.""" | |
273 tests = [] | |
274 client_path = os.path.normpath(os.path.join(self.root, '..', '..')) | |
275 | |
276 for src_dir in self.SourceDirs(): | |
277 for root, dirs, files in os.walk(os.path.join(client_path, src_dir)): | |
278 ignore_dirs = [d for d in dirs if d.startswith('.')] | |
279 for d in ignore_dirs: | |
280 dirs.remove(d) | |
281 for f in files: | |
282 filename = [os.path.basename(client_path)] | |
283 filename.extend(root[len(client_path) + 1:].split(os.path.sep)) | |
284 filename.append(f) # Remove .lib or .app suffix. | |
285 test_path = current_path + filename | |
286 test_dart_file = os.path.join(root, f) | |
287 if (not self.Contains(path, test_path) | |
288 or not self.IsTest(test_dart_file)): | |
289 continue | |
290 tests.append(test_case.CompilationTestCase(test_path, | |
291 self.context, | |
292 test_dart_file, | |
293 mode, | |
294 arch, | |
295 component)) | |
296 atexit.register(lambda: self._Cleanup(tests)) | |
297 return tests | |
298 | |
299 def SourceDirs(self): | |
300 """Returns a list of directories to scan for files to compile.""" | |
301 raise TestConfigurationError( | |
302 'Subclasses must implement SourceDirs()') | |
303 | |
304 def IsTest(self, name): | |
305 """Returns True if name is a test case to be compiled.""" | |
306 if not name.endswith('.dart'): | |
307 return False | |
308 if os.path.exists(name): | |
309 # TODO(dgrove): can we end reading the input early? | |
310 for line in fileinput.input(name): | |
311 if re.match('#', line): | |
312 fileinput.close() | |
313 return True | |
314 fileinput.close() | |
315 return False | |
316 return False | |
317 | |
318 def GetTestStatus(self, sections, defs): | |
319 status = os.path.join(self.root, 'dartc.status') | |
320 if os.path.exists(status): | |
321 test.ReadConfigurationInto(status, sections, defs) | |
322 | |
323 def _Cleanup(self, tests): | |
324 if not utils.Daemonize(): return | |
325 os.execlp('rm', *(['rm', '-rf'] + [t.temp_dir for t in tests])) | |
326 raise | |
OLD | NEW |