import WebIDL import traceback def WebIDLTest(parser, harness): def shouldPass(prefix, iface, expectedMembers, numProductions=1): p = parser.reset() p.parse(iface) results = p.finish() harness.check( len(results), numProductions, "%s - Should have production count %d" % (prefix, numProductions), ) harness.ok( isinstance(results[0], WebIDL.IDLInterface), "%s - Should be an IDLInterface" % (prefix), ) # Make a copy, since we plan to modify it expectedMembers = list(expectedMembers) for m in results[0].members: name = m.identifier.name if (name, type(m)) in expectedMembers: harness.ok(True, "%s - %s - Should be a %s" % (prefix, name, type(m))) expectedMembers.remove((name, type(m))) else: harness.ok( False, "%s - %s - Unknown symbol of type %s" % (prefix, name, type(m)), ) # A bit of a hoop because we can't generate the error string if we pass if len(expectedMembers) == 0: harness.ok(True, "Found all the members") else: harness.ok( False, "Expected member not found: %s of type %s" % (expectedMembers[0][0], expectedMembers[0][1]), ) return results def shouldFail(prefix, iface): try: p = parser.reset() p.parse(iface) p.finish() harness.ok(False, prefix + " - Interface passed when should've failed") except WebIDL.WebIDLError as e: harness.ok(True, prefix + " - Interface failed as expected") except Exception as e: harness.ok( False, prefix + " - Interface failed but not as a WebIDLError exception: %s" % e, ) iterableMembers = [ (x, WebIDL.IDLMethod) for x in ["entries", "keys", "values", "forEach"] ] setROMembers = ( [(x, WebIDL.IDLMethod) for x in ["has"]] + [("__setlike", WebIDL.IDLMaplikeOrSetlike)] + iterableMembers ) setROMembers.extend([("size", WebIDL.IDLAttribute)]) setRWMembers = [ (x, WebIDL.IDLMethod) for x in ["add", "clear", "delete"] ] + setROMembers setROChromeMembers = [ (x, WebIDL.IDLMethod) for x in ["__add", "__clear", "__delete"] ] + setROMembers setRWChromeMembers = [ (x, WebIDL.IDLMethod) for x in ["__add", "__clear", "__delete"] ] + setRWMembers mapROMembers = ( [(x, WebIDL.IDLMethod) for x in ["get", "has"]] + [("__maplike", WebIDL.IDLMaplikeOrSetlike)] + iterableMembers ) mapROMembers.extend([("size", WebIDL.IDLAttribute)]) mapRWMembers = [ (x, WebIDL.IDLMethod) for x in ["set", "clear", "delete"] ] + mapROMembers mapRWChromeMembers = [ (x, WebIDL.IDLMethod) for x in ["__set", "__clear", "__delete"] ] + mapRWMembers # OK, now that we've used iterableMembers to set up the above, append # __iterable to it for the iterable<> case. iterableMembers.append(("__iterable", WebIDL.IDLIterable)) asyncIterableMembers = [ (x, WebIDL.IDLMethod) for x in ["entries", "keys", "values"] ] asyncIterableMembers.append(("__iterable", WebIDL.IDLAsyncIterable)) valueIterableMembers = [("__iterable", WebIDL.IDLIterable)] valueIterableMembers.append(("__indexedgetter", WebIDL.IDLMethod)) valueIterableMembers.append(("length", WebIDL.IDLAttribute)) valueAsyncIterableMembers = [("__iterable", WebIDL.IDLAsyncIterable)] valueAsyncIterableMembers.append(("values", WebIDL.IDLMethod)) disallowedIterableNames = ["keys", "entries", "values"] disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames mapDisallowedMemberNames = ["get"] + disallowedMemberNames disallowedNonMethodNames = ["clear", "delete"] mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames setDisallowedNonMethodNames = ["add"] + disallowedNonMethodNames unrelatedMembers = [ ("unrelatedAttribute", WebIDL.IDLAttribute), ("unrelatedMethod", WebIDL.IDLMethod), ] # # Simple Usage Tests # shouldPass( "Iterable (key only)", """ interface Foo1 { iterable; readonly attribute unsigned long length; getter long(unsigned long index); attribute long unrelatedAttribute; long unrelatedMethod(); }; """, valueIterableMembers + unrelatedMembers, ) shouldPass( "Iterable (key only) inheriting from parent", """ interface Foo1 : Foo2 { iterable; readonly attribute unsigned long length; getter long(unsigned long index); }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, valueIterableMembers, numProductions=2, ) shouldPass( "Iterable (key and value)", """ interface Foo1 { iterable; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, iterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2, ) shouldPass( "Iterable (key and value) inheriting from parent", """ interface Foo1 : Foo2 { iterable; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, iterableMembers, # numProductions == 3 because of the generated iterator iface, numProductions=3, ) shouldPass( "Async iterable (key only)", """ interface Foo1 { async iterable; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, valueAsyncIterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2, ) shouldPass( "Async iterable (key only) inheriting from parent", """ interface Foo1 : Foo2 { async iterable; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, valueAsyncIterableMembers, # numProductions == 3 because of the generated iterator iface, numProductions=3, ) shouldPass( "Async iterable with argument (key only)", """ interface Foo1 { async iterable(optional long foo); attribute long unrelatedAttribute; long unrelatedMethod(); }; """, valueAsyncIterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2, ) shouldPass( "Async iterable (key and value)", """ interface Foo1 { async iterable; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, asyncIterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2, ) shouldPass( "Async iterable (key and value) inheriting from parent", """ interface Foo1 : Foo2 { async iterable; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, asyncIterableMembers, # numProductions == 3 because of the generated iterator iface, numProductions=3, ) shouldPass( "Async iterable with argument (key and value)", """ interface Foo1 { async iterable(optional long foo); attribute long unrelatedAttribute; long unrelatedMethod(); }; """, asyncIterableMembers + unrelatedMembers, # numProductions == 2 because of the generated iterator iface, numProductions=2, ) shouldPass( "Maplike (readwrite)", """ interface Foo1 { maplike; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapRWMembers + unrelatedMembers, ) shouldPass( "Maplike (readwrite) inheriting from parent", """ interface Foo1 : Foo2 { maplike; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapRWMembers, numProductions=2, ) shouldPass( "Maplike (readwrite)", """ interface Foo1 { maplike; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapRWMembers + unrelatedMembers, ) shouldPass( "Maplike (readwrite) inheriting from parent", """ interface Foo1 : Foo2 { maplike; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapRWMembers, numProductions=2, ) shouldPass( "Maplike (readonly)", """ interface Foo1 { readonly maplike; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapROMembers + unrelatedMembers, ) shouldPass( "Maplike (readonly) inheriting from parent", """ interface Foo1 : Foo2 { readonly maplike; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, mapROMembers, numProductions=2, ) shouldPass( "Setlike (readwrite)", """ interface Foo1 { setlike; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, setRWMembers + unrelatedMembers, ) shouldPass( "Setlike (readwrite) inheriting from parent", """ interface Foo1 : Foo2 { setlike; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, setRWMembers, numProductions=2, ) shouldPass( "Setlike (readonly)", """ interface Foo1 { readonly setlike; attribute long unrelatedAttribute; long unrelatedMethod(); }; """, setROMembers + unrelatedMembers, ) shouldPass( "Setlike (readonly) inheriting from parent", """ interface Foo1 : Foo2 { readonly setlike; }; interface Foo2 { attribute long unrelatedAttribute; long unrelatedMethod(); }; """, setROMembers, numProductions=2, ) shouldPass( "Inheritance of maplike/setlike", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { }; """, mapRWMembers, numProductions=2, ) shouldFail( "JS Implemented maplike interface", """ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { constructor(); setlike; }; """, ) shouldFail( "JS Implemented maplike interface", """ [JSImplementation="@mozilla.org/dom/test-interface-js-maplike;1"] interface Foo1 { constructor(); maplike; }; """, ) # # Multiple maplike/setlike tests # shouldFail( "Two maplike/setlikes on same interface", """ interface Foo1 { setlike; maplike; }; """, ) shouldFail( "Two iterable/setlikes on same interface", """ interface Foo1 { iterable; maplike; }; """, ) shouldFail( "Two iterables on same interface", """ interface Foo1 { iterable; iterable; }; """, ) shouldFail( "Two iterables on same interface", """ interface Foo1 { iterable; async iterable; }; """, ) shouldFail( "Two iterables on same interface", """ interface Foo1 { async iterable; async iterable; }; """, ) shouldFail( "Async iterable with non-optional arguments", """ interface Foo1 { async iterable(long foo); }; """, ) shouldFail( "Async iterable with non-optional arguments", """ interface Foo1 { async iterable(optional long foo, long bar); }; """, ) shouldFail( "Async iterable with non-optional arguments", """ interface Foo1 { async iterable(long foo); }; """, ) shouldFail( "Two maplike/setlikes in partials", """ interface Foo1 { maplike; }; partial interface Foo1 { setlike; }; """, ) shouldFail( "Conflicting maplike/setlikes across inheritance", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { setlike; }; """, ) shouldFail( "Conflicting maplike/iterable across inheritance", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { iterable; }; """, ) shouldFail( "Conflicting maplike/setlikes across multistep inheritance", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { setlike; }; """, ) # # Member name collision tests # def testConflictingMembers(likeMember, conflictName, expectedMembers, methodPasses): """ Tests for maplike/setlike member generation against conflicting member names. If methodPasses is True, this means we expect the interface to pass in the case of method shadowing, and expectedMembers should be the list of interface members to check against on the passing interface. """ if methodPasses: shouldPass( "Conflicting method: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s; [Throws] undefined %s(long test1, double test2, double test3); }; """ % (likeMember, conflictName), expectedMembers, ) else: shouldFail( "Conflicting method: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s; [Throws] undefined %s(long test1, double test2, double test3); }; """ % (likeMember, conflictName), ) # Inherited conflicting methods should ALWAYS fail shouldFail( "Conflicting inherited method: %s and %s" % (likeMember, conflictName), """ interface Foo1 { undefined %s(long test1, double test2, double test3); }; interface Foo2 : Foo1 { %s; }; """ % (conflictName, likeMember), ) shouldFail( "Conflicting static method: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s; static undefined %s(long test1, double test2, double test3); }; """ % (likeMember, conflictName), ) shouldFail( "Conflicting attribute: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s attribute double %s; }; """ % (likeMember, conflictName), ) shouldFail( "Conflicting const: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s; const double %s = 0; }; """ % (likeMember, conflictName), ) shouldFail( "Conflicting static attribute: %s and %s" % (likeMember, conflictName), """ interface Foo1 { %s; static attribute long %s; }; """ % (likeMember, conflictName), ) for member in disallowedIterableNames: testConflictingMembers("iterable", member, iterableMembers, False) for member in mapDisallowedMemberNames: testConflictingMembers("maplike", member, mapRWMembers, False) for member in disallowedMemberNames: testConflictingMembers("setlike", member, setRWMembers, False) for member in mapDisallowedNonMethodNames: testConflictingMembers("maplike", member, mapRWMembers, True) for member in setDisallowedNonMethodNames: testConflictingMembers("setlike", member, setRWMembers, True) shouldPass( "Inheritance of maplike/setlike with child member collision", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { undefined entries(); }; """, mapRWMembers, numProductions=2, ) shouldPass( "Inheritance of multi-level maplike/setlike with child member collision", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { undefined entries(); }; """, mapRWMembers, numProductions=3, ) shouldFail( "Maplike interface with mixin member collision", """ interface Foo1 { maplike; }; interface mixin Foo2 { undefined entries(); }; Foo1 includes Foo2; """, ) shouldPass( "Inherited Maplike interface with consequential interface member collision", """ interface Foo1 { maplike; }; interface mixin Foo2 { undefined entries(); }; interface Foo3 : Foo1 { }; Foo3 includes Foo2; """, mapRWMembers, numProductions=4, ) shouldFail( "Inheritance of name collision with child maplike/setlike", """ interface Foo1 { undefined entries(); }; interface Foo2 : Foo1 { maplike; }; """, ) shouldFail( "Inheritance of multi-level name collision with child maplike/setlike", """ interface Foo1 { undefined entries(); }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { maplike; }; """, ) shouldPass( "Inheritance of attribute collision with parent maplike/setlike", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { attribute double size; }; """, mapRWMembers, numProductions=2, ) shouldPass( "Inheritance of multi-level attribute collision with parent maplike/setlike", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { attribute double size; }; """, mapRWMembers, numProductions=3, ) shouldFail( "Inheritance of attribute collision with child maplike/setlike", """ interface Foo1 { attribute double size; }; interface Foo2 : Foo1 { maplike; }; """, ) shouldFail( "Inheritance of multi-level attribute collision with child maplike/setlike", """ interface Foo1 { attribute double size; }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { maplike; }; """, ) shouldFail( "Inheritance of attribute/rw function collision with child maplike/setlike", """ interface Foo1 { attribute double set; }; interface Foo2 : Foo1 { maplike; }; """, ) shouldFail( "Inheritance of const/rw function collision with child maplike/setlike", """ interface Foo1 { const double set = 0; }; interface Foo2 : Foo1 { maplike; }; """, ) shouldPass( "Inheritance of rw function with same name in child maplike/setlike", """ interface Foo1 { maplike; }; interface Foo2 : Foo1 { undefined clear(); }; """, mapRWMembers, numProductions=2, ) shouldFail( "Inheritance of unforgeable attribute collision with child maplike/setlike", """ interface Foo1 { [LegacyUnforgeable] attribute double size; }; interface Foo2 : Foo1 { maplike; }; """, ) shouldFail( "Inheritance of multi-level unforgeable attribute collision with child maplike/setlike", """ interface Foo1 { [LegacyUnforgeable] attribute double size; }; interface Foo2 : Foo1 { }; interface Foo3 : Foo2 { maplike; }; """, ) shouldPass( "Interface with readonly allowable overrides", """ interface Foo1 { readonly setlike; readonly attribute boolean clear; }; """, setROMembers + [("clear", WebIDL.IDLAttribute)], ) r = shouldPass( "Check proper override of clear/delete/set", """ interface Foo1 { maplike; long clear(long a, long b, double c, double d); long set(long a, long b, double c, double d); long delete(long a, long b, double c, double d); }; """, mapRWMembers, ) for m in r[0].members: if m.identifier.name in ["clear", "set", "delete"]: harness.ok(m.isMethod(), "%s should be a method" % m.identifier.name) harness.check( m.maxArgCount, 4, "%s should have 4 arguments" % m.identifier.name ) harness.ok( not m.isMaplikeOrSetlikeOrIterableMethod(), "%s should not be a maplike/setlike function" % m.identifier.name, )