| // Copyright (c) 2015, 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 'package:analysis_server/protocol/protocol.dart'; | |
| import 'package:analysis_server/protocol/protocol_constants.dart'; | |
| import 'package:analysis_server/protocol/protocol_generated.dart' | |
| hide AnalysisOptions; | |
| import 'package:analysis_server/src/utilities/mocks.dart'; | |
| import 'package:analyzer/file_system/file_system.dart'; | |
| import 'package:analyzer_plugin/protocol/protocol_common.dart'; | |
| import 'package:linter/src/rules.dart'; | |
| import 'package:path/path.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| import '../analysis_server_base.dart'; | |
| void main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(AnalysisOptionsFileNotificationTest); | |
| defineReflectiveTests(PluginConflictsNotificationTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class AnalysisOptionsFileNotificationTest extends PubPackageAnalysisServerTest { | |
| late File optionsFile; | |
| Map<File, List<AnalysisError>> filesErrors = {}; | |
| final testSource = ''' | |
| void f() { | |
| var x = ''; | |
| int y = x; // Not assignable. | |
| print(y); | |
| }'''; | |
| List<AnalysisError> get testFileErrors => filesErrors[testFile]!; | |
| void addOptionsFile(String contents) { | |
| optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, contents); | |
| } | |
| @override | |
| void processNotification(Notification notification) { | |
| if (notification.event == analysisNotificationErrors) { | |
| var decoded = AnalysisErrorsParams.fromNotification( | |
| notification, | |
| clientUriConverter: server.uriConverter, | |
| ); | |
| filesErrors[getFile(decoded.file)] = decoded.errors; | |
| } | |
| } | |
| @override | |
| Future<void> setUp() async { | |
| registerLintRules(); | |
| super.setUp(); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| } | |
| Future<void> test_error_filter() async { | |
| addOptionsFile(''' | |
| analyzer: | |
| errors: | |
| unused_local_variable: ignore | |
| '''); | |
| addTestFile(''' | |
| void f() { | |
| String unused = ""; | |
| } | |
| '''); | |
| await waitForTasksFinished(); | |
| // Verify options file. | |
| // TODO(brianwilkerson): Implement options file analysis in the new driver. | |
| // expect(optionsFileErrors, isNotNull); | |
| // expect(optionsFileErrors, isEmpty); | |
| // Verify test file. | |
| expect(testFileErrors, isNotNull); | |
| expect(testFileErrors, isEmpty); | |
| } | |
| Future<void> test_error_filter_removed() async { | |
| addOptionsFile(''' | |
| analyzer: | |
| errors: | |
| unused_local_variable: ignore | |
| '''); | |
| addTestFile(''' | |
| void f() { | |
| String unused = ""; | |
| } | |
| '''); | |
| await waitForTasksFinished(); | |
| // Verify options file. | |
| // TODO(brianwilkerson): Implement options file analysis in the new driver. | |
| // expect(optionsFileErrors, isNotNull); | |
| // expect(optionsFileErrors, isEmpty); | |
| // Verify test file. | |
| expect(testFileErrors, isNotNull); | |
| expect(testFileErrors, isEmpty); | |
| addOptionsFile(''' | |
| analyzer: | |
| errors: | |
| # unused_local_variable: ignore | |
| '''); | |
| await pumpEventQueue(); | |
| await waitForTasksFinished(); | |
| // Verify options file. | |
| // TODO(brianwilkerson): Implement options file analysis in the new driver. | |
| // expect(optionsFileErrors, isEmpty); | |
| // Verify test file. | |
| expect(testFileErrors, hasLength(1)); | |
| } | |
| Future<void> test_lint_options_changes() async { | |
| addOptionsFile(''' | |
| linter: | |
| rules: | |
| - camel_case_types | |
| - constant_identifier_names | |
| '''); | |
| addTestFile(testSource); | |
| await waitForTasksFinished(); | |
| verifyLintsEnabled(['camel_case_types', 'constant_identifier_names']); | |
| addOptionsFile(''' | |
| linter: | |
| rules: | |
| - camel_case_types | |
| '''); | |
| await pumpEventQueue(); | |
| await waitForTasksFinished(); | |
| verifyLintsEnabled(['camel_case_types']); | |
| } | |
| Future<void> test_lint_options_unsupported() async { | |
| addOptionsFile(''' | |
| linter: | |
| rules: | |
| - unsupported | |
| '''); | |
| addTestFile(testSource); | |
| await waitForTasksFinished(); | |
| // TODO(brianwilkerson): Implement options file analysis in the new driver. | |
| // expect(optionsFileErrors, hasLength(1)); | |
| // expect(optionsFileErrors.first.severity, AnalysisErrorSeverity.WARNING); | |
| // expect(optionsFileErrors.first.type, AnalysisErrorType.STATIC_WARNING); | |
| } | |
| Future<void> test_options_file_added() async { | |
| addTestFile(testSource); | |
| await waitForTasksFinished(); | |
| // Verify that lints are disabled. | |
| expect(testFileAnalysisOptions.lint, false); | |
| // Add options file with a lint enabled. | |
| addOptionsFile(''' | |
| linter: | |
| rules: | |
| - camel_case_types | |
| '''); | |
| await pumpEventQueue(); | |
| await waitForTasksFinished(); | |
| verifyLintsEnabled(['camel_case_types']); | |
| } | |
| Future<void> test_options_file_parse_error() async { | |
| addOptionsFile(''' | |
| ; #bang | |
| '''); | |
| await waitForTasksFinished(); | |
| // TODO(brianwilkerson): Implement options file analysis in the new driver. | |
| // expect(optionsFileErrors, hasLength(1)); | |
| // expect(optionsFileErrors.first.severity, AnalysisErrorSeverity.ERROR); | |
| // expect(optionsFileErrors.first.type, AnalysisErrorType.COMPILE_TIME_ERROR); | |
| } | |
| Future<void> test_options_file_removed() async { | |
| addOptionsFile(''' | |
| linter: | |
| rules: | |
| - camel_case_types | |
| '''); | |
| addTestFile(testSource); | |
| await waitForTasksFinished(); | |
| verifyLintsEnabled(['camel_case_types']); | |
| deleteFile(optionsFile.path); | |
| await pumpEventQueue(); | |
| await waitForTasksFinished(); | |
| expect(testFileAnalysisOptions.lint, false); | |
| } | |
| void verifyLintsEnabled(List<String> lints) { | |
| var options = testFileAnalysisOptions; | |
| expect(options.lint, true); | |
| var rules = options.lintRules.map((rule) => rule.name); | |
| expect(rules, unorderedEquals(lints)); | |
| } | |
| } | |
| @reflectiveTest | |
| class PluginConflictsNotificationTest extends PubPackageAnalysisServerTest { | |
| @override | |
| late final TestPluginManager pluginManager = TestPluginManager( | |
| resourceProvider, | |
| ); | |
| Map<File, List<AnalysisError>?> filesErrors = {}; | |
| @override | |
| void processNotification(Notification notification) { | |
| if (notification.event == analysisNotificationErrors) { | |
| var decoded = AnalysisErrorsParams.fromNotification( | |
| notification, | |
| clientUriConverter: server.uriConverter, | |
| ); | |
| filesErrors[getFile(decoded.file)] = decoded.errors; | |
| } | |
| } | |
| @override | |
| void setUp() { | |
| registerLintRules(); | |
| super.setUp(); | |
| } | |
| Future<void> test_plugin_conflict_different_sources() async { | |
| var optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| var nestedOptionsFile = newAnalysisOptionsYamlFile( | |
| join(testPackageRootPath, 'sub'), | |
| ''' | |
| plugins: | |
| foo: | |
| path: ../foo | |
| ''', | |
| ); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| await waitForTasksFinished(); | |
| await pumpEventQueue(); | |
| var rootErrors = filesErrors[optionsFile]!; | |
| expect(rootErrors, hasLength(1)); | |
| expect(rootErrors[0].code, 'incompatible_plugin_source'); | |
| // We only report one file in the conflict, not the second. | |
| var nestedErrors = filesErrors[nestedOptionsFile]!; | |
| expect(nestedErrors, isEmpty); | |
| } | |
| Future<void> test_plugin_conflict_different_versions() async { | |
| var optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| var nestedOptionsFile = newAnalysisOptionsYamlFile( | |
| join(testPackageRootPath, 'sub'), | |
| ''' | |
| plugins: | |
| foo: ^2.0.0 | |
| ''', | |
| ); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| await waitForTasksFinished(); | |
| await pumpEventQueue(); | |
| var rootErrors = filesErrors[optionsFile]!; | |
| expect(rootErrors, hasLength(1)); | |
| expect(rootErrors[0].code, 'incompatible_plugin_source'); | |
| // We only report one file in the conflict, not the second. | |
| var nestedErrors = filesErrors[nestedOptionsFile]!; | |
| expect(nestedErrors, isEmpty); | |
| } | |
| Future<void> test_plugin_conflict_resolved_on_update() async { | |
| var optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| var nestedOptionsFile = newAnalysisOptionsYamlFile( | |
| join(testPackageRootPath, 'sub'), | |
| ''' | |
| plugins: | |
| foo: ^2.0.0 | |
| ''', | |
| ); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| await waitForTasksFinished(); | |
| await pumpEventQueue(); | |
| expect(filesErrors[optionsFile]!, hasLength(1)); | |
| // We only report one file in the conflict, not the second. | |
| expect(filesErrors[nestedOptionsFile]!, isEmpty); | |
| newAnalysisOptionsYamlFile(join(testPackageRootPath, 'sub'), ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| await pumpEventQueue(); | |
| await waitForTasksFinished(); | |
| expect(filesErrors[optionsFile]!, isEmpty); | |
| expect(filesErrors[nestedOptionsFile]!, isEmpty); | |
| } | |
| Future<void> test_plugin_no_conflict_different_plugins() async { | |
| var optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| var nestedOptionsFile = newAnalysisOptionsYamlFile( | |
| join(testPackageRootPath, 'sub'), | |
| ''' | |
| plugins: | |
| bar: ^1.0.0 | |
| ''', | |
| ); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| await waitForTasksFinished(); | |
| await pumpEventQueue(); | |
| var rootErrors = filesErrors[optionsFile]!; | |
| expect(rootErrors, isEmpty); | |
| var nestedErrors = filesErrors[nestedOptionsFile]!; | |
| expect(nestedErrors, isEmpty); | |
| } | |
| Future<void> test_plugin_no_conflict_identical_sources() async { | |
| var optionsFile = newAnalysisOptionsYamlFile(testPackageRootPath, ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| '''); | |
| var nestedOptionsFile = newAnalysisOptionsYamlFile( | |
| join(testPackageRootPath, 'sub'), | |
| ''' | |
| plugins: | |
| foo: ^1.0.0 | |
| ''', | |
| ); | |
| await setRoots(included: [workspaceRootPath], excluded: []); | |
| await waitForTasksFinished(); | |
| await pumpEventQueue(); | |
| var rootErrors = filesErrors[optionsFile]!; | |
| expect(rootErrors, isEmpty); | |
| var nestedErrors = filesErrors[nestedOptionsFile]!; | |
| expect(nestedErrors, isEmpty); | |
| } | |
| } |