| // 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:analysis_server/src/analytics/analytics_manager.dart'; | 
 | import 'package:analysis_server/src/legacy_analysis_server.dart'; | 
 | import 'package:analysis_server/src/server/crash_reporting_attachments.dart'; | 
 | import 'package:analysis_server/src/server/error_notifier.dart'; | 
 | import 'package:analysis_server/src/utilities/mocks.dart'; | 
 | import 'package:analyzer/file_system/file_system.dart'; | 
 | import 'package:analyzer/file_system/memory_file_system.dart'; | 
 | import 'package:analyzer/src/generated/sdk.dart'; | 
 | import 'package:analyzer/src/test_utilities/mock_sdk.dart'; | 
 | import 'package:analyzer/src/test_utilities/package_config_file_builder.dart'; | 
 | import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart'; | 
 | import 'package:analyzer_plugin/protocol/protocol_common.dart'; | 
 | import 'package:test/test.dart'; | 
 | import 'package:test_reflective_loader/test_reflective_loader.dart'; | 
 | import 'package:unified_analytics/unified_analytics.dart'; | 
 |  | 
 | void main() { | 
 |   defineReflectiveSuite(() { | 
 |     defineReflectiveTests(AnalysisServerTest); | 
 |   }); | 
 | } | 
 |  | 
 | @reflectiveTest | 
 | class AnalysisServerTest with ResourceProviderMixin { | 
 |   @override | 
 |   MemoryResourceProvider resourceProvider = MemoryResourceProvider( | 
 |     // Force the in-memory file watchers to be slowly initialized to emulate | 
 |     // the physical watchers (for test_concurrentContextRebuilds). | 
 |     delayWatcherInitialization: Duration(milliseconds: 1), | 
 |   ); | 
 |  | 
 |   late MockServerChannel channel; | 
 |   late ErrorNotifier errorNotifier; | 
 |   late LegacyAnalysisServer server; | 
 |  | 
 |   void setUp() { | 
 |     channel = MockServerChannel(); | 
 |  | 
 |     // Create an SDK in the mock file system. | 
 |     var sdkRoot = newFolder('/sdk'); | 
 |     createMockSdk( | 
 |       resourceProvider: resourceProvider, | 
 |       root: sdkRoot, | 
 |     ); | 
 |  | 
 |     errorNotifier = ErrorNotifier(); | 
 |     server = LegacyAnalysisServer( | 
 |         channel, | 
 |         resourceProvider, | 
 |         AnalysisServerOptions(), | 
 |         DartSdkManager(sdkRoot.path), | 
 |         AnalyticsManager(NoOpAnalytics()), | 
 |         CrashReportingAttachmentsBuilder.empty, | 
 |         errorNotifier); | 
 |     errorNotifier.server = server; | 
 |   } | 
 |  | 
 |   /// See https://github.com/dart-lang/sdk/issues/50496 | 
 |   Future<void> test_caching_mixin_superInvokedNames_setter_change() async { | 
 |     var lib = convertPath('/lib'); | 
 |     newFolder(lib); | 
 |     var foo = newFile('/lib/foo.dart', ''' | 
 | class A { | 
 |   set foo(int _) {} | 
 | } | 
 | mixin M on A { | 
 |   void bar() { | 
 |     super.boo = 0; | 
 |   } | 
 | } | 
 | class X extends A with M {} | 
 | '''); | 
 |     await server.setAnalysisRoots('0', [lib], []); | 
 |     await server.onAnalysisComplete; | 
 |     expect(server.statusAnalyzing, isFalse); | 
 |     channel.notificationsReceived.clear(); | 
 |  | 
 |     server.updateContent('0', { | 
 |       foo.path: AddContentOverlay(''' | 
 | class A { | 
 |   set foo(int _) {} | 
 | } | 
 | mixin M on A { | 
 |   void bar() { | 
 |     super.foo = 0; | 
 |   } | 
 | } | 
 | class X extends A with M {} | 
 | ''') | 
 |     }); | 
 |     await server.onAnalysisComplete; | 
 |     expect(server.statusAnalyzing, isFalse); | 
 |     var notifications = channel.notificationsReceived; | 
 |     expect(notifications, hasLength(1)); | 
 |     var notification = notifications.first; | 
 |     expect(notification.event, 'analysis.errors'); | 
 |     var params = notification.params!; | 
 |     var errors = params['errors'] as List<Map<String, Object?>>; | 
 |     expect(errors, isEmpty); | 
 |   } | 
 |  | 
 |   /// Test that modifying package_config again while a context rebuild is in | 
 |   /// progress does not get lost due to a gap between creating a file watcher | 
 |   /// and it raising events. | 
 |   /// https://github.com/Dart-Code/Dart-Code/issues/3438 | 
 |   Future<void> test_concurrentContextRebuilds() async { | 
 |     // Subscribe to STATUS so we'll know when analysis is done. | 
 |     server.serverServices = {ServerService.STATUS}; | 
 |     var projectRoot = convertPath('/foo'); | 
 |     var projectTestFile = convertPath('/foo/lib/test.dart'); | 
 |     var projectPackageConfigFile = | 
 |         convertPath('/foo/.dart_tool/package_config.json'); | 
 |  | 
 |     // Create a file that references two packages, which will we write to | 
 |     // package_config.json individually. | 
 |     newFolder(projectRoot); | 
 |     newFile( | 
 |       projectTestFile, | 
 |       r''' | 
 |       import "package:foo/foo.dart";' | 
 |       import "package:bar/bar.dart";' | 
 |       ''', | 
 |     ); | 
 |  | 
 |     // Ensure the packages and package_config exist. | 
 |     var fooLibFolder = _addSimplePackage('foo', ''); | 
 |     var barLibFolder = _addSimplePackage('bar', ''); | 
 |     var config = PackageConfigFileBuilder(); | 
 |     writePackageConfig(projectPackageConfigFile, config); | 
 |  | 
 |     // Track diagnostics that arrive. | 
 |     var errorsByFile = <String, List<AnalysisError>>{}; | 
 |     channel.notifications | 
 |         .where((notification) => notification.event == 'analysis.errors') | 
 |         .listen((notification) { | 
 |       var params = AnalysisErrorsParams.fromNotification(notification, | 
 |           clientUriConverter: server.uriConverter); | 
 |       errorsByFile[params.file] = params.errors; | 
 |     }); | 
 |  | 
 |     /// Helper that waits for analysis then returns the relevant errors. | 
 |     Future<List<AnalysisError>> getUriNotExistErrors() async { | 
 |       await server.onAnalysisComplete; | 
 |       expect(server.statusAnalyzing, isFalse); | 
 |       return errorsByFile[projectTestFile]! | 
 |           .where((error) => error.code == 'uri_does_not_exist') | 
 |           .toList(); | 
 |     } | 
 |  | 
 |     // Set roots and expect 2 uri_does_not_exist errors. | 
 |     await server.setAnalysisRoots('0', [projectRoot], []); | 
 |     expect(await getUriNotExistErrors(), hasLength(2)); | 
 |  | 
 |     // Write both packages, in two events so that the first one will trigger | 
 |     // a rebuild. | 
 |     config.add(name: 'foo', rootPath: fooLibFolder.parent.path); | 
 |     writePackageConfig(projectPackageConfigFile, config); | 
 |     await pumpEventQueue(times: 1); // Allow server to begin processing. | 
 |     config.add(name: 'bar', rootPath: barLibFolder.parent.path); | 
 |     writePackageConfig(projectPackageConfigFile, config); | 
 |  | 
 |     // Eventually the errors are gone. | 
 |     while (true) { | 
 |       var errors = await getUriNotExistErrors(); | 
 |       if (errors.isEmpty) { | 
 |         break; | 
 |       } | 
 |       await pumpEventQueue(times: 5000); | 
 |     } | 
 |   } | 
 |  | 
 |   Future<void> test_errorNotification_errorNotifier() async { | 
 |     errorNotifier.logException(Exception('dummy exception')); | 
 |  | 
 |     var errors = channel.notificationsReceived.where( | 
 |         (notification) => notification.event == SERVER_NOTIFICATION_ERROR); | 
 |     expect( | 
 |       errors.single.params![SERVER_NOTIFICATION_ERROR_MESSAGE], | 
 |       contains('dummy exception'), | 
 |     ); | 
 |   } | 
 |  | 
 |   Future<void> test_errorNotification_sendNotification() async { | 
 |     server.sendServerErrorNotification( | 
 |         'message', Exception('dummy exception'), null); | 
 |  | 
 |     var errors = channel.notificationsReceived.where( | 
 |         (notification) => notification.event == SERVER_NOTIFICATION_ERROR); | 
 |     expect( | 
 |       errors.single.params![SERVER_NOTIFICATION_ERROR_MESSAGE], | 
 |       contains('dummy exception'), | 
 |     ); | 
 |   } | 
 |  | 
 |   Future<void> test_serverStatusNotifications_hasFile() async { | 
 |     server.serverServices.add(ServerService.STATUS); | 
 |  | 
 |     newFile('/test/lib/a.dart', r''' | 
 | class A {} | 
 | '''); | 
 |     await server.setAnalysisRoots('0', [convertPath('/test')], []); | 
 |  | 
 |     // Pump the event queue, so that the server has finished any analysis. | 
 |     await pumpEventQueue(times: 5000); | 
 |  | 
 |     var notifications = channel.notificationsReceived; | 
 |     expect(notifications, isNotEmpty); | 
 |  | 
 |     // At least one notification indicating analysis is in progress. | 
 |     expect(notifications.any((Notification notification) { | 
 |       if (notification.event == SERVER_NOTIFICATION_STATUS) { | 
 |         var params = ServerStatusParams.fromNotification(notification, | 
 |             clientUriConverter: server.uriConverter); | 
 |         var analysis = params.analysis; | 
 |         if (analysis != null) { | 
 |           return analysis.isAnalyzing; | 
 |         } | 
 |       } | 
 |       return false; | 
 |     }), isTrue); | 
 |  | 
 |     // The last notification should indicate that analysis is complete. | 
 |     var notification = notifications[notifications.length - 1]; | 
 |     var params = ServerStatusParams.fromNotification(notification, | 
 |         clientUriConverter: server.uriConverter); | 
 |     expect(params.analysis!.isAnalyzing, isFalse); | 
 |   } | 
 |  | 
 |   Future<void> test_serverStatusNotifications_noFiles() async { | 
 |     server.serverServices.add(ServerService.STATUS); | 
 |  | 
 |     newFolder('/test'); | 
 |     await server.setAnalysisRoots('0', [convertPath('/test')], []); | 
 |  | 
 |     // Pump the event queue, so that the server has finished any analysis. | 
 |     await pumpEventQueue(times: 5000); | 
 |  | 
 |     var notifications = channel.notificationsReceived; | 
 |     expect(notifications, isNotEmpty); | 
 |  | 
 |     // At least one notification indicating analysis is in progress. | 
 |     expect(notifications.any((Notification notification) { | 
 |       if (notification.event == SERVER_NOTIFICATION_STATUS) { | 
 |         var params = ServerStatusParams.fromNotification(notification, | 
 |             clientUriConverter: server.uriConverter); | 
 |         var analysis = params.analysis; | 
 |         if (analysis != null) { | 
 |           return analysis.isAnalyzing; | 
 |         } | 
 |       } | 
 |       return false; | 
 |     }), isTrue); | 
 |  | 
 |     // The last notification should indicate that analysis is complete. | 
 |     var notification = notifications[notifications.length - 1]; | 
 |     var params = ServerStatusParams.fromNotification(notification, | 
 |         clientUriConverter: server.uriConverter); | 
 |     expect(params.analysis!.isAnalyzing, isFalse); | 
 |   } | 
 |  | 
 |   Future<void> | 
 |       test_setAnalysisSubscriptions_fileInIgnoredFolder_newOptions() async { | 
 |     var path = convertPath('/project/samples/sample.dart'); | 
 |     newFile(path, ''); | 
 |     newAnalysisOptionsYamlFile('/project', r''' | 
 | analyzer: | 
 |   exclude: | 
 |     - 'samples/**' | 
 | '''); | 
 |     await server.setAnalysisRoots('0', [convertPath('/project')], []); | 
 |     server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{ | 
 |       AnalysisService.NAVIGATION: <String>{path} | 
 |     }); | 
 |  | 
 |     // We respect subscriptions, even for excluded files. | 
 |     await pumpEventQueue(times: 5000); | 
 |     expect(channel.notificationsReceived.any((notification) { | 
 |       return notification.event == ANALYSIS_NOTIFICATION_NAVIGATION; | 
 |     }), isTrue); | 
 |   } | 
 |  | 
 |   Future<void> | 
 |       test_setAnalysisSubscriptions_fileInIgnoredFolder_oldOptions() async { | 
 |     var path = convertPath('/project/samples/sample.dart'); | 
 |     newFile(path, ''); | 
 |     newAnalysisOptionsYamlFile('/project', r''' | 
 | analyzer: | 
 |   exclude: | 
 |     - 'samples/**' | 
 | '''); | 
 |     await server.setAnalysisRoots('0', [convertPath('/project')], []); | 
 |     server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{ | 
 |       AnalysisService.NAVIGATION: <String>{path} | 
 |     }); | 
 |  | 
 |     // We respect subscriptions, even for excluded files. | 
 |     await pumpEventQueue(times: 5000); | 
 |     expect(channel.notificationsReceived.any((notification) { | 
 |       return notification.event == ANALYSIS_NOTIFICATION_NAVIGATION; | 
 |     }), isTrue); | 
 |   } | 
 |  | 
 |   Future<void> test_shutdown() { | 
 |     var request = Request('my28', SERVER_REQUEST_SHUTDOWN); | 
 |     return channel.simulateRequestFromClient(request).then((Response response) { | 
 |       expect(response.id, equals('my28')); | 
 |       expect(response.error, isNull); | 
 |     }); | 
 |   } | 
 |  | 
 |   Future<void> test_unknownRequest() { | 
 |     var request = Request('my22', 'randomRequest'); | 
 |     return channel.simulateRequestFromClient(request).then((Response response) { | 
 |       expect(response.id, equals('my22')); | 
 |       expect(response.error, isNotNull); | 
 |     }); | 
 |   } | 
 |  | 
 |   void writePackageConfig(String path, PackageConfigFileBuilder config) { | 
 |     newFile(path, config.toContent(toUriStr: toUriStr)); | 
 |   } | 
 |  | 
 |   /// Creates a simple package named [name] with [content] in the file at | 
 |   /// `package:$name/$name.dart`. | 
 |   /// | 
 |   /// Returns a [Folder] that represents the packages `lib` folder. | 
 |   Folder _addSimplePackage(String name, String content) { | 
 |     var packagePath = '/packages/$name'; | 
 |     var file = newFile('$packagePath/lib/$name.dart', content); | 
 |     return file.parent; | 
 |   } | 
 | } |