| OLD | NEW |
| (Empty) | |
| 1 library server.driver; |
| 2 |
| 3 import 'dart:async'; |
| 4 |
| 5 import 'package:logging/logging.dart'; |
| 6 |
| 7 import '../integration/integration_test_methods.dart'; |
| 8 import '../integration/integration_tests.dart'; |
| 9 import 'operation.dart'; |
| 10 |
| 11 /** |
| 12 * [Driver] launches and manages an instance of analysis server, |
| 13 * reads a stream of operations, sends requests to analysis server |
| 14 * based upon those operations, and evaluates the results. |
| 15 */ |
| 16 class Driver extends IntegrationTestMixin { |
| 17 /** |
| 18 * The amount of time to give the server to respond to a shutdown request |
| 19 * before forcibly terminating it. |
| 20 */ |
| 21 static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5); |
| 22 |
| 23 final Logger logger; |
| 24 |
| 25 /** |
| 26 * A flag indicating whether the server is running. |
| 27 */ |
| 28 bool running = false; |
| 29 |
| 30 @override |
| 31 Server server; |
| 32 |
| 33 /** |
| 34 * The results collected while running analysis server. |
| 35 */ |
| 36 final Results results = new Results(); |
| 37 |
| 38 /** |
| 39 * The [Completer] for [runComplete]. |
| 40 */ |
| 41 Completer<Results> _runCompleter = new Completer<Results>(); |
| 42 |
| 43 Driver(this.logger); |
| 44 |
| 45 /** |
| 46 * Return a [Future] that completes with the [Results] of running |
| 47 * the analysis server once all operations have been performed. |
| 48 */ |
| 49 Future<Results> get runComplete => _runCompleter.future; |
| 50 |
| 51 /** |
| 52 * Perform the given operation. |
| 53 * Return a [Future] that completes when the next operation can be performed, |
| 54 * or `null` if the next operation can be performed immediately |
| 55 */ |
| 56 Future perform(Operation op) { |
| 57 return op.perform(this); |
| 58 } |
| 59 |
| 60 /** |
| 61 * Send a command to the server. An 'id' will be automatically assigned. |
| 62 * The returned [Future] will be completed when the server acknowledges the |
| 63 * command with a response. If the server acknowledges the command with a |
| 64 * normal (non-error) response, the future will be completed with the 'result' |
| 65 * field from the response. If the server acknowledges the command with an |
| 66 * error response, the future will be completed with an error. |
| 67 */ |
| 68 Future send(String method, Map<String, dynamic> params) { |
| 69 return server.send(method, params); |
| 70 } |
| 71 |
| 72 /** |
| 73 * Launch the analysis server. |
| 74 * Return a [Future] that completes when analysis server has started. |
| 75 */ |
| 76 Future startServer() async { |
| 77 logger.log(Level.FINE, 'starting server'); |
| 78 initializeInttestMixin(); |
| 79 server = new Server(); |
| 80 Completer serverConnected = new Completer(); |
| 81 onServerConnected.listen((_) { |
| 82 logger.log(Level.FINE, 'connected to server'); |
| 83 serverConnected.complete(); |
| 84 }); |
| 85 running = true; |
| 86 return server.start(/*profileServer: true*/).then((params) { |
| 87 server.listenToOutput(dispatchNotification); |
| 88 server.exitCode.then((_) { |
| 89 logger.log(Level.FINE, 'server stopped'); |
| 90 running = false; |
| 91 _resultsReady(); |
| 92 }); |
| 93 return serverConnected.future; |
| 94 }); |
| 95 } |
| 96 |
| 97 /** |
| 98 * Shutdown the analysis server if it is running. |
| 99 */ |
| 100 Future stopServer() async { |
| 101 if (running) { |
| 102 logger.log(Level.FINE, 'requesting server shutdown'); |
| 103 // Give the server a short time to comply with the shutdown request; if it |
| 104 // doesn't exit, then forcibly terminate it. |
| 105 sendServerShutdown(); |
| 106 await server.exitCode.timeout(SHUTDOWN_TIMEOUT, onTimeout: () { |
| 107 return server.kill(); |
| 108 }); |
| 109 } |
| 110 _resultsReady(); |
| 111 } |
| 112 |
| 113 /** |
| 114 * If not already complete, signal the completer with the collected results. |
| 115 */ |
| 116 void _resultsReady() { |
| 117 if (!_runCompleter.isCompleted) { |
| 118 _runCompleter.complete(results); |
| 119 } |
| 120 } |
| 121 } |
| 122 |
| 123 /** |
| 124 * [Results] contains information gathered by [Driver] |
| 125 * while running the analysis server |
| 126 */ |
| 127 class Results { |
| 128 Map<String, Measurement> measurements = new Map<String, Measurement>(); |
| 129 |
| 130 /** |
| 131 * Display results on stdout. |
| 132 */ |
| 133 void printResults() { |
| 134 print('=================================================================='); |
| 135 print('Results:'); |
| 136 for (String tag in measurements.keys.toList()..sort()) { |
| 137 measurements[tag].printResults(); |
| 138 } |
| 139 } |
| 140 |
| 141 /** |
| 142 * Record the elapsed time for the given operation. |
| 143 */ |
| 144 void record(String tag, Duration elapsed) { |
| 145 Measurement measurement = measurements[tag]; |
| 146 if (measurement == null) { |
| 147 measurement = new Measurement(tag); |
| 148 measurements[tag] = measurement; |
| 149 } |
| 150 measurement.record(elapsed); |
| 151 } |
| 152 } |
| 153 |
| 154 /** |
| 155 * [Measurement] tracks elapsed time for a given operation. |
| 156 */ |
| 157 class Measurement { |
| 158 final String tag; |
| 159 final List<Duration> elapsedTimes = new List<Duration>(); |
| 160 |
| 161 Measurement(this.tag); |
| 162 |
| 163 void record(Duration elapsed) { |
| 164 elapsedTimes.add(elapsed); |
| 165 } |
| 166 |
| 167 void printResults() { |
| 168 if (elapsedTimes.length == 0) { |
| 169 return; |
| 170 } |
| 171 print('=== $tag'); |
| 172 for (Duration elapsed in elapsedTimes) { |
| 173 print(elapsed); |
| 174 } |
| 175 } |
| 176 } |
| OLD | NEW |