diff --git a/client.py b/client.py index 3dcd37056b46267ea61890516966a9ca0fb14844..064d09bed970611cf0ac0d7bfaff8db6cce560c8 100755 --- a/client.py +++ b/client.py @@ -7,6 +7,9 @@ NSS_DIRS = (('dbm', 'mozilla/dbm'), ('security/dbm', 'mozilla/security/dbm')) NSSCKBI_DIRS = (('security/nss/lib/ckfw/builtins', 'mozilla/security/nss/lib/ckfw/builtins'),) LIBFFI_DIRS = (('js/ctypes/libffi', 'libffi'),) +WEBIDLPARSER_DIR = 'dom/bindings/parser' +WEBIDLPARSER_REPO = 'https://hg.mozilla.org/users/khuey_mozilla.com/webidl-parser' +WEBIDLPARSER_EXCLUSIONS = ['.hgignore', '.gitignore', '.hg', 'ply'] CVSROOT_MOZILLA = ':pserver:anonymous@cvs-mirror.mozilla.org:/cvsroot' CVSROOT_LIBFFI = ':pserver:anoncvs@sources.redhat.com:/cvs/libffi' @@ -15,6 +18,7 @@ import os import sys import datetime import shutil +import glob from optparse import OptionParser from subprocess import check_call @@ -30,7 +34,6 @@ def do_hg_pull(dir, repository, hg): fulldir = os.path.join(topsrcdir, dir) # clone if the dir doesn't exist, pull if it does if not os.path.exists(fulldir): - fulldir = os.path.join(topsrcdir, dir) check_call_noisy([hg, 'clone', repository, fulldir]) else: cmd = [hg, 'pull', '-u', '-R', fulldir] @@ -40,6 +43,25 @@ def do_hg_pull(dir, repository, hg): check_call([hg, 'parent', '-R', fulldir, '--template=Updated to revision {node}.\n']) +def do_hg_replace(dir, repository, tag, exclusions, hg): + """ + Replace the contents of dir with the contents of repository, except for + files matching exclusions. + """ + fulldir = os.path.join(topsrcdir, dir) + if os.path.exists(fulldir): + shutil.rmtree(fulldir) + + assert not os.path.exists(fulldir) + check_call_noisy([hg, 'clone', '-u', tag, repository, fulldir]) + + for thing in exclusions: + for excluded in glob.iglob(os.path.join(fulldir, thing)): + if os.path.isdir(excluded): + shutil.rmtree(excluded) + else: + os.remove(excluded) + def do_cvs_export(modules, tag, cvsroot, cvs): """Check out a CVS directory without CVS metadata, using "export" modules is a list of directories to check out and the corresponding @@ -60,7 +82,7 @@ def do_cvs_export(modules, tag, cvsroot, cvs): cwd=os.path.join(topsrcdir, parent)) print "CVS export end: " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") -o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname") +o = OptionParser(usage="client.py [options] update_nspr tagname | update_nss tagname | update_libffi tagname | update_webidlparser tagname") o.add_option("--skip-mozilla", dest="skip_mozilla", action="store_true", default=False, help="Obsolete") @@ -69,6 +91,8 @@ o.add_option("--cvs", dest="cvs", default=os.environ.get('CVS', 'cvs'), help="The location of the cvs binary") o.add_option("--cvsroot", dest="cvsroot", help="The CVSROOT (default for mozilla checkouts: %s)" % CVSROOT_MOZILLA) +o.add_option("--hg", dest="hg", default=os.environ.get('HG', 'hg'), + help="The location of the hg binary") try: options, args = o.parse_args() @@ -104,6 +128,9 @@ elif action in ('update_libffi'): if not options.cvsroot: options.cvsroot = CVSROOT_LIBFFI do_cvs_export(LIBFFI_DIRS, tag, options.cvsroot, options.cvs) +elif action in ('update_webidlparser'): + tag, = args[1:] + do_hg_replace(WEBIDLPARSER_DIR, WEBIDLPARSER_REPO, tag, WEBIDLPARSER_EXCLUSIONS, options.hg) else: o.print_help() sys.exit(2) diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index ad7539cdc63e32ca7e264b1eba153d530e97a172..ac59acd9845b03b6dec7ce623b6b87f01635e450 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -69,14 +69,16 @@ def parseInt(literal): # Magic for creating enums def M_add_class_attribs(attribs): def foo(name, bases, dict_): - for v, k in attribs: + for v, k in enumerate(attribs): dict_[k] = v + assert 'length' not in dict_ + dict_['length'] = len(attribs) return type(name, bases, dict_) return foo def enum(*names): class Foo(object): - __metaclass__ = M_add_class_attribs(enumerate(names)) + __metaclass__ = M_add_class_attribs(names) def __setattr__(self, name, value): # this makes it read-only raise NotImplementedError return Foo() @@ -93,9 +95,8 @@ class WebIDLError(Exception): self.location) class Location(object): - _line = None - def __init__(self, lexer, lineno, lexpos, filename): + self._line = None self._lineno = lineno self._lexpos = lexpos self._lexdata = lexer.lexdata @@ -105,36 +106,47 @@ class Location(object): return self._lexpos == other._lexpos and \ self._file == other._file + def filename(self): + return self.filename + def resolve(self): if self._line: return startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1 endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80) - self._line = self._lexdata[startofline:endofline] + if endofline != -1: + self._line = self._lexdata[startofline:endofline] + else: + self._line = self._lexdata[startofline:] self._colno = self._lexpos - startofline - def pointerline(self): - def i(): - for i in xrange(0, self._colno): - yield " " - yield "^" - - return "".join(i()) - def get(self): self.resolve() return "%s line %s:%s" % (self._file, self._lineno, self._colno) + def _pointerline(self): + return " " * self._colno + "^" + def __str__(self): self.resolve() return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno, - self._line, self.pointerline()) + self._line, self._pointerline()) class BuiltinLocation(object): def __init__(self, text): self.msg = text + def __eq__(self, other): + return isinstance(other, BuiltinLocation) and \ + self.msg == other.msg + + def filename(self): + return '<builtin>' + + def resolve(self): + pass + def get(self): return self.msg @@ -150,7 +162,7 @@ class IDLObject(object): self.userData = dict() def filename(self): - return self.location._file + return self.location.filename() def isInterface(self): return False @@ -198,6 +210,11 @@ class IDLScope(IDLObject): return "::" def ensureUnique(self, identifier, object): + """ + Ensure that there is at most one 'identifier' in scope ('self'). + Note that object can be None. This occurs if we end up here for an + interface type we haven't seen yet. + """ assert isinstance(identifier, IDLUnresolvedIdentifier) assert not object or isinstance(object, IDLObjectWithIdentifier) assert not object or object.identifier == identifier @@ -300,6 +317,9 @@ class IDLUnresolvedIdentifier(IDLObject): object.identifier = identifier return identifier + def finish(self): + assert False # Should replace with a resolved identifier first. + class IDLObjectWithIdentifier(IDLObject): def __init__(self, location, parentScope, identifier): IDLObject.__init__(self, location) @@ -368,9 +388,8 @@ class IDLInterface(IDLObjectWithScope): self.parent = parent self._callback = False - + self._finished = False self.members = list(members) # clone the list - assert iter(self.members) # Assert it's iterable IDLObjectWithScope.__init__(self, location, parentScope, name) @@ -404,7 +423,7 @@ class IDLInterface(IDLObjectWithScope): return retval def finish(self, scope): - if hasattr(self, "_finished"): + if self._finished: return self._finished = True @@ -416,7 +435,6 @@ class IDLInterface(IDLObjectWithScope): self.parent = parent assert iter(self.members) - members = None if self.parent: self.parent.finish(scope) @@ -427,19 +445,6 @@ class IDLInterface(IDLObjectWithScope): else: members = list(self.members) - SpecialType = enum( - 'NamedGetter', - 'NamedSetter', - 'NamedCreator', - 'NamedDeleter', - 'IndexedGetter', - 'IndexedSetter', - 'IndexedCreator', - 'IndexedDeleter' - ) - - specialMembersSeen = [False for i in range(8)] - def memberNotOnParentChain(member, iface): assert iface @@ -451,59 +456,39 @@ class IDLInterface(IDLObjectWithScope): return False return memberNotOnParentChain(member, iface.parent) + # Ensure that there's at most one of each {named,indexed} + # {getter,setter,creator,deleter}. + specialMembersSeen = set() for member in members: if memberNotOnParentChain(member, self): member.resolve(self) - - if member.tag == IDLInterfaceMember.Tags.Method: - if member.isGetter(): - if member.isNamed(): - if specialMembersSeen[SpecialType.NamedGetter]: - raise WebIDLError("Multiple named getters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.NamedGetter] = True - else: - assert member.isIndexed() - if specialMembersSeen[SpecialType.IndexedGetter]: - raise WebIDLError("Multiple indexed getters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.IndexedGetter] = True - if member.isSetter(): - if member.isNamed(): - if specialMembersSeen[SpecialType.NamedSetter]: - raise WebIDLError("Multiple named setters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.NamedSetter] = True - else: - assert member.isIndexed() - if specialMembersSeen[SpecialType.IndexedSetter]: - raise WebIDLError("Multiple indexed setters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.IndexedSetter] = True - if member.isCreator(): - if member.isNamed(): - if specialMembersSeen[SpecialType.NamedCreator]: - raise WebIDLError("Multiple named creators on %s" % (self), - self.location) - specialMembersSeen[SpecialType.NamedCreator] = True - else: - assert member.isIndexed() - if specialMembersSeen[SpecialType.IndexedCreator]: - raise WebIDLError("Multiple indexed creators on %s" % (self), - self.location) - specialMembersSeen[SpecialType.IndexedCreator] = True - if member.isDeleter(): - if member.isNamed(): - if specialMembersSeen[SpecialType.NamedDeleter]: - raise WebIDLError("Multiple named deleters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.NamedDeleter] = True - else: - assert member.isIndexed() - if specialMembersSeen[SpecialType.IndexedDeleter]: - raise WebIDLError("Multiple indexed Deleters on %s" % (self), - self.location) - specialMembersSeen[SpecialType.IndexedDeleter] = True + + if member.tag != IDLInterfaceMember.Tags.Method: + continue + + if member.isGetter(): + memberType = "getters" + elif member.isSetter(): + memberType = "setters" + elif member.isCreator(): + memberType = "creators" + elif member.isDeleter(): + memberType = "deleters" + else: + continue + + if member.isNamed(): + memberType = "named " + memberType + elif member.isIndexed(): + memberType = "indexed " + memberType + else: + continue + + if memberType in specialMembersSeen: + raise WebIDLError("Multiple " + memberType + " on %s" % (self), + self.location) + + specialMembersSeen.add(memberType) for member in self.members: member.finish(scope) @@ -529,7 +514,7 @@ class IDLInterface(IDLObjectWithScope): return depth def hasConstants(self): - return reduce(lambda b, m: b or m.isConst(), self.members, False) + return any(m.isConst() for m in self.members) def hasInterfaceObject(self): if self.isCallback(): @@ -567,10 +552,7 @@ class IDLInterface(IDLObjectWithScope): identifier = IDLUnresolvedIdentifier(self.location, "constructor", allowForbidden=True) - method = IDLMethod(self.location, identifier, retType, args, - False, False, False, False, False, False, - False, False) - + method = IDLMethod(self.location, identifier, retType, args) method.resolve(self) self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True @@ -763,6 +745,12 @@ class IDLNullableType(IDLType): def isString(self): return self.inner.isString() + def isFloat(self): + return self.inner.isFloat() + + def isInteger(self): + return self.inner.isInteger() + def isVoid(self): return False @@ -772,6 +760,9 @@ class IDLNullableType(IDLType): def isArray(self): return self.inner.isArray() + def isArrayBuffer(self): + return self.inner.isArrayBuffer() + def isDictionary(self): return self.inner.isDictionary() @@ -797,7 +788,7 @@ class IDLNullableType(IDLType): return self def unroll(self): - return self.inner + return self.inner.unroll() def isDistinguishableFrom(self, other): if other.nullable(): @@ -835,18 +826,19 @@ class IDLSequenceType(IDLType): return True def isArray(self): - return self.inner.isArray() + return False def isDictionary(self): - return self.inner.isDictionary() + return False def isInterface(self): - return self.inner.isInterface() + return False def isEnum(self): - return self.inner.isEnum(); + return False def tag(self): + # XXXkhuey this is probably wrong. return self.inner.tag() def resolveType(self, parentScope): @@ -858,10 +850,11 @@ class IDLSequenceType(IDLType): def complete(self, scope): self.inner = self.inner.complete(scope) + self.name = self.inner.name return self def unroll(self): - return self.inner + return self.inner.unroll() def isDistinguishableFrom(self, other): return (other.isPrimitive() or other.isString() or other.isEnum() or @@ -895,32 +888,33 @@ class IDLArrayType(IDLType): return False def isPrimitive(self): - return self.inner.isPrimitive() + return False def isString(self): - return self.inner.isString() + return False def isVoid(self): return False def isSequence(self): assert not self.inner.isSequence() - return self.inner.isSequence() + return False def isArray(self): return True def isDictionary(self): assert not self.inner.isDictionary() - return self.inner.isDictionary() + return False def isInterface(self): - return self.inner.isInterface() + return False def isEnum(self): - return self.inner.isEnum() + return False def tag(self): + # XXXkhuey this is probably wrong. return self.inner.tag() def resolveType(self, parentScope): @@ -932,10 +926,11 @@ class IDLArrayType(IDLType): def complete(self, scope): self.inner = self.inner.complete(scope) + self.name = self.inner.name return self def unroll(self): - return self.inner + return self.inner.unroll() def isDistinguishableFrom(self, other): return (other.isPrimitive() or other.isString() or other.isEnum() or @@ -995,7 +990,7 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier): return self.inner.tag() def unroll(self): - return self.inner + return self.inner.unroll() def isDistinguishableFrom(self, other): return self.inner.isDistinguishableFrom(other) @@ -1041,6 +1036,10 @@ class IDLWrapperType(IDLType): def isEnum(self): return isinstance(self.inner, IDLEnum) + def resolveType(self, parentScope): + assert isinstance(parentScope, IDLScope) + self.inner.resolve(parentScope) + def isComplete(self): return True @@ -1122,32 +1121,32 @@ class IDLBuiltinType(IDLType): def __init__(self, location, name, type): IDLType.__init__(self, location, name) self.builtin = True - self.type = type + self._typeTag = type def isPrimitive(self): - return self.type <= IDLBuiltinType.Types.double + return self._typeTag <= IDLBuiltinType.Types.double def isString(self): - return self.type == IDLBuiltinType.Types.domstring + return self._typeTag == IDLBuiltinType.Types.domstring def isInteger(self): - return self.type <= IDLBuiltinType.Types.unsigned_long_long + return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long def isArrayBuffer(self): - return self.type == IDLBuiltinType.Types.ArrayBuffer + return self._typeTag == IDLBuiltinType.Types.ArrayBuffer def isInterface(self): # ArrayBuffers are interface types per the TypedArray spec, # but we handle them as builtins because SpiderMonkey implements # ArrayBuffers. - return self.type == IDLBuiltinType.Types.ArrayBuffer + return self._typeTag == IDLBuiltinType.Types.ArrayBuffer def isFloat(self): - return self.type == IDLBuiltinType.Types.float or \ - self.type == IDLBuiltinType.Types.double + return self._typeTag == IDLBuiltinType.Types.float or \ + self._typeTag == IDLBuiltinType.Types.double def tag(self): - return IDLBuiltinType.TagLookup[self.type] + return IDLBuiltinType.TagLookup[self._typeTag] def isDistinguishableFrom(self, other): if self.isPrimitive() or self.isString(): @@ -1280,7 +1279,7 @@ class IDLValue(IDLObject): # We're both integer types. See if we fit. - (min, max) = integerTypeSizes[type.type] + (min, max) = integerTypeSizes[type._typeTag] if self.value <= max and self.value >= min: # Promote return IDLValue(self.location, type, self.value) @@ -1492,8 +1491,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope): ) def __init__(self, location, identifier, returnType, arguments, - static, getter, setter, creator, deleter, specialType, legacycaller, - stringifier): + static=False, getter=False, setter=False, creator=False, + deleter=False, specialType=NamedOrIndexed.Neither, + legacycaller=False, stringifier=False): + # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.Tags.Method) @@ -1678,6 +1679,7 @@ class Tokenizer(object): def t_INTEGER(self, t): r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)' try: + # Can't use int(), because that doesn't handle octal properly. t.value = parseInt(t.value) except: raise WebIDLError("Invalid integer literal", @@ -2261,8 +2263,9 @@ class Parser(Tokenizer): "legacycaller" if legacycaller else ""), allowDoubleUnderscore=True) method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments, - static, getter, setter, creator, deleter, specialType, - legacycaller, False) + static=static, getter=getter, setter=setter, creator=creator, + deleter=deleter, specialType=specialType, + legacycaller=legacycaller, stringifier=False) p[0] = method def p_QualifiersStatic(self, p): @@ -2861,7 +2864,14 @@ class Parser(Tokenizer): for production in self._productions: production.finish(self.globalScope()) - return set(self._productions) + # De-duplicate self._productions, without modifying its order. + seen = set() + result = [] + for p in self._productions: + if p not in seen: + seen.add(p) + result.append(p) + return result def reset(self): return Parser() diff --git a/dom/bindings/parser/__init__.py b/dom/bindings/parser/__init__.py deleted file mode 100644 index c082fe541a204da58b1de885cc2c12f9536399b5..0000000000000000000000000000000000000000 --- a/dom/bindings/parser/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ['WebIDL'] diff --git a/dom/bindings/parser/runtests.py b/dom/bindings/parser/runtests.py index 3835d4bcf2b6a5429b032cfdb46b30027d9c5a5b..37b6e07ce2fafb3aeaca0c016bb572b7f448ad56 100644 --- a/dom/bindings/parser/runtests.py +++ b/dom/bindings/parser/runtests.py @@ -37,36 +37,76 @@ import os, sys import glob +import optparse +import traceback import WebIDL class TestHarness(object): + def __init__(self, test, verbose): + self.test = test + self.verbose = verbose + self.printed_intro = False + + def start(self): + if self.verbose: + self.maybe_print_intro() + + def finish(self): + if self.verbose or self.printed_intro: + print "Finished test %s" % self.test + + def maybe_print_intro(self): + if not self.printed_intro: + print "Starting test %s" % self.test + self.printed_intro = True + + def test_pass(self, msg): + if self.verbose: + print "TEST-PASS | %s" % msg + + def test_fail(self, msg): + self.maybe_print_intro() + print "TEST-UNEXPECTED-FAIL | %s" % msg + def ok(self, condition, msg): if condition: - print "TEST-PASS | %s" % msg + self.test_pass(msg) else: - print "TEST-UNEXPECTED-FAIL | %s" % msg + self.test_fail(msg) def check(self, a, b, msg): if a == b: - print "TEST-PASS | %s" % msg + self.test_pass(msg) else: - print "TEST-UNEXPECTED-FAIL | %s" % msg + self.test_fail(msg) print "\tGot %s expected %s" % (a, b) -def run_tests(): - harness = TestHarness() +def run_tests(tests, verbose): + testdir = os.path.join(os.path.dirname(__file__), 'tests') + if not tests: + tests = glob.iglob(os.path.join(testdir, "*.py")) + sys.path.append(testdir) - tests = glob.iglob("tests/*.py") - sys.path.append("./tests") for test in tests: (testpath, ext) = os.path.splitext(os.path.basename(test)) _test = __import__(testpath, globals(), locals(), ['WebIDLTest']) - #try: - _test.WebIDLTest.__call__(WebIDL.Parser(), harness) - #except: - # print "TEST-UNEXPECTED-FAIL | Unhandled exception in Test %s" % testpath - # print sys.exc_info()[0] - print "Test %s Complete\n" % testpath + + harness = TestHarness(test, verbose) + harness.start() + try: + _test.WebIDLTest.__call__(WebIDL.Parser(), harness) + except: + print "TEST-UNEXPECTED-FAIL | Unhandled exception in test %s" % testpath + traceback.print_exc() + finally: + harness.finish() if __name__ == '__main__': - run_tests() + usage = """%prog [OPTIONS] [TESTS] + Where TESTS are relative to the tests directory.""" + parser = optparse.OptionParser(usage=usage) + parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True, + help="Don't print passing tests.") + options, tests = parser.parse_args() + + run_tests(tests, verbose=options.verbose) diff --git a/dom/bindings/parser/tests/test_array_of_interface.py b/dom/bindings/parser/tests/test_array_of_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..265289845957a0eec0ad1004e25c87e006fee325 --- /dev/null +++ b/dom/bindings/parser/tests/test_array_of_interface.py @@ -0,0 +1,13 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + interface A { + attribute long a; + }; + + interface B { + attribute A[] b; + }; + """); + parser.finish() diff --git a/dom/bindings/parser/tests/test_builtin_filename.py b/dom/bindings/parser/tests/test_builtin_filename.py new file mode 100644 index 0000000000000000000000000000000000000000..631e52eba0b10848e3c1903120975c18fef546fa --- /dev/null +++ b/dom/bindings/parser/tests/test_builtin_filename.py @@ -0,0 +1,11 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + interface Test { + attribute long b; + }; + """); + + attr = parser.finish()[0].members[0] + harness.check(attr.type.filename(), '<builtin>', 'Filename on builtin type') diff --git a/dom/bindings/parser/tests/test_constructor.py b/dom/bindings/parser/tests/test_constructor.py index 03819ed8ccf452e97f8c8b3b89924f8de0dd9933..6ec1be1871bdadbf88d45b73ed8cc7450a0fc67c 100644 --- a/dom/bindings/parser/tests/test_constructor.py +++ b/dom/bindings/parser/tests/test_constructor.py @@ -62,14 +62,14 @@ def WebIDLTest(parser, harness): "Should be an IDLInterface") checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor", - "constructor", [("TestConstructorNoArgs", [])]) + "constructor", [("TestConstructorNoArgs (Wrapper)", [])]) checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor", "constructor", - [("TestConstructorWithArgs", + [("TestConstructorWithArgs (Wrapper)", [("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])]) checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor", "constructor", - [("TestConstructorOverloads", + [("TestConstructorOverloads (Wrapper)", [("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]), - ("TestConstructorOverloads", + ("TestConstructorOverloads (Wrapper)", [("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])]) diff --git a/dom/bindings/parser/tests/test_deduplicate.py b/dom/bindings/parser/tests/test_deduplicate.py new file mode 100644 index 0000000000000000000000000000000000000000..6249d36fb8f3c3a58f97aca072a86143c6946f71 --- /dev/null +++ b/dom/bindings/parser/tests/test_deduplicate.py @@ -0,0 +1,15 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + interface Foo; + interface Bar; + interface Foo; + """); + + results = parser.finish() + + # There should be no duplicate interfaces in the result. + expectedNames = sorted(['Foo', 'Bar']) + actualNames = sorted(map(lambda iface: iface.identifier.name, results)) + harness.check(actualNames, expectedNames, "Parser shouldn't output duplicate names.") diff --git a/dom/bindings/parser/tests/test_enum.py b/dom/bindings/parser/tests/test_enum.py index 68ec73b2ff2171f7d0e3b4a7a4b75ae3ef8b24b9..069c7f336a74a7bfaa2b7dfbac962bc53967586d 100644 --- a/dom/bindings/parser/tests/test_enum.py +++ b/dom/bindings/parser/tests/test_enum.py @@ -47,7 +47,7 @@ def WebIDLTest(parser, harness): harness.check(len(signatures), 1, "Expect one signature") (returnType, arguments) = signatures[0] - harness.check(str(returnType), "TestEnum", "Method type is the correct name") + harness.check(str(returnType), "TestEnum (Wrapper)", "Method type is the correct name") harness.check(len(arguments), 1, "Method has the right number of arguments") arg = arguments[0] harness.ok(isinstance(arg, WebIDL.IDLArgument), "Should be an IDLArgument") @@ -58,4 +58,4 @@ def WebIDLTest(parser, harness): "Attr has correct QName") harness.check(attr.identifier.name, "foo", "Attr has correct name") - harness.check(str(attr.type), "TestEnum", "Attr type is the correct name") + harness.check(str(attr.type), "TestEnum (Wrapper)", "Attr type is the correct name") diff --git a/dom/bindings/parser/tests/test_error_colno.py b/dom/bindings/parser/tests/test_error_colno.py new file mode 100644 index 0000000000000000000000000000000000000000..7afd15513c6d04100dd190fb936981535b01e6aa --- /dev/null +++ b/dom/bindings/parser/tests/test_error_colno.py @@ -0,0 +1,20 @@ +import WebIDL + +def WebIDLTest(parser, harness): + # Check that error messages put the '^' in the right place. + + threw = False + input = 'interface ?' + try: + parser.parse(input) + results = parser.finish() + except WebIDL.WebIDLError as e: + threw = True + lines = str(e).split('\n') + + harness.check(len(lines), 3, 'Expected number of lines in error message') + harness.check(lines[1], input, 'Second line shows error') + harness.check(lines[2], ' ' * (len(input) - 1) + '^', + 'Correct column pointer in error message') + + harness.ok(threw, "Should have thrown.") diff --git a/dom/bindings/parser/tests/test_nullable_equivalency.py b/dom/bindings/parser/tests/test_nullable_equivalency.py new file mode 100644 index 0000000000000000000000000000000000000000..f8211a8bb57155c5ee28824a01c957cea19521cb --- /dev/null +++ b/dom/bindings/parser/tests/test_nullable_equivalency.py @@ -0,0 +1,134 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + interface TestNullableEquivalency1 { + attribute long a; + attribute long? b; + }; + + interface TestNullableEquivalency2 { + attribute ArrayBuffer a; + attribute ArrayBuffer? b; + }; + + /* Not implemented */ + /*dictionary TestNullableEquivalency3Dict { + long foo = 42; + }; + + interface TestNullableEquivalency3 { + attribute Test3Dict a; + attribute Test3Dict? b; + };*/ + + enum TestNullableEquivalency4Enum { + "Foo", + "Bar" + }; + + interface TestNullableEquivalency4 { + attribute TestNullableEquivalency4Enum a; + attribute TestNullableEquivalency4Enum? b; + }; + + interface TestNullableEquivalency5 { + attribute TestNullableEquivalency4 a; + attribute TestNullableEquivalency4? b; + }; + + interface TestNullableEquivalency6 { + attribute boolean a; + attribute boolean? b; + }; + + interface TestNullableEquivalency7 { + attribute DOMString a; + attribute DOMString? b; + }; + + /* Not implemented. */ + /*interface TestNullableEquivalency8 { + attribute float a; + attribute float? b; + };*/ + + interface TestNullableEquivalency8 { + attribute double a; + attribute double? b; + }; + + interface TestNullableEquivalency9 { + attribute object a; + attribute object? b; + }; + + interface TestNullableEquivalency10 { + attribute double[] a; + attribute double[]? b; + }; + + interface TestNullableEquivalency11 { + attribute TestNullableEquivalency9[] a; + attribute TestNullableEquivalency9[]? b; + }; + """) + + for decl in parser.finish(): + if decl.isInterface(): + checkEquivalent(decl, harness) + +def checkEquivalent(iface, harness): + type1 = iface.members[0].type + type2 = iface.members[1].type + + harness.check(type1.nullable(), False, 'attr1 should not be nullable') + harness.check(type2.nullable(), True, 'attr2 should be nullable') + + # We don't know about type1, but type2, the nullable type, definitely + # shouldn't be builtin. + harness.check(type2.builtin, False, 'attr2 should not be builtin') + + # Ensure that all attributes of type2 match those in type1, except for: + # - names on an ignore list, + # - names beginning with '_', + # - functions which throw when called with no args, and + # - class-level non-callables ("static variables"). + # + # Yes, this is an ugly, fragile hack. But it finds bugs... + for attr in dir(type1): + if attr.startswith('_') or \ + attr in ['nullable', 'builtin', 'filename', 'location', + 'inner', 'QName'] or \ + (hasattr(type(type1), attr) and not callable(getattr(type1, attr))): + continue + + a1 = getattr(type1, attr) + + if callable(a1): + try: + v1 = a1() + except: + # Can't call a1 with no args, so skip this attriute. + continue + + try: + a2 = getattr(type2, attr) + except: + harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface)) + continue + + if not callable(a2): + harness.ok(False, "%s attribute on type %s in %s wasn't callable" % (attr, type2, iface)) + continue + + v2 = a2() + harness.check(v2, v1, '%s method return value' % attr) + else: + try: + a2 = getattr(type2, attr) + except: + harness.ok(False, 'Missing %s attribute on type %s in %s' % (attr, type2, iface)) + continue + + harness.check(a2, a1, '%s attribute should match' % attr)