Chromium Code Reviews| Index: pkg/kernel/test/class_hierarchy_test.dart |
| diff --git a/pkg/kernel/test/class_hierarchy_test.dart b/pkg/kernel/test/class_hierarchy_test.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7032354b8f27a9212a88be10301747a6e3197b1a |
| --- /dev/null |
| +++ b/pkg/kernel/test/class_hierarchy_test.dart |
| @@ -0,0 +1,657 @@ |
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +import 'package:kernel/ast.dart'; |
| +import 'package:kernel/class_hierarchy.dart'; |
| +import 'package:kernel/core_types.dart'; |
| +import 'package:kernel/testing/mock_sdk_program.dart'; |
| +import 'package:test/test.dart'; |
| +import 'package:test_reflective_loader/test_reflective_loader.dart'; |
|
Siggi Cherem (dart-lang)
2017/05/31 00:32:16
Kevin - you may note that there are a lot of struc
Siggi Cherem (dart-lang)
2017/05/31 00:32:16
note: this needs to be added as a dev_dependency i
ahe
2017/05/31 11:23:47
I'm not a big fan of the reflective test suite and
scheglov
2017/05/31 15:43:05
1. Well, I think making it easier to write unit te
|
| + |
| +main() { |
| + defineReflectiveSuite(() { |
| + defineReflectiveTests(ClosedWorldClassHierarchyTest); |
| + }); |
| +} |
| + |
| +@reflectiveTest |
| +class ClosedWorldClassHierarchyTest extends _ClassHierarchyTest { |
| + ClassHierarchy createClassHierarchy(Program program) { |
| + return new ClosedWorldClassHierarchy(program); |
| + } |
| +} |
| + |
| +abstract class _ClassHierarchyTest { |
| + Program program; |
| + CoreTypes coreTypes; |
| + |
| + /// The test library. |
| + Library library; |
| + |
| + ClassHierarchy _hierarchy; |
| + |
| + /// Return the new or existing instance of [ClassHierarchy]. |
| + ClassHierarchy get hierarchy { |
| + return _hierarchy ??= createClassHierarchy(program); |
| + } |
| + |
| + Class get objectClass => coreTypes.objectClass; |
| + |
| + Supertype get objectSuper => coreTypes.objectClass.asThisSupertype; |
| + |
| + Class addClass(Class c) { |
| + if (_hierarchy != null) { |
| + fail('The classs hierarchy has already been created.'); |
| + } |
| + library.addClass(c); |
| + return c; |
| + } |
| + |
| + /// Add a new generic class with the given [name] and [typeParameterNames]. |
| + /// The [TypeParameterType]s corresponding to [typeParameterNames] are |
| + /// passed to optional [extends_] and [implements_] callbacks. |
| + Class addGenericClass(String name, List<String> typeParameterNames, |
| + {Supertype extends_(List<DartType> typeParameterTypes), |
| + List<Supertype> implements_(List<DartType> typeParameterTypes)}) { |
| + var typeParameters = typeParameterNames |
| + .map((name) => new TypeParameter(name, objectClass.rawType)) |
| + .toList(); |
| + var typeParameterTypes = typeParameters |
| + .map((parameter) => new TypeParameterType(parameter)) |
| + .toList(); |
| + var supertype = |
| + extends_ != null ? extends_(typeParameterTypes) : objectSuper; |
| + var implementedTypes = |
| + implements_ != null ? implements_(typeParameterTypes) : []; |
| + return addClass(new Class( |
| + name: name, |
| + typeParameters: typeParameters, |
| + supertype: supertype, |
| + implementedTypes: implementedTypes)); |
| + } |
| + |
| + /// Add a new class with the given [name] that extends `Object` and |
| + /// [implements_] the given classes. |
| + Class addImplementsClass(String name, List<Class> implements_) { |
| + return addClass(new Class( |
| + name: name, |
| + supertype: objectSuper, |
| + implementedTypes: implements_.map((c) => c.asThisSupertype).toList())); |
| + } |
| + |
| + ClassHierarchy createClassHierarchy(Program program); |
| + |
| + Procedure newEmptyMethod(String name, {bool abstract: false}) { |
| + var body = abstract ? null : new Block([]); |
| + return new Procedure( |
| + new Name(name), ProcedureKind.Method, new FunctionNode(body)); |
| + } |
| + |
| + Procedure newEmptySetter(String name) { |
| + return new Procedure( |
| + new Name(name), |
| + ProcedureKind.Setter, |
| + new FunctionNode(new Block([]), |
| + positionalParameters: [new VariableDeclaration('_')])); |
| + } |
| + |
| + void setUp() { |
| + // Start with mock SDK libraries. |
| + program = createMockSdkProgram(); |
| + coreTypes = new CoreTypes(program); |
| + |
| + // Add the test library. |
| + library = new Library(Uri.parse('org-dartlang:///test.dart'), name: 'test'); |
| + library.parent = program; |
| + program.libraries.add(library); |
| + } |
| + |
| + void test_forEachOverridePair_overrideSupertype() { |
| + // Create the class hierarchy: |
| + // |
| + // abstract class A extends Object { |
| + // foo() {} |
| + // bar() {} |
| + // } |
| + // class B extends A { |
| + // foo() {} |
| + // } |
| + // class C extends B { |
| + // bar() {} |
| + // } |
| + var aFoo = newEmptyMethod('foo'); |
| + var aBar = newEmptyMethod('bar'); |
| + var bFoo = newEmptyMethod('foo'); |
| + var cBar = newEmptyMethod('bar'); |
| + var a = addClass( |
| + new Class(name: 'A', supertype: objectSuper, procedures: [aFoo, aBar])); |
| + var b = addClass( |
| + new Class(name: 'B', supertype: a.asThisSupertype, procedures: [bFoo])); |
| + var c = addClass( |
| + new Class(name: 'C', supertype: b.asThisSupertype, procedures: [cBar])); |
| + |
| + _assertOverridePairs(b, ['test::B::foo overrides test::A::foo']); |
| + _assertOverridePairs(c, ['test::C::bar overrides test::A::bar']); |
| + } |
| + |
| + void test_getClassAsInstanceOf_generic_extends() { |
| + // Create the class hierarchy: |
| + // |
| + // class A<T, U> extends Object {} |
| + // class B<T> extends A<T, bool> {} |
| + // class C extends B<int> {} |
| + var int = coreTypes.intClass.rawType; |
| + var bool = coreTypes.boolClass.rawType; |
| + |
| + var a = addGenericClass('A', ['T', 'U']); |
| + |
| + var bT = new TypeParameter('T', objectClass.rawType); |
| + var bTT = new TypeParameterType(bT); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + typeParameters: [bT], |
| + supertype: new Supertype(a, [bTT, bool]))); |
| + |
| + var c = addClass(new Class(name: 'C', supertype: new Supertype(b, [int]))); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper); |
| + expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool])); |
| + expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int])); |
| + expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool])); |
| + } |
| + |
| + void test_getClassAsInstanceOf_generic_implements() { |
| + // Create the class hierarchy: |
| + // |
| + // class A<T, U> extends Object {} |
| + // class B<T> extends Object implements A<T, bool> {} |
| + // class C extends Object implements B<int> {} |
| + var int = coreTypes.intClass.rawType; |
| + var bool = coreTypes.boolClass.rawType; |
| + |
| + var a = addGenericClass('A', ['T', 'U']); |
| + |
| + var bT = new TypeParameter('T', objectClass.rawType); |
| + var bTT = new TypeParameterType(bT); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + typeParameters: [bT], |
| + supertype: objectSuper, |
| + implementedTypes: [ |
| + new Supertype(a, [bTT, bool]) |
| + ])); |
| + |
| + var c = addClass( |
| + new Class(name: 'C', supertype: objectSuper, implementedTypes: [ |
| + new Supertype(b, [int]) |
| + ])); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper); |
| + expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool])); |
| + expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int])); |
| + expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool])); |
| + } |
| + |
| + void test_getClassAsInstanceOf_generic_with() { |
| + // Create the class hierarchy: |
| + // |
| + // class A<T, U> extends Object {} |
| + // class B<T> extends Object with A<T, bool> {} |
| + // class C extends Object with B<int> {} |
| + var int = coreTypes.intClass.rawType; |
| + var bool = coreTypes.boolClass.rawType; |
| + |
| + var a = addGenericClass('A', ['T', 'U']); |
| + |
| + var bT = new TypeParameter('T', objectClass.rawType); |
| + var bTT = new TypeParameterType(bT); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + typeParameters: [bT], |
| + supertype: objectSuper, |
| + mixedInType: new Supertype(a, [bTT, bool]))); |
| + |
| + var c = addClass(new Class( |
| + name: 'C', |
| + supertype: objectSuper, |
| + mixedInType: new Supertype(b, [int]))); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper); |
| + expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(b, a), new Supertype(a, [bTT, bool])); |
| + expect(hierarchy.getClassAsInstanceOf(c, b), new Supertype(b, [int])); |
| + expect(hierarchy.getClassAsInstanceOf(c, a), new Supertype(a, [int, bool])); |
| + } |
| + |
| + void test_getClassAsInstanceOf_notGeneric_extends() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object {} |
| + // class B extends A {} |
| + // class C extends B {} |
| + // class Z extends Object {} |
| + var a = addClass(new Class(name: 'A', supertype: objectSuper)); |
| + var b = addClass(new Class(name: 'B', supertype: a.asThisSupertype)); |
| + var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype)); |
| + var z = addClass(new Class(name: 'Z', supertype: objectSuper)); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(a, objectClass), objectSuper); |
| + expect(hierarchy.getClassAsInstanceOf(a, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(c, b), b.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(z, a), null); |
| + expect(hierarchy.getClassAsInstanceOf(z, objectClass), objectSuper); |
| + } |
| + |
| + void test_getClassAsInstanceOf_notGeneric_implements() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object {} |
| + // class B extends Object {} |
| + // class C extends Object implements A {} |
| + // class D extends Object implements C {} |
| + // class E extends A implements B {} |
| + // class Z extends Object {} |
| + var a = addClass(new Class(name: 'A', supertype: objectSuper)); |
| + var b = addClass(new Class(name: 'B', supertype: objectSuper)); |
| + var c = addClass(new Class( |
| + name: 'C', |
| + supertype: objectSuper, |
| + implementedTypes: [a.asThisSupertype])); |
| + var d = addClass(new Class( |
| + name: 'D', |
| + supertype: objectSuper, |
| + implementedTypes: [c.asThisSupertype])); |
| + var e = addClass(new Class( |
| + name: 'D', |
| + supertype: a.asThisSupertype, |
| + implementedTypes: [b.asThisSupertype])); |
| + var z = addClass(new Class(name: 'Z', supertype: objectSuper)); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(c, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(d, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(d, c), c.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(e, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(e, b), b.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(z, a), null); |
| + } |
| + |
| + void test_getClassAsInstanceOf_notGeneric_with() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object {} |
| + // class B extends Object with A {} |
| + // class Z extends Object {} |
| + var a = addClass(new Class(name: 'A', supertype: objectSuper)); |
| + var b = addClass(new Class( |
| + name: 'B', supertype: objectSuper, mixedInType: a.asThisSupertype)); |
| + var z = addClass(new Class(name: 'Z', supertype: objectSuper)); |
| + |
| + expect(hierarchy.getClassAsInstanceOf(b, objectClass), objectSuper); |
| + expect(hierarchy.getClassAsInstanceOf(b, a), a.asThisSupertype); |
| + expect(hierarchy.getClassAsInstanceOf(z, a), null); |
| + } |
| + |
| + void test_getClassDepth() { |
| + var base = addClass(new Class(name: 'base', supertype: objectSuper)); |
| + var extends_ = |
| + addClass(new Class(name: 'extends_', supertype: base.asThisSupertype)); |
| + var with_ = addClass(new Class( |
| + name: 'with_', |
| + supertype: objectSuper, |
| + mixedInType: base.asThisSupertype)); |
| + var implements_ = addClass(new Class( |
| + name: 'implements_', |
| + supertype: objectSuper, |
| + implementedTypes: [base.asThisSupertype])); |
| + |
| + expect(hierarchy.getClassDepth(objectClass), 0); |
| + expect(hierarchy.getClassDepth(base), 1); |
| + expect(hierarchy.getClassDepth(extends_), 2); |
| + expect(hierarchy.getClassDepth(with_), 2); |
| + expect(hierarchy.getClassDepth(implements_), 2); |
| + } |
| + |
| + void test_getClassicLeastUpperBound_generic() { |
| + var int = coreTypes.intClass.rawType; |
| + var double = coreTypes.doubleClass.rawType; |
| + var bool = coreTypes.boolClass.rawType; |
| + |
| + // Create the class hierarchy: |
| + // |
| + // Object |
| + // | |
| + // A |
| + // / \ |
| + // B<T> C<U> |
| + // \ / |
| + // D<T,U> |
| + // / \ |
| + // E F |
| + // |
| + // Where E implements D<int, double> and F implements D<int, bool>. |
| + var a = addGenericClass('A', []); |
| + var b = |
| + addGenericClass('B', ['T'], implements_: (_) => [a.asThisSupertype]); |
| + var c = |
| + addGenericClass('C', ['U'], implements_: (_) => [a.asThisSupertype]); |
| + var d = addGenericClass('D', ['T', 'U'], implements_: (typeParameterTypes) { |
| + var t = typeParameterTypes[0]; |
| + var u = typeParameterTypes[1]; |
| + return [ |
| + new Supertype(b, [t]), |
| + new Supertype(c, [u]) |
| + ]; |
| + }); |
| + var e = addGenericClass('E', [], |
| + implements_: (_) => [ |
| + new Supertype(d, [int, double]) |
| + ]); |
| + var f = addGenericClass('F', [], |
| + implements_: (_) => [ |
| + new Supertype(d, [int, bool]) |
| + ]); |
| + |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]), |
| + new InterfaceType(d, [int, double])), |
| + new InterfaceType(d, [int, double])); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]), |
| + new InterfaceType(d, [int, bool])), |
| + new InterfaceType(b, [int])); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]), |
| + new InterfaceType(d, [bool, double])), |
| + new InterfaceType(c, [double])); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]), |
| + new InterfaceType(d, [bool, int])), |
| + a.rawType); |
| + expect(hierarchy.getClassicLeastUpperBound(e.rawType, f.rawType), |
| + new InterfaceType(b, [int])); |
| + } |
| + |
| + void test_getClassicLeastUpperBound_nonGeneric() { |
| + // Create the class hierarchy: |
| + // |
| + // Object |
| + // / \ |
| + // A B |
| + // /|\ |
| + // C D E |
| + // |X|/ |
| + // FG HI |
| + // |
| + // (F and G both implement (C, D); H and I both implement (C, D, E). |
| + var a = addImplementsClass('A', []); |
| + var b = addImplementsClass('B', []); |
| + var c = addImplementsClass('C', [a]); |
| + var d = addImplementsClass('D', [a]); |
| + var e = addImplementsClass('E', [a]); |
| + var f = addImplementsClass('F', [c, d]); |
| + var g = addImplementsClass('G', [c, d]); |
| + var h = addImplementsClass('H', [c, d, e]); |
| + var i = addImplementsClass('I', [c, d, e]); |
| + var hierarchy = new ClassHierarchy(program); |
| + |
| + expect(hierarchy.getClassicLeastUpperBound(a.rawType, b.rawType), |
| + objectClass.rawType); |
| + expect(hierarchy.getClassicLeastUpperBound(a.rawType, objectClass.rawType), |
| + objectClass.rawType); |
| + expect(hierarchy.getClassicLeastUpperBound(objectClass.rawType, b.rawType), |
| + objectClass.rawType); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(c.rawType, d.rawType), a.rawType); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(c.rawType, a.rawType), a.rawType); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(a.rawType, d.rawType), a.rawType); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(f.rawType, g.rawType), a.rawType); |
| + expect( |
| + hierarchy.getClassicLeastUpperBound(h.rawType, i.rawType), a.rawType); |
| + } |
| + |
| + void test_getDispatchTarget() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object { |
| + // aMethod() {} |
| + // void set aSetter(_) {} |
| + // } |
| + // class B extends A { |
| + // bMethod() {} |
| + // void set bSetter(_) {} |
| + // } |
| + // class C extends B {} |
| + var aMethod = newEmptyMethod('aMethod'); |
| + var aSetter = newEmptySetter('aSetter'); |
| + var bMethod = newEmptyMethod('bMethod'); |
| + var bSetter = newEmptySetter('bSetter'); |
| + var a = addClass(new Class( |
| + name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter])); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + supertype: a.asThisSupertype, |
| + procedures: [bMethod, bSetter])); |
| + var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype)); |
| + |
| + var aMethodName = new Name('aMethod'); |
| + var aSetterName = new Name('aSetter'); |
| + var bMethodName = new Name('bMethod'); |
| + var bSetterName = new Name('bSetter'); |
| + expect(hierarchy.getDispatchTarget(a, aMethodName), aMethod); |
| + expect(hierarchy.getDispatchTarget(a, bMethodName), isNull); |
| + expect(hierarchy.getDispatchTarget(a, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getDispatchTarget(a, bSetterName, setter: true), isNull); |
| + expect(hierarchy.getDispatchTarget(b, aMethodName), aMethod); |
| + expect(hierarchy.getDispatchTarget(b, bMethodName), bMethod); |
| + expect(hierarchy.getDispatchTarget(b, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getDispatchTarget(b, bSetterName, setter: true), bSetter); |
| + expect(hierarchy.getDispatchTarget(c, aMethodName), aMethod); |
| + expect(hierarchy.getDispatchTarget(c, bMethodName), bMethod); |
| + expect(hierarchy.getDispatchTarget(c, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getDispatchTarget(c, bSetterName, setter: true), bSetter); |
| + } |
| + |
| + void test_getDispatchTarget_abstract() { |
| + // Create the class hierarchy: |
| + // |
| + // abstract class A extends Object { |
| + // aMethodAbstract(); |
| + // aMethodConcrete() {} |
| + // } |
| + // abstract class B extends A { |
| + // aMethodConcrete(); |
| + // bMethodAbstract(); |
| + // bMethodConcrete() {} |
| + // } |
| + // class C extends B {} |
| + var aMethodConcrete = newEmptyMethod('aMethodConcrete'); |
| + var bMethodConcrete = newEmptyMethod('aMethodConcrete'); |
| + var a = addClass(new Class(name: 'A', supertype: objectSuper, procedures: [ |
| + newEmptyMethod('aMethodAbstract', abstract: true), |
| + aMethodConcrete |
| + ])); |
| + var b = addClass( |
| + new Class(name: 'B', supertype: a.asThisSupertype, procedures: [ |
| + newEmptyMethod('aMethodConcrete', abstract: true), |
| + newEmptyMethod('bMethodAbstract', abstract: true), |
| + bMethodConcrete |
| + ])); |
| + addClass(new Class(name: 'C', supertype: b.asThisSupertype)); |
| + |
| + expect(hierarchy.getDispatchTarget(a, new Name('aMethodConcrete')), |
| + aMethodConcrete); |
| + // TODO(scheglov) The next two commented statements verify the behavior |
| + // documented as "If the class is abstract, abstract members are ignored and |
| + // the dispatch is resolved if the class was not abstract.". Unfortunately |
| + // the implementation does not follow the documentation. We need to fix |
| + // either documentation, or implementation. |
| +// expect(hierarchy.getDispatchTarget(c, new Name('aMethodConcrete')), |
| +// aMethodConcrete); |
| +// expect(hierarchy.getDispatchTarget(b, new Name('aMethodConcrete')), |
| +// aMethodConcrete); |
| + } |
| + |
| + void test_getInterfaceMember_extends() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object { |
| + // aMethod() {} |
| + // void set aSetter(_) {} |
| + // } |
| + // class B extends A { |
| + // bMethod() {} |
| + // void set bSetter(_) {} |
| + // } |
| + // class C extends B {} |
| + var aMethod = newEmptyMethod('aMethod'); |
| + var aSetter = newEmptySetter('aSetter'); |
| + var bMethod = newEmptyMethod('bMethod'); |
| + var bSetter = newEmptySetter('bSetter'); |
| + var a = addClass(new Class( |
| + name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter])); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + supertype: a.asThisSupertype, |
| + procedures: [bMethod, bSetter])); |
| + var c = addClass(new Class(name: 'C', supertype: b.asThisSupertype)); |
| + |
| + var aMethodName = new Name('aMethod'); |
| + var aSetterName = new Name('aSetter'); |
| + var bMethodName = new Name('bMethod'); |
| + var bSetterName = new Name('bSetter'); |
| + expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(a, bMethodName), isNull); |
| + expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull); |
| + expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod); |
| + expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter); |
| + expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod); |
| + expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter); |
| + } |
| + |
| + void test_getInterfaceMember_implements() { |
| + // Create the class hierarchy: |
| + // |
| + // class A extends Object { |
| + // aMethod() {} |
| + // void set aSetter(_) {} |
| + // } |
| + // class B extends Object implements A { |
| + // bMethod() {} |
| + // void set bSetter(_) {} |
| + // } |
| + // class C extends Object implements B {} |
| + var aMethod = newEmptyMethod('aMethod'); |
| + var aSetter = newEmptySetter('aSetter'); |
| + var bMethod = newEmptyMethod('bMethod'); |
| + var bSetter = newEmptySetter('bSetter'); |
| + var a = addClass(new Class( |
| + name: 'A', supertype: objectSuper, procedures: [aMethod, aSetter])); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + supertype: objectSuper, |
| + implementedTypes: [a.asThisSupertype], |
| + procedures: [bMethod, bSetter])); |
| + var c = addClass(new Class( |
| + name: 'C', |
| + supertype: objectSuper, |
| + implementedTypes: [b.asThisSupertype])); |
| + |
| + var aMethodName = new Name('aMethod'); |
| + var aSetterName = new Name('aSetter'); |
| + var bMethodName = new Name('bMethod'); |
| + var bSetterName = new Name('bSetter'); |
| + expect(hierarchy.getInterfaceMember(a, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(a, bMethodName), isNull); |
| + expect(hierarchy.getInterfaceMember(a, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(a, bSetterName, setter: true), isNull); |
| + expect(hierarchy.getInterfaceMember(b, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(b, bMethodName), bMethod); |
| + expect(hierarchy.getInterfaceMember(b, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(b, bSetterName, setter: true), bSetter); |
| + expect(hierarchy.getInterfaceMember(c, aMethodName), aMethod); |
| + expect(hierarchy.getInterfaceMember(c, bMethodName), bMethod); |
| + expect(hierarchy.getInterfaceMember(c, aSetterName, setter: true), aSetter); |
| + expect(hierarchy.getInterfaceMember(c, bSetterName, setter: true), bSetter); |
| + } |
| + |
| + void test_getRankedSuperclasses() { |
| + // Create the class hierarchy: |
| + // |
| + // Object |
| + // | |
| + // A |
| + // / \ |
| + // B C |
| + // | | |
| + // | D |
| + // \ / |
| + // E |
| + var a = addImplementsClass('A', []); |
| + var b = addImplementsClass('B', [a]); |
| + var c = addImplementsClass('C', [a]); |
| + var d = addImplementsClass('D', [c]); |
| + var e = addImplementsClass('E', [b, d]); |
| + |
| + expect(hierarchy.getRankedSuperclasses(a), [a, objectClass]); |
| + expect(hierarchy.getRankedSuperclasses(b), [b, a, objectClass]); |
| + expect(hierarchy.getRankedSuperclasses(c), [c, a, objectClass]); |
| + expect(hierarchy.getRankedSuperclasses(d), [d, c, a, objectClass]); |
| + if (hierarchy.getClassIndex(b) < hierarchy.getClassIndex(c)) { |
| + expect(hierarchy.getRankedSuperclasses(e), [e, d, b, c, a, objectClass]); |
| + } else { |
| + expect(hierarchy.getRankedSuperclasses(e), [e, d, c, b, a, objectClass]); |
| + } |
| + } |
| + |
| + void test_getTypeAsInstanceOf_generic_extends() { |
| + // Create the class hierarchy: |
| + // |
| + // class A<T, U> extends Object {} |
| + // class B<T> extends A<T, bool> {} |
| + var int = coreTypes.intClass.rawType; |
| + var bool = coreTypes.boolClass.rawType; |
| + |
| + var a = addGenericClass('A', ['T', 'U']); |
| + |
| + var bT = new TypeParameter('T', objectClass.rawType); |
| + var bTT = new TypeParameterType(bT); |
| + var b = addClass(new Class( |
| + name: 'B', |
| + typeParameters: [bT], |
| + supertype: new Supertype(a, [bTT, bool]))); |
| + |
| + var b_int = new InterfaceType(b, [int]); |
| + expect(hierarchy.getTypeAsInstanceOf(b_int, a), |
| + new InterfaceType(a, [int, bool])); |
| + expect(hierarchy.getTypeAsInstanceOf(b_int, objectClass), |
| + new InterfaceType(objectClass)); |
| + } |
| + |
| + void test_rootClass() { |
| + addClass(new Class(name: 'A', supertype: objectSuper)); |
| + expect(hierarchy.rootClass, objectClass); |
| + } |
| + |
| + void _assertOverridePairs(Class class_, List<String> expected) { |
| + List<String> overrideDescriptions = []; |
| + hierarchy.forEachOverridePair(class_, |
| + (Member declaredMember, Member interfaceMember, bool isSetter) { |
| + var desc = '$declaredMember overrides $interfaceMember'; |
| + overrideDescriptions.add(desc); |
| + }); |
| + expect(overrideDescriptions, unorderedEquals(expected)); |
| + } |
| +} |