afcfbbeba8
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the packages owned by the developer experience team to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) bumping the packages' SDK constraints to `3.13.0-0`, (b) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint failures using `dart fix`, and then (d) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Since this change requires bumping SDK constaints to `3.13.0-0`, it was only performed on packages that are *not* published on pub. (Packages that *are* published on pub should remain on lower language versions until at least after the stable version of 3.13 is released, so that we don't block users on the stable channel from receiving updates to those packages.) Change-Id: Ibb4daebafd239da58251e838ea6a3f336a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/505046 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com>
2732 lines
74 KiB
Dart
2732 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/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_plugin/protocol/protocol_common.dart';
|
|
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
|
import 'package:analyzer_testing/package_config_file_builder.dart';
|
|
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', rootFolder: getFolder(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', rootFolder: getFolder(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', rootFolder: getFolder(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', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(plugin1.path))
|
|
..add(name: 'plugin2', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(plugin1.path))
|
|
..add(name: 'plugin2', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(plugin1.path))
|
|
..add(name: 'plugin2', rootFolder: getFolder(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 {
|
|
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', rootFolder: getFolder(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, rootFolder: getFolder(packagePath));
|
|
for (var plugin in plugins) {
|
|
packageConfig.add(
|
|
name: plugin.name,
|
|
rootFolder: getFolder(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 == analysisNotificationHighlights) {
|
|
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', rootFolder: getFolder('/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', rootFolder: getFolder('/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 {
|
|
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 == analysisNotificationFlushResults) {
|
|
var decoded = AnalysisFlushResultsParams.fromNotification(
|
|
notification,
|
|
clientUriConverter: server.uriConverter,
|
|
);
|
|
notifications.add((notification.event, decoded));
|
|
}
|
|
if (notification.event == analysisNotificationErrors) {
|
|
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;
|
|
|
|
new({
|
|
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;
|
|
}
|