bd700772ff
Work towards https://github.com/dart-lang/sdk/issues/55660 Change-Id: Iae062d95e5cf3b94b540496242b3627268ac2612 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428844 Commit-Queue: Samuel Rawlins <srawlins@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
2743 lines
74 KiB
Dart
2743 lines
74 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';
|
|
import 'package:analysis_server/src/analysis_server.dart';
|
|
import 'package:analysis_server/src/plugin/plugin_locator.dart';
|
|
import 'package:analyzer/file_system/file_system.dart';
|
|
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
|
|
import 'package:analyzer/src/utilities/extensions/file_system.dart';
|
|
import 'package:analyzer/utilities/package_config_file_builder.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
|
import 'package:analyzer_testing/utilities/utilities.dart';
|
|
import 'package:analyzer_utilities/testing/tree_string_sink.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:test/test.dart';
|
|
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
|
|
|
import 'analysis_server_base.dart';
|
|
import 'mocks.dart';
|
|
import 'services/completion/dart/text_expectations.dart';
|
|
|
|
void main() {
|
|
defineReflectiveSuite(() {
|
|
defineReflectiveTests(AnalysisDomainBlazeTest);
|
|
defineReflectiveTests(AnalysisDomainPubTest);
|
|
defineReflectiveTests(SetAnalysisRootsTest);
|
|
defineReflectiveTests(SetSubscriptionsTest);
|
|
defineReflectiveTests(UpdateTextExpectations);
|
|
});
|
|
}
|
|
|
|
@reflectiveTest
|
|
class AnalysisDomainBlazeTest extends _AnalysisDomainTest {
|
|
String get myPackageLibPath => '$myPackageRootPath/lib';
|
|
|
|
String get myPackageRootPath => '$workspaceRootPath/dart/my';
|
|
|
|
String get myPackageTestFilePath => '$myPackageLibPath/test.dart';
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
newFile('$workspaceRootPath/${file_paths.blazeWorkspaceMarker}', '');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_buildFile_legacy() async {
|
|
// Make it a Blaze package.
|
|
newBazelBuildFile(myPackageRootPath, r'''
|
|
# foo
|
|
''');
|
|
|
|
newFile(myPackageTestFilePath, '''
|
|
void f(int? a) {}
|
|
''');
|
|
|
|
await setRoots(included: [myPackageRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No errors after initial analysis.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/dart/my/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Change BUILD file, nothing interesting.
|
|
newBazelBuildFile(myPackageRootPath, r'''
|
|
# bar
|
|
''');
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// BUILD file change caused rebuilding analysis contexts.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/dart/my/lib/test.dart
|
|
AnalysisErrors
|
|
file: /home/dart/my/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class AnalysisDomainPubTest extends _AnalysisDomainTest {
|
|
Future<void> test_fileSystem_addFile_analysisOptions() async {
|
|
deleteTestPackageAnalysisOptionsFile();
|
|
|
|
_createFilesWithErrors([
|
|
'$testPackageLibPath/a.dart',
|
|
'$testPackageLibPath/b.dart',
|
|
]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Both a.dart and b.dart are analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Write the options file that excludes b.dart
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- lib/b.dart
|
|
''');
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Errors for all files were flushed, b.dart is not reported.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/a.dart
|
|
/home/test/lib/b.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_analysisOptions_analysis() async {
|
|
deleteTestPackageAnalysisOptionsFile();
|
|
_createFilesWithErrors(['$testPackageLibPath/a.dart']);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart was analyzed
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Add 'analysis_options.yaml' that has an error.
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
error:
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Both files were analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/a.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_androidManifestXml() async {
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
optional-checks:
|
|
chrome-os-manifest-checks: true
|
|
''');
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
newFile('$testPackageRootPath/AndroidManifest.xml', '<manifest/>');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No touch-screen.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_dart() async {
|
|
// We have to create the folder, otherwise there is nothing to watch.
|
|
newFolder(testPackageLibPath);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We don't have a.dart yet.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
''');
|
|
|
|
_createFilesWithErrors(['$testPackageLibPath/a.dart']);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We created a.dart, so it should be analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_dart_dotFolder() async {
|
|
var aPath = '$testPackageLibPath/.foo/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newFile(bPath, r'''
|
|
import '.foo/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We don't have a.dart, so the import cannot be resolved.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class A {}
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// 'a.dart' is in a dot-folder, so excluded from analysis.
|
|
// We added a.dart with `A`, so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_dart_excluded() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- "**/a.dart"
|
|
''');
|
|
|
|
newFile(bPath, r'''
|
|
import 'a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We don't have a.dart, so the import cannot be resolved.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class A {}
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We excluded 'a.dart' from analysis, no errors notification for it.
|
|
// We added a.dart with `A`, so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_fixDataFolderYaml() async {
|
|
var path = '$testPackageLibPath/fix_data/foo.yaml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No `fix_data.yaml` to analyze yet.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Create it, will be analyzed.
|
|
newFile(path, '0: 1');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// And it has errors.
|
|
// We don't recreate analysis contexts.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data/foo.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data/foo.yaml
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_fixDataYaml() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No `fix_data.yaml` to analyze yet.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Create it, will be analyzed.
|
|
newFile(path, '0: 1');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// And it has errors.
|
|
// We don't recreate analysis contexts.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_packageConfigJsonFile() async {
|
|
var aaaRootPath = '/packages/aaa';
|
|
var aPath = '$aaaRootPath/lib/a.dart';
|
|
|
|
newFile(aPath, '''
|
|
class A {}
|
|
''');
|
|
|
|
newFile(testFilePath, '''
|
|
import 'package:aaa/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We cannot resolve `package:aaa/a.dart`
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Write `package_config.json`, recreate analysis contexts.
|
|
writeTestPackageConfig(
|
|
config:
|
|
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
|
|
);
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We rebuilt analysis contexts.
|
|
// We have `A` in 'package:aaa/a.dart', so no errors.
|
|
// Errors are not reported for packages.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/test.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_pubspec_analysis() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var pubspecPath = '$testPackageRootPath/pubspec.yaml';
|
|
|
|
newFile(aPath, 'error');
|
|
|
|
// Write an empty file to force a new analysis context.
|
|
// We look for `pubspec.yaml` files only in analysis context roots.
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart was analyzed
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Add a non-Dart file that we know how to analyze.
|
|
newFile(pubspecPath, '''
|
|
name: sample
|
|
dependencies: true
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We rebuilt analysis contexts.
|
|
// All files were analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/a.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_addFile_unrelated() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var unrelatedPath = '$testPackageRootPath/unrelated.txt';
|
|
|
|
newFile(aPath, 'error');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart was analyzed
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Add an unrelated file, no analysis.
|
|
newFile(unrelatedPath, 'anything');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No analysis.
|
|
assertNotificationsText(r'''
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_analysisOptions() async {
|
|
var optionsPath = '$testPackageRootPath/analysis_options.yaml';
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
var cPath = '$testPackageLibPath/c.dart';
|
|
|
|
_createFilesWithErrors([aPath, bPath, cPath]);
|
|
|
|
// Exclude 'b.dart' from analysis.
|
|
newFile(optionsPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- lib/b.dart
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Only 'a.dart' and 'c.dart' are analyzed, because 'b.dart' is excluded.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/c.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Exclude 'c.dart' from analysis.
|
|
newFile(optionsPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- lib/c.dart
|
|
''');
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Analysis contexts were rebuilt.
|
|
// Only 'a.dart' and 'b.dart' analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/a.dart
|
|
/home/test/lib/c.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_androidManifestXml() async {
|
|
var path = '$testPackageRootPath/AndroidManifest.xml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
// Has an error - no touch screen.
|
|
newFile(path, '<manifest/>');
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
optional-checks:
|
|
chrome-os-manifest-checks: true
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Update the file, so analyze it.
|
|
newFile(path, '<manifest/>');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// An error was reported.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_dart() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newFile(aPath, r'''
|
|
class A2 {}
|
|
''');
|
|
|
|
newFile(bPath, r'''
|
|
import 'a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Update a.dart so that b.dart has no error.
|
|
newFile(aPath, 'class A {}');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The update of a.dart fixed the error in b.dart
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_dart_dotFolder() async {
|
|
var aPath = '$testPackageLibPath/.foo/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newFile(aPath, r'''
|
|
class B {}
|
|
''');
|
|
|
|
newFile(bPath, r'''
|
|
import '.foo/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// 'a.dart' is in a dot-folder, so excluded from analysis.
|
|
// We have `B`, not `A`, in a.dart, so has errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class A {}
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// 'a.dart' is in a dot-folder, so excluded from analysis.
|
|
// We changed a.dart, to have `A`, so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_dart_excluded() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- "**/a.dart"
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class B {}
|
|
''');
|
|
|
|
newFile(bPath, r'''
|
|
import 'a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We excluded 'a.dart' from analysis, no errors notification for it.
|
|
// We have `B`, not `A`, in a.dart, so has errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class A {}
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We changed a.dart, to have `A`, so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_fixDataFolderYaml() async {
|
|
var path = '$testPackageLibPath/fix_data/foo.yaml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
// This file has an error.
|
|
newFile(path, '0: 1');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The file was analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data/foo.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Replace with the content that does not have errors.
|
|
newFile(path, r'''
|
|
version: 1
|
|
transforms: []
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No errors in `foo.yaml` anymore.
|
|
// We don't recreate analysis contexts.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data/foo.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data/foo.yaml
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_fixDataYaml() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
// This file has an error.
|
|
newFile(path, '0: 1');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The file was analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Replace with the content that does not have errors.
|
|
newFile(path, r'''
|
|
version: 1
|
|
transforms: []
|
|
''');
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No errors in `fix_data.yaml` anymore.
|
|
// We don't recreate analysis contexts.
|
|
// TODO(scheglov): Why duplicate analysis?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_hasOverlay_removeOverlay() async {
|
|
newFile(testFilePath, '');
|
|
|
|
// Add an overlay without errors.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: AddContentOverlay(''),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
// The test file (overlay) is analyzed, no errors.
|
|
await _waitAnalysisComplete();
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Change the file, has errors.
|
|
newFile(testFilePath, 'error');
|
|
|
|
// But the overlay is still present, so the file is not analyzed.
|
|
await _waitAnalysisComplete();
|
|
assertNotificationsText(r'''
|
|
''');
|
|
|
|
// Remove the overlay, now the file will be read.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: RemoveContentOverlay(),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
// The file has errors.
|
|
await _waitAnalysisComplete();
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_changeFile_packageConfigJsonFile() async {
|
|
var aaaRootPath = '/packages/aaa';
|
|
var aPath = '$aaaRootPath/lib/a.dart';
|
|
|
|
newFile(aPath, '''
|
|
class A {}
|
|
''');
|
|
|
|
newFile(testFilePath, '''
|
|
import 'package:aaa/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The default `package_config.json` is without `package:aaa`.
|
|
// We cannot resolve `package:aaa/a.dart`
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Write `package_config.json`, recreate analysis contexts.
|
|
writeTestPackageConfig(
|
|
config:
|
|
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
|
|
);
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We have `A` in 'package:aaa/a.dart', so no errors.
|
|
// Errors are not reported for packages.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/test.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_analysisOptions() async {
|
|
var optionsPath = '$testPackageRootPath/analysis_options.yaml';
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
_createFilesWithErrors([aPath, bPath]);
|
|
|
|
// Exclude b.dart from analysis.
|
|
newFile(optionsPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- lib/b.dart
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Only a.dart is analyzed, because b.dart is excluded.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Delete the options file.
|
|
deleteFile(optionsPath);
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Errors for a.dart were flushed, a.dart and b.dart analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/a.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_androidManifestXml() async {
|
|
var path = '$testPackageRootPath/AndroidManifest.xml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
// Has an error - no touch screen.
|
|
newFile(path, '<manifest/>');
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
optional-checks:
|
|
chrome-os-manifest-checks: true
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// An error was reported.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Delete the file.
|
|
deleteFile(path);
|
|
await pumpEventQueue(times: 5000);
|
|
|
|
// We received a flush notification.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/AndroidManifest.xml
|
|
''');
|
|
}
|
|
|
|
/// Tests that deleting and re-creating a file while an overlay is active
|
|
/// keeps the diagnotics when the overlay is then removed, then removes them
|
|
/// when the file is deleted.
|
|
///
|
|
/// https://github.com/dart-lang/sdk/issues/53475
|
|
Future<void> test_fileSystem_deleteFile_createFile_withOverlay_dart() async {
|
|
var aPath = convertPath('$testPackageLibPath/a.dart');
|
|
|
|
_createFilesWithErrors([aPath]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Initial file has errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Set the overlay, with a different content.
|
|
// We get another notification with errors.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
aPath: AddContentOverlay('error2'),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Delete the file, we have the overlay, so no notifications.
|
|
deleteFile(aPath);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
''');
|
|
|
|
// Re-creating the file, we have the overlay, so no notifications.
|
|
_createFilesWithErrors([aPath]);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
''');
|
|
|
|
// Remove the overlay, the file has different content, so notification.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
aPath: RemoveContentOverlay(),
|
|
}).toRequest('1', clientUriConverter: server.uriConverter),
|
|
);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Delete the file, errors are now gone.
|
|
deleteFile(aPath);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_dart() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
|
|
_createFilesWithErrors([aPath]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart was analyzed
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
deleteFile(aPath);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We deleted a.dart, its errors should be flushed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/a.dart
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_dart_excluded() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, r'''
|
|
analyzer:
|
|
exclude:
|
|
- "**/a.dart"
|
|
''');
|
|
|
|
newFile(aPath, r'''
|
|
class A {}
|
|
''');
|
|
|
|
newFile(bPath, r'''
|
|
import 'a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We excluded 'a.dart' from analysis, no errors notification for it.
|
|
// We have `A` in a.dart, so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: empty
|
|
''');
|
|
|
|
deleteFile(aPath);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We deleted a.dart, so `A` cannot be resolved.
|
|
// TODO(scheglov): Should we get flush for a.dart also?
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_fixDataYaml() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
// This file has an error.
|
|
newFile(path, '0: 1');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The file was analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Delete the file.
|
|
deleteFile(path);
|
|
await pumpEventQueue(times: 5000);
|
|
|
|
// We received a flush notification.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/fix_data.yaml
|
|
''');
|
|
}
|
|
|
|
Future<void> test_fileSystem_deleteFile_packageConfigJsonFile() async {
|
|
var aaaRootPath = '/packages/aaa';
|
|
var aPath = '$aaaRootPath/lib/a.dart';
|
|
|
|
newFile(aPath, '''
|
|
class A {}
|
|
''');
|
|
|
|
// Write the empty file, without `package:aaa`.
|
|
writeTestPackageConfig(
|
|
config:
|
|
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
|
|
);
|
|
|
|
newFile(testFilePath, '''
|
|
import 'package:aaa/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We have `A` in 'package:aaa/a.dart', so no errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
|
|
// Delete `package_config.json`, recreate analysis contexts.
|
|
deleteFile('$testPackageRootPath/.dart_tool/package_config.json');
|
|
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We cannot resolve 'package:aaa/a.dart', so errors.
|
|
// Errors are not reported for packages.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/analysis_options.yaml
|
|
/home/test/lib/test.dart
|
|
/home/test/pubspec.yaml
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
/// Tests that deleting a file does not clear diagnostics if there's still
|
|
/// an active overlay for the file.
|
|
///
|
|
/// https://github.com/dart-lang/sdk/issues/53475
|
|
Future<void> test_fileSystem_deleteFile_withOverlay_dart() async {
|
|
var aPath = convertPath('$testPackageLibPath/a.dart');
|
|
|
|
_createFilesWithErrors([aPath]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// Initial file has errors.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Set overlay with different content, with errors.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
aPath: AddContentOverlay('error2'),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Delete file, has the overlay, no notifications.
|
|
deleteFile(aPath);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
''');
|
|
|
|
// After removing the overlay, errors are gone.
|
|
// TODO(scheglov): why not flush?
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
aPath: RemoveContentOverlay(),
|
|
}).toRequest('1', clientUriConverter: server.uriConverter),
|
|
);
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setPriorityFiles() async {
|
|
var a = getFile('$workspaceRootPath/foo/lib/a.dart');
|
|
var b = getFile('$workspaceRootPath/foo/lib/b.dart');
|
|
var c = getFile('$workspaceRootPath/bar/lib/c.dart');
|
|
var d = getFile('$workspaceRootPath/bar/lib/d.dart');
|
|
|
|
a.writeAsStringSync('');
|
|
b.writeAsStringSync('');
|
|
c.writeAsStringSync('');
|
|
d.writeAsStringSync('');
|
|
|
|
await handleSuccessfulRequest(
|
|
AnalysisSetPriorityFilesParams([
|
|
a.path,
|
|
c.path,
|
|
]).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart and c.dart are priority files.
|
|
// So, they are analyzed before b.dart and d.dart
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/foo/lib/a.dart
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/bar/lib/c.dart
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/foo/lib/b.dart
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/bar/lib/d.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setPriorityFiles_notAbsolute() async {
|
|
var response = await handleRequest(
|
|
AnalysisSetPriorityFilesParams([
|
|
'a.dart',
|
|
]).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setPriorityFiles_withoutRoots() async {
|
|
await handleSuccessfulRequest(
|
|
AnalysisSetPriorityFilesParams([
|
|
convertPath('$testPackageLibPath/a.dart'),
|
|
]).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setRoots_excluded_notAbsolute() async {
|
|
var response = await handleRequest(
|
|
AnalysisSetAnalysisRootsParams(
|
|
[workspaceRootPath],
|
|
['foo'],
|
|
packageRoots: {},
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setRoots_excluded_notNormalized() async {
|
|
var response = await handleRequest(
|
|
AnalysisSetAnalysisRootsParams(
|
|
[workspaceRootPath],
|
|
[convertPath('/foo/../bar')],
|
|
packageRoots: {},
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setRoots_included_notAbsolute() async {
|
|
var response = await handleRequest(
|
|
AnalysisSetAnalysisRootsParams(
|
|
['foo'],
|
|
[],
|
|
packageRoots: {},
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setRoots_included_notNormalized() async {
|
|
var response = await handleRequest(
|
|
AnalysisSetAnalysisRootsParams(
|
|
[convertPath('/foo/../bar')],
|
|
[],
|
|
packageRoots: {},
|
|
).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_FILE_PATH_FORMAT),
|
|
);
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFile() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
_createFilesWithErrors([aPath, bPath]);
|
|
|
|
await setRoots(included: [aPath], excluded: []);
|
|
|
|
// Only a.dart is included, so b.dart is not analyzed.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFile_setRoots() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
_createFilesWithErrors([aPath, bPath]);
|
|
|
|
// Include only single file.
|
|
await setRoots(included: [aPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// So, only a.dart is analyzed, and b.dart is not.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Include the folder that contains both a.dart and b.dart
|
|
await setRoots(included: [testPackageRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// So, both a.dart and b.dart are analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisFlush
|
|
/home/test/lib/a.dart
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/b.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFileFolder() async {
|
|
var includedFile = '$testPackageLibPath/a.dart';
|
|
var includedFolder = '$testPackageLibPath/foo';
|
|
var includedFolderFile1 = '$includedFolder/1.dart';
|
|
var includedFolderFile2 = '$includedFolder/2.dart';
|
|
var notIncludedFile = '$testPackageLibPath/b.dart';
|
|
|
|
_createFilesWithErrors([
|
|
includedFile,
|
|
includedFolderFile1,
|
|
includedFolderFile2,
|
|
notIncludedFile,
|
|
]);
|
|
|
|
await setRoots(included: [includedFile, includedFolder], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We can combine a file, and a folder as included paths.
|
|
// And the file that is not in there is not analyzed.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/foo/1.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/foo/2.dart
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFolder_analysisOptions_exclude() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var bPath = '$testPackageLibPath/b.dart';
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
exclude:
|
|
- "**/b.dart"
|
|
''');
|
|
|
|
_createFilesWithErrors([aPath, bPath]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
// b.dart is excluded using the options file.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
@FailingTest(reason: 'Not implemented in ContextLocator')
|
|
Future<void> test_setRoots_includedFolder_excludedFile() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var excluded_path = '$testPackageRootPath/excluded/b.dart';
|
|
|
|
_createFilesWithErrors([aPath, excluded_path]);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: [excluded_path]);
|
|
await server.onAnalysisComplete;
|
|
|
|
fail('Not implemented');
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFolder_excludedFolder() async {
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var excluded_path = '$testPackageRootPath/excluded/b.dart';
|
|
|
|
_createFilesWithErrors([aPath, excluded_path]);
|
|
|
|
await setRoots(
|
|
included: [workspaceRootPath],
|
|
excluded: ['$testPackageRootPath/excluded'],
|
|
);
|
|
await server.onAnalysisComplete;
|
|
|
|
// a.dart is analyzed, but b.dart is in the excluded folder.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_includedFolder_notExisting() async {
|
|
var existingFolder_path = '$testPackageLibPath/exiting';
|
|
var notExistingFolder_path = '$testPackageLibPath/notExisting';
|
|
var existingFile_path = '$existingFolder_path/1.dart';
|
|
|
|
_createFilesWithErrors([existingFile_path]);
|
|
|
|
await setRoots(
|
|
included: [existingFolder_path, notExistingFolder_path],
|
|
excluded: [],
|
|
);
|
|
await server.onAnalysisComplete;
|
|
|
|
// The not existing root does not prevent analysis of the existing one.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/exiting/1.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_analysisOptions_excluded() async {
|
|
deleteTestPackageAnalysisOptionsFile();
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var optionsPath = '$testPackageRootPath/analysis_options.yaml';
|
|
|
|
newFile(aPath, 'error');
|
|
|
|
// 'analysis_options.yaml' that has an error and excludes itself.
|
|
newFile(optionsPath, '''
|
|
analyzer:
|
|
exclude:
|
|
- analysis_options.yaml
|
|
error:
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_androidManifestXml() async {
|
|
var path = '$testPackageRootPath/AndroidManifest.xml';
|
|
|
|
newFile('$testPackageLibPath/a.dart', '');
|
|
|
|
newAnalysisOptionsYamlFile(testPackageRootPath, '''
|
|
analyzer:
|
|
optional-checks:
|
|
chrome-os-manifest-checks: true
|
|
''');
|
|
|
|
newFile(path, '<manifest/>');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
// No touch-screen.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/AndroidManifest.xml
|
|
errors: notEmpty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_fixDataYaml() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
|
|
// `lib/fix_data.yaml` will be analyzed.
|
|
newFile(path, '0: 1');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_fixDataYaml_empty() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
newFile(path, '');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_fixDataYaml_onlyComments() async {
|
|
var path = '$testPackageLibPath/fix_data.yaml';
|
|
newFile(path, '# one\n#two');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/fix_data.yaml
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_notDartFile_pubspec_excluded() async {
|
|
deleteTestPackageAnalysisOptionsFile();
|
|
var aPath = '$testPackageLibPath/a.dart';
|
|
var optionsPath = '$testPackageRootPath/analysis_options.yaml';
|
|
|
|
newFile(aPath, 'error');
|
|
|
|
writeTestPackagePubspecYamlFile('''
|
|
name:
|
|
- error
|
|
''');
|
|
|
|
// 'analysis_options.yaml' that excludes pubspec.yaml.
|
|
newFile(optionsPath, '''
|
|
analyzer:
|
|
exclude:
|
|
- pubspec.yaml
|
|
''');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// No pubspec.yaml
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/a.dart
|
|
errors: notEmpty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_setRoots_packageConfigJsonFile() async {
|
|
var aaaRootPath = '/packages/aaa';
|
|
var aPath = '$aaaRootPath/lib/a.dart';
|
|
|
|
newFile(aPath, '''
|
|
class A {}
|
|
''');
|
|
|
|
writeTestPackageConfig(
|
|
config:
|
|
PackageConfigFileBuilder()..add(name: 'aaa', rootPath: aaaRootPath),
|
|
);
|
|
|
|
newFile(testFilePath, '''
|
|
import 'package:aaa/a.dart';
|
|
void f(A a) {}
|
|
''');
|
|
|
|
// create project and wait for analysis
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
// We have `A` in 'package:aaa/a.dart', so no errors.
|
|
// Errors are not reported for packages.
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_updateContent_addOverlay() async {
|
|
newFile(testFilePath, 'error');
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
// The file in the file system has errors.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Add an overlay without errors.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: AddContentOverlay(''),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
// A new errors notification was received, no errors.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_updateContent_changeOverlay() async {
|
|
newFile(testFilePath, '');
|
|
|
|
// Add the content with an error.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: AddContentOverlay('var v = 0'),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
|
|
// The overlay has an error.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/pubspec.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/analysis_options.yaml
|
|
errors: empty
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: notEmpty
|
|
''');
|
|
|
|
// Add the missing `;`.
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: ChangeContentOverlay([SourceEdit(9, 0, ';')]),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
// A new errors notification was received, no errors.
|
|
await server.onAnalysisComplete;
|
|
assertNotificationsText(r'''
|
|
AnalysisErrors
|
|
file: /home/test/lib/test.dart
|
|
errors: empty
|
|
''');
|
|
}
|
|
|
|
Future<void> test_updateContent_notAbsolute() async {
|
|
var response = await handleRequest(
|
|
AnalysisUpdateContentParams({
|
|
'a.dart': AddContentOverlay(''),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
expect(response, isResponseFailure('0'));
|
|
}
|
|
|
|
Future<void> test_updateContent_outOfRange_beyondEnd() {
|
|
return _updateContent_outOfRange('012', SourceEdit(0, 5, 'foo'));
|
|
}
|
|
|
|
Future<void> test_updateContent_outOfRange_negativeLength() {
|
|
return _updateContent_outOfRange('', SourceEdit(3, -1, 'foo'));
|
|
}
|
|
|
|
Future<void> test_updateContent_outOfRange_negativeOffset() {
|
|
return _updateContent_outOfRange('', SourceEdit(-1, 3, 'foo'));
|
|
}
|
|
|
|
Future<void> _updateContent_outOfRange(
|
|
String initialContent,
|
|
SourceEdit edit,
|
|
) async {
|
|
newFile(testFilePath, initialContent);
|
|
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await server.onAnalysisComplete;
|
|
|
|
await handleSuccessfulRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: AddContentOverlay(initialContent),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
var response = await handleRequest(
|
|
AnalysisUpdateContentParams({
|
|
testFile.path: ChangeContentOverlay([edit]),
|
|
}).toRequest('0', clientUriConverter: server.uriConverter),
|
|
);
|
|
|
|
expect(
|
|
response,
|
|
isResponseFailure('0', RequestErrorCode.INVALID_OVERLAY_CHANGE),
|
|
);
|
|
}
|
|
|
|
/// Pump the event queue, so that watch events are processed.
|
|
/// Wait for analysis to complete.
|
|
/// Repeat a few times, eventually there will be no work to do.
|
|
Future<void> _waitAnalysisComplete() async {
|
|
for (var i = 0; i < 128; i++) {
|
|
await pumpEventQueue(times: 5000);
|
|
await server.onAnalysisComplete;
|
|
}
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class SetAnalysisRootsTest extends PubPackageAnalysisServerTest {
|
|
/// Verifies the set of context roots created by the server and which were
|
|
/// provided to each plugin.
|
|
void expectPluginMapping(Map<String, List<String>> expected) {
|
|
var pluginMapping = pluginManager.contextRootPlugins.map(
|
|
(root, plugins) => MapEntry(
|
|
pathContext.basename(root.root.path),
|
|
plugins.map(
|
|
(pluginPath) => pathContext.basename(
|
|
pathContext.dirname(pathContext.dirname(pluginPath)),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
// Additionally, add any context roots from the server that were not
|
|
// provided to plugins so the tests can also explicitly verify roots were
|
|
// created that didn't show up in the plugins list.
|
|
for (var serverContext in server.contextManager.analysisContexts) {
|
|
var name = pathContext.basename(serverContext.contextRoot.root.path);
|
|
pluginMapping.putIfAbsent(name, () => []);
|
|
}
|
|
|
|
expect(pluginMapping, expected);
|
|
}
|
|
|
|
@override
|
|
void setUp() {
|
|
super.setUp();
|
|
|
|
// These tests don't use the "test" package folder but have their own named
|
|
// package folders. Delete the "test" folder so it doesn't show up as a
|
|
// context root (else it would need listing in each test expectation).
|
|
deleteFolder(testPackageRootPath);
|
|
}
|
|
|
|
/// Tests a package with a nested folder that has an additional
|
|
/// `analysis_options.yaml` that does not enable the plugin. An additional
|
|
/// context should be created for the non-plugin folder and it should be
|
|
/// excluded from the parents plugin root.
|
|
Future<void>
|
|
test_sentToPlugins_inNestedPackages_withNestedAnalysisOptions_enabledPlugin_disabledPlugin() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
|
|
// package1 has plugin2 enabled.
|
|
_createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
|
|
// nestedFolder1 has no plugins enabled.
|
|
newAnalysisOptionsYamlFile(
|
|
join(workspaceRootPath, 'package1', 'nestedFolder1'),
|
|
analysisOptionsContent(experiments: experiments),
|
|
);
|
|
|
|
// Write the single package config at the root that can resolve both
|
|
// plugins.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()..add(name: 'plugin1', rootPath: plugin1.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
'nestedFolder1': [],
|
|
});
|
|
}
|
|
|
|
/// Tests a package with a nested folder that has an additional
|
|
/// `analysis_options.yaml` that enables a different plugin. An additional
|
|
/// root should be created.
|
|
Future<void>
|
|
test_sentToPlugins_inNestedPackages_withNestedAnalysisOptions_enabledPlugin_enabledDifferentPlugin() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
var plugin2 = (name: 'plugin2', path: _createPlugin('plugin2'));
|
|
|
|
// package1 has plugin2 enabled.
|
|
_createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
|
|
// nestedFolder1 has plugin2 enabled.
|
|
newAnalysisOptionsYamlFile(
|
|
join(workspaceRootPath, 'package1', 'nestedFolder1'),
|
|
analysisOptionsContent(
|
|
experiments: experiments,
|
|
legacyPlugins: [plugin2.name],
|
|
),
|
|
);
|
|
|
|
// Write the single package config at the root that can resolve both
|
|
// plugins.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()
|
|
..add(name: 'plugin1', rootPath: plugin1.path)
|
|
..add(name: 'plugin2', rootPath: plugin2.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
'nestedFolder1': ['plugin2'],
|
|
});
|
|
}
|
|
|
|
/// Tests a package with a nested folder that has an additional
|
|
/// `analysis_options.yaml` that enables the same plugin explicitly. No
|
|
/// additional context needs to be created.
|
|
Future<void>
|
|
test_sentToPlugins_inNestedPackages_withNestedAnalysisOptions_enabledPlugin_enabledPluginExplicit() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
|
|
// package1 has plugin2 enabled.
|
|
_createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
|
|
// nestedFolder1 also has plugin1 enabled.
|
|
newAnalysisOptionsYamlFile(
|
|
join(workspaceRootPath, 'package1', 'nestedFolder1'),
|
|
analysisOptionsContent(
|
|
experiments: experiments,
|
|
legacyPlugins: [plugin1.name],
|
|
),
|
|
);
|
|
|
|
// Write the single package config at the root that can resolve both
|
|
// plugins.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()..add(name: 'plugin1', rootPath: plugin1.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
// nestedFolder1 is included as part of package1.
|
|
});
|
|
}
|
|
|
|
/// Tests a package with a nested folder that has an additional
|
|
/// `analysis_options.yaml` that enables the same plugin by including the
|
|
/// parents `analysis_options.yaml`. No additional context needs to be
|
|
/// created.
|
|
Future<void>
|
|
test_sentToPlugins_inNestedPackages_withNestedAnalysisOptions_enabledPlugin_enabledPluginInclude() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
|
|
// package1 has plugin2 enabled.
|
|
_createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
|
|
// nestedFolder1 also has plugin1 enabled because it includes the parent
|
|
// `analysis_options.yaml`.
|
|
newAnalysisOptionsYamlFile(
|
|
join(workspaceRootPath, 'package1', 'nestedFolder1'),
|
|
analysisOptionsContent(includes: ['../analysis_options.yaml']),
|
|
);
|
|
|
|
// Write the single package config at the root that can resolve both
|
|
// plugins.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()..add(name: 'plugin1', rootPath: plugin1.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
// nestedFolder1 is included as part of package1.
|
|
});
|
|
}
|
|
|
|
/// Test that the correct context roots are passed to plugins when they are
|
|
/// enabled only for projects nested within the workspace (which do not have
|
|
/// their own package configs but use the one from the root).
|
|
///
|
|
/// In this example, we have three nested projects that use a mix of plugins:
|
|
///
|
|
/// - root/ (no plugins enabled)
|
|
/// - package1/ (plugin1, (plugin2 - disabled due to limit))
|
|
/// - package2/ (plugin2, (plugin1 - disabled due to limit))
|
|
/// - package3/ (plugin1)
|
|
Future<void>
|
|
test_sentToPlugins_inNestedPackages_withoutPackageConfigs() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
var plugin2 = (name: 'plugin2', path: _createPlugin('plugin2'));
|
|
|
|
// Only the first plugin for each will be enabled due to the 1-plugin-limit.
|
|
_createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1, plugin2],
|
|
);
|
|
_createTestPackage(
|
|
'package2',
|
|
withPackageConfig: false,
|
|
plugins: [plugin2, plugin1],
|
|
);
|
|
_createTestPackage(
|
|
'package3',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
|
|
// Write the single package config at the root that can resolve both
|
|
// plugins.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()
|
|
..add(name: 'plugin1', rootPath: plugin1.path)
|
|
..add(name: 'plugin2', rootPath: plugin2.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
'package2': ['plugin2'],
|
|
'package3': ['plugin1'],
|
|
});
|
|
}
|
|
|
|
/// Test that the correct context roots are passed to plugins when they are
|
|
/// enabled only for projects nested within the workspace (which have their
|
|
/// own package configs).
|
|
///
|
|
/// In this example, we have three nested projects that use a mix of plugins:
|
|
///
|
|
/// - root/ (no plugins enabled)
|
|
/// - package1/ (plugin1, (plugin2 - disabled due to limit))
|
|
/// - package2/ (plugin2, (plugin1 - disabled due to limit))
|
|
/// - package3/ (plugin1)
|
|
Future<void> test_sentToPlugins_inNestedPackages_withPackageConfigs() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
var plugin2 = (name: 'plugin2', path: _createPlugin('plugin2'));
|
|
|
|
// Only the first plugin for each will be enabled due to the 1-plugin-limit.
|
|
_createTestPackage('package1', plugins: [plugin1, plugin2]);
|
|
_createTestPackage('package2', plugins: [plugin2, plugin1]);
|
|
_createTestPackage('package3', plugins: [plugin1]);
|
|
|
|
// Ensure the root directory can resolve both plugin packages even though
|
|
// we don't enabled them for the root. This will catch if we incorrectly try
|
|
// to enable them because we read a child analysis_options.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()
|
|
..add(name: 'plugin1', rootPath: plugin1.path)
|
|
..add(name: 'plugin2', rootPath: plugin2.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains both
|
|
// packages but not the plugins (which are in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
'package2': ['plugin2'],
|
|
'package3': ['plugin1'],
|
|
});
|
|
}
|
|
|
|
/// Test that the correct context roots are passed to plugins when they are
|
|
/// enabled for projects that contain subfolders that do not contain analysis
|
|
/// options and so the existing plugins apply.
|
|
///
|
|
/// In this example, a project has plugins and it has subfolders that inherit
|
|
/// those options (and thus do not produce a new root).
|
|
///
|
|
/// - root/ (no plugins enabled)
|
|
/// - package1/ (plugin1)
|
|
/// - package1/lib/ (no explicit options, plugin1 implied)
|
|
Future<void> test_sentToPlugins_inNestedPackages_withSubFolders() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
|
|
var plugin1 = (name: 'plugin1', path: _createPlugin('plugin1'));
|
|
|
|
// Only the first plugin for each will be enabled due to the 1-plugin-limit.
|
|
var package1Path = _createTestPackage(
|
|
'package1',
|
|
withPackageConfig: false,
|
|
plugins: [plugin1],
|
|
);
|
|
newFolder(join(package1Path, 'lib'));
|
|
|
|
// Write the single package config at the root that can resolve the plugin.
|
|
newPackageConfigJsonFileFromBuilder(
|
|
workspaceRootPath,
|
|
PackageConfigFileBuilder()..add(name: 'plugin1', rootPath: plugin1.path),
|
|
);
|
|
|
|
// Set the analysis roots to the folder ('/home') that contains the
|
|
// package but not the plugin (which is in '/plugins').
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
await waitForTasksFinished();
|
|
|
|
expectPluginMapping({
|
|
'home': [],
|
|
'package1': ['plugin1'],
|
|
});
|
|
}
|
|
|
|
/// Creates a plugin package named [name] and returns the path to the root
|
|
/// of the package.
|
|
String _createPlugin(String name) {
|
|
var pluginDirectory = convertPath(join('/plugins', name));
|
|
newPubspecYamlFile(pluginDirectory, 'name: $name');
|
|
newFile(convertPath(join(pluginDirectory, 'lib', '$name.dart')), '');
|
|
newFolder(
|
|
join(
|
|
pluginDirectory,
|
|
PluginLocator.toolsFolderName,
|
|
PluginLocator.defaultPluginFolderName,
|
|
'$name.dart',
|
|
),
|
|
);
|
|
return pluginDirectory;
|
|
}
|
|
|
|
/// Creates a package in the test workspace named [name] that uses [plugins].
|
|
String _createTestPackage(
|
|
String name, {
|
|
bool withPackageConfig = true,
|
|
List<({String name, String path})> plugins = const [],
|
|
}) {
|
|
var packagePath = join(workspaceRootPath, name);
|
|
|
|
newPubspecYamlFile(packagePath, 'name: $name');
|
|
|
|
newAnalysisOptionsYamlFile(
|
|
packagePath,
|
|
analysisOptionsContent(
|
|
experiments: experiments,
|
|
legacyPlugins: plugins.map((plugin) => plugin.name).toList(),
|
|
),
|
|
);
|
|
|
|
if (withPackageConfig) {
|
|
var packageConfig =
|
|
PackageConfigFileBuilder()..add(name: name, rootPath: packagePath);
|
|
for (var plugin in plugins) {
|
|
packageConfig.add(name: plugin.name, rootPath: plugin.path);
|
|
}
|
|
newPackageConfigJsonFileFromBuilder(packagePath, packageConfig);
|
|
}
|
|
|
|
return packagePath;
|
|
}
|
|
}
|
|
|
|
@reflectiveTest
|
|
class SetSubscriptionsTest extends PubPackageAnalysisServerTest {
|
|
Map<File, List<HighlightRegion>> filesHighlights = {};
|
|
|
|
final Completer<void> _resultsAvailable = Completer();
|
|
|
|
@override
|
|
void processNotification(Notification notification) {
|
|
super.processNotification(notification);
|
|
if (notification.event == ANALYSIS_NOTIFICATION_HIGHLIGHTS) {
|
|
var params = AnalysisHighlightsParams.fromNotification(
|
|
notification,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
filesHighlights[getFile(params.file)] = params.regions;
|
|
_resultsAvailable.complete();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> setUp() async {
|
|
super.setUp();
|
|
await setRoots(included: [workspaceRootPath], excluded: []);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis() async {
|
|
addTestFile('int V = 42;');
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[testFile], isNull);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[testFile], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis_noSuchFile() async {
|
|
var file = getFile('/no-such-file.dart');
|
|
addTestFile('// no matter');
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[testFile], isNull);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[file], isEmpty);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis_packageFile_external() async {
|
|
var pkgFile = newFile('/packages/pkgA/lib/libA.dart', '''
|
|
library lib_a;
|
|
class A {}
|
|
''');
|
|
writePackageConfig(
|
|
convertPath('/project'),
|
|
config:
|
|
(PackageConfigFileBuilder()
|
|
..add(name: 'pkgA', rootPath: convertPath('/packages/pkgA'))),
|
|
);
|
|
//
|
|
addTestFile('''
|
|
import 'package:pkgA/libA.dart';
|
|
void f() {
|
|
new A();
|
|
}
|
|
''');
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[pkgFile], isNull);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, pkgFile);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[pkgFile], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis_packageFile_inRoot() async {
|
|
var pkgA = convertPath('/pkgA');
|
|
var pkgB = convertPath('/pkgA');
|
|
var pkgFileA = newFile('$pkgA/lib/libA.dart', '''
|
|
library lib_a;
|
|
class A {}
|
|
''');
|
|
newFile('$pkgA/lib/libB.dart', '''
|
|
import 'package:pkgA/libA.dart';
|
|
void f() {
|
|
new A();
|
|
}
|
|
''');
|
|
// add 'pkgA' and 'pkgB' as projects
|
|
await setRoots(included: [pkgA, pkgB], excluded: []);
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[pkgFileA], isNull);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, pkgFileA);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[pkgFileA], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis_packageFile_notUsed() async {
|
|
var pkgFile = newFile('/packages/pkgA/lib/libA.dart', '''
|
|
library lib_a;
|
|
class A {}
|
|
''');
|
|
writePackageConfig(
|
|
convertPath('/project'),
|
|
config:
|
|
(PackageConfigFileBuilder()
|
|
..add(name: 'pkgA', rootPath: convertPath('/packages/pkgA'))),
|
|
);
|
|
//
|
|
addTestFile('// no "pkgA" reference');
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[pkgFile], isNull);
|
|
// make it a priority file, so make analyzable
|
|
server.setPriorityFiles('0', [pkgFile.path]);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, pkgFile);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[pkgFile], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_afterAnalysis_sdkFile() async {
|
|
var file = getFile('/sdk/lib/core/core.dart');
|
|
addTestFile('// no matter');
|
|
// wait for analysis, no results initially
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[file], isNull);
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, file);
|
|
await _resultsAvailable.future;
|
|
// there are results
|
|
expect(filesHighlights[file], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_beforeAnalysis() async {
|
|
addTestFile('int V = 42;');
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
|
|
// wait for analysis
|
|
await waitForTasksFinished();
|
|
expect(filesHighlights[testFile], isNotEmpty);
|
|
}
|
|
|
|
Future<void> test_sentToPlugins() async {
|
|
if (!AnalysisServer.supportsPlugins) return;
|
|
addTestFile('int V = 42;');
|
|
// subscribe
|
|
await addAnalysisSubscription(AnalysisService.HIGHLIGHTS, testFile);
|
|
// wait for analysis
|
|
await waitForTasksFinished();
|
|
var params = pluginManager.analysisSetSubscriptionsParams!;
|
|
var subscriptions = params.subscriptions;
|
|
expect(subscriptions, hasLength(1));
|
|
var files = subscriptions[plugin.AnalysisService.HIGHLIGHTS];
|
|
expect(files, [testFile.path]);
|
|
}
|
|
}
|
|
|
|
class _AnalysisDomainTest extends PubPackageAnalysisServerTest {
|
|
final configuration = _NotificationPrinterConfiguration();
|
|
final List<(String, Object)> notifications = [];
|
|
|
|
void assertNotificationsText(String expected) {
|
|
var buffer = StringBuffer();
|
|
var sink = TreeStringSink(sink: buffer, indent: '');
|
|
_NotificationPrinter(
|
|
configuration: configuration,
|
|
resourceProvider: resourceProvider,
|
|
sink: sink,
|
|
).writeNotifications(notifications);
|
|
notifications.clear();
|
|
|
|
var actual = buffer.toString();
|
|
if (actual != expected) {
|
|
print('-------- Actual --------');
|
|
print('$actual------------------------');
|
|
TextExpectationsCollector.add(actual);
|
|
}
|
|
expect(actual, expected);
|
|
}
|
|
|
|
@override
|
|
void processNotification(Notification notification) {
|
|
if (notification.event == ANALYSIS_NOTIFICATION_FLUSH_RESULTS) {
|
|
var decoded = AnalysisFlushResultsParams.fromNotification(
|
|
notification,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
notifications.add((notification.event, decoded));
|
|
}
|
|
if (notification.event == ANALYSIS_NOTIFICATION_ERRORS) {
|
|
var decoded = AnalysisErrorsParams.fromNotification(
|
|
notification,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
notifications.add((notification.event, decoded));
|
|
}
|
|
}
|
|
|
|
/// Create files with a content that has a compile time error.
|
|
void _createFilesWithErrors(List<String> paths) {
|
|
for (var path in paths) {
|
|
newFile(path, 'error');
|
|
}
|
|
}
|
|
}
|
|
|
|
class _NotificationPrinter {
|
|
final _NotificationPrinterConfiguration configuration;
|
|
final ResourceProvider resourceProvider;
|
|
final TreeStringSink sink;
|
|
|
|
_NotificationPrinter({
|
|
required this.configuration,
|
|
required this.resourceProvider,
|
|
required this.sink,
|
|
});
|
|
|
|
void writeNotifications(List<(String, Object)> notifications) {
|
|
for (var entry in notifications) {
|
|
_writeNotification(entry.$1, entry.$2);
|
|
}
|
|
}
|
|
|
|
void _writelnFile(String path, {String? name}) {
|
|
sink.writeIndentedLine(() {
|
|
if (name != null) {
|
|
sink.write('$name: ');
|
|
}
|
|
var file = resourceProvider.getFile(path);
|
|
sink.write(file.posixPath);
|
|
});
|
|
}
|
|
|
|
void _writeNotification(String event, Object notification) {
|
|
switch (notification) {
|
|
case AnalysisFlushResultsParams():
|
|
var files = notification.files.sorted();
|
|
sink.writeElements('AnalysisFlush', files, _writelnFile);
|
|
case AnalysisErrorsParams():
|
|
sink.writelnWithIndent('AnalysisErrors');
|
|
sink.withIndent(() {
|
|
_writelnFile(name: 'file', notification.file);
|
|
if (configuration.withAnalysisErrorDetails) {
|
|
throw UnimplementedError();
|
|
} else if (notification.errors.isNotEmpty) {
|
|
sink.writelnWithIndent('errors: notEmpty');
|
|
} else {
|
|
sink.writelnWithIndent('errors: empty');
|
|
}
|
|
});
|
|
default:
|
|
throw UnimplementedError('${notification.runtimeType}');
|
|
}
|
|
}
|
|
}
|
|
|
|
class _NotificationPrinterConfiguration {
|
|
bool withAnalysisErrorDetails = false;
|
|
}
|