| // Copyright (c) 2014, 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 'dart:async'; |
| |
| import 'package:analysis_server/protocol/protocol.dart'; |
| import 'package:analysis_server/protocol/protocol_constants.dart'; |
| import 'package:analysis_server/protocol/protocol_generated.dart'; |
| import 'package:test/test.dart'; |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| |
| import '../analysis_abstract.dart'; |
| |
| void main() { |
| defineReflectiveSuite(() { |
| defineReflectiveTests(AnalysisNotificationOverridesTest); |
| }); |
| } |
| |
| @reflectiveTest |
| class AnalysisNotificationOverridesTest extends AbstractAnalysisTest { |
| late List<Override> overridesList; |
| late Override overrideObject; |
| |
| final Completer<void> _resultsAvailable = Completer(); |
| |
| /// Asserts that there is an overridden interface [OverriddenMember] at the |
| /// offset of [search] in [override]. |
| void assertHasInterfaceMember(String search) { |
| var offset = findOffset(search); |
| var interfaceMembers = overrideObject.interfaceMembers!; |
| for (var member in interfaceMembers) { |
| if (member.element.location!.offset == offset) { |
| return; |
| } |
| } |
| fail('Expect to find an overridden interface members at $offset in ' |
| '${interfaceMembers.join('\n')}'); |
| } |
| |
| /// Validates that there is an [Override] at the offset of [search]. |
| /// |
| /// If [length] is not specified explicitly, then length of an identifier |
| /// from [search] is used. |
| void assertHasOverride(String search, [int length = -1]) { |
| var offset = findOffset(search); |
| if (length == -1) { |
| length = findIdentifierLength(search); |
| } |
| findOverride(offset, length, true); |
| } |
| |
| /// Asserts that there is an overridden superclass [OverriddenMember] at the |
| /// offset of [search] in [override]. |
| void assertHasSuperElement(String search) { |
| var offset = findOffset(search); |
| var member = overrideObject.superclassMember; |
| expect(member!.element.location!.offset, offset); |
| } |
| |
| /// Asserts that there are no overridden members from interfaces. |
| void assertNoInterfaceMembers() { |
| expect(overrideObject.interfaceMembers, isNull); |
| } |
| |
| /// Validates that there is no [Override] at the offset of [search]. |
| /// |
| /// If [length] is not specified explicitly, then length of an identifier |
| /// from [search] is used. |
| void assertNoOverride(String search, [int length = -1]) { |
| var offset = findOffset(search); |
| if (length == -1) { |
| length = findIdentifierLength(search); |
| } |
| findOverride(offset, length, false); |
| } |
| |
| /// Asserts that there are no overridden member from the superclass. |
| void assertNoSuperMember() { |
| expect(overrideObject.superclassMember, isNull); |
| } |
| |
| /// Finds an [Override] with the given [offset] and [length]. |
| /// |
| /// If [exists] is `true`, then fails if such [Override] does not exist. |
| /// Otherwise remembers this it into [override]. |
| /// |
| /// If [exists] is `false`, then fails if such [Override] exists. |
| void findOverride(int offset, int length, [bool? exists]) { |
| for (var override in overridesList) { |
| if (override.offset == offset && override.length == length) { |
| if (exists == false) { |
| fail('Not expected to find (offset=$offset; length=$length) in\n' |
| '${overridesList.join('\n')}'); |
| } |
| overrideObject = override; |
| return; |
| } |
| } |
| if (exists == true) { |
| fail('Expected to find (offset=$offset; length=$length) in\n' |
| '${overridesList.join('\n')}'); |
| } |
| } |
| |
| Future prepareOverrides() { |
| addAnalysisSubscription(AnalysisService.OVERRIDES, testFile); |
| return _resultsAvailable.future; |
| } |
| |
| @override |
| void processNotification(Notification notification) { |
| if (notification.event == ANALYSIS_NOTIFICATION_OVERRIDES) { |
| var params = AnalysisOverridesParams.fromNotification(notification); |
| if (params.file == testFile) { |
| overridesList = params.overrides; |
| _resultsAvailable.complete(); |
| } |
| } |
| } |
| |
| @override |
| void setUp() { |
| super.setUp(); |
| createProject(); |
| } |
| |
| Future<void> test_afterAnalysis() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B implements A { |
| m() {} // in B |
| } |
| '''); |
| await waitForTasksFinished(); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in B'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_BAD_fieldByMethod() async { |
| addTestFile(''' |
| class A { |
| int fff; // in A |
| } |
| class B extends A { |
| fff() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff() {} // in B'); |
| } |
| |
| Future<void> test_BAD_getterByMethod() async { |
| addTestFile(''' |
| class A { |
| get fff => null; |
| } |
| class B extends A { |
| fff() {} |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff() {}'); |
| } |
| |
| Future<void> test_BAD_getterBySetter() async { |
| addTestFile(''' |
| class A { |
| get fff => null; |
| } |
| class B extends A { |
| set fff(x) {} |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff(x) {}'); |
| } |
| |
| Future<void> test_BAD_methodByField() async { |
| addTestFile(''' |
| class A { |
| fff() {} // in A |
| } |
| class B extends A { |
| int fff; // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff; // in B'); |
| } |
| |
| Future<void> test_BAD_methodByGetter() async { |
| addTestFile(''' |
| class A { |
| fff() {} |
| } |
| class B extends A { |
| int get fff => null; |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff => null'); |
| } |
| |
| Future<void> test_BAD_methodBySetter() async { |
| addTestFile(''' |
| class A { |
| fff(x) {} // A |
| } |
| class B extends A { |
| set fff(x) {} // B |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff(x) {} // B'); |
| } |
| |
| Future<void> test_BAD_privateByPrivate_inDifferentLib() async { |
| newFile(join(testFolder, 'lib.dart'), content: r''' |
| class A { |
| void _m() {} |
| } |
| '''); |
| addTestFile(''' |
| import 'lib.dart'; |
| class B extends A { |
| void _m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('_m() {} // in B'); |
| } |
| |
| Future<void> test_BAD_setterByGetter() async { |
| addTestFile(''' |
| class A { |
| set fff(x) {} |
| } |
| class B extends A { |
| get fff => null; |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff => null;'); |
| } |
| |
| Future<void> test_BAD_setterByMethod() async { |
| addTestFile(''' |
| class A { |
| set fff(x) {} // A |
| } |
| class B extends A { |
| fff(x) {} // B |
| } |
| '''); |
| await prepareOverrides(); |
| assertNoOverride('fff(x) {} // B'); |
| } |
| |
| Future<void> test_definedInInterface_ofInterface() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B implements A {} |
| class C implements B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_definedInInterface_ofSuper() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B implements A {} |
| class C extends B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_inMixin_interface_method_direct_single() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| |
| mixin M implements A { |
| m() {} // in M |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in M'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_inMixin_superclassConstraint_method_direct() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| |
| mixin M on A { |
| m() {} // in M |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in M'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_interface_method_direct_multiple() async { |
| addTestFile(''' |
| class IA { |
| m() {} // in IA |
| } |
| class IB { |
| m() {} // in IB |
| } |
| class A implements IA, IB { |
| m() {} // in A |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in A'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in IA'); |
| assertHasInterfaceMember('m() {} // in IB'); |
| } |
| |
| Future<void> test_interface_method_direct_single() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B implements A { |
| m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in B'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_interface_method_indirect_single() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends A { |
| } |
| class C implements B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertNoSuperMember(); |
| assertHasInterfaceMember('m() {} // in A'); |
| } |
| |
| Future<void> test_interface_stopWhenFound() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends A { |
| m() {} // in B |
| } |
| class C implements B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| expect(overrideObject.interfaceMembers, hasLength(2)); |
| assertHasInterfaceMember('m() {} // in B'); |
| } |
| |
| Future<void> test_mix_sameMethod() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| abstract class B extends A { |
| } |
| class C extends A implements A { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_mix_sameMethod_Object_hashCode() async { |
| addTestFile(''' |
| class A {} |
| abstract class B {} |
| class C extends A implements A { |
| int get hashCode => 42; |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('hashCode => 42;'); |
| expect(overrideObject.superclassMember, isNotNull); |
| expect(overrideObject.interfaceMembers, isNull); |
| } |
| |
| Future<void> test_mixin_method_direct() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends Object with A { |
| m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in B'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_mixin_method_indirect() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends A { |
| } |
| class C extends Object with B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_mixin_method_indirect2() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends Object with A { |
| } |
| class C extends B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_staticMembers() async { |
| addTestFile(''' |
| class A { |
| static int F = 0; |
| static void M() {} |
| static int get G => 0; |
| static void set S(int v) {} |
| } |
| class B extends A { |
| static int F = 0; |
| static void M() {} |
| static int get G => 0; |
| static void set S(int v) {} |
| } |
| '''); |
| await prepareOverrides(); |
| expect(overridesList, isEmpty); |
| } |
| |
| Future<void> test_super_fieldByField() async { |
| addTestFile(''' |
| class A { |
| int fff; // in A |
| } |
| class B extends A { |
| int fff; // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff; // in B'); |
| assertHasSuperElement('fff; // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_fieldByGetter() async { |
| addTestFile(''' |
| class A { |
| int fff; // in A |
| } |
| class B extends A { |
| get fff => 0; // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff => 0; // in B'); |
| assertHasSuperElement('fff; // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_fieldBySetter() async { |
| addTestFile(''' |
| class A { |
| int fff; // in A |
| } |
| class B extends A { |
| set fff(x) {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff(x) {} // in B'); |
| assertHasSuperElement('fff; // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_getterByField() async { |
| addTestFile(''' |
| class A { |
| get fff => 0; // in A |
| set fff(x) {} // in A |
| } |
| class B extends A { |
| int fff; // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff; // in B'); |
| assertHasSuperElement('fff => 0; // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_getterByGetter() async { |
| addTestFile(''' |
| class A { |
| get fff => 0; // in A |
| } |
| class B extends A { |
| get fff => 0; // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff => 0; // in B'); |
| assertHasSuperElement('fff => 0; // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_method_direct() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends A { |
| m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in B'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_method_indirect() async { |
| addTestFile(''' |
| class A { |
| m() {} // in A |
| } |
| class B extends A { |
| } |
| class C extends B { |
| m() {} // in C |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('m() {} // in C'); |
| assertHasSuperElement('m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_method_privateByPrivate() async { |
| addTestFile(''' |
| class A { |
| _m() {} // in A |
| } |
| class B extends A { |
| _m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('_m() {} // in B'); |
| assertHasSuperElement('_m() {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| |
| Future<void> test_super_method_superTypeCycle() async { |
| addTestFile(''' |
| class A extends B { |
| m() {} // in A |
| } |
| class B extends A { |
| m() {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| // must finish |
| } |
| |
| Future<void> test_super_setterBySetter() async { |
| addTestFile(''' |
| class A { |
| set fff(x) {} // in A |
| } |
| class B extends A { |
| set fff(x) {} // in B |
| } |
| '''); |
| await prepareOverrides(); |
| assertHasOverride('fff(x) {} // in B'); |
| assertHasSuperElement('fff(x) {} // in A'); |
| assertNoInterfaceMembers(); |
| } |
| } |