OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library server.operation; | 5 library server.operation; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'package:analysis_server/src/protocol.dart'; | 9 import 'package:analysis_server/src/protocol.dart'; |
10 import 'package:logging/logging.dart'; | 10 import 'package:logging/logging.dart'; |
11 | 11 |
12 import 'driver.dart'; | 12 import 'driver.dart'; |
13 import 'input_converter.dart'; | 13 import 'input_converter.dart'; |
14 | 14 |
15 /** | 15 /** |
| 16 * A [CompletionRequestOperation] tracks response time along with |
| 17 * the first and last completion notifications. |
| 18 */ |
| 19 class CompletionRequestOperation extends RequestOperation { |
| 20 Driver driver; |
| 21 StreamSubscription<CompletionResultsParams> subscription; |
| 22 String notificationId; |
| 23 Stopwatch stopwatch; |
| 24 bool firstNotification = true; |
| 25 |
| 26 CompletionRequestOperation( |
| 27 CommonInputConverter converter, Map<String, dynamic> json) |
| 28 : super(converter, json); |
| 29 |
| 30 @override |
| 31 Future perform(Driver driver) { |
| 32 this.driver = driver; |
| 33 subscription = driver.onCompletionResults.listen(processNotification); |
| 34 return super.perform(driver); |
| 35 } |
| 36 |
| 37 void processNotification(CompletionResultsParams event) { |
| 38 if (event.id == notificationId) { |
| 39 Duration elapsed = stopwatch.elapsed; |
| 40 if (firstNotification) { |
| 41 firstNotification = false; |
| 42 driver.results.record('completion notification first', elapsed, |
| 43 notification: true); |
| 44 } |
| 45 if (event.isLast) { |
| 46 subscription.cancel(); |
| 47 driver.results.record('completion notification last', elapsed, |
| 48 notification: true); |
| 49 } |
| 50 } |
| 51 } |
| 52 |
| 53 @override |
| 54 void processResult( |
| 55 String id, Map<String, dynamic> result, Stopwatch stopwatch) { |
| 56 notificationId = result['id']; |
| 57 this.stopwatch = stopwatch; |
| 58 super.processResult(id, result, stopwatch); |
| 59 } |
| 60 } |
| 61 |
| 62 /** |
16 * An [Operation] represents an action such as sending a request to the server. | 63 * An [Operation] represents an action such as sending a request to the server. |
17 */ | 64 */ |
18 abstract class Operation { | 65 abstract class Operation { |
19 Future perform(Driver driver); | 66 Future perform(Driver driver); |
20 } | 67 } |
21 | 68 |
22 /** | 69 /** |
23 * A [RequestOperation] sends a [JSON] request to the server. | 70 * A [RequestOperation] sends a [JSON] request to the server. |
24 */ | 71 */ |
25 class RequestOperation extends Operation { | 72 class RequestOperation extends Operation { |
26 final CommonInputConverter converter; | 73 final CommonInputConverter converter; |
27 final Map<String, dynamic> json; | 74 final Map<String, dynamic> json; |
28 | 75 |
29 RequestOperation(this.converter, this.json); | 76 RequestOperation(this.converter, this.json); |
30 | 77 |
31 @override | 78 @override |
32 Future perform(Driver driver) { | 79 Future perform(Driver driver) { |
33 Stopwatch stopwatch = new Stopwatch(); | 80 Stopwatch stopwatch = new Stopwatch(); |
| 81 String originalId = json['id']; |
34 String method = json['method']; | 82 String method = json['method']; |
35 driver.logger.log(Level.FINE, 'Sending request: $method\n $json'); | 83 driver.logger.log(Level.FINE, 'Sending request: $method\n $json'); |
36 stopwatch.start(); | 84 stopwatch.start(); |
37 void recordResponse(bool success, response) { | 85 |
38 stopwatch.stop(); | 86 void recordResult(bool success, result) { |
39 Duration elapsed = stopwatch.elapsed; | 87 Duration elapsed = stopwatch.elapsed; |
40 driver.results.record(method, elapsed, success: success); | 88 driver.results.record(method, elapsed, success: success); |
41 driver.logger.log( | 89 driver.logger.log( |
42 Level.FINE, 'Response received: $method : $elapsed\n $response'); | 90 Level.FINE, 'Response received: $method : $elapsed\n $result'); |
43 } | 91 } |
44 driver.send(method, json['params']).then((response) { | 92 |
45 recordResponse(true, response); | 93 driver.send(method, json['params']).then((Map<String, dynamic> result) { |
46 }).catchError((e, s) { | 94 recordResult(true, result); |
47 recordResponse(false, e); | 95 processResult(originalId, result, stopwatch); |
48 converter.recordErrorResponse(json, e); | 96 }).catchError((exception) { |
| 97 recordResult(false, exception); |
| 98 converter.processErrorResponse(originalId, exception); |
49 }); | 99 }); |
50 return null; | 100 return null; |
51 } | 101 } |
| 102 |
| 103 void processResult( |
| 104 String id, Map<String, dynamic> result, Stopwatch stopwatch) { |
| 105 converter.processResponseResult(id, result); |
| 106 } |
| 107 } |
| 108 |
| 109 /** |
| 110 * A [ResponseOperation] waits for a [JSON] response from the server. |
| 111 */ |
| 112 class ResponseOperation extends Operation { |
| 113 static final Duration responseTimeout = new Duration(seconds: 5); |
| 114 final CommonInputConverter converter; |
| 115 final Map<String, dynamic> requestJson; |
| 116 final Map<String, dynamic> responseJson; |
| 117 final Completer completer = new Completer(); |
| 118 Driver driver; |
| 119 |
| 120 ResponseOperation(this.converter, this.requestJson, this.responseJson) { |
| 121 completer.future.then(_processResult).timeout(responseTimeout); |
| 122 } |
| 123 |
| 124 @override |
| 125 Future perform(Driver driver) { |
| 126 this.driver = driver; |
| 127 return converter.processExpectedResponse(responseJson['id'], completer); |
| 128 } |
| 129 |
| 130 bool _equal(expectedResult, actualResult) { |
| 131 if (expectedResult is Map && actualResult is Map) { |
| 132 if (expectedResult.length == actualResult.length) { |
| 133 return expectedResult.keys.every((String key) { |
| 134 return key == |
| 135 'fileStamp' || // fileStamp values will not be the same across
runs |
| 136 _equal(expectedResult[key], actualResult[key]); |
| 137 }); |
| 138 } |
| 139 } else if (expectedResult is List && actualResult is List) { |
| 140 if (expectedResult.length == actualResult.length) { |
| 141 for (int i = 0; i < expectedResult.length; ++i) { |
| 142 if (!_equal(expectedResult[i], actualResult[i])) { |
| 143 return false; |
| 144 } |
| 145 } |
| 146 return true; |
| 147 } |
| 148 } |
| 149 return expectedResult == actualResult; |
| 150 } |
| 151 |
| 152 /** |
| 153 * Compare the expected and actual server response result. |
| 154 */ |
| 155 void _processResult(actualResult) { |
| 156 var expectedResult = responseJson['result']; |
| 157 if (!_equal(expectedResult, actualResult)) { |
| 158 var expectedError = responseJson['error']; |
| 159 String format(value) { |
| 160 String text = '\n$value'; |
| 161 if (text.endsWith('\n')) { |
| 162 text = text.substring(0, text.length - 1); |
| 163 } |
| 164 return text.replaceAll('\n', '\n '); |
| 165 } |
| 166 String message = 'Request:${format(requestJson)}\n' |
| 167 'expected result:${format(expectedResult)}\n' |
| 168 'expected error:${format(expectedError)}\n' |
| 169 'but received:${format(actualResult)}'; |
| 170 driver.results.recordUnexpectedResults(requestJson['method']); |
| 171 if (expectedError == null) { |
| 172 converter.logger.log(Level.SEVERE, message); |
| 173 } else { |
| 174 throw message; |
| 175 } |
| 176 } |
| 177 } |
52 } | 178 } |
53 | 179 |
54 class StartServerOperation extends Operation { | 180 class StartServerOperation extends Operation { |
55 @override | 181 @override |
56 Future perform(Driver driver) { | 182 Future perform(Driver driver) { |
57 return driver.startServer(); | 183 return driver.startServer(); |
58 } | 184 } |
59 } | 185 } |
60 | 186 |
61 class WaitForAnalysisCompleteOperation extends Operation { | 187 class WaitForAnalysisCompleteOperation extends Operation { |
(...skipping 10 matching lines...) Expand all Loading... |
72 if (params.analysis != null) { | 198 if (params.analysis != null) { |
73 if (params.analysis.isAnalyzing) { | 199 if (params.analysis.isAnalyzing) { |
74 isAnalyzing = true; | 200 isAnalyzing = true; |
75 } else { | 201 } else { |
76 subscription.cancel(); | 202 subscription.cancel(); |
77 timer.cancel(); | 203 timer.cancel(); |
78 DateTime end = new DateTime.now(); | 204 DateTime end = new DateTime.now(); |
79 Duration delta = end.difference(start); | 205 Duration delta = end.difference(start); |
80 driver.logger.log(Level.FINE, 'analysis complete after $delta'); | 206 driver.logger.log(Level.FINE, 'analysis complete after $delta'); |
81 completer.complete(); | 207 completer.complete(); |
82 driver.results.record('analysis complete', delta); | 208 driver.results.record('analysis complete', delta, notification: true); |
83 } | 209 } |
84 } | 210 } |
85 }); | 211 }); |
86 timer = new Timer.periodic(new Duration(milliseconds: 20), (_) { | 212 timer = new Timer.periodic(new Duration(milliseconds: 20), (_) { |
87 if (!isAnalyzing) { | 213 if (!isAnalyzing) { |
88 // TODO (danrubel) revisit this once source change requests are implemen
ted | 214 // TODO (danrubel) revisit this once source change requests are implemen
ted |
89 subscription.cancel(); | 215 subscription.cancel(); |
90 timer.cancel(); | 216 timer.cancel(); |
91 driver.logger.log(Level.INFO, 'analysis never started'); | 217 driver.logger.log(Level.INFO, 'analysis never started'); |
92 completer.complete(); | 218 completer.complete(); |
93 return; | 219 return; |
94 } | 220 } |
95 // Timeout if no communcation received within the last 10 seconds. | 221 // Timeout if no communcation received within the last 10 seconds. |
96 double currentTime = driver.server.currentElapseTime; | 222 double currentTime = driver.server.currentElapseTime; |
97 double lastTime = driver.server.lastCommunicationTime; | 223 double lastTime = driver.server.lastCommunicationTime; |
98 if (currentTime - lastTime > 10) { | 224 if (currentTime - lastTime > 10) { |
99 subscription.cancel(); | 225 subscription.cancel(); |
100 timer.cancel(); | 226 timer.cancel(); |
101 String message = 'gave up waiting for analysis to complete'; | 227 String message = 'gave up waiting for analysis to complete'; |
102 driver.logger.log(Level.WARNING, message); | 228 driver.logger.log(Level.WARNING, message); |
103 completer.completeError(message); | 229 completer.completeError(message); |
104 } | 230 } |
105 }); | 231 }); |
106 return completer.future; | 232 return completer.future; |
107 } | 233 } |
108 } | 234 } |
OLD | NEW |