916272331b
This avoids accidentally using the converter when talking to plugins, (at least for now) URIs are never expected, and file paths should always be used regardless of which mode the server is in. This unfortunately touches a _lot_ of code, so I've pushed in many separate patch sets to Gerrit. Fixes https://github.com/Dart-Code/Dart-Code/issues/5156 Change-Id: I312c3e2cbc35a05a078aaa0138aec7288b3c7dd6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/373745 Reviewed-by: Samuel Rawlins <srawlins@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
318 lines
9.4 KiB
Dart
318 lines
9.4 KiB
Dart
// Copyright (c) 2022, 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/src/analytics/analytics_manager.dart';
|
|
import 'package:analysis_server/src/legacy_analysis_server.dart';
|
|
import 'package:analysis_server/src/protocol_server.dart';
|
|
import 'package:analysis_server/src/server/crash_reporting_attachments.dart';
|
|
import 'package:analysis_server/src/services/user_prompts/dart_fix_prompt_manager.dart';
|
|
import 'package:analysis_server/src/utilities/mocks.dart';
|
|
import 'package:analyzer/dart/analysis/analysis_options.dart' as analysis;
|
|
import 'package:analyzer/dart/analysis/features.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/instrumentation/service.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/src/util/file_paths.dart' as file_paths;
|
|
import 'package:analyzer_utilities/test/mock_packages/mock_packages.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:unified_analytics/unified_analytics.dart';
|
|
|
|
import 'mocks.dart';
|
|
import 'support/configuration_files.dart';
|
|
import 'test_macros.dart';
|
|
|
|
// TODO(scheglov): this is duplicate
|
|
class AnalysisOptionsFileConfig {
|
|
final List<String> experiments;
|
|
final List<String> lints;
|
|
final bool strictCasts;
|
|
final bool strictInference;
|
|
final bool strictRawTypes;
|
|
|
|
AnalysisOptionsFileConfig({
|
|
this.experiments = const [],
|
|
this.lints = const [],
|
|
this.strictCasts = false,
|
|
this.strictInference = false,
|
|
this.strictRawTypes = false,
|
|
});
|
|
|
|
String toContent() {
|
|
var buffer = StringBuffer();
|
|
|
|
buffer.writeln('analyzer:');
|
|
if (experiments.isNotEmpty) {
|
|
buffer.writeln(' enable-experiment:');
|
|
for (var experiment in experiments) {
|
|
buffer.writeln(' - $experiment');
|
|
}
|
|
}
|
|
buffer.writeln(' language:');
|
|
buffer.writeln(' strict-casts: $strictCasts');
|
|
buffer.writeln(' strict-inference: $strictInference');
|
|
buffer.writeln(' strict-raw-types: $strictRawTypes');
|
|
|
|
buffer.writeln('linter:');
|
|
buffer.writeln(' rules:');
|
|
for (var lint in lints) {
|
|
buffer.writeln(' - $lint');
|
|
}
|
|
|
|
return buffer.toString();
|
|
}
|
|
}
|
|
|
|
class BlazeWorkspaceAnalysisServerTest extends ContextResolutionTest {
|
|
String get myPackageLibPath => '$myPackageRootPath/lib';
|
|
|
|
String get myPackageRootPath => '$workspaceRootPath/dart/my';
|
|
|
|
Folder get workspaceRoot => getFolder(workspaceRootPath);
|
|
|
|
String get workspaceRootPath => '/workspace';
|
|
|
|
@override
|
|
void createDefaultFiles() {
|
|
newFile('$workspaceRootPath/${file_paths.blazeWorkspaceMarker}', '');
|
|
}
|
|
}
|
|
|
|
abstract class ContextResolutionTest with ResourceProviderMixin {
|
|
final TestPluginManager pluginManager = TestPluginManager();
|
|
late final MockServerChannel serverChannel;
|
|
late final LegacyAnalysisServer server;
|
|
|
|
DartFixPromptManager? dartFixPromptManager;
|
|
|
|
final List<GeneralAnalysisService> _analysisGeneralServices = [];
|
|
final Map<AnalysisService, List<String>> _analysisFileSubscriptions = {};
|
|
|
|
void Function(Notification)? notificationListener;
|
|
|
|
Folder get sdkRoot => newFolder('/sdk');
|
|
|
|
Future<void> addGeneralAnalysisSubscription(
|
|
GeneralAnalysisService service,
|
|
) async {
|
|
_analysisGeneralServices.add(service);
|
|
await _setGeneralAnalysisSubscriptions();
|
|
}
|
|
|
|
void assertResponseFailure(
|
|
Response response, {
|
|
required String requestId,
|
|
required RequestErrorCode errorCode,
|
|
}) {
|
|
expect(
|
|
response,
|
|
isResponseFailure(requestId, errorCode),
|
|
);
|
|
}
|
|
|
|
void createDefaultFiles() {}
|
|
|
|
Future<Response> handleRequest(Request request) async {
|
|
return await serverChannel.simulateRequestFromClient(request);
|
|
}
|
|
|
|
/// Validates that the given [request] is handled successfully.
|
|
Future<Response> handleSuccessfulRequest(Request request) async {
|
|
var response = await handleRequest(request);
|
|
expect(response, isResponseSuccess(request.id));
|
|
return response;
|
|
}
|
|
|
|
void processNotification(Notification notification) {
|
|
notificationListener?.call(notification);
|
|
}
|
|
|
|
Future<void> removeGeneralAnalysisSubscription(
|
|
GeneralAnalysisService service,
|
|
) async {
|
|
_analysisGeneralServices.remove(service);
|
|
await _setGeneralAnalysisSubscriptions();
|
|
}
|
|
|
|
void setPriorityFiles(List<File> files) {
|
|
handleSuccessfulRequest(
|
|
AnalysisSetPriorityFilesParams(
|
|
files.map((e) => e.path).toList(),
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
}
|
|
|
|
Future<void> setRoots({
|
|
required List<String> included,
|
|
required List<String> excluded,
|
|
}) async {
|
|
var includedConverted = included.map(convertPath).toList();
|
|
var excludedConverted = excluded.map(convertPath).toList();
|
|
await handleSuccessfulRequest(
|
|
AnalysisSetAnalysisRootsParams(
|
|
includedConverted,
|
|
excludedConverted,
|
|
packageRoots: {},
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
}
|
|
|
|
@mustCallSuper
|
|
void setUp() {
|
|
serverChannel = MockServerChannel();
|
|
|
|
createMockSdk(
|
|
resourceProvider: resourceProvider,
|
|
root: sdkRoot,
|
|
);
|
|
|
|
createDefaultFiles();
|
|
|
|
serverChannel.notifications.listen(processNotification);
|
|
|
|
server = LegacyAnalysisServer(
|
|
serverChannel,
|
|
resourceProvider,
|
|
AnalysisServerOptions(),
|
|
DartSdkManager(sdkRoot.path),
|
|
AnalyticsManager(NoOpAnalytics()),
|
|
CrashReportingAttachmentsBuilder.empty,
|
|
InstrumentationService.NULL_SERVICE,
|
|
dartFixPromptManager: dartFixPromptManager,
|
|
);
|
|
|
|
server.pluginManager = pluginManager;
|
|
server.completionState.budgetDuration = const Duration(seconds: 30);
|
|
}
|
|
|
|
Future<void> tearDown() async {
|
|
await server.shutdown();
|
|
}
|
|
|
|
/// Returns a [Future] that completes when the server's analysis is complete.
|
|
Future<void> waitForTasksFinished() async {
|
|
await pumpEventQueue(times: 1 << 10);
|
|
await server.onAnalysisComplete;
|
|
}
|
|
|
|
Future<void> _setGeneralAnalysisSubscriptions() async {
|
|
await handleSuccessfulRequest(
|
|
AnalysisSetGeneralSubscriptionsParams(
|
|
_analysisGeneralServices,
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
}
|
|
}
|
|
|
|
class PubPackageAnalysisServerTest extends ContextResolutionTest
|
|
with MockPackagesMixin, ConfigurationFilesMixin, TestMacros {
|
|
// TODO(scheglov): Consider turning it back into a getter.
|
|
late String testFilePath = '$testPackageLibPath/test.dart';
|
|
|
|
// If experiments are needed,
|
|
// add `import 'package:analyzer/dart/analysis/features.dart';`
|
|
// and list the necessary experiments here.
|
|
List<String> get experiments => [
|
|
Feature.macros.enableString,
|
|
];
|
|
|
|
/// The path that is not in [workspaceRootPath], contains external packages.
|
|
@override
|
|
String get packagesRootPath => resourceProvider.convertPath('/packages');
|
|
|
|
File get testFile => getFile(testFilePath);
|
|
|
|
analysis.AnalysisOptions get testFileAnalysisOptions {
|
|
var analysisDriver = server.getAnalysisDriver(testFile.path)!;
|
|
return analysisDriver.getAnalysisOptionsForFile(testFile);
|
|
}
|
|
|
|
String get testFileContent => testFile.readAsStringSync();
|
|
|
|
String get testPackageLibPath => '$testPackageRootPath/lib';
|
|
|
|
Folder get testPackageRoot => getFolder(testPackageRootPath);
|
|
|
|
@override
|
|
String get testPackageRootPath => '$workspaceRootPath/test';
|
|
|
|
String get testPackageTestPath => '$testPackageRootPath/test';
|
|
|
|
Folder get workspaceRoot => getFolder(workspaceRootPath);
|
|
|
|
String get workspaceRootPath => '/home';
|
|
|
|
Future<void> addAnalysisSubscription(
|
|
AnalysisService service,
|
|
File file,
|
|
) async {
|
|
(_analysisFileSubscriptions[service] ??= []).add(file.path);
|
|
await handleSuccessfulRequest(
|
|
AnalysisSetSubscriptionsParams(
|
|
_analysisFileSubscriptions,
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
}
|
|
|
|
// TODO(scheglov): rename
|
|
void addTestFile(String content) {
|
|
newFile(testFilePath, content);
|
|
}
|
|
|
|
@override
|
|
void createDefaultFiles() {
|
|
writeTestPackageConfig();
|
|
writeTestPackagePubspecYamlFile('name: test');
|
|
|
|
writeTestPackageAnalysisOptionsFile(
|
|
AnalysisOptionsFileConfig(
|
|
experiments: experiments,
|
|
),
|
|
);
|
|
}
|
|
|
|
void deleteTestPackageAnalysisOptionsFile() {
|
|
deleteAnalysisOptionsYamlFile(testPackageRootPath);
|
|
}
|
|
|
|
void deleteTestPackageConfigJsonFile() {
|
|
deletePackageConfigJsonFile(testPackageRootPath);
|
|
}
|
|
|
|
/// Returns the offset of [search] in [testFileContent].
|
|
/// Fails if not found.
|
|
// TODO(scheglov): Rename it.
|
|
int findOffset(String search) {
|
|
return offsetInFile(testFile, search);
|
|
}
|
|
|
|
void modifyTestFile(String content) {
|
|
modifyFile(testFilePath, content);
|
|
}
|
|
|
|
/// Returns the offset of [search] in [file].
|
|
/// Fails if not found.
|
|
int offsetInFile(File file, String search) {
|
|
var content = file.readAsStringSync();
|
|
var offset = content.indexOf(search);
|
|
expect(offset, isNot(-1));
|
|
return offset;
|
|
}
|
|
|
|
void writeTestPackageAnalysisOptionsFile(AnalysisOptionsFileConfig config) {
|
|
newAnalysisOptionsYamlFile(
|
|
testPackageRootPath,
|
|
config.toContent(),
|
|
);
|
|
}
|
|
|
|
void writeTestPackagePubspecYamlFile(String content) {
|
|
newPubspecYamlFile(testPackageRootPath, content);
|
|
}
|
|
}
|