// 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 overridesList; late Override overrideObject; final Completer _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 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 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 test_BAD_getterByMethod() async { addTestFile(''' class A { get fff => null; } class B extends A { fff() {} } '''); await prepareOverrides(); assertNoOverride('fff() {}'); } Future test_BAD_getterBySetter() async { addTestFile(''' class A { get fff => null; } class B extends A { set fff(x) {} } '''); await prepareOverrides(); assertNoOverride('fff(x) {}'); } Future 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 test_BAD_methodByGetter() async { addTestFile(''' class A { fff() {} } class B extends A { int get fff => null; } '''); await prepareOverrides(); assertNoOverride('fff => null'); } Future 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 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 test_BAD_setterByGetter() async { addTestFile(''' class A { set fff(x) {} } class B extends A { get fff => null; } '''); await prepareOverrides(); assertNoOverride('fff => null;'); } Future 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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(); } }