| // Copyright (c) 2024, 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_generated.dart' as server; | |
| import 'package:analysis_server/src/protocol/protocol_internal.dart'; | |
| import 'package:analysis_server/src/protocol_server.dart'; | |
| import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin_common; | |
| import 'package:analyzer_plugin/protocol/protocol_generated.dart' | |
| as analyzer_plugin; | |
| import 'package:analyzer_plugin/src/utilities/client_uri_converter.dart'; | |
| import 'package:analyzer_testing/resource_provider_mixin.dart'; | |
| import 'package:test/test.dart'; | |
| import 'package:test_reflective_loader/test_reflective_loader.dart'; | |
| void main() { | |
| defineReflectiveSuite(() { | |
| defineReflectiveTests(JsonTest); | |
| defineReflectiveTests(JsonWithConvertedFilePathsTest); | |
| }); | |
| } | |
| @reflectiveTest | |
| class JsonTest { | |
| final decoder = ResponseDecoder(null); | |
| void test_fromJson() { | |
| var json = {'offset': 0, 'length': 1, 'label': 'x'}; | |
| var label = ClosingLabel.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: null, | |
| ); | |
| expect(label.label, 'x'); | |
| expect(label.offset, 0); | |
| expect(label.length, 1); | |
| } | |
| void test_toJson() { | |
| var closingLabel = ClosingLabel(0, 1, 'x'); | |
| var json = closingLabel.toJson(clientUriConverter: null); | |
| expect(json, {'offset': 0, 'length': 1, 'label': 'x'}); | |
| } | |
| } | |
| @reflectiveTest | |
| class JsonWithConvertedFilePathsTest with ResourceProviderMixin { | |
| final decoder = ResponseDecoder(null); | |
| late ClientUriConverter clientUriConverter; | |
| void setUp() { | |
| // These tests use a dummy encoder that just prefixes the FilePaths with | |
| // "Encoded" and "Decoded" to simplify testing. The real implementation will | |
| // convert between file paths and URI strings. | |
| clientUriConverter = _PrefixingUriConverter(); | |
| } | |
| void tearDown() { | |
| // Because this is currently global, restore after the tests. | |
| clientUriConverter = ClientUriConverter.noop(pathContext); | |
| } | |
| void test_fromJson_filePath_list() { | |
| var json = { | |
| 'files': ['/my/file/1', '/my/file/2'], | |
| }; | |
| var params = AnalysisFlushResultsParams.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(params.files, ['Decoded /my/file/1', 'Decoded /my/file/2']); | |
| } | |
| void test_fromJson_filePath_map_keyValue() { | |
| var json = { | |
| 'included': [], | |
| 'excluded': [], | |
| 'packageRoots': {'/my/file/key': '/my/file/value'}, | |
| }; | |
| var params = AnalysisSetAnalysisRootsParams.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(params.packageRoots, { | |
| 'Decoded /my/file/key': 'Decoded /my/file/value', | |
| }); | |
| } | |
| void test_fromJson_filePath_map_value_list() { | |
| var json = { | |
| 'subscriptions': { | |
| 'CLOSING_LABELS': ['/my/file/value'], | |
| }, | |
| }; | |
| var params = AnalysisSetSubscriptionsParams.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(params.subscriptions, { | |
| AnalysisService.CLOSING_LABELS: ['Decoded /my/file/value'], | |
| }); | |
| } | |
| void test_fromJson_filePath_nested() { | |
| var json = { | |
| 'fixes': [ | |
| { | |
| 'error': { | |
| 'severity': 'INFO', | |
| 'type': 'LINT', | |
| 'location': { | |
| 'file': '/my/file', | |
| 'offset': 0, | |
| 'length': 1, | |
| 'startLine': 2, | |
| 'startColumn': 3, | |
| }, | |
| 'message': 'x', | |
| 'code': 'y', | |
| }, | |
| 'fixes': [], | |
| }, | |
| ], | |
| }; | |
| var result = EditGetFixesResult.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(result.fixes.single.error.location.file, 'Decoded /my/file'); | |
| } | |
| void test_fromJson_filePath_topLevel() { | |
| var json = {'file': '/my/file', 'labels': []}; | |
| var params = AnalysisClosingLabelsParams.fromJson( | |
| decoder, | |
| '', | |
| json, | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(params.file, 'Decoded /my/file'); | |
| } | |
| /// Verify that when a Path/URI encoder is set (done in [setUp]), it does not | |
| /// apply to common classes used within plugin protocol classes. | |
| void test_nestedCommonClasses_analyzerPlugin_noEncoder_fromJson_noDecoding() { | |
| var fixes = analyzer_plugin.AnalysisErrorFixes.fromJson(decoder, '', { | |
| 'error': { | |
| 'severity': 'ERROR', | |
| 'type': 'COMPILE_TIME_ERROR', | |
| 'location': { | |
| 'file': '/my/file', | |
| 'offset': 1, | |
| 'length': 2, | |
| 'startLine': 3, | |
| 'startColumn': 4, | |
| }, | |
| 'message': 'message', | |
| 'code': 'code', | |
| }, | |
| 'fixes': [], | |
| }); | |
| // Expect file was not prefixed with "Decoded". | |
| expect(fixes.error.location.file, '/my/file'); | |
| } | |
| /// Verify that when a Path/URI encoder is set (done in [setUp]), it does not | |
| /// apply to common classes used within plugin protocol classes. | |
| void test_nestedCommonClasses_analyzerPlugin_noEncoder_toJson_noEncoding() { | |
| var fixes = analyzer_plugin.AnalysisErrorFixes( | |
| plugin_common.AnalysisError( | |
| plugin_common.AnalysisErrorSeverity.ERROR, | |
| plugin_common.AnalysisErrorType.COMPILE_TIME_ERROR, | |
| plugin_common.Location('/my/file', 1, 2, 3, 4), | |
| 'message', | |
| 'code', | |
| ), | |
| ); | |
| // Expect file was not prefixed with "Encoded". | |
| expect( | |
| (fixes.toJson() as dynamic)['error']['location']['file'], | |
| '/my/file', | |
| ); | |
| } | |
| /// Verify that when a Path/URI encoder is set (done in [setUp]), it does | |
| /// apply to common classes used within server protocol classes. | |
| void test_nestedCommonClasses_server_withEncoder_fromJson_hasDecoding() { | |
| var fixes = server.AnalysisErrorFixes.fromJson(decoder, '', { | |
| 'error': { | |
| 'severity': 'ERROR', | |
| 'type': 'COMPILE_TIME_ERROR', | |
| 'location': { | |
| 'file': '/my/file', | |
| 'offset': 1, | |
| 'length': 2, | |
| 'startLine': 3, | |
| 'startColumn': 4, | |
| }, | |
| 'message': 'message', | |
| 'code': 'code', | |
| }, | |
| 'fixes': [], | |
| }, clientUriConverter: clientUriConverter); | |
| // Expect file was prefixed with "Decoded". | |
| expect(fixes.error.location.file, 'Decoded /my/file'); | |
| } | |
| /// Verify that when a Path/URI encoder is set (done in [setUp]), it does | |
| /// apply to common classes used within server protocol classes. | |
| void test_nestedCommonClasses_server_withEncoder_toJson_hasEncoding() { | |
| var fixes = server.AnalysisErrorFixes( | |
| plugin_common.AnalysisError( | |
| plugin_common.AnalysisErrorSeverity.ERROR, | |
| plugin_common.AnalysisErrorType.COMPILE_TIME_ERROR, | |
| plugin_common.Location('/my/file', 1, 2, 3, 4), | |
| 'message', | |
| 'code', | |
| ), | |
| ); | |
| // Expect file was prefixed with "Encoded". | |
| expect( | |
| (fixes.toJson(clientUriConverter: clientUriConverter) | |
| as dynamic)['error']['location']['file'], | |
| 'Encoded /my/file', | |
| ); | |
| } | |
| void test_toJson_list() { | |
| var params = AnalysisFlushResultsParams(['/my/file/1', '/my/file/2']); | |
| var json = params.toJson(clientUriConverter: clientUriConverter); | |
| expect(json, { | |
| 'files': ['Encoded /my/file/1', 'Encoded /my/file/2'], | |
| }); | |
| } | |
| void test_toJson_map_keyValue() { | |
| var params = AnalysisSetAnalysisRootsParams( | |
| [], | |
| [], | |
| packageRoots: {'/my/file/key': '/my/file/value'}, | |
| ); | |
| var json = params.toJson(clientUriConverter: clientUriConverter); | |
| expect(json, { | |
| 'included': [], | |
| 'excluded': [], | |
| 'packageRoots': {'Encoded /my/file/key': 'Encoded /my/file/value'}, | |
| }); | |
| } | |
| void test_toJson_map_value_list() { | |
| var params = AnalysisSetSubscriptionsParams({ | |
| AnalysisService.CLOSING_LABELS: ['/my/file/value'], | |
| }); | |
| var json = params.toJson(clientUriConverter: clientUriConverter); | |
| expect(json, { | |
| 'subscriptions': { | |
| 'CLOSING_LABELS': ['Encoded /my/file/value'], | |
| }, | |
| }); | |
| } | |
| void test_toJson_nested() { | |
| var result = EditGetFixesResult([ | |
| AnalysisErrorFixes( | |
| AnalysisError( | |
| AnalysisErrorSeverity.INFO, | |
| AnalysisErrorType.LINT, | |
| Location('/my/file', 0, 1, 2, 3), | |
| 'x', | |
| 'y', | |
| ), | |
| ), | |
| ]); | |
| var json = result.toJson(clientUriConverter: clientUriConverter); | |
| expect(json, { | |
| 'fixes': [ | |
| { | |
| 'error': { | |
| 'severity': 'INFO', | |
| 'type': 'LINT', | |
| 'location': { | |
| 'file': 'Encoded /my/file', | |
| 'offset': 0, | |
| 'length': 1, | |
| 'startLine': 2, | |
| 'startColumn': 3, | |
| }, | |
| 'message': 'x', | |
| 'code': 'y', | |
| }, | |
| 'fixes': [], | |
| }, | |
| ], | |
| }); | |
| } | |
| void test_toJson_topLevel() { | |
| var closingLabelParams = AnalysisClosingLabelsParams('/my/file', []); | |
| var json = closingLabelParams.toJson( | |
| clientUriConverter: clientUriConverter, | |
| ); | |
| expect(json, {'file': 'Encoded /my/file', 'labels': []}); | |
| } | |
| } | |
| /// A [ClientUriConverter] that just prefixes the input string for testing. | |
| class _PrefixingUriConverter implements ClientUriConverter { | |
| @override | |
| String fromClientFilePath(String filePathOrUri) => 'Decoded $filePathOrUri'; | |
| @override | |
| dynamic noSuchMethod(Invocation invocation) => throw UnimplementedError(); | |
| @override | |
| String toClientFilePath(String filePath) => 'Encoded $filePath'; | |
| } |