Remove unused support files for old version of tools/test.py.
BUG= TEST= Review URL: https://chromiumcodereview.appspot.com//9360017 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@4033 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.BrowserTestConfiguration(
|
||||
context, root, fatal_static_type_errors=True)
|
||||
@@ -1,31 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return ClientCompilationTestConfiguration(context, root)
|
||||
|
||||
|
||||
class ClientCompilationTestConfiguration(
|
||||
test_configuration.CompilationTestConfiguration):
|
||||
def __init__(self, context, root):
|
||||
super(ClientCompilationTestConfiguration, self).__init__(context, root)
|
||||
|
||||
def SourceDirs(self):
|
||||
return [
|
||||
'async',
|
||||
'base',
|
||||
'box2d',
|
||||
'dom',
|
||||
'json',
|
||||
'observable',
|
||||
'samples',
|
||||
'streams',
|
||||
'testing',
|
||||
'tests',
|
||||
'touch',
|
||||
'util',
|
||||
'view',
|
||||
'weld']
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import os
|
||||
from os.path import join, exists
|
||||
import re
|
||||
|
||||
import test
|
||||
import utils
|
||||
|
||||
|
||||
|
||||
class JUnitTestCase(test.TestCase):
|
||||
def __init__(self, path, context, classnames, mode, arch):
|
||||
super(JUnitTestCase, self).__init__(context, path)
|
||||
self.classnames = classnames
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
|
||||
def IsBatchable(self):
|
||||
return False
|
||||
|
||||
def IsNegative(self):
|
||||
return False
|
||||
|
||||
def GetLabel(self):
|
||||
return "%s/%s %s" % (self.mode, self.arch, '/'.join(self.path))
|
||||
|
||||
def GetClassPath(self):
|
||||
third_party = join(self.context.workspace, 'third_party')
|
||||
jars = ['args4j/2.0.12/args4j-2.0.12.jar',
|
||||
'guava/r09/guava-r09.jar',
|
||||
'json/r2_20080312/json.jar',
|
||||
'rhino/1_7R3/js.jar',
|
||||
'hamcrest/v1_3/hamcrest-core-1.3.0RC2.jar',
|
||||
'hamcrest/v1_3/hamcrest-generator-1.3.0RC2.jar',
|
||||
'hamcrest/v1_3/hamcrest-integration-1.3.0RC2.jar',
|
||||
'hamcrest/v1_3/hamcrest-library-1.3.0RC2.jar',
|
||||
'junit/v4_8_2/junit.jar']
|
||||
jars = [ join(third_party, jar) for jar in jars ]
|
||||
buildroot = utils.GetBuildRoot(self.context.os, self.mode, self.arch)
|
||||
dartc_classes = [ os.path.join(buildroot, 'compiler', 'lib', 'dartc.jar'),
|
||||
os.path.join(buildroot, 'compiler', 'lib', 'corelib.jar') ]
|
||||
test_classes = os.path.join(buildroot, 'compiler-tests.jar')
|
||||
closure_jar = os.path.sep.join([buildroot, 'closure_out', 'compiler.jar'])
|
||||
return os.path.pathsep.join(
|
||||
dartc_classes + [test_classes] + [closure_jar] + jars)
|
||||
|
||||
def GetCommand(self):
|
||||
test_py = join(join(self.context.workspace, 'tools'), 'test.py')
|
||||
d8 = self.context.GetD8(self.mode, self.arch)
|
||||
# Note that it is important to run all the JUnit tests in the same process.
|
||||
# This way we have a chance of causing problems with static state early.
|
||||
return ['java', '-ea', '-classpath', self.GetClassPath(),
|
||||
'-Dcom.google.dart.runner.d8=' + d8,
|
||||
'-Dcom.google.dart.corelib.SharedTests.test_py=' + test_py,
|
||||
'org.junit.runner.JUnitCore'] + self.classnames
|
||||
|
||||
def GetName(self):
|
||||
return self.path[-1]
|
||||
|
||||
|
||||
class JUnitTestConfiguration(test.TestConfiguration):
|
||||
def __init__(self, context, root):
|
||||
super(JUnitTestConfiguration, self).__init__(context, root)
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
test_path = current_path + ['junit_tests']
|
||||
if not self.Contains(path, test_path):
|
||||
return []
|
||||
classes = []
|
||||
javatests_path = join(join(join(self.root, '..'), '..'), 'javatests')
|
||||
javatests_path = os.path.normpath(javatests_path)
|
||||
for root, dirs, files in os.walk(javatests_path):
|
||||
if root.endswith('com/google/dart/compiler/vm'):
|
||||
continue
|
||||
for f in [x for x in files if self.IsTest(x)]:
|
||||
classname = []
|
||||
classname.extend(root[len(javatests_path) + 1:].split(os.path.sep))
|
||||
classname.append(f[:-5]) # Remove .java suffix.
|
||||
classname = '.'.join(classname)
|
||||
if classname == 'com.google.dart.corelib.SharedTests':
|
||||
continue
|
||||
classes.append(classname)
|
||||
return [JUnitTestCase(test_path, self.context, classes, mode, arch)]
|
||||
|
||||
def IsTest(self, name):
|
||||
return name.endswith('Tests.java')
|
||||
|
||||
def GetTestStatus(self, sections, defs):
|
||||
status = join(self.root, 'dartc.status')
|
||||
if exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return JUnitTestConfiguration(context, root)
|
||||
@@ -1,67 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import os
|
||||
from os.path import join, exists
|
||||
|
||||
import test
|
||||
from testing import test_runner
|
||||
|
||||
class VmTestCase(test.TestCase):
|
||||
def __init__(self, path, context, mode, arch, flags):
|
||||
super(VmTestCase, self).__init__(context, path)
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
self.flags = flags
|
||||
|
||||
def IsNegative(self):
|
||||
# TODO(kasperl): Figure out how to support negative tests. Maybe
|
||||
# just have a TEST_CASE_NEGATIVE macro?
|
||||
return False
|
||||
|
||||
def GetLabel(self):
|
||||
return '%s%s vm %s' % (self.mode, self.arch, '/'.join(self.path))
|
||||
|
||||
def GetCommand(self):
|
||||
command = self.context.GetRunTests(self.mode, self.arch)
|
||||
command += [ self.GetName() ]
|
||||
# Add flags being set in the context.
|
||||
for flag in self.context.flags:
|
||||
command.append(flag)
|
||||
if self.flags: command += self.flags
|
||||
return command
|
||||
|
||||
def GetName(self):
|
||||
return self.path[-1]
|
||||
|
||||
|
||||
class VmTestConfiguration(test.TestConfiguration):
|
||||
def __init__(self, context, root):
|
||||
super(VmTestConfiguration, self).__init__(context, root)
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
if component != 'vm': return []
|
||||
run_tests = self.context.GetRunTests(mode, arch)
|
||||
output = test_runner.Execute(run_tests + ['--list'], self.context)
|
||||
if output.exit_code != 0:
|
||||
print output.stdout
|
||||
print output.stderr
|
||||
return [ ]
|
||||
tests = [ ]
|
||||
for test_line in output.stdout.strip().split('\n'):
|
||||
name_and_flags = test_line.split()
|
||||
name = name_and_flags[0]
|
||||
flags = name_and_flags[1:]
|
||||
test_path = current_path + [name]
|
||||
if self.Contains(path, test_path):
|
||||
tests.append(VmTestCase(test_path, self.context, mode, arch, flags))
|
||||
return tests
|
||||
|
||||
def GetTestStatus(self, sections, defs):
|
||||
status = join(self.root, 'vm.status')
|
||||
if exists(status): test.ReadConfigurationInto(status, sections, defs)
|
||||
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return VmTestConfiguration(context, root)
|
||||
@@ -1,143 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
import os
|
||||
from os.path import join, exists
|
||||
import re
|
||||
|
||||
import test
|
||||
import utils
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Co19TestCase(test.TestCase):
|
||||
def __init__(self, path, context, filename, mode, arch, component):
|
||||
super(Co19TestCase, self).__init__(context, path)
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
self.component = component
|
||||
self._is_negative = None
|
||||
|
||||
def IsNegative(self):
|
||||
if self._is_negative is None :
|
||||
contents = self.GetSource()
|
||||
if '@compile-error' in contents or '@runtime-error' in contents:
|
||||
self._is_negative = True
|
||||
else:
|
||||
self._is_negative = False
|
||||
return self._is_negative
|
||||
|
||||
def GetLabel(self):
|
||||
return "%s%s %s %s" % (self.mode, self.arch, self.component,
|
||||
"/".join(self.path))
|
||||
|
||||
def GetCommand(self):
|
||||
# Parse the options by reading the .dart source file.
|
||||
source = self.GetSource()
|
||||
vm_options = utils.ParseTestOptions(test.VM_OPTIONS_PATTERN, source,
|
||||
self.context.workspace)
|
||||
dart_options = utils.ParseTestOptions(test.DART_OPTIONS_PATTERN, source,
|
||||
self.context.workspace)
|
||||
|
||||
# Combine everything into a command array and return it.
|
||||
command = self.context.GetDart(self.mode, self.arch, self.component)
|
||||
command += self.context.flags
|
||||
if self.mode == 'release': command += ['--optimize']
|
||||
if vm_options: command += vm_options
|
||||
if dart_options: command += dart_options
|
||||
else:
|
||||
command += [self.filename]
|
||||
return command
|
||||
|
||||
def GetName(self):
|
||||
return self.path[-1]
|
||||
|
||||
def GetPath(self):
|
||||
return os.path.dirname(self.filename)
|
||||
|
||||
def GetSource(self):
|
||||
return file(self.filename).read()
|
||||
|
||||
|
||||
class Co19TestConfiguration(test.TestConfiguration):
|
||||
def __init__(self, context, root):
|
||||
super(Co19TestConfiguration, self).__init__(context, root)
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
tests = []
|
||||
src_dir = join(self.root, "src")
|
||||
strip = len(src_dir.split(os.path.sep))
|
||||
for root, dirs, files in os.walk(src_dir):
|
||||
ignore_dirs = [d for d in dirs if d.startswith('.')]
|
||||
for d in ignore_dirs:
|
||||
dirs.remove(d)
|
||||
for f in [x for x in files if self.IsTest(x)]:
|
||||
test_path = [] + current_path
|
||||
test_path.extend(root.split(os.path.sep)[strip:])
|
||||
test_name = short_name = f
|
||||
|
||||
# remove suffixes
|
||||
if short_name.endswith(".dart"):
|
||||
short_name = short_name[:-5] # Remove .dart suffix.
|
||||
else:
|
||||
raise Error('Unknown suffix in "%s", fix IsTest() predicate' % f)
|
||||
|
||||
test_path.append(short_name)
|
||||
|
||||
# test full name and shorted name matches given path pattern
|
||||
if self.Contains(path, test_path): pass
|
||||
elif self.Contains(path, test_path + [test_name]): pass
|
||||
else:
|
||||
continue
|
||||
|
||||
tests.append(Co19TestCase(test_path,
|
||||
self.context,
|
||||
join(root, f),
|
||||
mode,
|
||||
arch,
|
||||
component))
|
||||
return tests
|
||||
|
||||
_TESTNAME_PATTERN = re.compile(r'.*_t[0-9]{2}\.dart$')
|
||||
def IsTest(self, name):
|
||||
return self._TESTNAME_PATTERN.match(name)
|
||||
|
||||
def GetTestStatus(self, sections, defs):
|
||||
status = join(self.root, "co19-runtime.status")
|
||||
if exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
status = join(self.root, "co19-compiler.status")
|
||||
if exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
status = join(self.root, "co19-frog.status")
|
||||
if exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
status = join(self.root, "co19-leg.status")
|
||||
if exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
|
||||
def Contains(self, path, file):
|
||||
""" reimplemented for support '**' glob pattern """
|
||||
if len(path) > len(file):
|
||||
return
|
||||
# ** matches to any number of directories, a/**/d matches a/b/c/d
|
||||
# paths like a/**/x/**/b not allowed
|
||||
patterns = [p.pattern for p in path]
|
||||
if '**' in patterns:
|
||||
idx = patterns.index('**')
|
||||
patterns[idx : idx] = ['*'] * (len(file) - len(path))
|
||||
path = [test.Pattern(p) for p in patterns]
|
||||
|
||||
for i in xrange(len(path)):
|
||||
if not path[i].match(file[i]):
|
||||
return False
|
||||
return True
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return Co19TestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,139 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import test
|
||||
from testing import test_case,test_configuration
|
||||
import utils
|
||||
|
||||
from os.path import join, exists, isdir
|
||||
|
||||
def GeneratedName(src):
|
||||
return re.sub('\.dart$', '-generatedTest.dart', src)
|
||||
|
||||
class DartStubTestCase(test_case.StandardTestCase):
|
||||
def __init__(self, context, path, filename, mode, arch, component):
|
||||
super(DartStubTestCase, self).__init__(context, path, filename, mode, arch,
|
||||
component)
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
self.component = component
|
||||
|
||||
def IsBatchable(self):
|
||||
return False
|
||||
|
||||
def GetStubs(self):
|
||||
source = self.GetSource()
|
||||
stub_classes = utils.ParseTestOptions(test.ISOLATE_STUB_PATTERN, source,
|
||||
self.context.workspace)
|
||||
if stub_classes is None:
|
||||
return (None, None, None)
|
||||
(interface, _, classes) = stub_classes[0].partition(':')
|
||||
(interface, _, implementation) = interface.partition('+')
|
||||
return (interface, classes, implementation)
|
||||
|
||||
def IsFailureOutput(self, output):
|
||||
return output.exit_code != 0 or not '##DONE##' in output.stdout
|
||||
|
||||
def BeforeRun(self):
|
||||
if not self.context.generate:
|
||||
return
|
||||
(interface, classes, _) = self.GetStubs()
|
||||
if interface is None:
|
||||
return
|
||||
d = join(self.GetPath(), 'generated')
|
||||
if not isdir(d):
|
||||
os.mkdir(d)
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
src = join(self.GetPath(), interface)
|
||||
dest = join(self.GetPath(), GeneratedName(interface))
|
||||
(_, tmp) = tempfile.mkstemp()
|
||||
command = self.context.GetDartC(self.mode, self.arch)
|
||||
self.RunCommand(command + [ src,
|
||||
# dartc generates output even if it has no
|
||||
# output to generate.
|
||||
'-noincremental',
|
||||
'-out', tmpdir,
|
||||
'-isolate-stub-out', tmp,
|
||||
'-generate-isolate-stubs', classes ])
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
# Copy comments and # commands from the beginning of the source to
|
||||
# the beginning of the generated file, then copy the remaining
|
||||
# source to the end.
|
||||
d = open(dest, 'w')
|
||||
s = open(src, 'r')
|
||||
t = open(tmp, 'r')
|
||||
while True:
|
||||
line = s.readline()
|
||||
if not (re.match('^\s+$', line) or line.startswith('//')
|
||||
or line.startswith('#')):
|
||||
break
|
||||
d.write(line)
|
||||
d.write(t.read())
|
||||
os.remove(tmp)
|
||||
d.write(line)
|
||||
d.write(s.read())
|
||||
|
||||
def GetCommand(self):
|
||||
# Parse the options by reading the .dart source file.
|
||||
source = self.GetSource()
|
||||
vm_options = utils.ParseTestOptions(test.VM_OPTIONS_PATTERN, source,
|
||||
self.context.workspace)
|
||||
dart_options = utils.ParseTestOptions(test.DART_OPTIONS_PATTERN, source,
|
||||
self.context.workspace)
|
||||
(interface, _, implementation) = self.GetStubs()
|
||||
|
||||
# Combine everything into a command array and return it.
|
||||
command = self.context.GetDart(self.mode, self.arch, self.component)
|
||||
if interface is None:
|
||||
f = self.filename
|
||||
else:
|
||||
f = GeneratedName(interface)
|
||||
files = [ join(self.GetPath(), f) ]
|
||||
if vm_options: command += vm_options
|
||||
if dart_options: command += dart_options
|
||||
else: command += files
|
||||
return command
|
||||
|
||||
|
||||
class DartStubTestConfiguration(test_configuration.StandardTestConfiguration):
|
||||
def __init__(self, context, root):
|
||||
super(DartStubTestConfiguration, self).__init__(context, root)
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
dartc = self.context.GetDartC(mode, arch)
|
||||
self.context.generate = os.access(dartc[0], os.X_OK)
|
||||
tests = []
|
||||
for root, dirs, files in os.walk(join(self.root, 'src')):
|
||||
# Skip remnants from the subdirectory that used to be used for
|
||||
# generated code.
|
||||
if root.endswith('generated'):
|
||||
continue
|
||||
for f in [x for x in files if self.IsTest(x)]:
|
||||
# If we can generate code, do not use the checked-in generated
|
||||
# code. Conversely, if we cannot, then only use the
|
||||
# checked-in generated code.
|
||||
if self.context.generate == f.endswith('-generatedTest.dart'):
|
||||
continue
|
||||
test_path = current_path + [ f[:-5] ] # Remove .dart suffix.
|
||||
if not self.Contains(path, test_path):
|
||||
continue
|
||||
tests.append(DartStubTestCase(self.context,
|
||||
test_path,
|
||||
join(root, f),
|
||||
mode,
|
||||
arch,
|
||||
component))
|
||||
return tests
|
||||
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return DartStubTestConfiguration(context, root)
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
#
|
||||
|
||||
"""
|
||||
Runs a Dart unit test in different configurations: dartium, chromium, ia32, x64,
|
||||
arm, simarm, and dartc. Example:
|
||||
|
||||
run.py --arch=dartium --mode=release --test=Test.dart
|
||||
"""
|
||||
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
from testing import architecture
|
||||
import utils
|
||||
|
||||
|
||||
def AreOptionsValid(options):
|
||||
if not options.arch in ['ia32', 'x64', 'arm', 'simarm', 'dartc', 'dartium',
|
||||
'chromium', 'frogium']:
|
||||
print 'Unknown arch %s' % options.arch
|
||||
return None
|
||||
|
||||
return options.test
|
||||
|
||||
|
||||
def Flags():
|
||||
result = optparse.OptionParser()
|
||||
result.add_option("-v", "--verbose",
|
||||
help="Print messages",
|
||||
default=False,
|
||||
action="store_true")
|
||||
result.add_option("-t", "--test",
|
||||
help="App or Dart file containing the test",
|
||||
type="string",
|
||||
action="store",
|
||||
default=None)
|
||||
result.add_option("--arch",
|
||||
help="The architecture to run tests for",
|
||||
metavar="[ia32,x64,arm,simarm,dartc,chromium,dartium]",
|
||||
default=utils.GuessArchitecture())
|
||||
result.add_option("-m", "--mode",
|
||||
help="The test modes in which to run",
|
||||
metavar='[debug,release]',
|
||||
default='debug')
|
||||
result.set_usage("run.py --arch ARCH --mode MODE -t TEST")
|
||||
return result
|
||||
|
||||
|
||||
def Main():
|
||||
parser = Flags()
|
||||
(options, args) = parser.parse_args()
|
||||
if not AreOptionsValid(options):
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
return architecture.GetArchitecture(options.arch, options.mode,
|
||||
options.test).RunTest(options.verbose)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(Main())
|
||||
Regular → Executable
@@ -1,605 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
#
|
||||
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
import utils
|
||||
|
||||
OS_GUESS = utils.GuessOS()
|
||||
|
||||
HTML_CONTENTS = """
|
||||
<html>
|
||||
<head>
|
||||
<title> Test %(title)s </title>
|
||||
<style>
|
||||
.unittest-table { font-family:monospace; border:1px; }
|
||||
.unittest-pass { background: #6b3;}
|
||||
.unittest-fail { background: #d55;}
|
||||
.unittest-error { background: #a11;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Running %(title)s </h1>
|
||||
<script type="text/javascript" src="%(controller_script)s"></script>
|
||||
<script type="text/javascript">
|
||||
// If nobody intercepts the error, finish the test.
|
||||
onerror = function() { window.layoutTestController.notifyDone() };
|
||||
|
||||
document.onreadystatechange = function() {
|
||||
if (document.readyState != "loaded") return;
|
||||
// If 'startedDartTest' is not set, that means that the test did not have
|
||||
// a chance to load. This will happen when a load error occurs in the VM.
|
||||
// Give the machine time to start up.
|
||||
setTimeout(function() {
|
||||
// A window.postMessage might have been enqueued after this timeout.
|
||||
// Just sleep another time to give the browser the time to process the
|
||||
// posted message.
|
||||
setTimeout(function() {
|
||||
if (layoutTestController && !layoutTestController.startedDartTest) {
|
||||
layoutTestController.notifyDone();
|
||||
}
|
||||
}, 0);
|
||||
}, 50);
|
||||
};
|
||||
</script>
|
||||
<script type="%(script_type)s" src="%(source_script)s"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
DART_TEST_AS_LIBRARY = """
|
||||
#library('test');
|
||||
#source('%(test)s');
|
||||
"""
|
||||
|
||||
DART_CONTENTS = """
|
||||
#library('test');
|
||||
|
||||
#import('%(dom_library)s');
|
||||
#import('%(test_framework)s');
|
||||
|
||||
#import('%(library)s', prefix: "Test");
|
||||
|
||||
waitForDone() {
|
||||
window.postMessage('unittest-suite-wait-for-done', '*');
|
||||
}
|
||||
|
||||
pass() {
|
||||
document.body.innerHTML = 'PASS';
|
||||
window.postMessage('unittest-suite-done', '*');
|
||||
}
|
||||
|
||||
fail(e, trace) {
|
||||
document.body.innerHTML = 'FAIL: $e, $trace';
|
||||
window.postMessage('unittest-suite-done', '*');
|
||||
}
|
||||
|
||||
main() {
|
||||
bool needsToWait = false;
|
||||
bool mainIsFinished = false;
|
||||
TestRunner.waitForDoneCallback = () { needsToWait = true; };
|
||||
TestRunner.doneCallback = () {
|
||||
if (mainIsFinished) {
|
||||
pass();
|
||||
} else {
|
||||
needsToWait = false;
|
||||
}
|
||||
};
|
||||
try {
|
||||
Test.main();
|
||||
if (needsToWait) {
|
||||
waitForDone();
|
||||
} else {
|
||||
pass();
|
||||
}
|
||||
mainIsFinished = true;
|
||||
} catch(var e, var trace) {
|
||||
fail(e, trace);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# Patterns for matching test options in .dart files.
|
||||
DART_OPTIONS_PATTERN = re.compile(r'// DartOptions=(.*)')
|
||||
|
||||
# Pattern for checking if the test is a web test.
|
||||
DOM_IMPORT_PATTERN = re.compile(r'#import.*(dart:(dom|html)|html\.dart).*\);',
|
||||
re.MULTILINE)
|
||||
|
||||
# Pattern for matching the output of a browser test.
|
||||
BROWSER_OUTPUT_PASS_PATTERN = re.compile(r'^Content-Type: text/plain\nPASS$',
|
||||
re.MULTILINE)
|
||||
|
||||
# Pattern for matching flaky errors of browser tests. xvfb-run by default uses
|
||||
# DISPLAY=:99, we keep that in the error pattern to avoid matching real
|
||||
# errors when DISPLAY is set incorrectly.
|
||||
BROWSER_FLAKY_DISPLAY_ERR_PATTERN = re.compile(
|
||||
r'Gtk-WARNING \*\*: cannot open display: :99', re.MULTILINE)
|
||||
|
||||
# Pattern for checking if the test is a library in itself.
|
||||
LIBRARY_DEFINITION_PATTERN = re.compile(r'^#library\(.*\);',
|
||||
re.MULTILINE)
|
||||
SOURCE_OR_IMPORT_PATTERN = re.compile(r'^#(source|import)\(.*\);',
|
||||
re.MULTILINE)
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for exceptions in this module."""
|
||||
pass
|
||||
|
||||
|
||||
def _IsWebTest(source):
|
||||
"""Returns True if the source includes a dart dom library #import."""
|
||||
return DOM_IMPORT_PATTERN.search(source)
|
||||
|
||||
|
||||
def IsLibraryDefinition(test, source):
|
||||
"""Returns True if the source has a #library statement."""
|
||||
if LIBRARY_DEFINITION_PATTERN.search(source):
|
||||
return True
|
||||
if SOURCE_OR_IMPORT_PATTERN.search(source):
|
||||
print ('WARNING for %s: Browser tests need a #library '
|
||||
'for a file that #import or #source' % test)
|
||||
return False
|
||||
|
||||
|
||||
class Architecture(object):
|
||||
"""Definitions for different ways to test based on the component flag."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
self.root_path = root_path
|
||||
self.arch = arch
|
||||
self.mode = mode
|
||||
self.component = component
|
||||
self.test = test
|
||||
self.build_root = utils.GetBuildRoot(OS_GUESS, self.mode, self.arch)
|
||||
source = file(test).read()
|
||||
self.vm_options = []
|
||||
self.dart_options = utils.ParseTestOptions(DART_OPTIONS_PATTERN,
|
||||
source,
|
||||
root_path)
|
||||
self.is_web_test = _IsWebTest(source)
|
||||
self.temp_dir = None
|
||||
|
||||
def GetVMOption(self, option):
|
||||
for flag in self.vm_options:
|
||||
if flag.startswith('--%s=' % option):
|
||||
return flag.split('=')[1]
|
||||
return None
|
||||
|
||||
def HasFatalTypeErrors(self):
|
||||
"""Returns True if this type of component supports --fatal-type-errors."""
|
||||
return False
|
||||
|
||||
def GetTestFrameworkPath(self):
|
||||
"""Path to dart source (TestFramework.dart) for testing framework."""
|
||||
return os.path.join(self.root_path, 'tests', 'isolate', 'src',
|
||||
'TestFramework.dart')
|
||||
|
||||
|
||||
class BrowserArchitecture(Architecture):
|
||||
"""Architecture that runs compiled dart->JS through a browser."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(BrowserArchitecture, self).__init__(root_path, arch, mode, component,
|
||||
test)
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
if not self.is_web_test: self.GenerateWebTestScript()
|
||||
|
||||
def GetTestScriptFile(self):
|
||||
"""Returns the name of the .dart file to compile."""
|
||||
if self.is_web_test: return os.path.abspath(self.test)
|
||||
return os.path.join(self.temp_dir, 'test.dart')
|
||||
|
||||
def GetHtmlContents(self):
|
||||
"""Fills in the HTML_CONTENTS template with info for this architecture."""
|
||||
script_type = self.GetScriptType()
|
||||
controller_path = os.path.join(self.root_path, 'client', 'testing',
|
||||
'unittest', 'test_controller.js')
|
||||
return HTML_CONTENTS % {
|
||||
'title': self.test,
|
||||
'controller_script': controller_path,
|
||||
'script_type': script_type,
|
||||
'source_script': self.GetScriptPath()
|
||||
}
|
||||
|
||||
def GetHtmlPath(self):
|
||||
"""Creates a path for the generated .html file.
|
||||
|
||||
Resources for web tests are relative to the 'html' file. We
|
||||
output the 'html' file in the 'out' directory instead of the temporary
|
||||
directory because we can easily go the the resources in 'client' through
|
||||
'out'.
|
||||
|
||||
Returns:
|
||||
Created path for the generated .html file.
|
||||
"""
|
||||
if self.is_web_test:
|
||||
html_path = os.path.join(self.root_path, 'client', self.build_root)
|
||||
if not os.path.exists(html_path):
|
||||
os.makedirs(html_path)
|
||||
return html_path
|
||||
|
||||
return self.temp_dir
|
||||
|
||||
def GetTestContents(self, library_file):
|
||||
"""Pastes a preamble on the front of the .dart file before testing."""
|
||||
unittest_path = os.path.join(self.root_path, 'client', 'testing',
|
||||
'unittest', 'unittest.dart')
|
||||
|
||||
if self.component == 'chromium':
|
||||
dom_path = os.path.join(self.root_path, 'client', 'testing',
|
||||
'unittest', 'dom_for_unittest.dart')
|
||||
else:
|
||||
dom_path = os.path.join('dart:dom')
|
||||
|
||||
test_framework_path = self.GetTestFrameworkPath()
|
||||
test_path = os.path.abspath(self.test)
|
||||
|
||||
inputs = {
|
||||
'unittest': unittest_path,
|
||||
'test': test_path,
|
||||
'dom_library': dom_path,
|
||||
'test_framework': test_framework_path,
|
||||
'library': library_file
|
||||
}
|
||||
return DART_CONTENTS % inputs
|
||||
|
||||
def GenerateWebTestScript(self):
|
||||
"""Creates a .dart file to run in the test."""
|
||||
if IsLibraryDefinition(self.test, file(self.test).read()):
|
||||
library_file = os.path.abspath(self.test)
|
||||
else:
|
||||
library_file = 'test_as_library.dart'
|
||||
test_as_library = DART_TEST_AS_LIBRARY % {
|
||||
'test': os.path.abspath(self.test)
|
||||
}
|
||||
test_as_library_file = os.path.join(self.temp_dir, library_file)
|
||||
f = open(test_as_library_file, 'w')
|
||||
f.write(test_as_library)
|
||||
f.close()
|
||||
|
||||
app_output_file = self.GetTestScriptFile()
|
||||
f = open(app_output_file, 'w')
|
||||
f.write(self.GetTestContents(library_file))
|
||||
f.close()
|
||||
|
||||
def GetRunCommand(self, fatal_static_type_errors=False):
|
||||
"""Returns a command line to execute for the test."""
|
||||
fatal_static_type_errors = fatal_static_type_errors # shutup lint!
|
||||
# Find DRT
|
||||
# For some reason, DRT needs to be called via an absolute path
|
||||
drt_location = self.GetVMOption('browser')
|
||||
if drt_location is not None:
|
||||
drt_location = os.path.abspath(drt_location)
|
||||
else:
|
||||
drt_location = os.path.join(self.root_path, 'client', 'tests', 'drt',
|
||||
'DumpRenderTree')
|
||||
|
||||
# On Mac DumpRenderTree is a .app folder
|
||||
if platform.system() == 'Darwin':
|
||||
drt_location += '.app/Contents/MacOS/DumpRenderTree'
|
||||
|
||||
drt_flags = ['--no-timeout']
|
||||
if len(self.vm_options) > 0:
|
||||
dart_flags = '--dart-flags='
|
||||
dart_flags += ' '.join(self.vm_options)
|
||||
drt_flags.append(dart_flags)
|
||||
|
||||
html_output_file = os.path.join(self.GetHtmlPath(), self.GetHtmlName())
|
||||
f = open(html_output_file, 'w')
|
||||
f.write(self.GetHtmlContents())
|
||||
f.close()
|
||||
|
||||
drt_flags.append(html_output_file)
|
||||
|
||||
return [drt_location] + drt_flags
|
||||
|
||||
def HasFailed(self, output):
|
||||
"""Return True if the 'PASS' result string isn't in the output."""
|
||||
return not BROWSER_OUTPUT_PASS_PATTERN.search(output)
|
||||
|
||||
def WasFlakyDrt(self, error):
|
||||
"""Return whether the error indicates a flaky error from running.
|
||||
DumpRenderTree within xvfb-run.
|
||||
"""
|
||||
return BROWSER_FLAKY_DISPLAY_ERR_PATTERN.search(error)
|
||||
|
||||
def RunTest(self, verbose):
|
||||
"""Calls GetRunCommand() and executes the returned commandline.
|
||||
|
||||
Args:
|
||||
verbose: if True, print additional diagnostics to stdout.
|
||||
|
||||
Returns:
|
||||
Return code from executable. 0 == PASS, 253 = CRASH, anything
|
||||
else is treated as FAIL
|
||||
"""
|
||||
retcode = self.Compile()
|
||||
if retcode != 0: return 1
|
||||
|
||||
command = self.GetRunCommand()
|
||||
|
||||
unused_status, output, err = ExecutePipedCommand(command, verbose)
|
||||
if not self.HasFailed(output):
|
||||
return 0
|
||||
|
||||
# TODO(sigmund): print better error message, including how to run test
|
||||
# locally, and translate error traces using source map info.
|
||||
print '(FAIL) test page:\033[31m %s \033[0m' % command[2]
|
||||
if verbose:
|
||||
print 'Additional info: '
|
||||
print output
|
||||
print err
|
||||
return 1
|
||||
|
||||
def Cleanup(self):
|
||||
"""Removes temporary files created for the test."""
|
||||
if self.temp_dir:
|
||||
shutil.rmtree(self.temp_dir)
|
||||
self.temp_dir = None
|
||||
|
||||
|
||||
class ChromiumArchitecture(BrowserArchitecture):
|
||||
"""Architecture that runs compiled dart->JS through a chromium DRT."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(ChromiumArchitecture, self).__init__(root_path, arch, mode, component, test)
|
||||
|
||||
def GetScriptType(self):
|
||||
return 'text/javascript'
|
||||
|
||||
def GetScriptPath(self):
|
||||
"""Returns the name of the output .js file to create."""
|
||||
path = self.GetTestScriptFile()
|
||||
return os.path.abspath(os.path.join(self.temp_dir,
|
||||
os.path.basename(path) + '.js'))
|
||||
|
||||
def GetHtmlName(self):
|
||||
"""Returns the name of the output .html file to create."""
|
||||
relpath = os.path.relpath(self.test, self.root_path)
|
||||
return relpath.replace(os.sep, '_') + '.html'
|
||||
|
||||
def Compile(self):
|
||||
return ExecuteCommand(self.GetCompileCommand())
|
||||
|
||||
class DartcChromiumArchitecture(ChromiumArchitecture):
|
||||
"""ChromiumArchitecture that compiles code using dartc."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(DartcChromiumArchitecture, self).__init__(
|
||||
root_path, arch, mode, component, test)
|
||||
|
||||
def GetCompileCommand(self, fatal_static_type_errors=False):
|
||||
"""Returns cmdline as an array to invoke the compiler on this test."""
|
||||
|
||||
# We need an absolute path because the compilation will run
|
||||
# in a temporary directory.
|
||||
build_root = utils.GetBuildRoot(OS_GUESS, self.mode, 'ia32')
|
||||
dartc = os.path.abspath(os.path.join(build_root, 'compiler', 'bin',
|
||||
'dartc'))
|
||||
if utils.IsWindows(): dartc += '.exe'
|
||||
cmd = [dartc, '--work', self.temp_dir]
|
||||
if self.mode == 'release':
|
||||
cmd += ['--optimize']
|
||||
cmd += self.vm_options
|
||||
cmd += ['--out', self.GetScriptPath()]
|
||||
if fatal_static_type_errors:
|
||||
# TODO(zundel): update to --fatal_type_errors for both VM and Compiler
|
||||
cmd.append('-fatal-type-errors')
|
||||
cmd.append(self.GetTestScriptFile())
|
||||
return cmd
|
||||
|
||||
|
||||
class FrogChromiumArchitecture(ChromiumArchitecture):
|
||||
"""ChromiumArchitecture that compiles code using frog."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(FrogChromiumArchitecture, self).__init__(
|
||||
root_path, arch, mode, component, test)
|
||||
|
||||
def GetCompileCommand(self, fatal_static_type_errors=False):
|
||||
"""Returns cmdline as an array to invoke the compiler on this test."""
|
||||
|
||||
# Get a frog executable from the command line. Default to frogsh.
|
||||
# We need an absolute path because the compilation will run
|
||||
# in a temporary directory.
|
||||
frog = self.GetVMOption('frog')
|
||||
if frog is not None:
|
||||
frog = os.path.abspath(frog)
|
||||
else:
|
||||
frog = os.path.abspath(utils.GetDartRunner(self.mode, self.arch, 'frogsh'))
|
||||
frog_libdir = self.GetVMOption('froglib')
|
||||
if frog_libdir is not None:
|
||||
frog_libdir = os.path.abspath(frog_libdir)
|
||||
else:
|
||||
frog_libdir = os.path.abspath(os.path.join(self.root_path, 'frog', 'lib'))
|
||||
cmd = [frog,
|
||||
'--libdir=%s' % frog_libdir,
|
||||
'--compile-only',
|
||||
'--out=%s' % self.GetScriptPath()]
|
||||
cmd.extend(self.vm_options)
|
||||
cmd.append(self.GetTestScriptFile())
|
||||
return cmd
|
||||
|
||||
|
||||
class DartiumArchitecture(BrowserArchitecture):
|
||||
"""Architecture that runs dart in an VM embedded in DumpRenderTree."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(DartiumArchitecture, self).__init__(root_path, arch, mode, component, test)
|
||||
|
||||
def GetScriptType(self):
|
||||
return 'application/dart'
|
||||
|
||||
def GetScriptPath(self):
|
||||
return 'file:///' + self.GetTestScriptFile()
|
||||
|
||||
def GetHtmlName(self):
|
||||
path = os.path.relpath(self.test, self.root_path).replace(os.sep, '_')
|
||||
return path + '.dartium.html'
|
||||
|
||||
def GetCompileCommand(self, fatal_static_type_errors=False):
|
||||
fatal_static_type_errors = fatal_static_type_errors # shutup lint!
|
||||
return None
|
||||
|
||||
def Compile(self):
|
||||
return 0
|
||||
|
||||
|
||||
class WebDriverArchitecture(FrogChromiumArchitecture):
|
||||
"""Architecture that runs compiled dart->JS (via frog) through a variety of
|
||||
real browsers using WebDriver."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(WebDriverArchitecture, self).__init__(root_path, arch, mode,
|
||||
component, test)
|
||||
|
||||
def GetRunCommand(self, fatal_static_type_errors=False):
|
||||
"""Returns a command line to execute for the test."""
|
||||
flags = self.vm_options
|
||||
browser_flag = 'chrome'
|
||||
if 'ff' in flags or 'firefox' in flags:
|
||||
browser_flag = 'ff'
|
||||
elif 'ie' in flags or 'explorer' in flags or 'internet-explorer' in flags:
|
||||
browser_flag = 'ie'
|
||||
elif 'safari' in flags:
|
||||
browser_flag = 'safari'
|
||||
|
||||
selenium_location = os.path.join(self.root_path, 'tools', 'testing',
|
||||
'run_selenium.py')
|
||||
|
||||
html_output_file = os.path.join(self.GetHtmlPath(), self.GetHtmlName())
|
||||
f = open(html_output_file, 'w')
|
||||
f.write(self.GetHtmlContents())
|
||||
f.close()
|
||||
return [selenium_location, '--out', html_output_file, '--browser',
|
||||
browser_flag]
|
||||
|
||||
|
||||
class StandaloneArchitecture(Architecture):
|
||||
"""Base class for architectures that run tests without a browser."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(StandaloneArchitecture, self).__init__(root_path, arch, mode, component,
|
||||
test)
|
||||
|
||||
def GetExecutable(self):
|
||||
"""Returns the path to the Dart test runner (executes the .dart file)."""
|
||||
return utils.GetDartRunner(self.mode, self.arch, self.component)
|
||||
|
||||
|
||||
def GetCompileCommand(self, fatal_static_type_errors=False):
|
||||
fatal_static_type_errors = fatal_static_type_errors # shutup lint!
|
||||
return None
|
||||
|
||||
def GetOptions(self):
|
||||
return []
|
||||
|
||||
def GetRunCommand(self, fatal_static_type_errors=False):
|
||||
"""Returns a command line to execute for the test."""
|
||||
dart = self.GetExecutable()
|
||||
command = [dart] + self.GetOptions() + self.vm_options
|
||||
if fatal_static_type_errors:
|
||||
command += self.GetFatalTypeErrorsFlags()
|
||||
|
||||
if self.dart_options:
|
||||
command += self.dart_options
|
||||
else:
|
||||
command += [self.test]
|
||||
|
||||
return command
|
||||
|
||||
def GetFatalTypeErrorsFlags(self):
|
||||
return []
|
||||
|
||||
def RunTest(self, verbose):
|
||||
command = self.GetRunCommand()
|
||||
return ExecuteCommand(command, verbose)
|
||||
|
||||
def Cleanup(self):
|
||||
return
|
||||
|
||||
|
||||
class LegArchitecture(StandaloneArchitecture):
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(LegArchitecture, self).__init__(root_path, arch, mode, component,
|
||||
test)
|
||||
def GetOptions(self):
|
||||
return ['--leg_only']
|
||||
|
||||
def GetExecutable(self):
|
||||
"""Returns the path to the Dart test runner (executes the .dart file)."""
|
||||
return utils.GetDartRunner(self.mode, self.arch, 'frog')
|
||||
|
||||
|
||||
# Long term, we should do the running machinery that is currently in
|
||||
# DartRunner.java
|
||||
class DartcArchitecture(StandaloneArchitecture):
|
||||
"""Runs the Dart ->JS compiler then runs the result in a standalone JS VM."""
|
||||
|
||||
def __init__(self, root_path, arch, mode, component, test):
|
||||
super(DartcArchitecture, self).__init__(root_path, arch, mode, component, test)
|
||||
|
||||
def GetOptions(self):
|
||||
if self.mode == 'release': return ['--optimize']
|
||||
return []
|
||||
|
||||
def GetFatalTypeErrorsFlags(self):
|
||||
return ['--fatal-type-errors']
|
||||
|
||||
def HasFatalTypeErrors(self):
|
||||
return True
|
||||
|
||||
|
||||
def ExecutePipedCommand(cmd, verbose):
|
||||
"""Execute a command in a subprocess."""
|
||||
if verbose:
|
||||
print 'Executing: ' + ' '.join(cmd)
|
||||
pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = pipe.communicate()
|
||||
if pipe.returncode != 0 and verbose:
|
||||
print 'Execution failed: ' + output + '\n' + err
|
||||
print output
|
||||
print err
|
||||
return pipe.returncode, output, err
|
||||
|
||||
|
||||
def ExecuteCommand(cmd, verbose=False):
|
||||
"""Execute a command in a subprocess."""
|
||||
if verbose: print 'Executing: ' + ' '.join(cmd)
|
||||
return subprocess.call(cmd)
|
||||
|
||||
|
||||
def GetArchitecture(arch, mode, component, test):
|
||||
root_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '..'))
|
||||
if component == 'chromium':
|
||||
return DartcChromiumArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component == 'dartium':
|
||||
return DartiumArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component == 'frogium':
|
||||
return FrogChromiumArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component == 'webdriver':
|
||||
return WebDriverArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component in ['vm', 'frog', 'frogsh']:
|
||||
return StandaloneArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component == 'leg':
|
||||
return LegArchitecture(root_path, arch, mode, component, test)
|
||||
|
||||
elif component == 'dartc':
|
||||
return DartcArchitecture(root_path, arch, mode, component, test)
|
||||
@@ -1,178 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
"""Common TestCase subclasses used to define a single test."""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import test
|
||||
from testing import architecture
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class StandardTestCase(test.TestCase):
|
||||
"""A test case defined by a *Test.dart file."""
|
||||
|
||||
def __init__(self, context, path, filename, mode, arch, component,
|
||||
vm_options=None):
|
||||
super(StandardTestCase, self).__init__(context, path)
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
self.component = component
|
||||
self.run_arch = architecture.GetArchitecture(self.arch, self.mode,
|
||||
self.component,
|
||||
self.filename)
|
||||
for flag in context.flags:
|
||||
self.run_arch.vm_options.append(flag)
|
||||
|
||||
if vm_options:
|
||||
for flag in vm_options:
|
||||
self.run_arch.vm_options.append(flag)
|
||||
|
||||
def IsNegative(self):
|
||||
return self.GetName().endswith('NegativeTest')
|
||||
|
||||
def GetLabel(self):
|
||||
return '%s%s %s %s' % (self.mode, self.arch, self.component,
|
||||
'/'.join(self.path))
|
||||
|
||||
def GetCommand(self):
|
||||
return self.run_arch.GetRunCommand()
|
||||
|
||||
def GetName(self):
|
||||
return self.path[-1]
|
||||
|
||||
def GetPath(self):
|
||||
return os.path.dirname(self.filename)
|
||||
|
||||
def GetSource(self):
|
||||
return file(self.filename).read()
|
||||
|
||||
def Cleanup(self):
|
||||
# TODO(ngeoffray): We run out of space on the build bots for these tests if
|
||||
# the temp directories are not removed right after running the test.
|
||||
if not self.context.keep_temporary_files:
|
||||
self.run_arch.Cleanup()
|
||||
|
||||
|
||||
class MultiTestCase(StandardTestCase):
|
||||
"""Multiple test cases defined within a single *Test.dart file."""
|
||||
|
||||
def __init__(self, context, path, filename, kind, mode, arch, component,
|
||||
vm_options = None):
|
||||
super(MultiTestCase, self).__init__(context, path, filename, mode, arch,
|
||||
component, vm_options)
|
||||
self.kind = kind
|
||||
|
||||
def GetCommand(self):
|
||||
"""Returns a commandline to execute to perform the test."""
|
||||
return self.run_arch.GetRunCommand(
|
||||
fatal_static_type_errors=(self.kind == 'static type error'))
|
||||
|
||||
def IsNegative(self):
|
||||
"""Determine if this is a negative test. by looking at @ directives.
|
||||
|
||||
A negative test is considered to pas if its outcome is FAIL.
|
||||
|
||||
Returns:
|
||||
True if this is a negative test.
|
||||
"""
|
||||
if self.kind == 'compile-time error':
|
||||
return True
|
||||
if self.kind == 'runtime error':
|
||||
return True
|
||||
if self.kind == 'static type error':
|
||||
return self.run_arch.HasFatalTypeErrors()
|
||||
return False
|
||||
|
||||
|
||||
class BrowserTestCase(StandardTestCase):
|
||||
"""A test case that executes inside DumpRenderTree or a browser."""
|
||||
|
||||
def __init__(self, context, path, filename,
|
||||
fatal_static_type_errors, mode, arch, component, vm_options=None):
|
||||
super(BrowserTestCase, self).__init__(
|
||||
context, path, filename, mode, arch, component, vm_options)
|
||||
self.fatal_static_type_errors = fatal_static_type_errors
|
||||
|
||||
def Run(self):
|
||||
"""Optionally compiles and then runs the specified test."""
|
||||
command = self.run_arch.GetCompileCommand(self.fatal_static_type_errors)
|
||||
if command:
|
||||
# We change the directory where dartc will be launched because
|
||||
# it is not predictable on the location of the compiled file. In
|
||||
# case the test is a web test, we make sure the app file is not
|
||||
# in a subdirectory.
|
||||
cwd = None
|
||||
if self.run_arch.is_web_test: cwd = self.run_arch.temp_dir
|
||||
command = command[:1] + self.context.flags + command[1:]
|
||||
test_output = self.RunCommand(command, cwd=cwd, cleanup=False)
|
||||
|
||||
# If errors were found, fail fast and show compile errors:
|
||||
if test_output.output.exit_code != 0:
|
||||
return test_output
|
||||
|
||||
command = self.run_arch.GetRunCommand()
|
||||
# Don't clean up just in case test turned out flaky and we want to retry.
|
||||
test_output = self.RunCommand(command, cleanup=False)
|
||||
# The return value of DumpRenderedTree does not indicate test failing, but
|
||||
# the output does.
|
||||
if self.run_arch.HasFailed(test_output.output.stdout):
|
||||
test_output.output.exit_code = 1
|
||||
# DumpRenderTree is sometimes flaky in xvfb-run, try again in that case.
|
||||
if (self.run_arch.WasFlakyDrt(test_output.output.stderr)):
|
||||
print "\nFlaky Gtw-WARNING error found, trying again..."
|
||||
test_output = self.RunCommand(command, cleanup=False)
|
||||
if self.run_arch.HasFailed(test_output.output.stdout):
|
||||
test_output.output.exit_code = 1
|
||||
self.Cleanup();
|
||||
|
||||
return test_output
|
||||
|
||||
|
||||
class CompilationTestCase(test.TestCase):
|
||||
"""Run the dartc compiler on a given top level .dart file."""
|
||||
|
||||
def __init__(self, path, context, filename, mode, arch, component):
|
||||
super(CompilationTestCase, self).__init__(context, path)
|
||||
self.filename = filename
|
||||
self.mode = mode
|
||||
self.arch = arch
|
||||
self.component = component
|
||||
self.run_arch = architecture.GetArchitecture(self.arch,
|
||||
self.mode,
|
||||
self.component,
|
||||
self.filename)
|
||||
self.temp_dir = tempfile.mkdtemp(prefix='dartc-output-')
|
||||
|
||||
def IsNegative(self):
|
||||
return False
|
||||
|
||||
def GetLabel(self):
|
||||
return '%s/%s %s %s' % (self.mode, self.arch, self.component,
|
||||
'/'.join(self.path))
|
||||
|
||||
def GetCommand(self):
|
||||
"""Returns a command line to run the test."""
|
||||
cmd = self.context.GetDartC(self.mode, self.arch)
|
||||
cmd += self.context.flags
|
||||
cmd += ['-check-only',
|
||||
'-fatal-type-errors',
|
||||
'-Werror',
|
||||
'-out', self.temp_dir,
|
||||
self.filename]
|
||||
|
||||
return cmd
|
||||
|
||||
def GetName(self):
|
||||
return self.path[-1]
|
||||
|
||||
def Cleanup(self):
|
||||
if not self.context.keep_temporary_files:
|
||||
self.run_arch.Cleanup()
|
||||
@@ -1,326 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
"""Common Testconfiguration subclasses used to define a class of tests."""
|
||||
|
||||
import atexit
|
||||
import fileinput
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
|
||||
import test
|
||||
from testing import test_case
|
||||
import utils
|
||||
|
||||
|
||||
# Patterns for matching test options in .dart files.
|
||||
VM_OPTIONS_PATTERN = re.compile(r"// VMOptions=(.*)")
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TestConfigurationError(Error):
|
||||
pass
|
||||
|
||||
|
||||
class StandardTestConfiguration(test.TestConfiguration):
|
||||
"""Configuration that looks for .dart files in the tests/*/src dirs."""
|
||||
LEGAL_KINDS = set(['compile-time error',
|
||||
'runtime error',
|
||||
'static type error',
|
||||
'dynamic type error'])
|
||||
|
||||
def __init__(self, context, root, flags = []):
|
||||
super(StandardTestConfiguration, self).__init__(context, root, flags)
|
||||
|
||||
def _Cleanup(self, tests):
|
||||
"""Remove any temporary files created by running the test."""
|
||||
if self.context.keep_temporary_files:
|
||||
return
|
||||
|
||||
dirs = []
|
||||
for t in tests:
|
||||
if t.run_arch:
|
||||
temp_dir = t.run_arch.temp_dir
|
||||
if temp_dir:
|
||||
dirs.append(temp_dir)
|
||||
if not dirs:
|
||||
return
|
||||
if not utils.Daemonize():
|
||||
return
|
||||
os.execlp('rm', *(['rm', '-rf'] + dirs))
|
||||
|
||||
def CreateTestCases(self, test_path, path, filename, mode, arch, component):
|
||||
"""Given a .dart filename, create a StandardTestCase from it."""
|
||||
# Look for VM specified as comments in the source file. If
|
||||
# several sets of VM options are specified create a separate
|
||||
# test for each set.
|
||||
source = file(filename).read()
|
||||
vm_options_list = utils.ParseTestOptionsMultiple(VM_OPTIONS_PATTERN,
|
||||
source,
|
||||
test_path)
|
||||
tags = {}
|
||||
if filename.endswith('.dart'):
|
||||
tags = self.SplitMultiTest(test_path, filename)
|
||||
if component in ['dartium', 'chromium', 'frogium', 'webdriver']:
|
||||
if tags:
|
||||
return []
|
||||
else:
|
||||
if vm_options_list:
|
||||
tests = []
|
||||
for options in vm_options_list:
|
||||
tests.append(test_case.BrowserTestCase(
|
||||
self.context, test_path, filename, False, mode, arch, component,
|
||||
options + self.flags))
|
||||
return tests
|
||||
else:
|
||||
return [test_case.BrowserTestCase(
|
||||
self.context, test_path, filename, False, mode, arch, component,
|
||||
self.flags)]
|
||||
else:
|
||||
tests = []
|
||||
if tags:
|
||||
for tag in sorted(tags):
|
||||
kind, test_source = tags[tag]
|
||||
if not self.Contains(path, test_path + [tag]):
|
||||
continue
|
||||
if vm_options_list:
|
||||
for options in vm_options_list:
|
||||
tests.append(test_case.MultiTestCase(self.context,
|
||||
test_path + [tag],
|
||||
test_source,
|
||||
kind,
|
||||
mode, arch, component,
|
||||
options + self.flags))
|
||||
else:
|
||||
tests.append(test_case.MultiTestCase(self.context,
|
||||
test_path + [tag],
|
||||
test_source,
|
||||
kind,
|
||||
mode, arch, component,
|
||||
self.flags))
|
||||
else:
|
||||
if vm_options_list:
|
||||
for options in vm_options_list:
|
||||
tests.append(test_case.StandardTestCase(self.context,
|
||||
test_path, filename, mode, arch, component,
|
||||
options + self.flags))
|
||||
else:
|
||||
tests.append(test_case.StandardTestCase(self.context,
|
||||
test_path, filename, mode, arch, component, self.flags))
|
||||
return tests
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
"""Searches for *Test.dart files and returns list of TestCases."""
|
||||
tests = []
|
||||
for root, unused_dirs, files in os.walk(os.path.join(self.root, 'src')):
|
||||
for f in [x for x in files if self.IsTest(x)]:
|
||||
if f.endswith('.dart'):
|
||||
test_path = current_path + [f[:-5]] # Remove .dart suffix.
|
||||
elif f.endswith('.app'):
|
||||
# TODO(zundel): .app files are used only the dromaeo test
|
||||
# and should be removed.
|
||||
test_path = current_path + [f[:-4]] # Remove .app suffix.
|
||||
if not self.Contains(path, test_path):
|
||||
continue
|
||||
tests.extend(self.CreateTestCases(test_path, path,
|
||||
os.path.join(root, f),
|
||||
mode, arch, component))
|
||||
atexit.register(lambda: self._Cleanup(tests))
|
||||
return tests
|
||||
|
||||
def IsTest(self, name):
|
||||
"""Returns True if the file name is a test file."""
|
||||
return name.endswith('Test.dart') or name.endswith('Test.app')
|
||||
|
||||
def GetTestStatus(self, sections, defs):
|
||||
"""Reads the .status file of the TestSuite."""
|
||||
basename = os.path.basename(self.root)
|
||||
for component in ['%s.status', '%s-leg.status']:
|
||||
status = os.path.join(self.root, component % basename)
|
||||
if os.path.exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
|
||||
def FindReferencedFiles(self, lines):
|
||||
"""Scours the lines containing source code for include directives."""
|
||||
referenced_files = []
|
||||
for line in lines:
|
||||
m = re.match("#(source|import)\(['\"](.*)['\"]\);", line)
|
||||
if m:
|
||||
file_name = m.group(2)
|
||||
if not file_name.startswith('dart:'):
|
||||
referenced_files.append(file_name)
|
||||
return referenced_files
|
||||
|
||||
def SplitMultiTest(self, test_path, filename):
|
||||
"""Takes a file with multiple test case defined.
|
||||
|
||||
Splits the file into multiple TestCase instances.
|
||||
|
||||
Args:
|
||||
test_path: temporary dir to write split test case data.
|
||||
filename: name of the file to split.
|
||||
|
||||
Returns:
|
||||
sequence of test cases split from file.
|
||||
|
||||
Raises:
|
||||
TestConfigurationError: when a problem with the multi-test-case
|
||||
syntax is encountered.
|
||||
"""
|
||||
(name, extension) = os.path.splitext(os.path.basename(filename))
|
||||
with open(filename, 'r') as s:
|
||||
source = s.read()
|
||||
lines = source.splitlines()
|
||||
tags = {}
|
||||
for line in lines:
|
||||
(unused_code, sep, info) = line.partition(' /// ')
|
||||
if sep:
|
||||
(tag, sep, kind) = info.partition(': ')
|
||||
if tag in tags:
|
||||
if kind != 'continued':
|
||||
raise TestConfigurationError('duplicated tag %s' % tag)
|
||||
elif kind not in StandardTestConfiguration.LEGAL_KINDS:
|
||||
raise TestConfigurationError('unrecognized kind %s' % kind)
|
||||
else:
|
||||
tags[tag] = kind
|
||||
if not tags:
|
||||
return {}
|
||||
# Prepare directory for generated tests.
|
||||
tests = {}
|
||||
generated_test_dir = os.path.join(utils.GetBuildRoot(utils.GuessOS()),
|
||||
'generated_tests')
|
||||
generated_test_dir = os.path.join(generated_test_dir, *test_path[:-1])
|
||||
if not os.path.exists(generated_test_dir):
|
||||
os.makedirs(generated_test_dir)
|
||||
# Copy referenced files to generated tests directory.
|
||||
referenced_files = self.FindReferencedFiles(lines)
|
||||
for referenced_file in referenced_files:
|
||||
shutil.copy(os.path.join(os.path.dirname(filename), referenced_file),
|
||||
os.path.join(generated_test_dir, referenced_file))
|
||||
# Generate test for each tag found in the main test file.
|
||||
for tag in tags:
|
||||
test_lines = []
|
||||
for line in lines:
|
||||
if ' /// ' in line:
|
||||
if ' /// %s:' % tag in line:
|
||||
test_lines.append(line)
|
||||
else:
|
||||
test_lines.append('// %s' % line)
|
||||
else:
|
||||
test_lines.append(line)
|
||||
test_filename = os.path.join(generated_test_dir,
|
||||
'%s_%s%s' % (name, tag, extension))
|
||||
with open(test_filename, 'w') as test_file:
|
||||
for line in test_lines:
|
||||
print >> test_file, line
|
||||
tests[tag] = (tags[tag], test_filename)
|
||||
test_filename = os.path.join(generated_test_dir,
|
||||
'%s%s' % (name, extension))
|
||||
with open(test_filename, 'w') as test_file:
|
||||
for line in lines:
|
||||
if ' /// ' not in line:
|
||||
print >> test_file, line
|
||||
else:
|
||||
print >> test_file, '//', line
|
||||
tests['none'] = ('', test_filename)
|
||||
return tests
|
||||
|
||||
|
||||
class BrowserTestConfiguration(StandardTestConfiguration):
|
||||
"""A configuration used to run tests inside a browser."""
|
||||
|
||||
def __init__(self, context, root, fatal_static_type_errors=False):
|
||||
super(BrowserTestConfiguration, self).__init__(context, root)
|
||||
self.fatal_static_type_errors = fatal_static_type_errors
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
"""Searches for *Test .dart files and returns list of TestCases."""
|
||||
tests = []
|
||||
for root, unused_dirs, files in os.walk(self.root):
|
||||
for f in [x for x in files if self.IsTest(x)]:
|
||||
relative = os.path.relpath(root, self.root).split(os.path.sep)
|
||||
test_path = current_path + relative + [os.path.splitext(f)[0]]
|
||||
if not self.Contains(path, test_path):
|
||||
continue
|
||||
tests.append(test_case.BrowserTestCase(self.context,
|
||||
test_path,
|
||||
os.path.join(root, f),
|
||||
self.fatal_static_type_errors,
|
||||
mode, arch, component))
|
||||
atexit.register(lambda: self._Cleanup(tests))
|
||||
return tests
|
||||
|
||||
def IsTest(self, name):
|
||||
return name.endswith('_tests.dart') or name.endswith('Test.dart')
|
||||
|
||||
|
||||
class CompilationTestConfiguration(test.TestConfiguration):
|
||||
"""Configuration that searches specific directories for apps to compile.
|
||||
|
||||
Expects a status file named dartc.status
|
||||
"""
|
||||
|
||||
def __init__(self, context, root):
|
||||
super(CompilationTestConfiguration, self).__init__(context, root)
|
||||
|
||||
def ListTests(self, current_path, path, mode, arch, component):
|
||||
"""Searches for files satisfying IsTest() and returns list of TestCases."""
|
||||
tests = []
|
||||
client_path = os.path.normpath(os.path.join(self.root, '..', '..'))
|
||||
|
||||
for src_dir in self.SourceDirs():
|
||||
for root, dirs, files in os.walk(os.path.join(client_path, src_dir)):
|
||||
ignore_dirs = [d for d in dirs if d.startswith('.')]
|
||||
for d in ignore_dirs:
|
||||
dirs.remove(d)
|
||||
for f in files:
|
||||
filename = [os.path.basename(client_path)]
|
||||
filename.extend(root[len(client_path) + 1:].split(os.path.sep))
|
||||
filename.append(f) # Remove .lib or .app suffix.
|
||||
test_path = current_path + filename
|
||||
test_dart_file = os.path.join(root, f)
|
||||
if (not self.Contains(path, test_path)
|
||||
or not self.IsTest(test_dart_file)):
|
||||
continue
|
||||
tests.append(test_case.CompilationTestCase(test_path,
|
||||
self.context,
|
||||
test_dart_file,
|
||||
mode,
|
||||
arch,
|
||||
component))
|
||||
atexit.register(lambda: self._Cleanup(tests))
|
||||
return tests
|
||||
|
||||
def SourceDirs(self):
|
||||
"""Returns a list of directories to scan for files to compile."""
|
||||
raise TestConfigurationError(
|
||||
'Subclasses must implement SourceDirs()')
|
||||
|
||||
def IsTest(self, name):
|
||||
"""Returns True if name is a test case to be compiled."""
|
||||
if not name.endswith('.dart'):
|
||||
return False
|
||||
if os.path.exists(name):
|
||||
# TODO(dgrove): can we end reading the input early?
|
||||
for line in fileinput.input(name):
|
||||
if re.match('#', line):
|
||||
fileinput.close()
|
||||
return True
|
||||
fileinput.close()
|
||||
return False
|
||||
return False
|
||||
|
||||
def GetTestStatus(self, sections, defs):
|
||||
status = os.path.join(self.root, 'dartc.status')
|
||||
if os.path.exists(status):
|
||||
test.ReadConfigurationInto(status, sections, defs)
|
||||
|
||||
def _Cleanup(self, tests):
|
||||
if not utils.Daemonize(): return
|
||||
os.execlp('rm', *(['rm', '-rf'] + [t.temp_dir for t in tests]))
|
||||
raise
|
||||
@@ -1,430 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
#
|
||||
"""Classes and methods for executing tasks for the test.py framework.
|
||||
|
||||
This module includes:
|
||||
- Managing parallel execution of tests using threads
|
||||
- Windows and Unix specific code for spawning tasks and retrieving results
|
||||
- Evaluating the output of each test as pass/fail/crash/timeout
|
||||
"""
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import Queue
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import testing
|
||||
import utils
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CommandOutput(object):
|
||||
"""Represents the output of running a command."""
|
||||
|
||||
def __init__(self, pid, exit_code, timed_out, stdout, stderr):
|
||||
self.pid = pid
|
||||
self.exit_code = exit_code
|
||||
self.timed_out = timed_out
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.failed = None
|
||||
|
||||
|
||||
class TestOutput(object):
|
||||
"""Represents the output of running a TestCase."""
|
||||
|
||||
def __init__(self, test, command, output):
|
||||
"""Represents the output of running a TestCase.
|
||||
|
||||
Args:
|
||||
test: A TestCase instance.
|
||||
command: the command line that was run
|
||||
output: A CommandOutput instance.
|
||||
"""
|
||||
self.test = test
|
||||
self.command = command
|
||||
self.output = output
|
||||
|
||||
def UnexpectedOutput(self):
|
||||
"""Compare the result of running the expected from the TestConfiguration.
|
||||
|
||||
Returns:
|
||||
True if the test had an unexpected output.
|
||||
"""
|
||||
return not self.GetOutcome() in self.test.outcomes
|
||||
|
||||
def GetOutcome(self):
|
||||
"""Returns one of testing.CRASH, testing.TIMEOUT, testing.FAIL, or
|
||||
testing.PASS."""
|
||||
if self.HasCrashed():
|
||||
return testing.CRASH
|
||||
if self.HasTimedOut():
|
||||
return testing.TIMEOUT
|
||||
if self.HasFailed():
|
||||
return testing.FAIL
|
||||
return testing.PASS
|
||||
|
||||
def HasCrashed(self):
|
||||
"""Returns True if the test should be considered testing.CRASH."""
|
||||
if utils.IsWindows():
|
||||
if self.output.exit_code == 3:
|
||||
# The VM uses std::abort to terminate on asserts.
|
||||
# std::abort terminates with exit code 3 on Windows.
|
||||
return True
|
||||
return (0x80000000 & self.output.exit_code
|
||||
and not 0x3FFFFF00 & self.output.exit_code)
|
||||
else:
|
||||
# Timed out tests will have exit_code -signal.SIGTERM.
|
||||
if self.output.timed_out:
|
||||
return False
|
||||
if self.output.exit_code == 253:
|
||||
# The Java dartc runners exit 253 in case of unhandled exceptions.
|
||||
return True
|
||||
return self.output.exit_code < 0
|
||||
|
||||
def HasTimedOut(self):
|
||||
"""Returns True if the test should be considered as testing.TIMEOUT."""
|
||||
return self.output.timed_out
|
||||
|
||||
def HasFailed(self):
|
||||
"""Returns True if the test should be considered as testing.FAIL."""
|
||||
execution_failed = self.test.DidFail(self.output)
|
||||
if self.test.IsNegative():
|
||||
return not execution_failed
|
||||
else:
|
||||
return execution_failed
|
||||
|
||||
|
||||
def Execute(args, context, timeout=None, cwd=None):
|
||||
"""Executes the specified command.
|
||||
|
||||
Args:
|
||||
args: sequence of the executable name + arguments.
|
||||
context: An instance of Context object with global settings for test.py.
|
||||
timeout: optional timeout to wait for results in seconds.
|
||||
cwd: optionally change to this working directory.
|
||||
|
||||
Returns:
|
||||
An instance of CommandOutput with the collected results.
|
||||
"""
|
||||
(fd_out, outname) = tempfile.mkstemp()
|
||||
(fd_err, errname) = tempfile.mkstemp()
|
||||
(process, exit_code, timed_out) = RunProcess(context, timeout, args=args,
|
||||
stdout=fd_out, stderr=fd_err,
|
||||
cwd=cwd)
|
||||
os.close(fd_out)
|
||||
os.close(fd_err)
|
||||
output = file(outname).read()
|
||||
errors = file(errname).read()
|
||||
utils.CheckedUnlink(outname)
|
||||
utils.CheckedUnlink(errname)
|
||||
result = CommandOutput(process.pid, exit_code, timed_out,
|
||||
output, errors)
|
||||
return result
|
||||
|
||||
|
||||
def KillProcessWithID(pid):
|
||||
"""Stop a process (with SIGTERM on Unix)."""
|
||||
if utils.IsWindows():
|
||||
os.popen('taskkill /T /F /PID %d' % pid)
|
||||
else:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
|
||||
|
||||
MAX_SLEEP_TIME = 0.1
|
||||
INITIAL_SLEEP_TIME = 0.0001
|
||||
SLEEP_TIME_FACTOR = 1.25
|
||||
SEM_INVALID_VALUE = -1
|
||||
SEM_NOGPFAULTERRORBOX = 0x0002 # Microsoft Platform SDK WinBase.h
|
||||
|
||||
|
||||
def Win32SetErrorMode(mode):
|
||||
"""Some weird Windows stuff you just have to do."""
|
||||
prev_error_mode = SEM_INVALID_VALUE
|
||||
try:
|
||||
prev_error_mode = ctypes.windll.kernel32.SetErrorMode(mode)
|
||||
except ImportError:
|
||||
pass
|
||||
return prev_error_mode
|
||||
|
||||
|
||||
def RunProcess(context, timeout, args, **rest):
|
||||
"""Handles the OS specific details of running a task and saving results."""
|
||||
if context.verbose: print '#', ' '.join(args)
|
||||
popen_args = args
|
||||
prev_error_mode = SEM_INVALID_VALUE
|
||||
if utils.IsWindows():
|
||||
popen_args = '"' + subprocess.list2cmdline(args) + '"'
|
||||
if context.suppress_dialogs:
|
||||
# Try to change the error mode to avoid dialogs on fatal errors. Don't
|
||||
# touch any existing error mode flags by merging the existing error mode.
|
||||
# See http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx.
|
||||
error_mode = SEM_NOGPFAULTERRORBOX
|
||||
prev_error_mode = Win32SetErrorMode(error_mode)
|
||||
Win32SetErrorMode(error_mode | prev_error_mode)
|
||||
process = subprocess.Popen(shell=utils.IsWindows(),
|
||||
args=popen_args,
|
||||
**rest)
|
||||
if (utils.IsWindows() and context.suppress_dialogs
|
||||
and prev_error_mode != SEM_INVALID_VALUE):
|
||||
Win32SetErrorMode(prev_error_mode)
|
||||
# Compute the end time - if the process crosses this limit we
|
||||
# consider it timed out.
|
||||
if timeout is None: end_time = None
|
||||
else: end_time = time.time() + timeout
|
||||
timed_out = False
|
||||
# Repeatedly check the exit code from the process in a
|
||||
# loop and keep track of whether or not it times out.
|
||||
exit_code = None
|
||||
sleep_time = INITIAL_SLEEP_TIME
|
||||
while exit_code is None:
|
||||
if (not end_time is None) and (time.time() >= end_time):
|
||||
# Kill the process and wait for it to exit.
|
||||
KillProcessWithID(process.pid)
|
||||
# Drain the output pipe from the process to avoid deadlock
|
||||
process.communicate()
|
||||
exit_code = process.wait()
|
||||
timed_out = True
|
||||
else:
|
||||
exit_code = process.poll()
|
||||
time.sleep(sleep_time)
|
||||
sleep_time *= SLEEP_TIME_FACTOR
|
||||
if sleep_time > MAX_SLEEP_TIME:
|
||||
sleep_time = MAX_SLEEP_TIME
|
||||
return (process, exit_code, timed_out)
|
||||
|
||||
|
||||
class TestRunner(object):
|
||||
"""Base class for runners."""
|
||||
|
||||
def __init__(self, work_queue, tasks, progress):
|
||||
self.work_queue = work_queue
|
||||
self.tasks = tasks
|
||||
self.terminate = False
|
||||
self.progress = progress
|
||||
self.threads = []
|
||||
self.shutdown_lock = threading.Lock()
|
||||
|
||||
|
||||
class BatchRunner(TestRunner):
|
||||
"""Implements communication with a set of subprocesses using threads."""
|
||||
|
||||
def __init__(self, work_queue, tasks, progress, batch_cmd):
|
||||
super(BatchRunner, self).__init__(work_queue, tasks, progress)
|
||||
self.runners = {}
|
||||
self.last_activity = {}
|
||||
self.context = progress.context
|
||||
|
||||
# Scale the number of tasks to the nubmer of CPUs on the machine
|
||||
# 1:1 is too much of an overload on many machines in batch mode,
|
||||
# so scale the ratio of threads to CPUs back. On Windows running
|
||||
# more than one task is not safe.
|
||||
if tasks == testing.USE_DEFAULT_CPUS:
|
||||
if utils.IsWindows():
|
||||
tasks = 1
|
||||
else:
|
||||
tasks = .75 * testing.HOST_CPUS
|
||||
|
||||
# Start threads
|
||||
for i in xrange(tasks):
|
||||
thread = threading.Thread(target=self.RunThread, args=[batch_cmd, i])
|
||||
self.threads.append(thread)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def RunThread(self, batch_cmd, thread_number):
|
||||
"""A thread started to feed a single TestRunner."""
|
||||
try:
|
||||
runner = None
|
||||
while not self.terminate and not self.work_queue.empty():
|
||||
runner = subprocess.Popen(batch_cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
stdout=subprocess.PIPE)
|
||||
self.runners[thread_number] = runner
|
||||
self.FeedTestRunner(runner, thread_number)
|
||||
if thread_number in self.last_activity:
|
||||
del self.last_activity[thread_number]
|
||||
|
||||
# Cleanup
|
||||
self.EndRunner(runner)
|
||||
|
||||
except:
|
||||
self.Shutdown()
|
||||
raise
|
||||
finally:
|
||||
if thread_number in self.last_activity:
|
||||
del self.last_activity[thread_number]
|
||||
if runner: self.EndRunner(runner)
|
||||
|
||||
def EndRunner(self, runner):
|
||||
"""Cleans up a single runner, killing the child if necessary."""
|
||||
with self.shutdown_lock:
|
||||
if runner:
|
||||
returncode = runner.poll()
|
||||
if returncode is None:
|
||||
runner.kill()
|
||||
for (found_runner, thread_number) in self.runners.items():
|
||||
if runner == found_runner:
|
||||
del self.runners[thread_number]
|
||||
break
|
||||
try:
|
||||
runner.communicate()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def CheckForTimeouts(self):
|
||||
now = time.time()
|
||||
for (thread_number, start_time) in self.last_activity.items():
|
||||
if now - start_time > self.context.timeout:
|
||||
self.runners[thread_number].kill()
|
||||
|
||||
def WaitForCompletion(self):
|
||||
"""Wait for threads to finish, and monitor test runners for timeouts."""
|
||||
for t in self.threads:
|
||||
while True:
|
||||
self.CheckForTimeouts()
|
||||
t.join(timeout=5)
|
||||
if not t.isAlive():
|
||||
break
|
||||
|
||||
def FeedTestRunner(self, runner, thread_number):
|
||||
"""Feed commands to the fork'ed TestRunner through a Popen object."""
|
||||
|
||||
last_case = {}
|
||||
last_buf = ''
|
||||
|
||||
while not self.terminate:
|
||||
# Is the runner still alive?
|
||||
returninfo = runner.poll()
|
||||
if returninfo is not None:
|
||||
buf = last_buf + '\n' + runner.stdout.read()
|
||||
if last_case:
|
||||
self.RecordPassFail(last_case, buf, testing.CRASH)
|
||||
else:
|
||||
with self.progress.lock:
|
||||
print >>sys. stderr, ('%s: runner unexpectedly exited: %d'
|
||||
% (threading.currentThread().name,
|
||||
returninfo))
|
||||
print 'Crash Output: '
|
||||
print
|
||||
print buf
|
||||
return
|
||||
|
||||
try:
|
||||
case = self.work_queue.get_nowait()
|
||||
with self.progress.lock:
|
||||
self.progress.AboutToRun(case.case)
|
||||
|
||||
except Queue.Empty:
|
||||
return
|
||||
test_case = case.case
|
||||
cmd = ' '.join(test_case.GetCommand()[1:])
|
||||
|
||||
try:
|
||||
print >>runner.stdin, cmd
|
||||
except IOError:
|
||||
with self.progress.lock:
|
||||
traceback.print_exc()
|
||||
|
||||
# Child exited before starting the next command.
|
||||
buf = last_buf + '\n' + runner.stdout.read()
|
||||
self.RecordPassFail(last_case, buf, testing.CRASH)
|
||||
|
||||
# We never got a chance to run this command - queue it back up.
|
||||
self.work_queue.put(case)
|
||||
return
|
||||
|
||||
buf = ''
|
||||
self.last_activity[thread_number] = time.time()
|
||||
while not self.terminate:
|
||||
line = runner.stdout.readline()
|
||||
if self.terminate:
|
||||
break
|
||||
case.case.duration = time.time() - self.last_activity[thread_number]
|
||||
if not line:
|
||||
# EOF. Child has exited.
|
||||
if case.case.duration > self.context.timeout:
|
||||
with self.progress.lock:
|
||||
print 'Child timed out after %d seconds' % self.context.timeout
|
||||
self.RecordPassFail(case, buf, testing.TIMEOUT)
|
||||
elif buf:
|
||||
self.RecordPassFail(case, buf, testing.CRASH)
|
||||
return
|
||||
|
||||
# Look for TestRunner batch status escape sequence. e.g.
|
||||
# >>> TEST PASS
|
||||
if line.startswith('>>> '):
|
||||
result = line.split()
|
||||
if result[1] == 'TEST':
|
||||
outcome = result[2].lower()
|
||||
|
||||
# Read the rest of the output buffer (possible crash output)
|
||||
if outcome == testing.CRASH:
|
||||
buf += runner.stdout.read()
|
||||
|
||||
self.RecordPassFail(case, buf, outcome)
|
||||
|
||||
# Always handle crashes by restarting the runner.
|
||||
if outcome == testing.CRASH:
|
||||
return
|
||||
break
|
||||
elif result[1] == 'BATCH':
|
||||
pass
|
||||
else:
|
||||
print 'Unknown cmd from batch runner: %s' % line
|
||||
else:
|
||||
buf += line
|
||||
|
||||
# If the process crashes before the next command is executed,
|
||||
# save info to report diagnostics.
|
||||
last_buf = buf
|
||||
last_case = case
|
||||
|
||||
def RecordPassFail(self, case, stdout_buf, outcome):
|
||||
"""An unexpected failure occurred."""
|
||||
if outcome == testing.PASS or outcome == testing.OKAY:
|
||||
exit_code = 0
|
||||
elif outcome == testing.CRASH:
|
||||
exit_code = -1
|
||||
elif outcome == testing.FAIL or outcome == testing.TIMEOUT:
|
||||
exit_code = 1
|
||||
else:
|
||||
assert False, 'Unexpected outcome: %s' % outcome
|
||||
|
||||
cmd_output = CommandOutput(0, exit_code,
|
||||
outcome == testing.TIMEOUT, stdout_buf, '')
|
||||
test_output = TestOutput(case.case,
|
||||
case.case.GetCommand(),
|
||||
cmd_output)
|
||||
with self.progress.lock:
|
||||
if test_output.UnexpectedOutput():
|
||||
self.progress.failed.append(test_output)
|
||||
else:
|
||||
self.progress.succeeded += 1
|
||||
if outcome == testing.CRASH:
|
||||
self.progress.crashed += 1
|
||||
self.progress.remaining -= 1
|
||||
self.progress.HasRun(test_output)
|
||||
|
||||
def Shutdown(self):
|
||||
"""Kill all active runners."""
|
||||
print 'Shutting down remaining runners.'
|
||||
self.terminate = True
|
||||
for runner in self.runners.values():
|
||||
runner.kill()
|
||||
# Give threads a chance to exit gracefully
|
||||
time.sleep(2)
|
||||
for runner in self.runners.values():
|
||||
self.EndRunner(runner)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
@@ -1,8 +0,0 @@
|
||||
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
from testing import test_configuration
|
||||
|
||||
def GetConfiguration(context, root):
|
||||
return test_configuration.StandardTestConfiguration(context, root)
|
||||
Reference in New Issue
Block a user