blob: c29a8fc37d529c061a8d4a0103ac739bd25c7d91 [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/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/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/test_code_format.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer_testing/experiments/experiments.dart';
import 'package:analyzer_testing/mock_packages/mock_packages.dart';
import 'package:analyzer_testing/resource_provider_mixin.dart';
import 'package:analyzer_testing/utilities/extensions/resource_provider.dart';
import 'package:analyzer_testing/utilities/utilities.dart';
import 'package:meta/meta.dart';
import 'package:test/test.dart';
import 'package:unified_analytics/unified_analytics.dart';
import 'constants.dart';
import 'mocks.dart';
import 'support/configuration_files.dart';
import 'utils/message_scheduler_test_view.dart';
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 {
/// The byte store that is reused between tests. This allows reusing all
/// unlinked and linked summaries for SDK, so that tests run much faster.
/// However nothing is preserved between Dart VM runs, so changes to the
/// implementation are still fully verified.
static final MemoryByteStore _sharedByteStore = MemoryByteStore();
/// The next ID to use in a request to the server.
var nextRequestId = 0;
MemoryByteStore _byteStore = _sharedByteStore;
final TestPluginManager pluginManager = TestPluginManager();
late final MockServerChannel serverChannel;
MessageSchedulerTestView? testView;
late final LegacyAnalysisServer server;
DartFixPromptManager? dartFixPromptManager;
final List<GeneralAnalysisService> _analysisGeneralServices = [];
final Map<AnalysisService, List<String>> _analysisFileSubscriptions = {};
void Function(Notification)? notificationListener;
bool get retainDataForTesting => false;
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(
'${nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
}
Future<void> setPriorityFiles2(List<File> files) async {
await handleSuccessfulRequest(
AnalysisSetPriorityFilesParams(
files.map((e) => e.path).toList(),
).toRequest(
'${nextRequestId++}',
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(
'${nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
}
@mustCallSuper
void setUp() {
serverChannel = MockServerChannel(printMessages: debugPrintCommunication);
createMockSdk(resourceProvider: resourceProvider, root: sdkRoot);
createDefaultFiles();
serverChannel.notifications.listen(processNotification);
testView = retainDataForTesting ? MessageSchedulerTestView() : null;
server = LegacyAnalysisServer(
serverChannel,
resourceProvider,
AnalysisServerOptions(),
DartSdkManager(sdkRoot.path),
AnalyticsManager(NoOpAnalytics()),
CrashReportingAttachmentsBuilder.empty,
InstrumentationService.NULL_SERVICE,
dartFixPromptManager: dartFixPromptManager,
providedByteStore: _byteStore,
pluginManager: pluginManager,
messageSchedulerListener: testView,
);
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(
'${nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
}
}
class PubPackageAnalysisServerTest extends ContextResolutionTest
with MockPackagesMixin, ConfigurationFilesMixin {
// TODO(scheglov): Consider turning it back into a getter.
late String testFilePath = resourceProvider.convertPath(
'$testPackageLibPath/test.dart',
);
late String pubspecFilePath = pathContext.normalize(
resourceProvider.convertPath('$testPackageRootPath/pubspec.yaml'),
);
late TestCode parsedTestCode;
final String testPackageName = 'test';
/// Return a list of the experiments that are to be enabled for tests in this
/// class, an empty list if there are no experiments that should be enabled.
List<String> get experiments => experimentsForTests;
/// 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/$testPackageName';
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(
'${nextRequestId++}',
clientUriConverter: server.uriConverter,
),
);
}
// TODO(scheglov): rename
void addTestFile(String content) {
parsedTestCode = TestCode.parse(content);
newFile(testFilePath, parsedTestCode.code);
}
@override
void createDefaultFiles() {
writeTestPackageConfig();
writeTestPackagePubspecYamlFile('name: $testPackageName');
writeTestPackageAnalysisOptionsFile(
analysisOptionsContent(experiments: experiments),
);
}
/// Deletes the analysis options YAML file at [testPackageRootPath].
void deleteTestPackageAnalysisOptionsFile() {
var path = join(testPackageRootPath, file_paths.analysisOptionsYaml);
deleteFile(path);
}
/// Deletes the `package_config.json` file at [testPackageRootPath].
void deleteTestPackageConfigJsonFile() {
var filePath = join(
testPackageRootPath,
file_paths.dotDartTool,
file_paths.packageConfigJson,
);
deleteFile(filePath);
}
/// 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) {
modifyFile2(testFile, 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;
}
/// Call this method if the test needs to use the empty byte store, without
/// any information cached.
void useEmptyByteStore() {
_byteStore = MemoryByteStore();
}
void writeTestPackageAnalysisOptionsFile(String content) {
newAnalysisOptionsYamlFile(testPackageRootPath, content);
}
void writeTestPackagePubspecYamlFile(String content) {
newPubspecYamlFile(testPackageRootPath, content);
}
}