Files
sdk/pkg/analysis_server/tool/spec/codegen_inttest_methods.dart
T
Danny Tuppeny 916272331b [analysis_server] Remove the singleton uriConverter and pass explicitly to all toJson/fromJson methods
This avoids accidentally using the converter when talking to plugins, (at least for now) URIs are never expected, and file paths should always be used regardless of which mode the server is in.

This unfortunately touches a _lot_ of code, so I've pushed in many separate patch sets to Gerrit.

Fixes https://github.com/Dart-Code/Dart-Code/issues/5156

Change-Id: I312c3e2cbc35a05a078aaa0138aec7288b3c7dd6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/373745
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
2024-07-02 14:15:11 +00:00

258 lines
8.7 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.
/// Code generation for the file "integration_test_methods.dart".
library;
import 'package:analyzer_utilities/tools.dart';
import 'package:path/path.dart' as path;
import 'api.dart';
import 'codegen_dart.dart';
import 'from_html.dart';
import 'to_html.dart';
final GeneratedFile target =
GeneratedFile('test/integration/support/integration_test_methods.dart',
(String pkgPath) async {
var visitor =
CodegenInttestMethodsVisitor(path.basename(pkgPath), readApi(pkgPath));
return visitor.collectCode(visitor.visitApi);
});
/// Visitor that generates the code for integration_test_methods.dart
class CodegenInttestMethodsVisitor extends DartCodegenVisitor
with CodeGenerator {
/// The name of the package into which code is being generated.
final String packageName;
/// Visitor used to produce doc comments.
final ToHtmlVisitor toHtmlVisitor;
/// Code snippets concatenated to initialize all of the class fields.
List<String> fieldInitializationCode = <String>[];
/// Code snippets concatenated to produce the contents of the switch statement
/// for dispatching notifications.
List<String> notificationSwitchContents = <String>[];
CodegenInttestMethodsVisitor(this.packageName, Api api)
: toHtmlVisitor = ToHtmlVisitor(api),
super(api) {
codeGeneratorSettings.commentLineLength = 79;
codeGeneratorSettings.docCommentStartMarker = null;
codeGeneratorSettings.docCommentLineLeader = '/// ';
codeGeneratorSettings.docCommentEndMarker = null;
codeGeneratorSettings.languageName = 'dart';
}
/// Generate a function argument for the given parameter field.
String formatArgument(TypeObjectField field) =>
'${fieldDartType(field)} ${field.name}';
/// Figure out the appropriate Dart type for data having the given API
/// protocol [type].
String jsonType(TypeDecl type) {
type = resolveTypeReferenceChain(type);
if (type is TypeEnum) {
return 'String';
} else if (type is TypeList) {
return 'List<${jsonType(type.itemType)}>';
} else if (type is TypeMap) {
return 'Map<String, ${jsonType(type.valueType)}>';
} else if (type is TypeObject) {
return 'Map<String, dynamic>';
} else if (type is TypeReference) {
switch (type.typeName) {
case 'String':
case 'int':
case 'bool':
// These types correspond exactly to Dart types
return type.typeName;
case 'object':
return 'Map<String, dynamic>';
default:
throw Exception(type.typeName);
}
} else if (type is TypeUnion) {
return 'Object';
} else {
throw Exception('Unexpected kind of TypeDecl');
}
}
@override
void visitApi() {
outputHeader(year: '2017');
writeln();
writeln('/// Convenience methods for running integration tests.');
writeln('library;');
writeln();
writeln("import 'dart:async';");
writeln();
writeln("import 'package:$packageName/protocol/protocol_generated.dart';");
writeln(
"import 'package:$packageName/src/protocol/protocol_internal.dart';");
writeln(
"import 'package:analyzer_plugin/src/utilities/client_uri_converter.dart';");
for (var uri in api.types.importUris) {
write("import '");
write(uri);
writeln("';");
}
writeln("import 'package:test/test.dart';");
writeln();
writeln("import 'integration_tests.dart';");
writeln("import 'protocol_matchers.dart';");
writeln();
writeln('/// Base implementation for running integration tests.');
writeln('abstract class IntegrationTest {');
indent(() {
writeln('Server get server;');
writeln();
writeln(
'/// The converter used to convert between URI/Paths in server communication.');
writeln('ClientUriConverter? uriConverter;');
super.visitApi();
writeln();
docComment(toHtmlVisitor.collectHtml(() {
toHtmlVisitor.writeln('Dispatch the notification named [event], and');
toHtmlVisitor.writeln('containing parameters [params], to the');
toHtmlVisitor.writeln('appropriate stream.');
}));
writeln('void dispatchNotification(String event, params) {');
indent(() {
writeln('var decoder = ResponseDecoder(null);');
writeln('switch (event) {');
indent(() {
write(notificationSwitchContents.join());
writeln('default:');
indent(() {
writeln("fail('Unexpected notification: \$event');");
});
});
writeln('}');
});
writeln('}');
});
writeln('}');
}
@override
void visitNotification(Notification notification) {
var streamName =
camelJoin(['on', notification.domainName, notification.event]);
var className = camelJoin(
[notification.domainName, notification.event, 'params'],
doCapitalize: true);
writeln();
docComment(toHtmlVisitor.collectHtml(() {
toHtmlVisitor.translateHtml(notification.html);
toHtmlVisitor.describePayload(notification.params, 'Parameters');
}));
writeln('late final Stream<$className> $streamName = '
'_$streamName.stream.asBroadcastStream();');
writeln();
docComment(toHtmlVisitor.collectHtml(() {
toHtmlVisitor.write('Stream controller for [$streamName].');
}));
writeln('final _$streamName = StreamController<$className>(sync: true);');
notificationSwitchContents.add(collectCode(() {
writeln("case '${notification.longEvent}':");
indent(() {
var paramsValidator = camelJoin(
['is', notification.domainName, notification.event, 'params']);
writeln('outOfTestExpect(params, $paramsValidator);');
String constructorCall;
if (notification.params == null) {
constructorCall = '$className()';
} else {
constructorCall =
"$className.fromJson(decoder, 'params', params, clientUriConverter: uriConverter)";
}
writeln('_$streamName.add($constructorCall);');
});
}));
}
@override
void visitRequest(Request request) {
var methodName = camelJoin(['send', request.domainName, request.method]);
var args = <String>[];
var optionalArgs = <String>[];
var params = request.params;
if (params != null) {
for (var field in params.fields) {
if (field.optional) {
optionalArgs.add(formatArgument(field));
} else {
args.add(formatArgument(field));
}
}
}
if (optionalArgs.isNotEmpty) {
args.add('{${optionalArgs.join(', ')}}');
}
writeln();
docComment(toHtmlVisitor.collectHtml(() {
toHtmlVisitor.translateHtml(request.html);
toHtmlVisitor.describePayload(params, 'Parameters');
toHtmlVisitor.describePayload(request.result, 'Returns');
}));
if (request.deprecated) {
writeln(' // TODO(srawlins): Provide a deprecation message, or remove.');
writeln(' // ignore: provide_deprecation_message');
writeln('@deprecated');
}
String? resultClass;
String futureClass;
var hasResult = request.result != null;
if (hasResult) {
resultClass = camelJoin([request.domainName, request.method, 'result'],
doCapitalize: true);
futureClass = 'Future<$resultClass>';
} else {
futureClass = 'Future<void>';
}
writeln('$futureClass $methodName(${args.join(', ')}) async {');
indent(() {
var requestClass = camelJoin(
[request.domainName, request.method, 'params'],
doCapitalize: true);
var paramsVar = 'null';
if (params != null) {
paramsVar = 'params';
var args = <String>[];
var optionalArgs = <String>[];
for (var field in params.fields) {
if (field.optional) {
optionalArgs.add('${field.name}: ${field.name}');
} else {
args.add(field.name);
}
}
args.addAll(optionalArgs);
writeln(
'var params = $requestClass(${args.join(', ')}).toJson(clientUriConverter: uriConverter);');
}
var methodJson = "'${request.longMethod}'";
writeln('var result = await server.send($methodJson, $paramsVar);');
if (resultClass != null) {
var kind = 'null';
if (requestClass == 'EditGetRefactoringParams') {
kind = 'kind';
}
writeln('var decoder = ResponseDecoder($kind);');
writeln(
"return $resultClass.fromJson(decoder, 'result', result, clientUriConverter: uriConverter);");
} else {
writeln('outOfTestExpect(result, isNull);');
}
});
writeln('}');
}
}