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 /// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met: /// that the following conditions are met:
@ -20,9 +20,9 @@
//Error Detector //Error Detector
if (this.window!==undefined) { //for console support if (this.window!==undefined) { //for console support
window.onerror = function(errorMsg, url, lineNumber) { this.window.onerror = function(errorMsg, url, lineNumber) {
window.iframeError = errorMsg; this.window.iframeError = errorMsg;
} };
} }
//This doesn't work with early errors in current versions of Opera //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 /// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met: /// 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 /// Redistribution and use in source and binary forms, with or without modification, are permitted provided
/// that the following conditions are met: /// that the following conditions are met:
@ -335,8 +335,8 @@ function getPrecision(num) {
//TODO: Create a table of prec's, //TODO: Create a table of prec's,
// because using Math for testing Math isn't that correct. // because using Math for testing Math isn't that correct.
log2num = Math.log(Math.abs(num)) / Math.LN2; var log2num = Math.log(Math.abs(num)) / Math.LN2;
pernum = Math.ceil(log2num); var pernum = Math.ceil(log2num);
return (2 * Math.pow(2, -52 + pernum)); return (2 * Math.pow(2, -52 + pernum));
//return(0); //return(0);
} }
@ -364,7 +364,7 @@ function isEqual(num1, num2) {
// This code is governed by the BSD license found in the LICENSE file. // This code is governed by the BSD license found in the LICENSE file.
function ToInteger(p) { function ToInteger(p) {
x = Number(p); var x = Number(p);
if (isNaN(x)) { if (isNaN(x)) {
return +0; return +0;
@ -437,7 +437,7 @@ var $LocalTZ,
current = new Date(current.getTime() + 1); current = new Date(current.getTime() + 1);
} }
return current; return current;
} };
var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0); var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0);
var decemberDate = new Date(2000, 11, 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){ function GetSundayInMonth(t, m, count){
var year = YearFromTime(t); var year = YearFromTime(t);
var tempDate;
if (count==='"first"') { if (count==='"first"') {
for (var d=1; d <= DaysInMonth(m, InLeapYear(t)); d++) { for (var d=1; d <= DaysInMonth(m, InLeapYear(t)); d++) {
@ -905,6 +906,6 @@ return attribs
//--Test case registration----------------------------------------------------- //--Test case registration-----------------------------------------------------
function runTestCase(testcase) { function runTestCase(testcase) {
if (testcase() !== true) { 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(globalScopeContents);
idoc.writeln("</script>"); idoc.writeln("</script>");
idoc.close(); idoc.close();
} };
//--Helper functions------------------------------------------------------- //--Helper functions-------------------------------------------------------
this.convertForEval = function(txt) { this.convertForEval = function(txt) {
@ -190,7 +190,7 @@ function BrowserRunner() {
txt = txt.replace(/\r/g,"\\r"); txt = txt.replace(/\r/g,"\\r");
txt = txt.replace(/\n/g,"\\n"); txt = txt.replace(/\n/g,"\\n");
return txt; return txt;
} };
} }
/* Loads tests from the sections specified in testcases.json. /* Loads tests from the sections specified in testcases.json.
@ -366,7 +366,7 @@ function Controller() {
try { try {
controller.implementerHook.finished(elapsed); controller.implementerHook.finished(elapsed);
} catch(e) { /*no-op*/} } catch(e) { /*no-op*/}
} };
this.start = function() { this.start = function() {
state = 'running'; 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

280
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. # Copyright 2009 the Sputnik authors. All rights reserved.
# This code is governed by the BSD license found in the LICENSE file. # 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 logging
import optparse import optparse
@ -13,25 +18,42 @@ import subprocess
import sys import sys
import tempfile import tempfile
import time 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): class Test262Error(Exception):
def __init__(self, message): def __init__(self, message):
self.message = message self.message = message
def ReportError(s): def ReportError(s):
raise Test262Error(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(): def BuildOptions():
result = optparse.OptionParser() result = optparse.OptionParser()
result.add_option("--command", default=None, help="The command-line to run") result.add_option("--command", default=None, help="The command-line to run")
result.add_option("--tests", default=path.abspath('.'), result.add_option("--tests", default=path.abspath('.'),
help="Path to the tests") help="Path to the tests")
result.add_option("--cat", default=False, action="store_true", 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", result.add_option("--summary", default=False, action="store_true",
help="Print summary after running tests") help="Print summary after running tests")
result.add_option("--full-summary", default=False, action="store_true", result.add_option("--full-summary", default=False, action="store_true",
@ -40,7 +62,10 @@ def BuildOptions():
help="Test only strict mode") help="Test only strict mode")
result.add_option("--non_strict_only", default=False, action="store_true", result.add_option("--non_strict_only", default=False, action="store_true",
help="Test only non-strict mode") 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 return result
@ -51,16 +76,7 @@ def ValidateOptions(options):
ReportError("Couldn't find test path '%s'" % options.tests) ReportError("Couldn't find test path '%s'" % options.tests)
_PLACEHOLDER_PATTERN = re.compile(r"\{\{(\w+)\}\}") placeHolderPattern = 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'
}
def IsWindows(): def IsWindows():
@ -68,12 +84,6 @@ def IsWindows():
return (p == 'Windows') or (p == 'Microsoft') 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): class TempFile(object):
def __init__(self, suffix="", prefix="tmp", text=False): def __init__(self, suffix="", prefix="tmp", text=False):
@ -89,8 +99,7 @@ class TempFile(object):
(self.fd, self.name) = tempfile.mkstemp( (self.fd, self.name) = tempfile.mkstemp(
suffix = self.suffix, suffix = self.suffix,
prefix = self.prefix, prefix = self.prefix,
text = self.text text = self.text)
)
def Write(self, str): def Write(self, str):
os.write(self.fd, str) os.write(self.fd, str)
@ -127,7 +136,7 @@ class TestResult(object):
mode = self.case.GetMode() mode = self.case.GetMode()
if self.HasUnexpectedOutcome(): if self.HasUnexpectedOutcome():
if self.case.IsNegative(): 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: else:
if long_format: if long_format:
print "=== %s failed in %s ===" % (name, mode) print "=== %s failed in %s ===" % (name, mode)
@ -164,11 +173,17 @@ class TestCase(object):
self.suite = suite self.suite = suite
self.name = name self.name = name
self.full_path = full_path self.full_path = full_path
self.contents = None
self.is_negative = None
self.strict_mode = strict_mode self.strict_mode = strict_mode
self.is_strict_only = None f = open(self.full_path)
self.is_non_strict_only = None 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): def GetName(self):
return path.join(*self.name) return path.join(*self.name)
@ -182,60 +197,33 @@ class TestCase(object):
def GetPath(self): def GetPath(self):
return self.name 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): def IsNegative(self):
if self.is_negative is None: return 'negative' in self.testRecord
self.is_negative = ("@negative" in self.GetRawContents())
return self.is_negative
def IsStrictOnly(self): def IsOnlyStrict(self):
if self.is_strict_only is None: return 'onlyStrict' in self.testRecord
self.is_strict_only = ("@strict_only" in self.GetRawContents())
return self.is_strict_only
def IsNonStrictOnly(self): def IsNoStrict(self):
if self.is_non_strict_only is None: return 'noStrict' in self.testRecord
self.is_non_strict_only = ("@non_strict_only" in self.GetRawContents())
return self.is_non_strict_only
def GetSource(self): def GetSource(self):
source = self.suite.GetInclude("framework.js", False) + \ # "var testDescrip = " + str(self.testRecord) + ';\n\n' + \
self.suite.GetInclude("sta.js", False) source = self.suite.GetInclude("cth.js") + \
source += StripHeader(self.GetRawContents()) self.suite.GetInclude("sta.js") + \
def IncludeFile(match): self.suite.GetInclude("ed.js") + \
return self.suite.GetInclude(match.group(1)) self.test + '\n'
source = _INCLUDE_PATTERN.sub(IncludeFile, source)
def SpecialCall(match):
key = match.group(1)
return _SPECIAL_CALLS.get(key, match.group(0))
if self.strict_mode: if self.strict_mode:
source = '"use strict";\nvar strict_mode = true;\n' + \ source = '"use strict";\nvar strict_mode = true;\n' + source
_SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
else: else:
source = "var strict_mode = false; \n" + \ source = "var strict_mode = false; \n" + source
_SPECIAL_CALL_PATTERN.sub(SpecialCall, source)
return source return source
def InstantiateTemplate(self, template, params): def InstantiateTemplate(self, template, params):
def GetParameter(match): def GetParameter(match):
key = match.group(1) key = match.group(1)
return params.get(key, match.group(0)) return params.get(key, match.group(0))
return _PLACEHOLDER_PATTERN.sub(GetParameter, template) return placeHolderPattern.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)
def Execute(self, command): def Execute(self, command):
if IsWindows(): if IsWindows():
@ -260,6 +248,15 @@ class TestCase(object):
stderr.Dispose() stderr.Dispose()
return (code, out, err) 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): def Run(self, command_template):
tmp = TempFile(suffix=".js", prefix="test262-", text=True) tmp = TempFile(suffix=".js", prefix="test262-", text=True)
try: try:
@ -298,11 +295,13 @@ def MakePlural(n):
class TestSuite(object): class TestSuite(object):
def __init__(self, root, strict_only, non_strict_only): def __init__(self, root, strict_only, non_strict_only, unmarked_default):
self.test_root = path.join(root, 'test', 'suite', 'converted') # TODO: derive from packagerConfig.py
self.test_root = path.join(root, 'test', 'suite')
self.lib_root = path.join(root, 'test', 'harness') self.lib_root = path.join(root, 'test', 'harness')
self.strict_only = strict_only self.strict_only = strict_only
self.non_strict_only = non_strict_only self.non_strict_only = non_strict_only
self.unmarked_default = unmarked_default
self.include_cache = { } self.include_cache = { }
def Validate(self): def Validate(self):
@ -325,41 +324,18 @@ class TestSuite(object):
return True return True
return False return False
def GetTimeZoneInfoInclude(self): def GetInclude(self, name):
dst_attribs = GetDaylightSavingsAttribs() if not name in self.include_cache:
if not dst_attribs: static = path.join(self.lib_root, name)
return None if path.exists(static):
lines = [] f = open(static)
for key in sorted(dst_attribs.keys()): contents = stripHeader(f.read())
lines.append('var $DST_%s = %s;' % (key, str(dst_attribs[key]))) contents = re.sub(r'\r\n', '\n', contents)
localtz = time.timezone / -3600 self.include_cache[name] = contents + "\n"
lines.append('var $LocalTZ = %i;' % localtz) f.close()
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: else:
static = path.join(self.lib_root, name) ReportError("Can't find: " + static)
if path.exists(static): return self.include_cache[name]
f = open(static)
contents = f.read()
if strip_header:
contents = StripHeader(contents)
self.include_cache[key] = contents + "\n"
f.close()
else:
self.include_cache[key] = ""
return self.include_cache[key]
def EnumerateTests(self, tests): def EnumerateTests(self, tests):
logging.info("Listing tests in %s", self.test_root) logging.info("Listing tests in %s", self.test_root)
@ -379,14 +355,21 @@ class TestSuite(object):
if self.ShouldRun(rel_path, tests): if self.ShouldRun(rel_path, tests):
basename = path.basename(full_path)[:-3] basename = path.basename(full_path)[:-3]
name = rel_path.split(path.sep)[:-1] + [basename] name = rel_path.split(path.sep)[:-1] + [basename]
if not self.non_strict_only: if EXCLUDE_LIST.count(basename) >= 1:
strict_case = TestCase(self, name, full_path, True) print 'Excluded: ' + basename
if not strict_case.IsNonStrictOnly(): else:
cases.append(strict_case) if not self.non_strict_only:
if not self.strict_only: strict_case = TestCase(self, name, full_path, True)
non_strict_case = TestCase(self, name, full_path, False) if not strict_case.IsNoStrict():
if not non_strict_case.IsStrictOnly(): if strict_case.IsOnlyStrict() or \
cases.append(non_strict_case) 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.IsOnlyStrict():
if non_strict_case.IsNoStrict() or \
self.unmarked_default in ['both', 'non_strict']:
cases.append(non_strict_case)
logging.info("Done listing tests") logging.info("Done listing tests")
return cases return cases
@ -447,83 +430,14 @@ class TestSuite(object):
cases[0].Print() 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(): def Main():
parser = BuildOptions() parser = BuildOptions()
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
ValidateOptions(options) ValidateOptions(options)
test_suite = TestSuite(options.tests, test_suite = TestSuite(options.tests,
options.strict_only, options.strict_only,
options.non_strict_only) options.non_strict_only,
options.unmarked_default)
test_suite.Validate() test_suite.Validate()
if options.cat: if options.cat:
test_suite.Print(args) test_suite.Print(args)