blob: 175454e1733404f443f07838671dd0679b153ef5 [file] [log] [blame]
// 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:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/domain_server.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:plugin/manager.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'mock_sdk.dart';
import 'mocks.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(AnalysisServerTest);
});
}
@reflectiveTest
class AnalysisServerTest extends Object with ResourceProviderMixin {
MockServerChannel channel;
AnalysisServer server;
MockPackageMapProvider packageMapProvider;
/**
* Test that having multiple analysis contexts analyze the same file doesn't
* cause that file to receive duplicate notifications when it's modified.
*/
Future do_not_test_no_duplicate_notifications() async {
// Subscribe to STATUS so we'll know when analysis is done.
server.serverServices = [ServerService.STATUS].toSet();
newFolder('/foo');
newFolder('/bar');
newFile('/foo/foo.dart', content: 'import "../bar/bar.dart";');
File bar = newFile('/bar/bar.dart', content: 'library bar;');
server.setAnalysisRoots('0', ['/foo', '/bar'], [], {});
Map<AnalysisService, Set<String>> subscriptions =
<AnalysisService, Set<String>>{};
for (AnalysisService service in AnalysisService.VALUES) {
subscriptions[service] = <String>[bar.path].toSet();
}
// The following line causes the isolate to continue running even though the
// test completes.
server.setAnalysisSubscriptions(subscriptions);
await server.onAnalysisComplete;
expect(server.statusAnalyzing, isFalse);
channel.notificationsReceived.clear();
server.updateContent(
'0', {bar.path: new AddContentOverlay('library bar; void f() {}')});
await server.onAnalysisComplete;
expect(server.statusAnalyzing, isFalse);
expect(channel.notificationsReceived, isNotEmpty);
Set<String> notificationTypesReceived = new Set<String>();
for (Notification notification in channel.notificationsReceived) {
String notificationType = notification.event;
switch (notificationType) {
case 'server.status':
case 'analysis.errors':
// It's normal for these notifications to be sent multiple times.
break;
case 'analysis.outline':
// It's normal for this notification to be sent twice.
// TODO(paulberry): why?
break;
default:
if (!notificationTypesReceived.add(notificationType)) {
fail('Notification type $notificationType received more than once');
}
break;
}
}
}
void processRequiredPlugins() {
ExtensionManager manager = new ExtensionManager();
manager.processPlugins(AnalysisEngine.instance.requiredPlugins);
}
void setUp() {
processRequiredPlugins();
channel = new MockServerChannel();
// Create an SDK in the mock file system.
new MockSdk(resourceProvider: resourceProvider);
packageMapProvider = new MockPackageMapProvider();
server = new AnalysisServer(
channel,
resourceProvider,
packageMapProvider,
new AnalysisServerOptions(),
new DartSdkManager(convertPath('/'), false),
InstrumentationService.NULL_SERVICE);
}
Future test_echo() {
server.handlers = [new EchoHandler()];
var request = new Request('my22', 'echo');
return channel.sendRequest(request).then((Response response) {
expect(response.id, equals('my22'));
expect(response.error, isNull);
});
}
Future test_serverStatusNotifications() {
server.serverServices.add(ServerService.STATUS);
var pkgFolder = convertPath('/pkg');
newFolder(pkgFolder);
newFolder(join(pkgFolder, 'lib'));
newFile(join(pkgFolder, 'lib', 'test.dart'),
content: 'class C {}');
server.setAnalysisRoots('0', [pkgFolder], [], {});
// Pump the event queue to make sure the server has finished any
// analysis.
return pumpEventQueue(times: 5000).then((_) {
List<Notification> notifications = channel.notificationsReceived;
expect(notifications, isNotEmpty);
// expect at least one notification indicating analysis is in progress
expect(notifications.any((Notification notification) {
if (notification.event == SERVER_NOTIFICATION_STATUS) {
var params = new ServerStatusParams.fromNotification(notification);
if (params.analysis != null) {
return params.analysis.isAnalyzing;
}
}
return false;
}), isTrue);
// the last notification should indicate that analysis is complete
Notification notification = notifications[notifications.length - 1];
var params = new ServerStatusParams.fromNotification(notification);
expect(params.analysis.isAnalyzing, isFalse);
});
}
test_setAnalysisSubscriptions_fileInIgnoredFolder_newOptions() async {
String path = convertPath('/project/samples/sample.dart');
newFile(path);
newFile('/project/analysis_options.yaml', content: r'''
analyzer:
exclude:
- 'samples/**'
''');
server.setAnalysisRoots('0', [convertPath('/project')], [], {});
server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{
AnalysisService.NAVIGATION: new Set<String>.from([path])
});
// We respect subscriptions, even for excluded files.
await server.onAnalysisComplete;
expect(channel.notificationsReceived.any((notification) {
return notification.event == ANALYSIS_NOTIFICATION_NAVIGATION;
}), isTrue);
}
test_setAnalysisSubscriptions_fileInIgnoredFolder_oldOptions() async {
String path = convertPath('/project/samples/sample.dart');
newFile(path);
newFile('/project/.analysis_options', content: r'''
analyzer:
exclude:
- 'samples/**'
''');
server.setAnalysisRoots('0', [convertPath('/project')], [], {});
server.setAnalysisSubscriptions(<AnalysisService, Set<String>>{
AnalysisService.NAVIGATION: new Set<String>.from([path])
});
// We respect subscriptions, even for excluded files.
await server.onAnalysisComplete;
expect(channel.notificationsReceived.any((notification) {
return notification.event == ANALYSIS_NOTIFICATION_NAVIGATION;
}), isTrue);
}
Future test_shutdown() {
server.handlers = [new ServerDomainHandler(server)];
var request = new Request('my28', SERVER_REQUEST_SHUTDOWN);
return channel.sendRequest(request).then((Response response) {
expect(response.id, equals('my28'));
expect(response.error, isNull);
});
}
Future test_unknownRequest() {
server.handlers = [new EchoHandler()];
var request = new Request('my22', 'randomRequest');
return channel.sendRequest(request).then((Response response) {
expect(response.id, equals('my22'));
expect(response.error, isNotNull);
});
}
}
class EchoHandler implements RequestHandler {
@override
Response handleRequest(Request request) {
if (request.method == 'echo') {
return new Response(request.id, result: {'echo': true});
}
return null;
}
}