| // 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<ImplementedClass>? implementedClasses; |
| List<ImplementedMember>? 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<void> test_afterAnalysis() async { |
| addTestFile(''' |
| class A {} |
| class B extends A {} |
| '''); |
| await waitForTasksFinished(); |
| await prepareImplementedElements(); |
| assertHasImplementedClass('A {'); |
| } |
| |
| Future<void> 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<void> test_class_extended() async { |
| addTestFile(''' |
| class A {} |
| class B extends A {} |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedClass('A {'); |
| } |
| |
| Future<void> test_class_implemented() async { |
| addTestFile(''' |
| class A {} |
| class B implements A {} |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedClass('A {'); |
| } |
| |
| Future<void> 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<void> test_class_mixed() async { |
| addTestFile(''' |
| class A {} |
| class B = Object with A; |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedClass('A {'); |
| } |
| |
| Future<void> test_field_withField() async { |
| addTestFile(''' |
| class A { |
| int f; // A |
| } |
| class B extends A { |
| int f; |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f; // A'); |
| } |
| |
| Future<void> test_field_withGetter() async { |
| addTestFile(''' |
| class A { |
| int f; // A |
| } |
| class B extends A { |
| get f => null; |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f; // A'); |
| } |
| |
| Future<void> test_field_withSetter() async { |
| addTestFile(''' |
| class A { |
| int f; // A |
| } |
| class B extends A { |
| void set f(_) {} |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f; // A'); |
| } |
| |
| Future<void> test_getter_withField() async { |
| addTestFile(''' |
| class A { |
| get f => null; // A |
| } |
| class B extends A { |
| int f; |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f => null; // A'); |
| } |
| |
| Future<void> 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<void> test_method_withMethod() async { |
| addTestFile(''' |
| class A { |
| m() {} // A |
| } |
| class B extends A { |
| m() {} // B |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('m() {} // A'); |
| assertNoImplementedMember('m() {} // B'); |
| } |
| |
| Future<void> 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<void> 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<void> 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<void> test_method_withMethod_wasAbstract() async { |
| addTestFile(''' |
| abstract class A { |
| m(); // A |
| } |
| class B extends A { |
| m() {} |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('m(); // A'); |
| } |
| |
| Future<void> 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<void> 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<void> test_setter_withField() async { |
| addTestFile(''' |
| class A { |
| set f(_) {} // A |
| } |
| class B extends A { |
| int f; |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f(_) {} // A'); |
| } |
| |
| Future<void> test_setter_withSetter() async { |
| addTestFile(''' |
| class A { |
| set f(_) {} // A |
| } |
| class B extends A { |
| set f(_) {} // B |
| } |
| '''); |
| await prepareImplementedElements(); |
| assertHasImplementedMember('f(_) {} // A'); |
| } |
| |
| Future<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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); |
| } |
| } |