// 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 'package:analysis_server/protocol/protocol.dart'; import 'package:analysis_server/protocol/protocol_constants.dart'; import 'package:analysis_server/protocol/protocol_generated.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; import '../analysis_abstract.dart'; void main() { defineReflectiveSuite(() { defineReflectiveTests(AnalysisNotificationImplementedTest); }); } @reflectiveTest class AnalysisNotificationImplementedTest extends AbstractAnalysisTest { List? implementedClasses; List? implementedMembers; /// Validates that there is an [ImplementedClass] at the offset of [search]. /// /// If [length] is not specified explicitly, then length of an identifier /// from [search] is used. void assertHasImplementedClass(String search, [int length = -1]) { var offset = findOffset(search); if (length == -1) { length = findIdentifierLength(search); } if (implementedClasses == null) { fail('No notification of implemented classes was received'); } for (var clazz in implementedClasses!) { if (clazz.offset == offset && clazz.length == length) { return; } } fail('Expect to find an implemented class at $offset' ' in $implementedClasses'); } /// Validates that there is an [ImplementedClass] at the offset of [search]. /// /// If [length] is not specified explicitly, then length of an identifier /// from [search] is used. void assertHasImplementedMember(String search, [int length = -1]) { var offset = findOffset(search); if (length == -1) { length = findIdentifierLength(search); } if (implementedMembers == null) { fail('No notification of implemented members was received'); } for (var member in implementedMembers!) { if (member.offset == offset && member.length == length) { return; } } fail('Expect to find an implemented member at $offset' ' in $implementedMembers'); } /// Validates that there is no [ImplementedMember] at the offset of [search]. /// /// If [length] is not specified explicitly, then length of an identifier /// from [search] is used. void assertNoImplementedMember(String search, [int length = -1]) { var offset = findOffset(search); if (length == -1) { length = findIdentifierLength(search); } if (implementedMembers == null) { fail('No notification of implemented members was received'); } for (var member in implementedMembers!) { if (member.offset == offset) { fail('Unexpected implemented member at $offset' ' in $implementedMembers'); } } } /// Subscribe for `IMPLEMENTED` and wait for the notification. Future prepareImplementedElements() { subscribeForImplemented(); return waitForImplementedElements(); } @override void processNotification(Notification notification) { if (notification.event == ANALYSIS_NOTIFICATION_IMPLEMENTED) { var params = AnalysisImplementedParams.fromNotification(notification); if (params.file == testFile) { implementedClasses = params.classes; implementedMembers = params.members; } } } @override void setUp() { super.setUp(); createProject(); } void subscribeForImplemented() { setPriorityFiles([testFile]); addAnalysisSubscription(AnalysisService.IMPLEMENTED, testFile); } Future test_afterAnalysis() async { addTestFile(''' class A {} class B extends A {} '''); await waitForTasksFinished(); await prepareImplementedElements(); assertHasImplementedClass('A {'); } Future test_afterIncrementalResolution() async { subscribeForImplemented(); addTestFile(''' class A {} class B extends A {} '''); await prepareImplementedElements(); assertHasImplementedClass('A {'); // add a space implementedClasses = null; testCode = ''' class A {} class B extends A {} '''; server.updateContent('1', {testFile: AddContentOverlay(testCode)}); await waitForImplementedElements(); assertHasImplementedClass('A {'); } Future test_class_extended() async { addTestFile(''' class A {} class B extends A {} '''); await prepareImplementedElements(); assertHasImplementedClass('A {'); } Future test_class_implemented() async { addTestFile(''' class A {} class B implements A {} '''); await prepareImplementedElements(); assertHasImplementedClass('A {'); } Future test_class_inMixin() async { addTestFile(''' class A {} // ref class B {} // ref class C {} // ref class D {} // ref mixin M on A, B implements C, D {} '''); await prepareImplementedElements(); assertHasImplementedClass('A {} // ref'); assertHasImplementedClass('B {} // ref'); assertHasImplementedClass('C {} // ref'); assertHasImplementedClass('D {} // ref'); } Future test_class_mixed() async { addTestFile(''' class A {} class B = Object with A; '''); await prepareImplementedElements(); assertHasImplementedClass('A {'); } Future test_field_withField() async { addTestFile(''' class A { int f; // A } class B extends A { int f; } '''); await prepareImplementedElements(); assertHasImplementedMember('f; // A'); } Future test_field_withGetter() async { addTestFile(''' class A { int f; // A } class B extends A { get f => null; } '''); await prepareImplementedElements(); assertHasImplementedMember('f; // A'); } Future test_field_withSetter() async { addTestFile(''' class A { int f; // A } class B extends A { void set f(_) {} } '''); await prepareImplementedElements(); assertHasImplementedMember('f; // A'); } Future test_getter_withField() async { addTestFile(''' class A { get f => null; // A } class B extends A { int f; } '''); await prepareImplementedElements(); assertHasImplementedMember('f => null; // A'); } Future test_getter_withGetter() async { addTestFile(''' class A { get f => null; // A } class B extends A { get f => null; } '''); await prepareImplementedElements(); assertHasImplementedMember('f => null; // A'); } Future test_method_withMethod() async { addTestFile(''' class A { m() {} // A } class B extends A { m() {} // B } '''); await prepareImplementedElements(); assertHasImplementedMember('m() {} // A'); assertNoImplementedMember('m() {} // B'); } Future test_method_withMethod_indirectSubclass() async { addTestFile(''' class A { m() {} // A } class B extends A { } class C extends A { m() {} } '''); await prepareImplementedElements(); assertHasImplementedMember('m() {} // A'); } Future test_method_withMethod_private_differentLib() async { newFile(join(testFolder, 'lib.dart'), content: r''' import 'test.dart'; class B extends A { void _m() {} } '''); addTestFile(''' class A { _m() {} // A } '''); await prepareImplementedElements(); assertNoImplementedMember('_m() {} // A'); } Future test_method_withMethod_private_sameLibrary() async { addTestFile(''' class A { _m() {} // A } class B extends A { _m() {} // B } '''); await prepareImplementedElements(); assertHasImplementedMember('_m() {} // A'); assertNoImplementedMember('_m() {} // B'); } Future test_method_withMethod_wasAbstract() async { addTestFile(''' abstract class A { m(); // A } class B extends A { m() {} } '''); await prepareImplementedElements(); assertHasImplementedMember('m(); // A'); } Future test_mixin_implemented() async { addTestFile(''' mixin M { // ref void foo() {} // ref void bar() {} // ref } class A implements M { void foo() {} } '''); await prepareImplementedElements(); assertHasImplementedClass('M { // ref'); assertHasImplementedMember('foo() {} // ref'); assertNoImplementedMember('bar() {} // ref'); } Future test_mixin_mixed() async { addTestFile(''' mixin M { // ref void foo() {} // ref void bar() {} // ref } class A extends Object with M { void foo() {} } '''); await prepareImplementedElements(); assertHasImplementedClass('M { // ref'); assertHasImplementedMember('foo() {} // ref'); assertNoImplementedMember('bar() {} // ref'); } Future test_setter_withField() async { addTestFile(''' class A { set f(_) {} // A } class B extends A { int f; } '''); await prepareImplementedElements(); assertHasImplementedMember('f(_) {} // A'); } Future test_setter_withSetter() async { addTestFile(''' class A { set f(_) {} // A } class B extends A { set f(_) {} // B } '''); await prepareImplementedElements(); assertHasImplementedMember('f(_) {} // A'); } Future test_static_field_instanceStatic() async { addTestFile(''' class A { int F = 0; } class B extends A { static int F = 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('F = 0'); } Future test_static_field_staticInstance() async { addTestFile(''' class A { static int F = 0; } class B extends A { int F = 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('F = 0'); } Future test_static_field_staticStatic() async { addTestFile(''' class A { static int F = 0; } class B extends A { static int F = 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('F = 0'); } Future test_static_method_instanceStatic() async { addTestFile(''' class A { int m() => 0; } class B extends A { static int m() => 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('m() => 0'); } Future test_static_method_staticInstance() async { addTestFile(''' class A { static int m() => 0; } class B extends A { int m() => 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('m() => 0'); } Future test_static_method_staticStatic() async { addTestFile(''' class A { static int m() => 0; } class B extends A { static int m() => 1; } '''); await prepareImplementedElements(); assertNoImplementedMember('m() => 0'); } Future waitForImplementedElements() { Future waitForNotification(int times) { if (times == 0 || implementedClasses != null) { return Future.value(); } return Future.delayed( Duration(milliseconds: 1), () => waitForNotification(times - 1)); } return waitForNotification(30000); } }