b3295a1327
Change-Id: I5f3b9274ab8ce0ce4259e273c5ea69585467df21 Reviewed-on: https://dart-review.googlesource.com/c/90783 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
352 lines
10 KiB
Dart
352 lines
10 KiB
Dart
// 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'
|
|
hide AnalysisOptions;
|
|
import 'package:analysis_server/src/analysis_server.dart';
|
|
import 'package:analysis_server/src/domain_analysis.dart';
|
|
import 'package:analysis_server/src/plugin/notification_manager.dart';
|
|
import 'package:analysis_server/src/plugin/plugin_manager.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/instrumentation/instrumentation.dart';
|
|
import 'package:analyzer/src/context/context_root.dart' as analyzer;
|
|
import 'package:analyzer/src/dart/analysis/driver.dart';
|
|
import 'package:analyzer/src/generated/engine.dart';
|
|
import 'package:analyzer/src/generated/sdk.dart';
|
|
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
|
|
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
|
|
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
|
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
|
|
import 'package:plugin/manager.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:watcher/watcher.dart';
|
|
|
|
import 'mocks.dart';
|
|
|
|
int findIdentifierLength(String search) {
|
|
int length = 0;
|
|
while (length < search.length) {
|
|
int c = search.codeUnitAt(length);
|
|
if (!(c >= 'a'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0) ||
|
|
c >= 'A'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0) ||
|
|
c >= '0'.codeUnitAt(0) && c <= '9'.codeUnitAt(0) ||
|
|
c == '_'.codeUnitAt(0))) {
|
|
break;
|
|
}
|
|
length++;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/**
|
|
* An abstract base for all 'analysis' domain tests.
|
|
*/
|
|
class AbstractAnalysisTest with ResourceProviderMixin {
|
|
bool generateSummaryFiles = false;
|
|
MockServerChannel serverChannel;
|
|
TestPluginManager pluginManager;
|
|
AnalysisServer server;
|
|
RequestHandler handler;
|
|
|
|
final List<ServerErrorParams> serverErrors = <ServerErrorParams>[];
|
|
final List<GeneralAnalysisService> generalServices =
|
|
<GeneralAnalysisService>[];
|
|
final Map<AnalysisService, List<String>> analysisSubscriptions = {};
|
|
|
|
String projectPath;
|
|
String testFolder;
|
|
String testFile;
|
|
String testCode;
|
|
|
|
AbstractAnalysisTest();
|
|
|
|
AnalysisDomainHandler get analysisHandler => server.handlers
|
|
.singleWhere((handler) => handler is AnalysisDomainHandler);
|
|
|
|
AnalysisOptions get analysisOptions => testDiver.analysisOptions;
|
|
|
|
AnalysisDriver get testDiver => server.getAnalysisDriver(testFile);
|
|
|
|
void addAnalysisOptionsFile(String content) {
|
|
newFile(
|
|
resourceProvider.pathContext.join(projectPath, 'analysis_options.yaml'),
|
|
content: content);
|
|
}
|
|
|
|
void addAnalysisSubscription(AnalysisService service, String file) {
|
|
// add file to subscription
|
|
var files = analysisSubscriptions[service];
|
|
if (files == null) {
|
|
files = <String>[];
|
|
analysisSubscriptions[service] = files;
|
|
}
|
|
files.add(file);
|
|
// set subscriptions
|
|
Request request = new AnalysisSetSubscriptionsParams(analysisSubscriptions)
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
}
|
|
|
|
void addGeneralAnalysisSubscription(GeneralAnalysisService service) {
|
|
generalServices.add(service);
|
|
Request request = new AnalysisSetGeneralSubscriptionsParams(generalServices)
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
}
|
|
|
|
String addTestFile(String content) {
|
|
newFile(testFile, content: content);
|
|
this.testCode = content;
|
|
return testFile;
|
|
}
|
|
|
|
AnalysisServer createAnalysisServer() {
|
|
//
|
|
// Process plugins
|
|
//
|
|
ExtensionManager manager = new ExtensionManager();
|
|
manager.processPlugins(AnalysisEngine.instance.requiredPlugins);
|
|
//
|
|
// Create an SDK in the mock file system.
|
|
//
|
|
new MockSdk(
|
|
generateSummaryFiles: generateSummaryFiles,
|
|
resourceProvider: resourceProvider);
|
|
//
|
|
// Create server
|
|
//
|
|
AnalysisServerOptions options = new AnalysisServerOptions();
|
|
return new AnalysisServer(
|
|
serverChannel,
|
|
resourceProvider,
|
|
options,
|
|
new DartSdkManager(resourceProvider.convertPath('/sdk'), true),
|
|
InstrumentationService.NULL_SERVICE);
|
|
}
|
|
|
|
/**
|
|
* Creates a project `/project`.
|
|
*/
|
|
void createProject({Map<String, String> packageRoots}) {
|
|
newFolder(projectPath);
|
|
Request request = new AnalysisSetAnalysisRootsParams([projectPath], [],
|
|
packageRoots: packageRoots)
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request, handler: analysisHandler);
|
|
}
|
|
|
|
/**
|
|
* Returns the offset of [search] in [testCode].
|
|
* Fails if not found.
|
|
*/
|
|
int findFileOffset(String path, String search) {
|
|
File file = getFile(path);
|
|
String code = file.createSource().contents.data;
|
|
int offset = code.indexOf(search);
|
|
expect(offset, isNot(-1), reason: '"$search" in\n$code');
|
|
return offset;
|
|
}
|
|
|
|
/**
|
|
* Returns the offset of [search] in [testCode].
|
|
* Fails if not found.
|
|
*/
|
|
int findOffset(String search) {
|
|
int offset = testCode.indexOf(search);
|
|
expect(offset, isNot(-1));
|
|
return offset;
|
|
}
|
|
|
|
/**
|
|
* Validates that the given [request] is handled successfully.
|
|
*/
|
|
Response handleSuccessfulRequest(Request request, {RequestHandler handler}) {
|
|
handler ??= this.handler;
|
|
Response response = handler.handleRequest(request);
|
|
expect(response, isResponseSuccess(request.id));
|
|
return response;
|
|
}
|
|
|
|
String modifyTestFile(String content) {
|
|
modifyFile(testFile, content);
|
|
this.testCode = content;
|
|
return testFile;
|
|
}
|
|
|
|
void processNotification(Notification notification) {
|
|
if (notification.event == SERVER_NOTIFICATION_ERROR) {
|
|
var params = new ServerErrorParams.fromNotification(notification);
|
|
serverErrors.add(params);
|
|
}
|
|
}
|
|
|
|
void removeGeneralAnalysisSubscription(GeneralAnalysisService service) {
|
|
generalServices.remove(service);
|
|
Request request = new AnalysisSetGeneralSubscriptionsParams(generalServices)
|
|
.toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
}
|
|
|
|
void setPriorityFiles(List<String> files) {
|
|
var request = new AnalysisSetPriorityFilesParams(files).toRequest('0');
|
|
handleSuccessfulRequest(request);
|
|
}
|
|
|
|
void setUp() {
|
|
serverChannel = new MockServerChannel();
|
|
projectPath = convertPath('/project');
|
|
testFolder = convertPath('/project/bin');
|
|
testFile = convertPath('/project/bin/test.dart');
|
|
pluginManager = new TestPluginManager();
|
|
server = createAnalysisServer();
|
|
server.pluginManager = pluginManager;
|
|
handler = analysisHandler;
|
|
// listen for notifications
|
|
Stream<Notification> notificationStream =
|
|
serverChannel.notificationController.stream;
|
|
notificationStream.listen((Notification notification) {
|
|
processNotification(notification);
|
|
});
|
|
}
|
|
|
|
void tearDown() {
|
|
server.done();
|
|
handler = null;
|
|
server = null;
|
|
serverChannel = null;
|
|
}
|
|
|
|
/**
|
|
* Returns a [Future] that completes when the server's analysis is complete.
|
|
*/
|
|
Future waitForTasksFinished() {
|
|
return server.onAnalysisComplete;
|
|
}
|
|
|
|
/**
|
|
* Completes with a successful [Response] for the given [request].
|
|
* Otherwise fails.
|
|
*/
|
|
Future<Response> waitResponse(Request request,
|
|
{bool throwOnError = true}) async {
|
|
return serverChannel.sendRequest(request, throwOnError: throwOnError);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A plugin manager that simulates broadcasting requests to plugins by
|
|
* hard-coding the responses.
|
|
*/
|
|
class TestPluginManager implements PluginManager {
|
|
plugin.AnalysisSetPriorityFilesParams analysisSetPriorityFilesParams;
|
|
plugin.AnalysisSetSubscriptionsParams analysisSetSubscriptionsParams;
|
|
plugin.AnalysisUpdateContentParams analysisUpdateContentParams;
|
|
plugin.RequestParams broadcastedRequest;
|
|
Map<PluginInfo, Future<plugin.Response>> broadcastResults;
|
|
|
|
@override
|
|
String get byteStorePath {
|
|
fail('Unexpected invocation of byteStorePath');
|
|
}
|
|
|
|
@override
|
|
InstrumentationService get instrumentationService {
|
|
fail('Unexpected invocation of instrumentationService');
|
|
}
|
|
|
|
@override
|
|
NotificationManager get notificationManager {
|
|
fail('Unexpected invocation of notificationManager');
|
|
}
|
|
|
|
@override
|
|
List<PluginInfo> get plugins {
|
|
fail('Unexpected invocation of plugins');
|
|
}
|
|
|
|
@override
|
|
ResourceProvider get resourceProvider {
|
|
fail('Unexpected invocation of resourceProvider');
|
|
}
|
|
|
|
@override
|
|
String get sdkPath {
|
|
fail('Unexpected invocation of sdkPath');
|
|
}
|
|
|
|
@override
|
|
Future<void> addPluginToContextRoot(
|
|
analyzer.ContextRoot contextRoot, String path) async {
|
|
fail('Unexpected invocation of addPluginToContextRoot');
|
|
}
|
|
|
|
@override
|
|
Map<PluginInfo, Future<plugin.Response>> broadcastRequest(
|
|
plugin.RequestParams params,
|
|
{analyzer.ContextRoot contextRoot}) {
|
|
broadcastedRequest = params;
|
|
return broadcastResults ?? <PluginInfo, Future<plugin.Response>>{};
|
|
}
|
|
|
|
@override
|
|
Future<List<Future<plugin.Response>>> broadcastWatchEvent(
|
|
WatchEvent watchEvent) async {
|
|
return <Future<plugin.Response>>[];
|
|
}
|
|
|
|
@override
|
|
List<String> pathsFor(String pluginPath) {
|
|
fail('Unexpected invocation of pathsFor');
|
|
}
|
|
|
|
@override
|
|
List<PluginInfo> pluginsForContextRoot(analyzer.ContextRoot contextRoot) {
|
|
fail('Unexpected invocation of pluginsForContextRoot');
|
|
}
|
|
|
|
@override
|
|
void recordPluginFailure(String hostPackageName, String message) {
|
|
fail('Unexpected invocation of recordPluginFailure');
|
|
}
|
|
|
|
@override
|
|
void removedContextRoot(analyzer.ContextRoot contextRoot) {
|
|
fail('Unexpected invocation of removedContextRoot');
|
|
}
|
|
|
|
@override
|
|
Future<void> restartPlugins() async {
|
|
// Nothing to restart.
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
void setAnalysisSetPriorityFilesParams(
|
|
plugin.AnalysisSetPriorityFilesParams params) {
|
|
analysisSetPriorityFilesParams = params;
|
|
}
|
|
|
|
@override
|
|
void setAnalysisSetSubscriptionsParams(
|
|
plugin.AnalysisSetSubscriptionsParams params) {
|
|
analysisSetSubscriptionsParams = params;
|
|
}
|
|
|
|
@override
|
|
void setAnalysisUpdateContentParams(
|
|
plugin.AnalysisUpdateContentParams params) {
|
|
analysisUpdateContentParams = params;
|
|
}
|
|
|
|
@override
|
|
Future<List<void>> stopAll() async {
|
|
fail('Unexpected invocation of stopAll');
|
|
}
|
|
}
|