diff options
Diffstat (limited to 'dom/bindings/parser/tests/test_distinguishability.py')
-rw-r--r-- | dom/bindings/parser/tests/test_distinguishability.py | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/dom/bindings/parser/tests/test_distinguishability.py b/dom/bindings/parser/tests/test_distinguishability.py new file mode 100644 index 0000000000..8c938d88a3 --- /dev/null +++ b/dom/bindings/parser/tests/test_distinguishability.py @@ -0,0 +1,421 @@ +def firstArgType(method): + return method.signatures()[0][1][0].type + + +def WebIDLTest(parser, harness): + parser.parse( + """ + // Give our dictionary a required member so we don't need to + // mess with optional and default values. + dictionary Dict { + required long member; + }; + callback interface Foo { + }; + interface Bar { + // Bit of a pain to get things that have dictionary types + undefined passDict(Dict arg); + undefined passFoo(Foo arg); + undefined passNullableUnion((object? or DOMString) arg); + undefined passNullable(Foo? arg); + }; + """ + ) + results = parser.finish() + + iface = results[2] + harness.ok(iface.isInterface(), "Should have interface") + dictMethod = iface.members[0] + ifaceMethod = iface.members[1] + nullableUnionMethod = iface.members[2] + nullableIfaceMethod = iface.members[3] + + dictType = firstArgType(dictMethod) + ifaceType = firstArgType(ifaceMethod) + + harness.ok(dictType.isDictionary(), "Should have dictionary type") + harness.ok(ifaceType.isInterface(), "Should have interface type") + harness.ok(ifaceType.isCallbackInterface(), "Should have callback interface type") + + harness.ok( + not dictType.isDistinguishableFrom(ifaceType), + "Dictionary not distinguishable from callback interface", + ) + harness.ok( + not ifaceType.isDistinguishableFrom(dictType), + "Callback interface not distinguishable from dictionary", + ) + + nullableUnionType = firstArgType(nullableUnionMethod) + nullableIfaceType = firstArgType(nullableIfaceMethod) + + harness.ok(nullableUnionType.isUnion(), "Should have union type") + harness.ok(nullableIfaceType.isInterface(), "Should have interface type") + harness.ok(nullableIfaceType.nullable(), "Should have nullable type") + + harness.ok( + not nullableUnionType.isDistinguishableFrom(nullableIfaceType), + "Nullable type not distinguishable from union with nullable " "member type", + ) + harness.ok( + not nullableIfaceType.isDistinguishableFrom(nullableUnionType), + "Union with nullable member type not distinguishable from " "nullable type", + ) + + parser = parser.reset() + parser.parse( + """ + interface TestIface { + undefined passKid(Kid arg); + undefined passParent(Parent arg); + undefined passGrandparent(Grandparent arg); + undefined passUnrelated1(Unrelated1 arg); + undefined passUnrelated2(Unrelated2 arg); + undefined passArrayBuffer(ArrayBuffer arg); + undefined passArrayBuffer(ArrayBufferView arg); + }; + + interface Kid : Parent {}; + interface Parent : Grandparent {}; + interface Grandparent {}; + interface Unrelated1 {}; + interface Unrelated2 {}; + """ + ) + results = parser.finish() + + iface = results[0] + harness.ok(iface.isInterface(), "Should have interface") + argTypes = [firstArgType(method) for method in iface.members] + unrelatedTypes = [firstArgType(method) for method in iface.members[-3:]] + + for type1 in argTypes: + for type2 in argTypes: + distinguishable = type1 is not type2 and ( + type1 in unrelatedTypes or type2 in unrelatedTypes + ) + + harness.check( + type1.isDistinguishableFrom(type2), + distinguishable, + "Type %s should %sbe distinguishable from type %s" + % (type1, "" if distinguishable else "not ", type2), + ) + harness.check( + type2.isDistinguishableFrom(type1), + distinguishable, + "Type %s should %sbe distinguishable from type %s" + % (type2, "" if distinguishable else "not ", type1), + ) + + parser = parser.reset() + parser.parse( + """ + interface Dummy {}; + interface TestIface { + undefined method(long arg1, TestIface arg2); + undefined method(long arg1, long arg2); + undefined method(long arg1, Dummy arg2); + undefined method(DOMString arg1, DOMString arg2, DOMString arg3); + }; + """ + ) + results = parser.finish() + harness.check(len(results[1].members), 1, "Should look like we have one method") + harness.check( + len(results[1].members[0].signatures()), 4, "Should have four signatures" + ) + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Dummy {}; + interface TestIface { + undefined method(long arg1, TestIface arg2); + undefined method(long arg1, long arg2); + undefined method(any arg1, Dummy arg2); + undefined method(DOMString arg1, DOMString arg2, DOMString arg3); + }; + """ + ) + parser.finish() + except Exception: + threw = True + + harness.ok( + threw, + "Should throw when args before the distinguishing arg are not " + "all the same type", + ) + + parser = parser.reset() + threw = False + try: + parser.parse( + """ + interface Dummy {}; + interface TestIface { + undefined method(long arg1, TestIface arg2); + undefined method(long arg1, long arg2); + undefined method(any arg1, DOMString arg2); + undefined method(DOMString arg1, DOMString arg2, DOMString arg3); + }; + """ + ) + parser.finish() + except Exception: + threw = True + + harness.ok(threw, "Should throw when there is no distinguishing index") + + # Now let's test our whole distinguishability table + argTypes = [ + "long", + "short", + "long?", + "short?", + "boolean", + "boolean?", + "undefined", + "undefined?", + "DOMString", + "ByteString", + "UTF8String", + "Enum", + "Enum2", + "Interface", + "Interface?", + "AncestorInterface", + "UnrelatedInterface", + "CallbackInterface", + "CallbackInterface?", + "CallbackInterface2", + "object", + "Callback", + "Callback2", + "Dict", + "Dict2", + "sequence<long>", + "sequence<short>", + "record<DOMString, object>", + "record<USVString, Dict>", + "record<ByteString, long>", + "record<UTF8String, long>", + "any", + "Promise<any>", + "Promise<any>?", + "USVString", + "JSString", + "ArrayBuffer", + "ArrayBufferView", + "Uint8Array", + "Uint16Array", + "(long or Callback)", + "(long or Dict)", + ] + + # Try to categorize things a bit to keep list lengths down + def allBut(list1, list2): + return [ + a + for a in list1 + if a not in list2 + and (a != "any" and a != "Promise<any>" and a != "Promise<any>?") + ] + + unions = ["(long or Callback)", "(long or Dict)"] + numerics = ["long", "short", "long?", "short?"] + booleans = ["boolean", "boolean?"] + undefineds = ["undefined", "undefined?"] + primitives = numerics + booleans + nonNumerics = allBut(argTypes, numerics + unions) + nonBooleans = allBut(argTypes, booleans) + strings = [ + "DOMString", + "ByteString", + "Enum", + "Enum2", + "USVString", + "JSString", + "UTF8String", + ] + nonStrings = allBut(argTypes, strings) + nonObjects = undefineds + primitives + strings + bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] + interfaces = [ + "Interface", + "Interface?", + "AncestorInterface", + "UnrelatedInterface", + ] + bufferSourceTypes + nullables = [ + "long?", + "short?", + "boolean?", + "undefined?", + "Interface?", + "CallbackInterface?", + "Dict", + "Dict2", + "Date?", + "any", + "Promise<any>?", + ] + allBut(unions, ["(long or Callback)"]) + sequences = ["sequence<long>", "sequence<short>"] + nonUserObjects = nonObjects + interfaces + sequences + otherObjects = allBut(argTypes, nonUserObjects + ["object"]) + notRelatedInterfaces = ( + nonObjects + + ["UnrelatedInterface"] + + otherObjects + + sequences + + bufferSourceTypes + ) + records = [ + "record<DOMString, object>", + "record<USVString, Dict>", + "record<ByteString, long>", + "record<UTF8String, long>", + ] # JSString not supported in records + dictionaryLike = ( + [ + "Dict", + "Dict2", + "CallbackInterface", + "CallbackInterface?", + "CallbackInterface2", + ] + + records + + allBut(unions, ["(long or Callback)"]) + ) + + # Build a representation of the distinguishability table as a dict + # of dicts, holding True values where needed, holes elsewhere. + data = dict() + for type in argTypes: + data[type] = dict() + + def setDistinguishable(type, types): + for other in types: + data[type][other] = True + + setDistinguishable("long", nonNumerics) + setDistinguishable("short", nonNumerics) + setDistinguishable("long?", allBut(nonNumerics, nullables)) + setDistinguishable("short?", allBut(nonNumerics, nullables)) + setDistinguishable("boolean", nonBooleans) + setDistinguishable("boolean?", allBut(nonBooleans, nullables)) + setDistinguishable("undefined", allBut(argTypes, undefineds + dictionaryLike)) + setDistinguishable( + "undefined?", allBut(argTypes, undefineds + dictionaryLike + nullables) + ) + setDistinguishable("DOMString", nonStrings) + setDistinguishable("ByteString", nonStrings) + setDistinguishable("UTF8String", nonStrings) + setDistinguishable("USVString", nonStrings) + setDistinguishable("JSString", nonStrings) + setDistinguishable("Enum", nonStrings) + setDistinguishable("Enum2", nonStrings) + setDistinguishable("Interface", notRelatedInterfaces) + setDistinguishable("Interface?", allBut(notRelatedInterfaces, nullables)) + setDistinguishable("AncestorInterface", notRelatedInterfaces) + setDistinguishable( + "UnrelatedInterface", allBut(argTypes, ["object", "UnrelatedInterface"]) + ) + setDistinguishable("CallbackInterface", allBut(nonUserObjects, undefineds)) + setDistinguishable( + "CallbackInterface?", allBut(nonUserObjects, nullables + undefineds) + ) + setDistinguishable("CallbackInterface2", allBut(nonUserObjects, undefineds)) + setDistinguishable("object", nonObjects) + setDistinguishable("Callback", nonUserObjects) + setDistinguishable("Callback2", nonUserObjects) + setDistinguishable("Dict", allBut(nonUserObjects, nullables + undefineds)) + setDistinguishable("Dict2", allBut(nonUserObjects, nullables + undefineds)) + setDistinguishable("sequence<long>", allBut(argTypes, sequences + ["object"])) + setDistinguishable("sequence<short>", allBut(argTypes, sequences + ["object"])) + setDistinguishable("record<DOMString, object>", allBut(nonUserObjects, undefineds)) + setDistinguishable("record<USVString, Dict>", allBut(nonUserObjects, undefineds)) + # JSString not supported in records + setDistinguishable("record<ByteString, long>", allBut(nonUserObjects, undefineds)) + setDistinguishable("record<UTF8String, long>", allBut(nonUserObjects, undefineds)) + setDistinguishable("any", []) + setDistinguishable("Promise<any>", []) + setDistinguishable("Promise<any>?", []) + setDistinguishable("ArrayBuffer", allBut(argTypes, ["ArrayBuffer", "object"])) + setDistinguishable( + "ArrayBufferView", + allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"]), + ) + setDistinguishable( + "Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"]) + ) + setDistinguishable( + "Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"]) + ) + setDistinguishable("(long or Callback)", allBut(nonUserObjects, numerics)) + setDistinguishable( + "(long or Dict)", allBut(nonUserObjects, numerics + nullables + undefineds) + ) + + def areDistinguishable(type1, type2): + return data[type1].get(type2, False) + + def checkDistinguishability(parser, type1, type2): + idlTemplate = """ + enum Enum { "a", "b" }; + enum Enum2 { "c", "d" }; + interface Interface : AncestorInterface {}; + interface AncestorInterface {}; + interface UnrelatedInterface {}; + callback interface CallbackInterface {}; + callback interface CallbackInterface2 {}; + callback Callback = any(); + callback Callback2 = long(short arg); + // Give our dictionaries required members so we don't need to + // mess with optional and default values. + dictionary Dict { required long member; }; + dictionary Dict2 { required long member; }; + interface TestInterface {%s + }; + """ + if type1 in undefineds or type2 in undefineds: + methods = """ + (%s or %s) myMethod();""" % ( + type1, + type2, + ) + else: + methodTemplate = """ + undefined myMethod(%s arg);""" + methods = (methodTemplate % type1) + (methodTemplate % type2) + idl = idlTemplate % methods + + parser = parser.reset() + threw = False + try: + parser.parse(idl) + parser.finish() + except Exception: + threw = True + + if areDistinguishable(type1, type2): + harness.ok( + not threw, + "Should not throw for '%s' and '%s' because they are distinguishable" + % (type1, type2), + ) + else: + harness.ok( + threw, + "Should throw for '%s' and '%s' because they are not distinguishable" + % (type1, type2), + ) + + # Enumerate over everything in both orders, since order matters in + # terms of our implementation of distinguishability checks + for type1 in argTypes: + for type2 in argTypes: + checkDistinguishability(parser, type1, type2) |