| // 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:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../analysis_abstract.dart'; | |
| import '../analysis_server_base.dart'; | |
| void main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(AnalysisNotificationImplementedTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class AnalysisNotificationImplementedTest extends PubPackageAnalysisServerTest { | |
| 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<void> prepareImplementedElements() async { | |
| await subscribeForImplemented(); | |
| return waitForImplementedElements(); | |
| } | |
| @override | |
| void processNotification(Notification notification) { | |
| if (notification.event == analysisNotificationImplemented) { | |
| var params = AnalysisImplementedParams.fromNotification( | |
| notification, | |
| clientUriConverter: server.uriConverter, | |
| ); | |
| if (params.file == testFile.path) { | |
| implementedClasses = params.classes; | |
| implementedMembers = params.members; | |
| } | |
| } | |
| } | |
| @override | |
| Future<void> setUp() async { | |
| super.setUp(); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| } | |
| Future<void> subscribeForImplemented() async { | |
| setPriorityFiles([testFile]); | |
| await 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_class_extended() async { | |
| addTestFile(''' | |
| class A {} | |
| class B extends A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| @SkippedTest() // TODO(scheglov): implement augmentation | |
| Future<void> test_class_extended_augment() async { | |
| newFile('$testPackageLibPath/b.dart', ''' | |
| part 'test.dart'; | |
| class A {} | |
| class B extends A {} | |
| '''); | |
| addTestFile(''' | |
| part of 'b.dart'; | |
| augment class A {} // 1 | |
| augment class A {} // 2 | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {} // 1'); | |
| assertHasImplementedClass('A {} // 2'); | |
| } | |
| Future<void> test_class_extended_inAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| class B extends A {} | |
| '''); | |
| addTestFile(''' | |
| part 'a.dart'; | |
| class A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_extended_inAugmented() async { | |
| newFile('$testPackageLibPath/b.dart', ''' | |
| part 'test.dart'; | |
| class B extends A {} | |
| '''); | |
| addTestFile(''' | |
| part of 'b.dart'; | |
| class A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_implementedBy_class() async { | |
| addTestFile(''' | |
| class A {} | |
| class B implements A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_implementedBy_enum() async { | |
| addTestFile(''' | |
| class A {} | |
| enum E implements A { | |
| v | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_implementedBy_enum_getterByGetter() async { | |
| addTestFile(''' | |
| class A { | |
| int get foo => 0; // A | |
| } | |
| enum E implements A { | |
| v; | |
| int get foo => 0; // E | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo => 0; // A'); | |
| assertNoImplementedMember('foo => 0; // E'); | |
| } | |
| Future<void> test_class_implementedBy_enum_methodByMethod() async { | |
| addTestFile(''' | |
| class A { | |
| void foo() {} // A | |
| } | |
| enum E implements A { | |
| v; | |
| void foo() {} // E | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo() {} // A'); | |
| assertNoImplementedMember('foo() {} // E'); | |
| } | |
| Future<void> test_class_implementedBy_enum_setterBySetter() async { | |
| addTestFile(''' | |
| class A { | |
| set foo(int _) {} // A | |
| } | |
| enum E implements A { | |
| v; | |
| set foo(int _) {} // E | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo(int _) {} // A'); | |
| assertNoImplementedMember('foo(int _) {} // E'); | |
| } | |
| Future<void> test_class_implementedBy_extensionType() async { | |
| addTestFile(''' | |
| class A {} | |
| extension type B(A it) implements A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_implementedBy_mixin() 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_mixedBy_class() async { | |
| addTestFile(''' | |
| class A {} | |
| class B = Object with A; | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A {'); | |
| } | |
| Future<void> test_class_mixedBy_enum() async { | |
| addTestFile(''' | |
| mixin M {} | |
| enum E with M { | |
| v | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('M {}'); | |
| } | |
| Future<void> test_class_mixedBy_enum_methodByMethod() async { | |
| addTestFile(''' | |
| class M { | |
| void foo() {} // M | |
| } | |
| enum E with M { | |
| v; | |
| void foo() {} // E | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo() {} // M'); | |
| assertNoImplementedMember('foo() {} // E'); | |
| } | |
| Future<void> test_extensionType_implementedBy_extensionType() async { | |
| addTestFile(''' | |
| extension type A(int it) {} | |
| extension type B(int it) implements A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedClass('A(int it)'); | |
| } | |
| 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_ofClass_byClass_field_withField() async { | |
| addTestFile(''' | |
| class A { | |
| int f; // A | |
| } | |
| class B extends A { | |
| int f; | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f; // A'); | |
| } | |
| Future<void> test_ofClass_byClass_field_withGetter() async { | |
| addTestFile(''' | |
| class A { | |
| int f; // A | |
| } | |
| class B extends A { | |
| get f => null; | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f; // A'); | |
| } | |
| Future<void> test_ofClass_byClass_field_withSetter() async { | |
| addTestFile(''' | |
| class A { | |
| int f; // A | |
| } | |
| class B extends A { | |
| void set f(_) {} | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f; // A'); | |
| } | |
| Future<void> test_ofClass_byClass_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_ofClass_byClass_getter_withGetter() async { | |
| addTestFile(''' | |
| class A { | |
| get f => null; // A | |
| } | |
| class B extends A { | |
| get f => null; | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f => null; // A'); | |
| } | |
| @SkippedTest() // TODO(scheglov): implement augmentation | |
| Future<void> test_ofClass_byClass_method_augment() async { | |
| newFile('$testPackageLibPath/b.dart', ''' | |
| part 'test.dart'; | |
| class A { | |
| m() {} | |
| } | |
| class B extends A { | |
| m() {} | |
| } | |
| '''); | |
| addTestFile(''' | |
| part of 'b.dart'; | |
| augment class A { | |
| augment m() {} // 1 | |
| augment m() {} // 2 | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('m() {} // 1'); | |
| assertHasImplementedMember('m() {} // 2'); | |
| } | |
| @SkippedTest() // TODO(scheglov): implement augmentation | |
| Future<void> test_ofClass_byClass_method_inAugmentation() async { | |
| newFile('$testPackageLibPath/a.dart', ''' | |
| part of 'test.dart'; | |
| augment class B extends A { | |
| m() {} | |
| } | |
| '''); | |
| addTestFile(''' | |
| part 'a.dart'; | |
| class A { | |
| m() {} | |
| } | |
| class B extends A {} | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('m()'); | |
| } | |
| Future<void> test_ofClass_byClass_method_inAugmented() async { | |
| newFile('$testPackageLibPath/b.dart', ''' | |
| part 'test.dart'; | |
| class A {} | |
| class B extends A { | |
| m() {} | |
| } | |
| '''); | |
| addTestFile(''' | |
| part of 'b.dart'; | |
| augment class A { | |
| m() {} | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('m()'); | |
| } | |
| Future<void> test_ofClass_byClass_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_ofClass_byClass_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_ofClass_byClass_method_withMethod_private_differentLib() async { | |
| newFile('$testPackageLibPath/lib.dart', r''' | |
| import 'test.dart'; | |
| class B extends A { | |
| void _m() {} | |
| } | |
| '''); | |
| addTestFile(''' | |
| class A { | |
| _m() {} // A | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertNoImplementedMember('_m() {} // A'); | |
| } | |
| Future<void> | |
| test_ofClass_byClass_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_ofClass_byClass_method_withMethod_wasAbstract() async { | |
| addTestFile(''' | |
| abstract class A { | |
| m(); // A | |
| } | |
| class B extends A { | |
| m() {} | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('m(); // A'); | |
| } | |
| Future<void> test_ofClass_byClass_setter_withField() async { | |
| addTestFile(''' | |
| class A { | |
| set f(_) {} // A | |
| } | |
| class B extends A { | |
| int f; | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f(_) {} // A'); | |
| } | |
| Future<void> test_ofClass_byClass_setter_withSetter() async { | |
| addTestFile(''' | |
| class A { | |
| set f(_) {} // A | |
| } | |
| class B extends A { | |
| set f(_) {} // B | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('f(_) {} // A'); | |
| } | |
| Future<void> test_ofClass_byClass_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_ofClass_byClass_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_ofClass_byClass_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_ofClass_byClass_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_ofClass_byClass_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_ofClass_byClass_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<void> test_ofClass_byExtensionType_method_withMethod() async { | |
| addTestFile(''' | |
| class A { | |
| void foo() {} // A | |
| } | |
| extension type E(A it) implements A { | |
| void foo() {} // B | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo() {} // A'); | |
| assertNoImplementedMember('foo() {} // B'); | |
| } | |
| Future<void> test_ofExtensionType_method_withMethod() async { | |
| addTestFile(''' | |
| extension type A(int) { | |
| void foo() {} // A | |
| } | |
| extension type B(int) implements A { | |
| void foo() {} // B | |
| } | |
| '''); | |
| await prepareImplementedElements(); | |
| assertHasImplementedMember('foo() {} // A'); | |
| assertNoImplementedMember('foo() {} // B'); | |
| } | |
| Future<void> waitForImplementedElements() { | |
| Future<void> waitForNotification(int times) { | |
| if (times == 0 || implementedClasses != null) { | |
| return Future.value(); | |
| } | |
| return Future.delayed( | |
| Duration(milliseconds: 1), | |
| () => waitForNotification(times - 1), | |
| ); | |
| } | |
| return waitForNotification(30000); | |
| } | |
| } |