test262 console runner working!

This commit is contained in:
Mark Miller 2011-09-30 08:24:38 -04:00
parent aad373e620
commit 13b63c5486
7 changed files with 231 additions and 425 deletions

View File

@ -1,4 +1,4 @@
/// Copyright (c) 2011 Microsoft Corporation
/// Copyright (c) 2011 Microsoft Corporation
///
/// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met:
@ -20,9 +20,9 @@
//Error Detector
if (this.window!==undefined) { //for console support
window.onerror = function(errorMsg, url, lineNumber) {
window.iframeError = errorMsg;
}
this.window.onerror = function(errorMsg, url, lineNumber) {
this.window.iframeError = errorMsg;
};
}
//This doesn't work with early errors in current versions of Opera

View File

@ -1,4 +1,4 @@
/// Copyright (c) 2011 Microsoft Corporation
/// Copyright (c) 2011 Microsoft Corporation
///
/// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met:

View File

@ -1,4 +1,4 @@
/// Copyright (c) 2009 Microsoft Corporation
/// Copyright (c) 2009 Microsoft Corporation
///
/// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met:
@ -335,8 +335,8 @@ function getPrecision(num) {
//TODO: Create a table of prec's,
// because using Math for testing Math isn't that correct.
log2num = Math.log(Math.abs(num)) / Math.LN2;
pernum = Math.ceil(log2num);
var log2num = Math.log(Math.abs(num)) / Math.LN2;
var pernum = Math.ceil(log2num);
return (2 * Math.pow(2, -52 + pernum));
//return(0);
}
@ -364,7 +364,7 @@ function isEqual(num1, num2) {
// This code is governed by the BSD license found in the LICENSE file.
function ToInteger(p) {
x = Number(p);
var x = Number(p);
if (isNaN(x)) {
return +0;
@ -437,7 +437,7 @@ var $LocalTZ,
current = new Date(current.getTime() + 1);
}
return current;
}
};
var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0);
var decemberDate = new Date(2000, 11, 20, 0, 0, 0, 0);
@ -601,6 +601,7 @@ function DaysInMonth(m, leap) {
function GetSundayInMonth(t, m, count){
var year = YearFromTime(t);
var tempDate;
if (count==='"first"') {
for (var d=1; d <= DaysInMonth(m, InLeapYear(t)); d++) {
@ -905,6 +906,6 @@ return attribs
//--Test case registration-----------------------------------------------------
function runTestCase(testcase) {
if (testcase() !== true) {
$ERROR("Test case returned non-true value!")
$ERROR("Test case returned non-true value!");
}
}

View File

@ -180,7 +180,7 @@ function BrowserRunner() {
idoc.writeln(globalScopeContents);
idoc.writeln("</script>");
idoc.close();
}
};
//--Helper functions-------------------------------------------------------
this.convertForEval = function(txt) {
@ -190,7 +190,7 @@ function BrowserRunner() {
txt = txt.replace(/\r/g,"\\r");
txt = txt.replace(/\n/g,"\\n");
return txt;
}
};
}
/* Loads tests from the sections specified in testcases.json.
@ -366,7 +366,7 @@ function Controller() {
try {
controller.implementerHook.finished(elapsed);
} catch(e) { /*no-op*/}
}
};
this.start = function() {
state = 'running';

View File

@ -1,174 +0,0 @@
#!/usr/bin/env python
# Copyright 2011 by Google, Inc. All rights reserved.
# This code is governed by the BSD license found in the LICENSE file.
# Follows convert.js as closely as possible. So to minimize
# divergence, see convert.js for doc-comments that are missing here.
import logging
import optparse
import os
from os import path
import platform
import re
import subprocess
import sys
import tempfile
import time
# from TestCasePackagerConfig import *
headerPattern = r"(?:(?:\/\/.*)?\s*\n)*"
captureCommentPattern = r"\/\*\*?((?:\s|\S)*?)\*\/\s*\n"
anyPattern = r"(?:\s|\S)*"
blanksPattern = r"(?:\s|\n)*"
# only until we've done our last conversion from current sputnik
# format to canonical test262 format
captureStrictPattern = r"\s*('use strict'|\"use strict\");"
# Should match anything
testEnvelopePattern = r"^(" + headerPattern +
r")(?:" + captureCommentPattern +
r")?(?:" + captureStrictPattern +
r")?(" + anyPattern +
r")$"
registerPattern = r"^(" + anyPattern + r"?)(" +
r"ES5Harness\.registerTest\s*\(\s*\{" + anyPattern +
r"\}\s*\)" + r")" +
r"\s*;?(?:\s|\n)*$"
Matches a named function. Captures both the name and the body.
captureFuncNameBodyPattern = r"^function\s+(\w*)\(\s*\)\s*\{" +
r"(" + anyPattern + r")" +
r";?" + blanksPattern +
r"\}$"
# captureExprBodyPattern = r"^return\s+" +
# r"(" + anyPattern + r"?)" +
# r";$"
# capturePredicatePattern = r"^if\s+\((.*?)\)\s*\{" + blanksPattern +
# r"return\s+true;?" + blanksPattern +
# r"\}$"
stars = r"\s*\n\s*\*\s?"
atattrs = r"\s*\n\s*\*\s*@"
def stripStars(text):
return re.sub(stars, '\n', text).strip()
def parseTestEnvelope(src, name):
envelope = { 'testRecord': {} }
envelopeMatch = re.match(testEnvelopePattern, src)
if (envelopeMatch == None):
raise Exception('unrecognized: ' + name)
envelope['header'] = envelopeMatch.group(1).strip()
if (envelopeMatch.group(2)):
propTexts = re.split(atattrs, envelopeMatch.group(2))
envelope['commentary'] = stripStars(propTexts[0])
del propTexts[0]
for propText in propTexts:
# TODO: error check for mismatch
propName = re.match(r"^\w+", propText).group(0)
propVal = propText[len(propName):]
# Just till last one-time conversion
# strip optional initial colon or final semicolon.
# The initial colon is only stripped if it comes immediately
# after the identifier with no intervening whitespace.
propVal = re.sub(r"^:\s*", '', propVal, 1)
propVal = re.sub(r";\s*$", '', propVal, 1)
propVal = stripStars(propVal)
if (propName in envelope['testRecord']):
raise Exception('duplicate: ' + propName)
envelope['testRecord'][propName] = propVal;
if (envelopeMatch.group(3)):
envelope['testRecord']['strict_only'] = '';
envelope['rest'] = envelopeMatch.group(4) # do not trim
# Just till last one time conversion
registerMatch = re.match(registerPattern, envelope['rest'])
if (registerMatch):
envelope['rest'] = registerMatch.group(1).strip()
envelope['registerExpr'] = registerMatch.group(2).strip()
else if ('ES5Harness.registerTest' in envelope['rest']):
raise Exception('Malformed harness? ' + name)
return envelope
def functionSrcToProgramSrc(funcSrc):
cfnbMatch = re.match(captureFuncNameBodyPattern, funcSrc)
if (not cfnbMatch):
raise Exception('Could not recognize: "' + funcSrc + '"')
name = cfnbMatch.group(1).strip()
body = cfnbMatch.group(2).strip()
# Look for special cases
cebMatch = re.match(captureExprBodyPattern, body)
if (cebMatch):
return 'assertTruthy(' + cebMatch.group(1).strip() + ');'
cpMatch = re.match(capturePredicatePattern, body)
if (cpMatch):
return 'assertTruthy(' + cpMatch.group(1).strip() + ');'
# General case
return (funcSrc + '\n' +
'runTestCase(' + name + ');')
def gatherOne(envelope, name):
# TODO(erights): implement by pattern match rather than evaluation
raise Exception('gatherOne not implemented yet')
def transferProp(record, fromName, toName):
if (((toName not in testRecord) or
(testRecord[toName] == '')) and
(fromName in testRecord)):
testRecord[toName] = testRecord[fromName]
del testRecord[fromName]
# TODO: new midcap names
# don't mask collisions -- give errors
# if unrecognized names remain, give errors
def normalizeProps(testRecord):
if (('strict_only' not in testRecord) and
('strict' in testRecord) and
(testRecord['strict'] == 1)):
testRecord['strict_only'] = ''
if (testRecord['strict'] == 1):
del testRecord['strict']
if ('strict_mode_negative' in testRecord):
if ('strict_only' not in testRecord):
testRecord['strict_only'] = ''
transferProp(testRecord, 'strict_mode_negative', 'negative')
transferProp(testRecord, 'errortype', 'negative')
transferProp(testRecord, 'assertion', 'description')
transferProp(testRecord, 'assertion', 'commentary')
def getGlobalScopeRecord(relPath):
# TODO(erights): implement
raise Exception('getGlobalScopeRecord not implemented yet')
def parseTestRecord(inBase, relPath, name):
nextRelPath = relPath + [name]
nextPath = inBase + [name]

View File

@ -0,0 +1,65 @@
#!/usr/bin/env python
# Copyright 2011 by Google, Inc. All rights reserved.
# This code is governed by the BSD license found in the LICENSE file.
# TODO: resolve differences with common.py and unify into one file.
import logging
import optparse
import os
from os import path
import platform
import re
import subprocess
import sys
import tempfile
import time
# from TestCasePackagerConfig import *
headerPatternStr = r"(?:(?:\s*\/\/.*)?\s*\n)*"
captureCommentPatternStr = r"\/\*\*?((?:\s|\S)*?)\*\/\s*\n"
anyPatternStr = r"(?:\s|\S)*"
headerPattern = re.compile("^" + headerPatternStr)
# Should match anything
testRecordPattern = re.compile(r"^(" + headerPatternStr +
r")(?:" + captureCommentPatternStr +
r")?(" + anyPatternStr +
r")$")
stars = re.compile(r"\s*\n\s*\*\s?")
atattrs = re.compile(r"\s*\n\s*\*\s*@")
def stripStars(text):
return stars.sub('\n', text).strip()
def stripHeader(src):
header = headerPattern.match(src).group(0)
return src[len(header):]
def parseTestRecord(src, name):
testRecord = {}
match = testRecordPattern.match(src)
if match == None:
raise Exception('unrecognized: ' + name)
testRecord['header'] = match.group(1).strip()
testRecord['test'] = match.group(3) # do not trim
if match.group(2):
propTexts = atattrs.split(match.group(2))
testRecord['commentary'] = stripStars(propTexts[0])
del propTexts[0]
for propText in propTexts:
propMatch = re.match(r"^\w+", propText)
if propMatch == None:
raise Exception('Malformed "@" attribute: ' + name)
propName = propMatch.group(0)
propVal = stripStars(propText[len(propName):])
if propName in testRecord:
raise Exception('duplicate: ' + propName)
testRecord[propName] = propVal;
return testRecord

260
tools/test262.py → tools/packaging/test262.py Normal file → Executable file
View File

@ -2,6 +2,11 @@
# Copyright 2009 the Sputnik authors. All rights reserved.
# This code is governed by the BSD license found in the LICENSE file.
# This is derived from sputnik.py, the Sputnik console test runner,
# with elements from packager.py, which is separately
# copyrighted. TODO: Refactor so there is less duplication between
# test262.py and packager.py.
import logging
import optparse
@ -13,25 +18,42 @@ import subprocess
import sys
import tempfile
import time
import xml.dom.minidom
import datetime
import shutil
import json
import stat
from parseTestRecord import parseTestRecord, stripHeader
from packagerConfig import *
class Test262Error(Exception):
def __init__(self, message):
self.message = message
def ReportError(s):
raise Test262Error(s)
if not os.path.exists(EXCLUDED_FILENAME):
print "Cannot generate (JSON) test262 tests without a file," + \
" %s, showing which tests have been disabled!" % EXCLUDED_FILENAME
sys.exit(1)
EXCLUDE_LIST = xml.dom.minidom.parse(EXCLUDED_FILENAME)
EXCLUDE_LIST = EXCLUDE_LIST.getElementsByTagName("test")
EXCLUDE_LIST = [x.getAttribute("id") for x in EXCLUDE_LIST]
def BuildOptions():
result = optparse.OptionParser()
result.add_option("--command", default=None, help="The command-line to run")
result.add_option("--tests", default=path.abspath('.'),
help="Path to the tests")
result.add_option("--cat", default=False, action="store_true",
help="Print test source code")
help="Print packaged test code that would be run")
result.add_option("--summary", default=False, action="store_true",
help="Print summary after running tests")
result.add_option("--full-summary", default=False, action="store_true",
@ -40,7 +62,10 @@ def BuildOptions():
help="Test only strict mode")
result.add_option("--non_strict_only", default=False, action="store_true",
help="Test only non-strict mode")
# TODO: Once enough tests are made strict compat, change the default
# to "both"
result.add_option("--unmarked_default", default="non_strict",
help="default mode for tests of unspecified strictness")
return result
@ -51,16 +76,7 @@ def ValidateOptions(options):
ReportError("Couldn't find test path '%s'" % options.tests)
_PLACEHOLDER_PATTERN = re.compile(r"\{\{(\w+)\}\}")
_INCLUDE_PATTERN = re.compile(r"\$INCLUDE\(\"(.*)\"\);")
_SPECIAL_CALL_PATTERN = re.compile(r"\$([A-Z]+)(?=\()")
_SPECIAL_CALLS = {
'ERROR': 'testFailed',
'FAIL': 'testFailed',
'PRINT': 'testPrint'
}
placeHolderPattern = re.compile(r"\{\{(\w+)\}\}")
def IsWindows():
@ -68,12 +84,6 @@ def IsWindows():
return (p == 'Windows') or (p == 'Microsoft')
def StripHeader(str):
while str.startswith('//') and "\n" in str:
str = str[str.index("\n")+1:]
return str.lstrip()
class TempFile(object):
def __init__(self, suffix="", prefix="tmp", text=False):
@ -89,8 +99,7 @@ class TempFile(object):
(self.fd, self.name) = tempfile.mkstemp(
suffix = self.suffix,
prefix = self.prefix,
text = self.text
)
text = self.text)
def Write(self, str):
os.write(self.fd, str)
@ -127,7 +136,7 @@ class TestResult(object):
mode = self.case.GetMode()
if self.HasUnexpectedOutcome():
if self.case.IsNegative():
print "%s was expected to fail in %s, but didn't" % (name, mode)
print "=== %s was expected to fail in %s, but didn't ===" % (name, mode)
else:
if long_format:
print "=== %s failed in %s ===" % (name, mode)
@ -164,11 +173,17 @@ class TestCase(object):
self.suite = suite
self.name = name
self.full_path = full_path
self.contents = None
self.is_negative = None
self.strict_mode = strict_mode
self.is_strict_only = None
self.is_non_strict_only = None
f = open(self.full_path)
self.contents = f.read()
f.close()
testRecord = parseTestRecord(self.contents, name)
self.test = testRecord["test"]
del testRecord["test"]
del testRecord["header"]
del testRecord["commentary"]
self.testRecord = testRecord;
def GetName(self):
return path.join(*self.name)
@ -182,60 +197,33 @@ class TestCase(object):
def GetPath(self):
return self.name
def GetRawContents(self):
if self.contents is None:
f = open(self.full_path)
self.contents = f.read()
f.close()
return self.contents
def IsNegative(self):
if self.is_negative is None:
self.is_negative = ("@negative" in self.GetRawContents())
return self.is_negative
return 'negative' in self.testRecord
def IsStrictOnly(self):
if self.is_strict_only is None:
self.is_strict_only = ("@strict_only" in self.GetRawContents())
return self.is_strict_only
def IsOnlyStrict(self):
return 'onlyStrict' in self.testRecord
def IsNonStrictOnly(self):
if self.is_non_strict_only is None:
self.is_non_strict_only = ("@non_strict_only" in self.GetRawContents())
return self.is_non_strict_only
def IsNoStrict(self):
return 'noStrict' in self.testRecord
def GetSource(self):
source = self.suite.GetInclude("framework.js", False) + \
self.suite.GetInclude("sta.js", False)
source += StripHeader(self.GetRawContents())
def IncludeFile(match):
return self.suite.GetInclude(match.group(1))
source = _INCLUDE_PATTERN.sub(IncludeFile, source)
def SpecialCall(match):
key = match.group(1)
return _SPECIAL_CALLS.get(key, match.group(0))
# "var testDescrip = " + str(self.testRecord) + ';\n\n' + \
source = self.suite.GetInclude("cth.js") + \
self.suite.GetInclude("sta.js") + \
self.suite.GetInclude("ed.js") + \
self.test + '\n'
if self.strict_mode:
source = '"use strict";\nvar strict_mode = true;\n' + \
_SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
source = '"use strict";\nvar strict_mode = true;\n' + source
else:
source = "var strict_mode = false; \n" + \
_SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
source = "var strict_mode = false; \n" + source
return source
def InstantiateTemplate(self, template, params):
def GetParameter(match):
key = match.group(1)
return params.get(key, match.group(0))
return _PLACEHOLDER_PATTERN.sub(GetParameter, template)
def RunTestIn(self, command_template, tmp):
tmp.Write(self.GetSource())
tmp.Close()
command = self.InstantiateTemplate(command_template, {
'path': tmp.name
})
(code, out, err) = self.Execute(command)
return TestResult(code, out, err, self)
return placeHolderPattern.sub(GetParameter, template)
def Execute(self, command):
if IsWindows():
@ -260,6 +248,15 @@ class TestCase(object):
stderr.Dispose()
return (code, out, err)
def RunTestIn(self, command_template, tmp):
tmp.Write(self.GetSource())
tmp.Close()
command = self.InstantiateTemplate(command_template, {
'path': tmp.name
})
(code, out, err) = self.Execute(command)
return TestResult(code, out, err, self)
def Run(self, command_template):
tmp = TempFile(suffix=".js", prefix="test262-", text=True)
try:
@ -298,11 +295,13 @@ def MakePlural(n):
class TestSuite(object):
def __init__(self, root, strict_only, non_strict_only):
self.test_root = path.join(root, 'test', 'suite', 'converted')
def __init__(self, root, strict_only, non_strict_only, unmarked_default):
# TODO: derive from packagerConfig.py
self.test_root = path.join(root, 'test', 'suite')
self.lib_root = path.join(root, 'test', 'harness')
self.strict_only = strict_only
self.non_strict_only = non_strict_only
self.unmarked_default = unmarked_default
self.include_cache = { }
def Validate(self):
@ -325,41 +324,18 @@ class TestSuite(object):
return True
return False
def GetTimeZoneInfoInclude(self):
dst_attribs = GetDaylightSavingsAttribs()
if not dst_attribs:
return None
lines = []
for key in sorted(dst_attribs.keys()):
lines.append('var $DST_%s = %s;' % (key, str(dst_attribs[key])))
localtz = time.timezone / -3600
lines.append('var $LocalTZ = %i;' % localtz)
return "\n".join(lines)
def GetSpecialInclude(self, name):
if name == "environment.js":
return self.GetTimeZoneInfoInclude()
else:
return None
def GetInclude(self, name, strip_header=True):
key = (name, strip_header)
if not key in self.include_cache:
value = self.GetSpecialInclude(name)
if value:
self.include_cache[key] = value
else:
def GetInclude(self, name):
if not name in self.include_cache:
static = path.join(self.lib_root, name)
if path.exists(static):
f = open(static)
contents = f.read()
if strip_header:
contents = StripHeader(contents)
self.include_cache[key] = contents + "\n"
contents = stripHeader(f.read())
contents = re.sub(r'\r\n', '\n', contents)
self.include_cache[name] = contents + "\n"
f.close()
else:
self.include_cache[key] = ""
return self.include_cache[key]
ReportError("Can't find: " + static)
return self.include_cache[name]
def EnumerateTests(self, tests):
logging.info("Listing tests in %s", self.test_root)
@ -379,13 +355,20 @@ class TestSuite(object):
if self.ShouldRun(rel_path, tests):
basename = path.basename(full_path)[:-3]
name = rel_path.split(path.sep)[:-1] + [basename]
if EXCLUDE_LIST.count(basename) >= 1:
print 'Excluded: ' + basename
else:
if not self.non_strict_only:
strict_case = TestCase(self, name, full_path, True)
if not strict_case.IsNonStrictOnly():
if not strict_case.IsNoStrict():
if strict_case.IsOnlyStrict() or \
self.unmarked_default in ['both', 'strict']:
cases.append(strict_case)
if not self.strict_only:
non_strict_case = TestCase(self, name, full_path, False)
if not non_strict_case.IsStrictOnly():
if not non_strict_case.IsOnlyStrict():
if non_strict_case.IsNoStrict() or \
self.unmarked_default in ['both', 'non_strict']:
cases.append(non_strict_case)
logging.info("Done listing tests")
return cases
@ -447,83 +430,14 @@ class TestSuite(object):
cases[0].Print()
def GetDaylightSavingsTimes():
# Is the given floating-point time in DST?
def IsDst(t):
return time.localtime(t)[-1]
# Binary search to find an interval between the two times no greater than
# delta where DST switches, returning the midpoint.
def FindBetween(start, end, delta):
while end - start > delta:
middle = (end + start) / 2
if IsDst(middle) == IsDst(start):
start = middle
else:
end = middle
return (start + end) / 2
now = time.time()
one_month = (30 * 24 * 60 * 60)
# First find a date with different daylight savings. To avoid corner cases
# we try four months before and after today.
after = now + 4 * one_month
before = now - 4 * one_month
if IsDst(now) == IsDst(before) and IsDst(now) == IsDst(after):
logging.warning("Was unable to determine DST info.")
return None
# Determine when the change occurs between now and the date we just found
# in a different DST.
if IsDst(now) != IsDst(before):
first = FindBetween(before, now, 1)
else:
first = FindBetween(now, after, 1)
# Determine when the change occurs between three and nine months from the
# first.
second = FindBetween(first + 3 * one_month, first + 9 * one_month, 1)
# Find out which switch is into and which if out of DST
if IsDst(first - 1) and not IsDst(first + 1):
start = second
end = first
else:
start = first
end = second
return (start, end)
def GetDaylightSavingsAttribs():
times = GetDaylightSavingsTimes()
if not times:
return None
(start, end) = times
def DstMonth(t):
return time.localtime(t)[1] - 1
def DstHour(t):
return time.localtime(t - 1)[3] + 1
def DstSunday(t):
if time.localtime(t)[2] > 15:
return "'last'"
else:
return "'first'"
def DstMinutes(t):
return (time.localtime(t - 1)[4] + 1) % 60
attribs = { }
attribs['start_month'] = DstMonth(start)
attribs['end_month'] = DstMonth(end)
attribs['start_sunday'] = DstSunday(start)
attribs['end_sunday'] = DstSunday(end)
attribs['start_hour'] = DstHour(start)
attribs['end_hour'] = DstHour(end)
attribs['start_minutes'] = DstMinutes(start)
attribs['end_minutes'] = DstMinutes(end)
return attribs
def Main():
parser = BuildOptions()
(options, args) = parser.parse_args()
ValidateOptions(options)
test_suite = TestSuite(options.tests,
options.strict_only,
options.non_strict_only)
options.non_strict_only,
options.unmarked_default)
test_suite.Validate()
if options.cat:
test_suite.Print(args)