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..eb41029a3fdd776658ed49e48b63bdb85e6db42e |
--- /dev/null |
+++ b/pkg/kernel/test/class_hierarchy_test.dart |
@@ -0,0 +1,709 @@ |
+// 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'; |
+ |
+main() { |
+ group('ClosedWorldClassHierarchyTest', () { |
+ _ClassHierarchyTest._runTests(() => new ClosedWorldClassHierarchyTest()); |
+ }); |
+} |
+ |
+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 class 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 tearDown() {} |
+ |
+ 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> {} |
ahe
2017/05/31 17:33:49
Instead of all these comments, I'd take advantage
scheglov
2017/05/31 17:53:27
That's an interesting idea.
I will do this in the
|
+ 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 |
ahe
2017/05/31 17:33:49
Add colon after ")".
scheglov
2017/05/31 17:53:27
Done.
|
+ // 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. |
ahe
2017/05/31 17:33:49
Since Fasta uses this method, it would be interest
scheglov
2017/05/31 17:53:28
I hope that Kevin will able to shed some light.
|
+// 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 |
ahe
2017/05/31 17:33:49
I'm thinking it might be a good idea to add more c
scheglov
2017/05/31 17:53:27
Sounds good to me.
Anything specific you have in m
ahe
2017/05/31 18:33:06
I'll discuss it with Johnni tomorrow. I can't reme
|
+ 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)); |
+ } |
+ |
+ /// Create a new [_ClassHierarchyTest] using [newInstance] and run [test] |
+ /// with the given [description] and [doRun] with the test instance. |
+ static void _runTest(_ClassHierarchyTest newInstance(), String description, |
+ void doRun(_ClassHierarchyTest instance)) { |
+ test(description, () { |
+ var instance = newInstance(); |
+ instance.setUp(); |
+ try { |
+ doRun(instance); |
+ } finally { |
+ instance.tearDown(); |
+ } |
+ }); |
+ } |
+ |
+ /// Run all tests declared in [_ClassHierarchyTest]. This method must be |
+ /// updated when a new `test_XYZ` method is added, renamed or deleted. |
+ static void _runTests(_ClassHierarchyTest newInstance()) { |
scheglov
2017/05/31 17:53:27
Given that this is considered as not an optimal so
ahe
2017/05/31 18:33:06
I agree.
|
+ _runTest(newInstance, 'test_forEachOverridePair_overrideSupertype', |
+ (i) => i.test_forEachOverridePair_overrideSupertype()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_generic_extends', |
+ (i) => i.test_getClassAsInstanceOf_generic_extends()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_generic_implements', |
+ (i) => i.test_getClassAsInstanceOf_generic_implements()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_generic_with', |
+ (i) => i.test_getClassAsInstanceOf_generic_with()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_notGeneric_extends', |
+ (i) => i.test_getClassAsInstanceOf_notGeneric_extends()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_notGeneric_implements', |
+ (i) => i.test_getClassAsInstanceOf_notGeneric_implements()); |
+ _runTest(newInstance, 'test_getClassAsInstanceOf_notGeneric_with', |
+ (i) => i.test_getClassAsInstanceOf_notGeneric_with()); |
+ _runTest(newInstance, 'test_getClassDepth', (i) => i.test_getClassDepth()); |
+ _runTest(newInstance, 'test_getClassicLeastUpperBound_generic', |
+ (i) => i.test_getClassicLeastUpperBound_generic()); |
+ _runTest(newInstance, 'test_getClassicLeastUpperBound_nonGeneric', |
+ (i) => i.test_getClassicLeastUpperBound_nonGeneric()); |
+ _runTest(newInstance, 'test_getDispatchTarget', |
+ (i) => i.test_getDispatchTarget()); |
+ _runTest(newInstance, 'test_getDispatchTarget_abstract', |
+ (i) => i.test_getDispatchTarget_abstract()); |
+ _runTest(newInstance, 'test_getInterfaceMember_extends', |
+ (i) => i.test_getInterfaceMember_extends()); |
+ _runTest(newInstance, 'test_getInterfaceMember_implements', |
+ (i) => i.test_getInterfaceMember_implements()); |
+ _runTest(newInstance, 'test_getRankedSuperclasses', |
+ (i) => i.test_getRankedSuperclasses()); |
+ _runTest(newInstance, 'test_getTypeAsInstanceOf_generic_extends', |
+ (i) => i.test_getTypeAsInstanceOf_generic_extends()); |
+ _runTest(newInstance, 'test_rootClass', (i) => i.test_rootClass()); |
+ } |
+} |