OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * Code generation for the file "integration_test_methods.dart". |
| 7 */ |
| 8 import 'dart:convert'; |
| 9 |
| 10 import 'package:analyzer/src/codegen/tools.dart'; |
| 11 |
| 12 import 'api.dart'; |
| 13 import 'codegen_dart.dart'; |
| 14 import 'from_html.dart'; |
| 15 import 'to_html.dart'; |
| 16 |
| 17 final GeneratedFile target = new GeneratedFile( |
| 18 'test/integration/support/integration_test_methods.dart', (String pkgPath) { |
| 19 CodegenInttestMethodsVisitor visitor = |
| 20 new CodegenInttestMethodsVisitor(readApi(pkgPath)); |
| 21 return visitor.collectCode(visitor.visitApi); |
| 22 }); |
| 23 |
| 24 /** |
| 25 * Visitor that generates the code for integration_test_methods.dart |
| 26 */ |
| 27 class CodegenInttestMethodsVisitor extends DartCodegenVisitor |
| 28 with CodeGenerator { |
| 29 /** |
| 30 * Visitor used to produce doc comments. |
| 31 */ |
| 32 final ToHtmlVisitor toHtmlVisitor; |
| 33 |
| 34 /** |
| 35 * Code snippets concatenated to initialize all of the class fields. |
| 36 */ |
| 37 List<String> fieldInitializationCode = <String>[]; |
| 38 |
| 39 /** |
| 40 * Code snippets concatenated to produce the contents of the switch statement |
| 41 * for dispatching notifications. |
| 42 */ |
| 43 List<String> notificationSwitchContents = <String>[]; |
| 44 |
| 45 CodegenInttestMethodsVisitor(Api api) |
| 46 : toHtmlVisitor = new ToHtmlVisitor(api), |
| 47 super(api) { |
| 48 codeGeneratorSettings.commentLineLength = 79; |
| 49 codeGeneratorSettings.languageName = 'dart'; |
| 50 } |
| 51 |
| 52 /** |
| 53 * Generate a function argument for the given parameter field. |
| 54 */ |
| 55 String formatArgument(TypeObjectField field) => |
| 56 '${dartType(field.type)} ${field.name}'; |
| 57 |
| 58 /** |
| 59 * Figure out the appropriate Dart type for data having the given API |
| 60 * protocol [type]. |
| 61 */ |
| 62 String jsonType(TypeDecl type) { |
| 63 type = resolveTypeReferenceChain(type); |
| 64 if (type is TypeEnum) { |
| 65 return 'String'; |
| 66 } else if (type is TypeList) { |
| 67 return 'List<${jsonType(type.itemType)}>'; |
| 68 } else if (type is TypeMap) { |
| 69 return 'Map<String, ${jsonType(type.valueType)}>'; |
| 70 } else if (type is TypeObject) { |
| 71 return 'Map<String, dynamic>'; |
| 72 } else if (type is TypeReference) { |
| 73 switch (type.typeName) { |
| 74 case 'String': |
| 75 case 'int': |
| 76 case 'bool': |
| 77 // These types correspond exactly to Dart types |
| 78 return type.typeName; |
| 79 case 'object': |
| 80 return 'Map<String, dynamic>'; |
| 81 default: |
| 82 throw new Exception(type.typeName); |
| 83 } |
| 84 } else if (type is TypeUnion) { |
| 85 return 'Object'; |
| 86 } else { |
| 87 throw new Exception('Unexpected kind of TypeDecl'); |
| 88 } |
| 89 } |
| 90 |
| 91 @override |
| 92 visitApi() { |
| 93 outputHeader(year: '2017'); |
| 94 writeln(); |
| 95 writeln('/**'); |
| 96 writeln(' * Convenience methods for running integration tests'); |
| 97 writeln(' */'); |
| 98 writeln("import 'dart:async';"); |
| 99 writeln(); |
| 100 writeln( |
| 101 "import 'package:analyzer_plugin/protocol/generated_protocol.dart';"); |
| 102 writeln( |
| 103 "import 'package:analyzer_plugin/src/protocol/protocol_internal.dart';")
; |
| 104 writeln("import 'package:test/test.dart';"); |
| 105 writeln(); |
| 106 writeln("import 'integration_tests.dart';"); |
| 107 writeln("import 'protocol_matchers.dart';"); |
| 108 writeln(); |
| 109 writeln('/**'); |
| 110 writeln(' * Convenience methods for running integration tests'); |
| 111 writeln(' */'); |
| 112 writeln('abstract class IntegrationTestMixin {'); |
| 113 indent(() { |
| 114 writeln('Server get server;'); |
| 115 super.visitApi(); |
| 116 writeln(); |
| 117 docComment(toHtmlVisitor.collectHtml(() { |
| 118 toHtmlVisitor.writeln('Initialize the fields in InttestMixin, and'); |
| 119 toHtmlVisitor.writeln('ensure that notifications will be handled.'); |
| 120 })); |
| 121 writeln('void initializeInttestMixin() {'); |
| 122 indent(() { |
| 123 write(fieldInitializationCode.join()); |
| 124 }); |
| 125 writeln('}'); |
| 126 writeln(); |
| 127 docComment(toHtmlVisitor.collectHtml(() { |
| 128 toHtmlVisitor.writeln('Dispatch the notification named [event], and'); |
| 129 toHtmlVisitor.writeln('containing parameters [params], to the'); |
| 130 toHtmlVisitor.writeln('appropriate stream.'); |
| 131 })); |
| 132 writeln('void dispatchNotification(String event, params) {'); |
| 133 indent(() { |
| 134 writeln('ResponseDecoder decoder = new ResponseDecoder(null);'); |
| 135 writeln('switch (event) {'); |
| 136 indent(() { |
| 137 write(notificationSwitchContents.join()); |
| 138 writeln('default:'); |
| 139 indent(() { |
| 140 writeln("fail('Unexpected notification: \$event');"); |
| 141 writeln('break;'); |
| 142 }); |
| 143 }); |
| 144 writeln('}'); |
| 145 }); |
| 146 writeln('}'); |
| 147 }); |
| 148 writeln('}'); |
| 149 } |
| 150 |
| 151 @override |
| 152 visitNotification(Notification notification) { |
| 153 String streamName = |
| 154 camelJoin(['on', notification.domainName, notification.event]); |
| 155 String className = camelJoin( |
| 156 [notification.domainName, notification.event, 'params'], |
| 157 doCapitalize: true); |
| 158 writeln(); |
| 159 docComment(toHtmlVisitor.collectHtml(() { |
| 160 toHtmlVisitor.translateHtml(notification.html); |
| 161 toHtmlVisitor.describePayload(notification.params, 'Parameters'); |
| 162 })); |
| 163 writeln('Stream<$className> $streamName;'); |
| 164 writeln(); |
| 165 docComment(toHtmlVisitor.collectHtml(() { |
| 166 toHtmlVisitor.write('Stream controller for [$streamName].'); |
| 167 })); |
| 168 writeln('StreamController<$className> _$streamName;'); |
| 169 fieldInitializationCode.add(collectCode(() { |
| 170 writeln('_$streamName = new StreamController<$className>(sync: true);'); |
| 171 writeln('$streamName = _$streamName.stream.asBroadcastStream();'); |
| 172 })); |
| 173 notificationSwitchContents.add(collectCode(() { |
| 174 writeln('case ${JSON.encode(notification.longEvent)}:'); |
| 175 indent(() { |
| 176 String paramsValidator = camelJoin( |
| 177 ['is', notification.domainName, notification.event, 'params']); |
| 178 writeln('outOfTestExpect(params, $paramsValidator);'); |
| 179 String constructorCall; |
| 180 if (notification.params == null) { |
| 181 constructorCall = 'new $className()'; |
| 182 } else { |
| 183 constructorCall = |
| 184 "new $className.fromJson(decoder, 'params', params)"; |
| 185 } |
| 186 writeln('_$streamName.add($constructorCall);'); |
| 187 writeln('break;'); |
| 188 }); |
| 189 })); |
| 190 } |
| 191 |
| 192 @override |
| 193 visitRequest(Request request) { |
| 194 String methodName = camelJoin(['send', request.domainName, request.method]); |
| 195 List<String> args = <String>[]; |
| 196 List<String> optionalArgs = <String>[]; |
| 197 if (request.params != null) { |
| 198 for (TypeObjectField field in request.params.fields) { |
| 199 if (field.optional) { |
| 200 optionalArgs.add(formatArgument(field)); |
| 201 } else { |
| 202 args.add(formatArgument(field)); |
| 203 } |
| 204 } |
| 205 } |
| 206 if (optionalArgs.isNotEmpty) { |
| 207 args.add('{${optionalArgs.join(', ')}}'); |
| 208 } |
| 209 writeln(); |
| 210 docComment(toHtmlVisitor.collectHtml(() { |
| 211 toHtmlVisitor.translateHtml(request.html); |
| 212 toHtmlVisitor.describePayload(request.params, 'Parameters'); |
| 213 toHtmlVisitor.describePayload(request.result, 'Returns'); |
| 214 })); |
| 215 String resultClass; |
| 216 String futureClass; |
| 217 if (request.result == null) { |
| 218 futureClass = 'Future'; |
| 219 } else { |
| 220 resultClass = camelJoin([request.domainName, request.method, 'result'], |
| 221 doCapitalize: true); |
| 222 futureClass = 'Future<$resultClass>'; |
| 223 } |
| 224 writeln('$futureClass $methodName(${args.join(', ')}) async {'); |
| 225 indent(() { |
| 226 String requestClass = camelJoin( |
| 227 [request.domainName, request.method, 'params'], |
| 228 doCapitalize: true); |
| 229 String paramsVar = 'null'; |
| 230 if (request.params != null) { |
| 231 paramsVar = 'params'; |
| 232 List<String> args = <String>[]; |
| 233 List<String> optionalArgs = <String>[]; |
| 234 for (TypeObjectField field in request.params.fields) { |
| 235 if (field.optional) { |
| 236 optionalArgs.add('${field.name}: ${field.name}'); |
| 237 } else { |
| 238 args.add(field.name); |
| 239 } |
| 240 } |
| 241 args.addAll(optionalArgs); |
| 242 writeln('var params = new $requestClass(${args.join(', ')}).toJson();'); |
| 243 } |
| 244 String methodJson = JSON.encode(request.longMethod); |
| 245 writeln('var result = await server.send($methodJson, $paramsVar);'); |
| 246 if (request.result != null) { |
| 247 String kind = 'null'; |
| 248 if (requestClass == 'EditGetRefactoringParams') { |
| 249 kind = 'kind'; |
| 250 } |
| 251 writeln('ResponseDecoder decoder = new ResponseDecoder($kind);'); |
| 252 writeln("return new $resultClass.fromJson(decoder, 'result', result);"); |
| 253 } else { |
| 254 writeln('outOfTestExpect(result, isNull);'); |
| 255 writeln('return null;'); |
| 256 } |
| 257 }); |
| 258 writeln('}'); |
| 259 } |
| 260 } |
OLD | NEW |