| Index: pkg/analysis_server/test/performance/input_converter.dart
|
| diff --git a/pkg/analysis_server/test/performance/input_converter.dart b/pkg/analysis_server/test/performance/input_converter.dart
|
| index 9acf61a71c56560912b7f93a1ee5e902b97032bf..ccf598e9d2a5552f47594c70aeb147027c00e451 100644
|
| --- a/pkg/analysis_server/test/performance/input_converter.dart
|
| +++ b/pkg/analysis_server/test/performance/input_converter.dart
|
| @@ -4,12 +4,12 @@
|
|
|
| library input.transformer;
|
|
|
| +import 'dart:async';
|
| import 'dart:convert';
|
| import 'dart:io';
|
|
|
| import 'package:analysis_server/src/constants.dart';
|
| import 'package:analysis_server/src/protocol.dart';
|
| -import 'package:analyzer/src/generated/java_engine.dart';
|
| import 'package:logging/logging.dart';
|
| import 'package:path/path.dart' as path;
|
|
|
| @@ -26,16 +26,31 @@ abstract class CommonInputConverter extends Converter<String, Operation> {
|
| final Set<String> eventsSeen = new Set<String>();
|
|
|
| /**
|
| - * A mapping from request/response id to expected error message.
|
| + * A mapping from request/response id to request json
|
| + * for those requests for which a response has not been processed.
|
| */
|
| - final Map<String, dynamic> expectedErrors = new Map<String, dynamic>();
|
| + final Map<String, dynamic> requestMap = {};
|
| +
|
| + /**
|
| + * A mapping from request/response id to a completer
|
| + * for those requests for which a response has not been processed.
|
| + * The completer is called with the actual json response
|
| + * when it becomes available.
|
| + */
|
| + final Map<String, Completer> responseCompleters = {};
|
| +
|
| + /**
|
| + * A mapping from request/response id to the actual response result
|
| + * for those responses that have not been processed.
|
| + */
|
| + final Map<String, dynamic> responseMap = {};
|
|
|
| /**
|
| * A mapping of current overlay content
|
| * parallel to what is in the analysis server
|
| * so that we can update the file system.
|
| */
|
| - final Map<String, String> overlays = new Map<String, String>();
|
| + final Map<String, String> overlays = {};
|
|
|
| /**
|
| * The prefix used to determine if a request parameter is a file path.
|
| @@ -87,54 +102,54 @@ abstract class CommonInputConverter extends Converter<String, Operation> {
|
| */
|
| Operation convertRequest(Map<String, dynamic> origJson) {
|
| Map<String, dynamic> json = translateSrcPaths(origJson);
|
| + requestMap[json['id']] = json;
|
| String method = json['method'];
|
| // Sanity check operations that modify source
|
| // to ensure that the operation is on source in temp space
|
| if (method == ANALYSIS_UPDATE_CONTENT) {
|
| - try {
|
| - validateSrcPaths(json);
|
| - } catch (e, s) {
|
| - throw new AnalysisException('invalid src path in update request\n$json',
|
| - new CaughtException(e, s));
|
| - }
|
| // Track overlays in parallel with the analysis server
|
| // so that when an overlay is removed, the file can be updated on disk
|
| Request request = new Request.fromJson(json);
|
| var params = new AnalysisUpdateContentParams.fromRequest(request);
|
| - params.files.forEach((String path, change) {
|
| + params.files.forEach((String filePath, change) {
|
| if (change is AddContentOverlay) {
|
| String content = change.content;
|
| if (content == null) {
|
| throw 'expected new overlay content\n$json';
|
| }
|
| - overlays[path] = content;
|
| + overlays[filePath] = content;
|
| } else if (change is ChangeContentOverlay) {
|
| - String content = overlays[path];
|
| + String content = overlays[filePath];
|
| if (content == null) {
|
| throw 'expected cached overlay content\n$json';
|
| }
|
| - overlays[path] = SourceEdit.applySequence(content, change.edits);
|
| + overlays[filePath] = SourceEdit.applySequence(content, change.edits);
|
| } else if (change is RemoveContentOverlay) {
|
| - String content = overlays.remove(path);
|
| + String content = overlays.remove(filePath);
|
| if (content == null) {
|
| throw 'expected cached overlay content\n$json';
|
| }
|
| - validateSrcPaths(path);
|
| - new File(path).writeAsStringSync(content);
|
| + if (!path.isWithin(tmpSrcDirPath, filePath)) {
|
| + throw 'found path referencing source outside temp space\n$filePath\n$json';
|
| + }
|
| + new File(filePath).writeAsStringSync(content);
|
| } else {
|
| throw 'unknown overlay change $change\n$json';
|
| }
|
| });
|
| return new RequestOperation(this, json);
|
| }
|
| - // TODO(danrubel) replace this with code
|
| + // Track performance for completion notifications
|
| + if (method == COMPLETION_GET_SUGGESTIONS) {
|
| + return new CompletionRequestOperation(this, json);
|
| + }
|
| + // TODO(danrubel) replace this with code
|
| // that just forwards the translated request
|
| if (method == ANALYSIS_GET_HOVER ||
|
| method == ANALYSIS_SET_ANALYSIS_ROOTS ||
|
| method == ANALYSIS_SET_PRIORITY_FILES ||
|
| method == ANALYSIS_SET_SUBSCRIPTIONS ||
|
| method == ANALYSIS_UPDATE_OPTIONS ||
|
| - method == COMPLETION_GET_SUGGESTIONS ||
|
| method == EDIT_GET_ASSISTS ||
|
| method == EDIT_GET_AVAILABLE_REFACTORINGS ||
|
| method == EDIT_GET_FIXES ||
|
| @@ -145,6 +160,7 @@ abstract class CommonInputConverter extends Converter<String, Operation> {
|
| method == EXECUTION_MAP_URI ||
|
| method == EXECUTION_SET_SUBSCRIPTIONS ||
|
| method == SEARCH_FIND_ELEMENT_REFERENCES ||
|
| + method == SEARCH_FIND_MEMBER_DECLARATIONS ||
|
| method == SERVER_GET_VERSION ||
|
| method == SERVER_SET_SUBSCRIPTIONS) {
|
| return new RequestOperation(this, json);
|
| @@ -153,49 +169,63 @@ abstract class CommonInputConverter extends Converter<String, Operation> {
|
| }
|
|
|
| /**
|
| - * Determine if the given request is expected to fail
|
| - * and log an exception if not.
|
| + * Return an operation for the recorded/expected response.
|
| */
|
| - void recordErrorResponse(Map<String, dynamic> jsonRequest, exception) {
|
| - var actualErr;
|
| + Operation convertResponse(Map<String, dynamic> json) {
|
| + return new ResponseOperation(
|
| + this, requestMap.remove(json['id']), translateSrcPaths(json));
|
| + }
|
| +
|
| + /**
|
| + * Process an error response from the server by either
|
| + * completing the associated completer in the [responseCompleters]
|
| + * or stashing it in [responseMap] if no completer exists.
|
| + */
|
| + void processErrorResponse(String id, exception) {
|
| + var result = exception;
|
| if (exception is UnimplementedError) {
|
| if (exception.message.startsWith(ERROR_PREFIX)) {
|
| - Map<String, dynamic> jsonResponse =
|
| - JSON.decode(exception.message.substring(ERROR_PREFIX.length));
|
| - actualErr = jsonResponse['error'];
|
| + result = JSON.decode(exception.message.substring(ERROR_PREFIX.length));
|
| }
|
| }
|
| - String id = jsonRequest['id'];
|
| - if (id != null && actualErr != null) {
|
| - var expectedErr = expectedErrors[id];
|
| - if (expectedErr != null && actualErr == expectedErr) {
|
| - return;
|
| - }
|
| -// if (jsonRequest['method'] == EDIT_SORT_MEMBERS) {
|
| -// var params = jsonRequest['params'];
|
| -// if (params is Map) {
|
| -// var filePath = params['file'];
|
| -// if (filePath is String) {
|
| -// var content = overlays[filePath];
|
| -// if (content is String) {
|
| -// logger.log(Level.WARNING, 'sort failed: $filePath\n$content');
|
| -// }
|
| -// }
|
| -// }
|
| -// }
|
| + processResponseResult(id, result);
|
| + }
|
| +
|
| + /**
|
| + * Process the expected response by completing the given completer
|
| + * with the result if it has alredy been received,
|
| + * or caching the completer to be completed when the server
|
| + * returns the associated result.
|
| + * Return a future that completes when the response is received
|
| + * or `null` if the response has already been received
|
| + * and the completer completed.
|
| + */
|
| + Future processExpectedResponse(String id, Completer completer) {
|
| + if (responseMap.containsKey(id)) {
|
| + logger.log(Level.INFO, 'processing cached response $id');
|
| + completer.complete(responseMap.remove(id));
|
| + return null;
|
| + } else {
|
| + logger.log(Level.INFO, 'waiting for response $id');
|
| + responseCompleters[id] = completer;
|
| + return completer.future;
|
| }
|
| - logger.log(
|
| - Level.SEVERE, 'Send request failed for $id\n$exception\n$jsonRequest');
|
| }
|
|
|
| /**
|
| - * Examine recorded responses and record any expected errors.
|
| + * Process a success response result from the server by either
|
| + * completing the associated completer in the [responseCompleters]
|
| + * or stashing it in [responseMap] if no completer exists.
|
| + * The response result may be `null`.
|
| */
|
| - void recordResponse(Map<String, dynamic> json) {
|
| - var error = json['error'];
|
| - if (error != null) {
|
| - String id = json['id'];
|
| - print('expected error for $id is $error');
|
| + void processResponseResult(String id, result) {
|
| + Completer completer = responseCompleters[id];
|
| + if (completer != null) {
|
| + logger.log(Level.INFO, 'processing response $id');
|
| + completer.complete(result);
|
| + } else {
|
| + logger.log(Level.INFO, 'caching response $id');
|
| + responseMap[id] = result;
|
| }
|
| }
|
|
|
| @@ -230,29 +260,6 @@ abstract class CommonInputConverter extends Converter<String, Operation> {
|
| }
|
| return json;
|
| }
|
| -
|
| - /**
|
| - * Recursively verify that the source paths in the specified JSON
|
| - * only reference the temporary source used during performance measurement.
|
| - */
|
| - void validateSrcPaths(json) {
|
| - if (json is String) {
|
| - if (json != null &&
|
| - path.isWithin(rootPrefix, json) &&
|
| - !path.isWithin(tmpSrcDirPath, json)) {
|
| - throw 'found path referencing source outside temp space\n$json';
|
| - }
|
| - } else if (json is List) {
|
| - for (int i = json.length - 1; i >= 0; --i) {
|
| - validateSrcPaths(json[i]);
|
| - }
|
| - } else if (json is Map) {
|
| - json.forEach((String key, value) {
|
| - validateSrcPaths(key);
|
| - validateSrcPaths(value);
|
| - });
|
| - }
|
| - }
|
| }
|
|
|
| /**
|
|
|