blob: 9406033f7742288768f0b5642fe1c8fcf924ceff [file] [log] [blame]
// 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/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/utilities/mocks.dart';
import 'package:analyzer/dart/analysis/analysis_options.dart' as analysis;
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/src/dart/analysis/experiments.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:meta/meta.dart';
import 'package:test/test.dart';
import 'mocks.dart';
/// TODO(scheglov) this is duplicate
class AnalysisOptionsFileConfig {
final List<String> experiments;
final bool implicitCasts;
final bool implicitDynamic;
final List<String> lints;
final bool strictCasts;
final bool strictInference;
final bool strictRawTypes;
AnalysisOptionsFileConfig({
this.experiments = const [],
this.implicitCasts = true,
this.implicitDynamic = true,
this.lints = const [],
this.strictCasts = false,
this.strictInference = false,
this.strictRawTypes = false,
});
String toContent() {
var buffer = StringBuffer();
buffer.writeln('analyzer:');
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(' strong-mode:');
buffer.writeln(' implicit-casts: $implicitCasts');
buffer.writeln(' implicit-dynamic: $implicitDynamic');
buffer.writeln('linter:');
buffer.writeln(' rules:');
for (var lint in lints) {
buffer.writeln(' - $lint');
}
return buffer.toString();
}
}
class BazelWorkspaceAnalysisServerTest 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/WORKSPACE', '');
}
}
class ContextResolutionTest with ResourceProviderMixin {
final TestPluginManager pluginManager = TestPluginManager();
late final MockServerChannel serverChannel;
late final AnalysisServer server;
final List<GeneralAnalysisService> _analysisGeneralServices = [];
final Map<AnalysisService, List<String>> _analysisFileSubscriptions = {};
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.sendRequest(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) {}
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'),
);
}
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'),
);
}
@mustCallSuper
void setUp() {
serverChannel = MockServerChannel();
createMockSdk(
resourceProvider: resourceProvider,
root: sdkRoot,
);
createDefaultFiles();
serverChannel.notifications.listen(processNotification);
server = AnalysisServer(
serverChannel,
resourceProvider,
AnalysisServerOptions(),
DartSdkManager(sdkRoot.path),
CrashReportingAttachmentsBuilder.empty,
InstrumentationService.NULL_SERVICE,
);
server.pendingFilesRemoveOverlayDelay = const Duration(milliseconds: 10);
server.pluginManager = pluginManager;
server.completionState.budgetDuration = const Duration(seconds: 30);
}
Future<void> tearDown() async {
await server.dispose();
}
/// 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'),
);
}
}
class PubPackageAnalysisServerTest extends ContextResolutionTest {
List<String> get experiments => [
EnableString.enhanced_enums,
EnableString.named_arguments_anywhere,
EnableString.super_parameters,
];
/// The path that is not in [workspaceRootPath], contains external packages.
String get packagesRootPath => '/packages';
File get testFile => getFile(testFilePath);
analysis.AnalysisOptions get testFileAnalysisOptions {
var analysisDriver = server.getAnalysisDriver(testFile.path)!;
return analysisDriver.analysisOptions;
}
String get testFileContent => testFile.readAsStringSync();
String get testFilePath => '$testPackageLibPath/test.dart';
String get testPackageLibPath => '$testPackageRootPath/lib';
Folder get testPackageRoot => getFolder(testPackageRootPath);
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'),
);
}
/// TODO(scheglov) rename
void addTestFile(String content) {
newFile(testFilePath, content);
}
@override
void createDefaultFiles() {
writeTestPackageConfig();
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 writePackageConfig(Folder root, PackageConfigFileBuilder config) {
newPackageConfigJsonFile(
root.path,
config.toContent(toUriStr: toUriStr),
);
}
void writeTestPackageAnalysisOptionsFile(AnalysisOptionsFileConfig config) {
newAnalysisOptionsYamlFile(
testPackageRootPath,
config.toContent(),
);
}
void writeTestPackageConfig({
PackageConfigFileBuilder? config,
String? languageVersion,
}) {
if (config == null) {
config = PackageConfigFileBuilder();
} else {
config = config.copy();
}
config.add(
name: 'test',
rootPath: testPackageRootPath,
languageVersion: languageVersion,
);
writePackageConfig(testPackageRoot, config);
}
void writeTestPackagePubspecYamlFile(String content) {
newPubspecYamlFile(testPackageRootPath, content);
}
}