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); |
- }); |
- } |
- } |
} |
/** |