Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 # BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 """Common Testconfiguration subclasses used to define a class of tests.""" | |
| 6 | |
| 5 import atexit | 7 import atexit |
| 6 import fileinput | 8 import fileinput |
| 7 import os | 9 import os |
| 8 import test | |
| 9 import platform | |
| 10 import re | 10 import re |
| 11 import shutil | 11 import shutil |
| 12 import sys | |
| 13 import tempfile | |
| 14 | 12 |
| 13 | |
| 14 import test | |
| 15 from testing import test_case | 15 from testing import test_case |
| 16 import test | |
| 17 import utils | 16 import utils |
| 18 | 17 |
| 19 from os.path import join, exists, basename | |
| 20 | |
| 21 import utils | |
| 22 | 18 |
| 23 class Error(Exception): | 19 class Error(Exception): |
| 24 pass | 20 pass |
| 25 | 21 |
| 22 | |
| 26 class TestConfigurationError(Error): | 23 class TestConfigurationError(Error): |
| 27 pass | 24 pass |
| 28 | 25 |
| 26 | |
| 29 class StandardTestConfiguration(test.TestConfiguration): | 27 class StandardTestConfiguration(test.TestConfiguration): |
| 28 """Configuration that looks for .dart files in the tests/*/src dirs.""" | |
|
ngeoffray
2011/10/13 08:03:38
*Test.dart?
zundel
2011/10/13 11:07:44
Done.
| |
| 30 LEGAL_KINDS = set(['compile-time error', | 29 LEGAL_KINDS = set(['compile-time error', |
| 31 'runtime error', | 30 'runtime error', |
| 32 'static type error', | 31 'static type error', |
| 33 'dynamic type error']) | 32 'dynamic type error']) |
| 34 | 33 |
| 35 def __init__(self, context, root): | 34 def __init__(self, context, root): |
| 36 super(StandardTestConfiguration, self).__init__(context, root) | 35 super(StandardTestConfiguration, self).__init__(context, root) |
| 37 | 36 |
| 38 def _cleanup(self, tests): | 37 def _Cleanup(self, tests): |
| 39 if self.context.keep_temporary_files: return | 38 """Remove any temporary files created by running the test.""" |
| 39 if self.context.keep_temporary_files: | |
| 40 return | |
| 40 | 41 |
| 41 dirs = [] | 42 dirs = [] |
| 42 for t in tests: | 43 for t in tests: |
| 43 if t.run_arch != None: | 44 if t.run_arch: |
| 44 temp_dir = t.run_arch.temp_dir | 45 temp_dir = t.run_arch.temp_dir |
| 45 if temp_dir != None: dirs.append(temp_dir) | 46 if temp_dir: |
| 46 | 47 dirs.append(temp_dir) |
| 47 if len(dirs) == 0: return | 48 if not dirs: |
| 48 if not utils.Daemonize(): return | 49 return |
| 49 | 50 if not utils.Daemonize(): |
| 51 return | |
| 50 os.execlp('rm', *(['rm', '-rf'] + dirs)) | 52 os.execlp('rm', *(['rm', '-rf'] + dirs)) |
| 51 | 53 |
| 52 | |
| 53 def CreateTestCases(self, test_path, path, filename, mode, arch): | 54 def CreateTestCases(self, test_path, path, filename, mode, arch): |
| 55 """Given a .dart filename, create a StandardTestCase from it.""" | |
| 54 tags = {} | 56 tags = {} |
| 55 if filename.endswith(".dart"): | 57 if filename.endswith('.dart'): |
| 56 tags = self.SplitMultiTest(test_path, filename) | 58 tags = self.SplitMultiTest(test_path, filename) |
| 57 if arch in ['dartium', 'chromium']: | 59 if arch in ['dartium', 'chromium']: |
| 58 if tags: | 60 if tags: |
| 59 return [] | 61 return [] |
| 60 else: | 62 else: |
| 61 return [test_case.BrowserTestCase( | 63 return [test_case.BrowserTestCase( |
| 62 self.context, test_path, filename, False, mode, arch)] | 64 self.context, test_path, filename, False, mode, arch)] |
| 63 else: | 65 else: |
| 64 tests = [] | 66 tests = [] |
| 65 if tags: | 67 if tags: |
| 66 for tag in sorted(tags): | 68 for tag in sorted(tags): |
| 67 kind, test_source = tags[tag] | 69 kind, test_source = tags[tag] |
| 68 if not self.Contains(path, test_path + [tag]): | 70 if not self.Contains(path, test_path + [tag]): |
| 69 continue | 71 continue |
| 70 tests.append(test_case.MultiTestCase(self.context, | 72 tests.append(test_case.MultiTestCase(self.context, |
| 71 test_path + [tag], test_source, kind, mode, arch)) | 73 test_path + [tag], |
| 74 test_source, | |
| 75 kind, | |
| 76 mode, arch)) | |
| 72 else: | 77 else: |
| 73 tests.append(test_case.StandardTestCase(self.context, | 78 tests.append(test_case.StandardTestCase(self.context, |
| 74 test_path, filename, mode, arch)) | 79 test_path, |
| 80 filename, | |
| 81 mode, arch)) | |
| 75 return tests | 82 return tests |
| 76 | 83 |
| 77 def ListTests(self, current_path, path, mode, arch): | 84 def ListTests(self, current_path, path, mode, arch): |
| 85 """Searches for .dart files that qualify and returns list of TestCases.""" | |
|
ngeoffray
2011/10/13 08:03:38
ditto
zundel
2011/10/13 11:07:44
Done.
| |
| 78 tests = [] | 86 tests = [] |
| 79 for root, dirs, files in os.walk(join(self.root, 'src')): | 87 for root, unused_dirs, files in os.walk(os.path.join(self.root, 'src')): |
| 80 for f in [x for x in files if self.IsTest(x)]: | 88 for f in [x for x in files if self.IsTest(x)]: |
| 81 if f.endswith(".dart"): | 89 if f.endswith('.dart'): |
| 82 test_path = current_path + [ f[:-5] ] # Remove .dart suffix. | 90 test_path = current_path + [f[:-5]] # Remove .dart suffix. |
| 83 elif f.endswith(".app"): | 91 elif f.endswith('.app'): |
| 84 test_path = current_path + [ f[:-4] ] # Remove .app suffix. | 92 # TODO(zundel): .app files are used only the dromaeo test |
| 93 # and should be removed. | |
| 94 test_path = current_path + [f[:-4]] # Remove .app suffix. | |
| 85 if not self.Contains(path, test_path): | 95 if not self.Contains(path, test_path): |
| 86 continue | 96 continue |
| 87 tests.extend(self.CreateTestCases(test_path, path, join(root, f), | 97 tests.extend(self.CreateTestCases(test_path, path, |
| 98 os.path.join(root, f), | |
| 88 mode, arch)) | 99 mode, arch)) |
| 89 atexit.register(lambda: self._cleanup(tests)) | 100 atexit.register(lambda: self._Cleanup(tests)) |
| 90 return tests | 101 return tests |
| 91 | 102 |
| 92 def IsTest(self, name): | 103 def IsTest(self, name): |
| 93 return name.endswith('Test.dart') or name.endswith("Test.app") | 104 """Returns True if the file name looks like a test file.""" |
|
ngeoffray
2011/10/13 08:03:38
looks like -> is?
zundel
2011/10/13 11:07:44
Done.
| |
| 105 return name.endswith('Test.dart') or name.endswith('Test.app') | |
| 94 | 106 |
| 95 def GetTestStatus(self, sections, defs): | 107 def GetTestStatus(self, sections, defs): |
| 96 status = join(self.root, basename(self.root) + '.status') | 108 """Populates the sections and defs parameters if the file exists.""" |
|
ngeoffray
2011/10/13 08:03:38
Change to """Reads the status file of the test sui
zundel
2011/10/13 11:07:44
Done.
| |
| 97 if exists(status): | 109 status = os.path.join(self.root, os.path.basename(self.root) + '.status') |
| 110 if os.path.exists(status): | |
| 98 test.ReadConfigurationInto(status, sections, defs) | 111 test.ReadConfigurationInto(status, sections, defs) |
| 99 | 112 |
| 100 def FindReferencedFiles(self, lines): | 113 def FindReferencedFiles(self, lines): |
| 114 """Scours the lines containing source code for include directives.""" | |
| 101 referenced_files = [] | 115 referenced_files = [] |
| 102 for line in lines: | 116 for line in lines: |
| 103 m = re.match("#(source|import)\(['\"](.*)['\"]\);", line) | 117 m = re.match("#(source|import)\(['\"](.*)['\"]\);", line) |
| 104 if m: | 118 if m: |
| 105 referenced_files.append(m.group(2)) | 119 referenced_files.append(m.group(2)) |
| 106 return referenced_files | 120 return referenced_files |
| 107 | 121 |
| 108 def SplitMultiTest(self, test_path, filename): | 122 def SplitMultiTest(self, test_path, filename): |
| 123 """Takes a file with multiple test case defined. | |
| 124 | |
| 125 Splits the file into multiple TestCase instances. | |
| 126 | |
| 127 Args: | |
| 128 test_path: temporary dir to write split test case data. | |
| 129 filename: name of the file to split. | |
| 130 | |
| 131 Returns: | |
| 132 sequence of test cases split from file. | |
| 133 | |
| 134 Raises: | |
| 135 TestConfigurationError: when a problem with the multi-test-case | |
| 136 syntax is encountered. | |
| 137 """ | |
| 109 (name, extension) = os.path.splitext(os.path.basename(filename)) | 138 (name, extension) = os.path.splitext(os.path.basename(filename)) |
| 110 with open(filename, 'r') as s: | 139 with open(filename, 'r') as s: |
| 111 source = s.read() | 140 source = s.read() |
| 112 lines = source.splitlines() | 141 lines = source.splitlines() |
| 113 tags = {} | 142 tags = {} |
| 114 for line in lines: | 143 for line in lines: |
| 115 (code, sep, info) = line.partition(' /// ') | 144 (unused_code, sep, info) = line.partition(' /// ') |
| 116 if sep: | 145 if sep: |
| 117 (tag, sep, kind) = info.partition(': ') | 146 (tag, sep, kind) = info.partition(': ') |
| 118 if tags.has_key(tag): | 147 if tag in tags: |
| 119 raise utils.Error('duplicated tag %s' % tag) | 148 raise TestConfigurationError('duplicated tag %s' % tag) |
| 120 if kind not in StandardTestConfiguration.LEGAL_KINDS: | 149 if kind not in StandardTestConfiguration.LEGAL_KINDS: |
| 121 raise utils.Error('unrecognized kind %s' % kind) | 150 raise TestConfigurationError('unrecognized kind %s' % kind) |
| 122 tags[tag] = kind | 151 tags[tag] = kind |
| 123 if not tags: | 152 if not tags: |
| 124 return {} | 153 return {} |
| 125 # Prepare directory for generated tests. | 154 # Prepare directory for generated tests. |
| 126 tests = {} | 155 tests = {} |
| 127 generated_test_dir = os.path.join(self.context.workspace, 'generated_tests') | 156 generated_test_dir = os.path.join(self.context.workspace, 'generated_tests') |
| 128 generated_test_dir = os.path.join(generated_test_dir, *test_path[:-1]) | 157 generated_test_dir = os.path.join(generated_test_dir, *test_path[:-1]) |
| 129 if not os.path.exists(generated_test_dir): | 158 if not os.path.exists(generated_test_dir): |
| 130 os.makedirs(generated_test_dir) | 159 os.makedirs(generated_test_dir) |
| 131 # Copy referenced files to generated tests directory. | 160 # Copy referenced files to generated tests directory. |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 154 '%s%s' % (name, extension)) | 183 '%s%s' % (name, extension)) |
| 155 with open(test_filename, 'w') as test_file: | 184 with open(test_filename, 'w') as test_file: |
| 156 for line in lines: | 185 for line in lines: |
| 157 if ' /// ' not in line: | 186 if ' /// ' not in line: |
| 158 print >> test_file, line | 187 print >> test_file, line |
| 159 else: | 188 else: |
| 160 print >> test_file, '//', line | 189 print >> test_file, '//', line |
| 161 tests['none'] = ('', test_filename) | 190 tests['none'] = ('', test_filename) |
| 162 return tests | 191 return tests |
| 163 | 192 |
| 164 class BrowserTestCase(test_case.StandardTestCase): | |
| 165 def __init__(self, context, path, filename, | |
| 166 fatal_static_type_errors, mode, arch): | |
| 167 super(test_case.BrowserTestCase, self).__init__(context, path, filename, mod e, arch) | |
| 168 self.fatal_static_type_errors = fatal_static_type_errors | |
| 169 | |
| 170 def IsBatchable(self): | |
| 171 return True | |
| 172 | |
| 173 def Run(self): | |
| 174 command = self.run_arch.GetCompileCommand(self.fatal_static_type_errors) | |
| 175 if command != None: | |
| 176 # We change the directory where dartc will be launched because | |
| 177 # it is not predictable on the location of the compiled file. In | |
| 178 # case the test is a web test, we make sure the app file is not | |
| 179 # in a subdirectory. | |
| 180 cwd = None | |
| 181 if self.run_arch.is_web_test: cwd = self.run_arch.temp_dir | |
| 182 command = command[:1] + self.context.flags + command[1:] | |
| 183 test_output = self.RunCommand(command, cwd=cwd) | |
| 184 | |
| 185 # If errors were found, fail fast and show compile errors: | |
| 186 if test_output.output.exit_code != 0: | |
| 187 if not self.context.keep_temporary_files: | |
| 188 self.run_arch.Cleanup() | |
| 189 return test_output | |
| 190 | |
| 191 command = self.run_arch.GetRunCommand(); | |
| 192 test_output = self.RunCommand(command) | |
| 193 # The return value of DumpRenderedTree does not indicate test failing, but | |
| 194 # the output does. | |
| 195 if self.run_arch.HasFailed(test_output.output.stdout): | |
| 196 test_output.output.exit_code = 1 | |
| 197 | |
| 198 # TODO(ngeoffray): We run out of space on the build bots for these tests if | |
| 199 # the temp directories are not removed right after running the test. | |
| 200 if not self.context.keep_temporary_files: | |
| 201 self.run_arch.Cleanup() | |
| 202 | |
| 203 return test_output | |
| 204 | |
| 205 | 193 |
| 206 class BrowserTestConfiguration(StandardTestConfiguration): | 194 class BrowserTestConfiguration(StandardTestConfiguration): |
| 195 """A configuration used to run tests inside a browser.""" | |
| 196 | |
| 207 def __init__(self, context, root, fatal_static_type_errors=False): | 197 def __init__(self, context, root, fatal_static_type_errors=False): |
| 208 super(BrowserTestConfiguration, self).__init__(context, root) | 198 super(BrowserTestConfiguration, self).__init__(context, root) |
| 209 self.fatal_static_type_errors = fatal_static_type_errors | 199 self.fatal_static_type_errors = fatal_static_type_errors |
| 210 | 200 |
| 211 def ListTests(self, current_path, path, mode, arch): | 201 def ListTests(self, current_path, path, mode, arch): |
| 202 """Searches for .dart files that qualify and returns list of TestCases.""" | |
|
ngeoffray
2011/10/13 08:03:38
*Test.dart
zundel
2011/10/13 11:07:44
Done.
| |
| 212 tests = [] | 203 tests = [] |
| 213 for root, dirs, files in os.walk(self.root): | 204 for root, unused_dirs, files in os.walk(self.root): |
| 214 for f in [x for x in files if self.IsTest(x)]: | 205 for f in [x for x in files if self.IsTest(x)]: |
| 215 relative = os.path.relpath(root, self.root).split(os.path.sep) | 206 relative = os.path.relpath(root, self.root).split(os.path.sep) |
| 216 test_path = current_path + relative + [os.path.splitext(f)[0]] | 207 test_path = current_path + relative + [os.path.splitext(f)[0]] |
| 217 if not self.Contains(path, test_path): | 208 if not self.Contains(path, test_path): |
| 218 continue | 209 continue |
| 219 tests.append(test_case.BrowserTestCase(self.context, | 210 tests.append(test_case.BrowserTestCase(self.context, |
| 220 test_path, join(root, f), self.fatal_static_type_errors, mode, | 211 test_path, |
| 221 arch)) | 212 os.path.join(root, f), |
| 222 atexit.register(lambda: self._cleanup(tests)) | 213 self.fatal_static_type_errors, |
| 214 mode, arch)) | |
| 215 atexit.register(lambda: self._Cleanup(tests)) | |
| 223 return tests | 216 return tests |
| 224 | 217 |
| 225 def IsTest(self, name): | 218 def IsTest(self, name): |
| 226 return name.endswith('_tests.dart') | 219 return name.endswith('_tests.dart') |
| 227 | 220 |
| 228 | 221 |
| 229 class CompilationTestConfiguration(test.TestConfiguration): | 222 class CompilationTestConfiguration(test.TestConfiguration): |
| 230 """ Configuration that searches specific directories for apps to compile | 223 """Configuration that searches specific directories for apps to compile. |
| 231 | 224 |
| 232 Expects a status file named dartc.status | 225 Expects a status file named dartc.status |
| 233 """ | 226 """ |
| 227 | |
| 234 def __init__(self, context, root): | 228 def __init__(self, context, root): |
| 235 super(CompilationTestConfiguration, self).__init__(context, root) | 229 super(CompilationTestConfiguration, self).__init__(context, root) |
| 236 | 230 |
| 237 def ListTests(self, current_path, path, mode, arch): | 231 def ListTests(self, current_path, path, mode, arch): |
| 232 """Searches for .dart files that qualify and returns list of TestCases.""" | |
|
ngeoffray
2011/10/13 08:03:38
*Test.dart
zundel
2011/10/13 11:07:44
Done.
| |
| 238 tests = [] | 233 tests = [] |
| 239 client_path = os.path.normpath(os.path.join(self.root, '..', '..')) | 234 client_path = os.path.normpath(os.path.join(self.root, '..', '..')) |
| 240 | 235 |
| 241 for src_dir in self.SourceDirs(): | 236 for src_dir in self.SourceDirs(): |
| 242 for root, dirs, files in os.walk(os.path.join(client_path, src_dir)): | 237 for root, dirs, files in os.walk(os.path.join(client_path, src_dir)): |
| 243 ignore_dirs = [d for d in dirs if d.startswith('.')] | 238 ignore_dirs = [d for d in dirs if d.startswith('.')] |
| 244 for d in ignore_dirs: | 239 for d in ignore_dirs: |
| 245 dirs.remove(d) | 240 dirs.remove(d) |
| 246 for f in files: | 241 for f in files: |
| 247 filename = [os.path.basename(client_path)] | 242 filename = [os.path.basename(client_path)] |
| 248 filename.extend(root[len(client_path) + 1:].split(os.path.sep)) | 243 filename.extend(root[len(client_path) + 1:].split(os.path.sep)) |
| 249 filename.append(f) # Remove .lib or .app suffix. | 244 filename.append(f) # Remove .lib or .app suffix. |
| 250 test_path = current_path + filename | 245 test_path = current_path + filename |
| 251 test_dart_file = os.path.join(root, f) | 246 test_dart_file = os.path.join(root, f) |
| 252 if ((not self.Contains(path, test_path)) | 247 if (not self.Contains(path, test_path) |
| 253 or (not self.IsTest(test_dart_file))): | 248 or not self.IsTest(test_dart_file)): |
| 254 continue | 249 continue |
| 255 tests.append(test_case.CompilationTestCase(test_path, | 250 tests.append(test_case.CompilationTestCase(test_path, |
| 256 self.context, | 251 self.context, |
| 257 test_dart_file, | 252 test_dart_file, |
| 258 mode, | 253 mode, |
| 259 arch)) | 254 arch)) |
| 260 atexit.register(lambda: self._cleanup(tests)) | 255 atexit.register(lambda: self._Cleanup(tests)) |
| 261 return tests | 256 return tests |
| 262 | 257 |
| 263 def SourceDirs(self): | 258 def SourceDirs(self): |
| 264 """ Returns a list of directories to scan for files to compile """ | 259 """Returns a list of directories to scan for files to compile.""" |
| 265 raise TestConfigurationError( | 260 raise TestConfigurationError( |
| 266 "Subclasses must implement SourceDirs()") | 261 'Subclasses must implement SourceDirs()') |
| 267 | 262 |
| 268 def IsTest(self, name): | 263 def IsTest(self, name): |
| 269 if (not name.endswith('.dart')): | 264 """Returns True if name looks like a test case to be compiled.""" |
|
ngeoffray
2011/10/13 08:03:38
looks like -> is
zundel
2011/10/13 11:07:44
Done.
| |
| 265 if not name.endswith('.dart'): | |
| 270 return False | 266 return False |
| 271 if (os.path.exists(name)): | 267 if os.path.exists(name): |
| 272 # TODO(dgrove): can we end reading the input early? | 268 # TODO(dgrove): can we end reading the input early? |
| 273 for line in fileinput.input(name): | 269 for line in fileinput.input(name): |
| 274 if (re.match('#', line)): | 270 if re.match('#', line): |
| 275 fileinput.close() | 271 fileinput.close() |
| 276 return True | 272 return True |
| 277 fileinput.close() | 273 fileinput.close() |
| 278 return False | 274 return False |
| 279 return False | 275 return False |
| 280 | 276 |
| 281 def GetTestStatus(self, sections, defs): | 277 def GetTestStatus(self, sections, defs): |
| 282 status = os.path.join(self.root, 'dartc.status') | 278 status = os.path.join(self.root, 'dartc.status') |
| 283 if os.path.exists(status): | 279 if os.path.exists(status): |
| 284 test.ReadConfigurationInto(status, sections, defs) | 280 test.ReadConfigurationInto(status, sections, defs) |
| 285 | 281 |
| 286 def _cleanup(self, tests): | 282 def _Cleanup(self, tests): |
| 287 if not utils.Daemonize(): return | 283 if not utils.Daemonize(): return |
| 288 os.execlp('rm', *(['rm', '-rf'] + [t.temp_dir for t in tests])) | 284 os.execlp('rm', *(['rm', '-rf'] + [t.temp_dir for t in tests])) |
| 289 raise | 285 raise |
| OLD | NEW |