| Index: pkg/analysis_server/test/performance/driver.dart
|
| diff --git a/pkg/analysis_server/test/performance/driver.dart b/pkg/analysis_server/test/performance/driver.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ca34e276acae478a8e15aa715a03c79ca9432201
|
| --- /dev/null
|
| +++ b/pkg/analysis_server/test/performance/driver.dart
|
| @@ -0,0 +1,176 @@
|
| +library server.driver;
|
| +
|
| +import 'dart:async';
|
| +
|
| +import 'package:logging/logging.dart';
|
| +
|
| +import '../integration/integration_test_methods.dart';
|
| +import '../integration/integration_tests.dart';
|
| +import 'operation.dart';
|
| +
|
| +/**
|
| + * [Driver] launches and manages an instance of analysis server,
|
| + * reads a stream of operations, sends requests to analysis server
|
| + * based upon those operations, and evaluates the results.
|
| + */
|
| +class Driver extends IntegrationTestMixin {
|
| + /**
|
| + * The amount of time to give the server to respond to a shutdown request
|
| + * before forcibly terminating it.
|
| + */
|
| + static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5);
|
| +
|
| + final Logger logger;
|
| +
|
| + /**
|
| + * A flag indicating whether the server is running.
|
| + */
|
| + bool running = false;
|
| +
|
| + @override
|
| + Server server;
|
| +
|
| + /**
|
| + * The results collected while running analysis server.
|
| + */
|
| + final Results results = new Results();
|
| +
|
| + /**
|
| + * The [Completer] for [runComplete].
|
| + */
|
| + Completer<Results> _runCompleter = new Completer<Results>();
|
| +
|
| + Driver(this.logger);
|
| +
|
| + /**
|
| + * Return a [Future] that completes with the [Results] of running
|
| + * the analysis server once all operations have been performed.
|
| + */
|
| + Future<Results> get runComplete => _runCompleter.future;
|
| +
|
| + /**
|
| + * Perform the given operation.
|
| + * Return a [Future] that completes when the next operation can be performed,
|
| + * or `null` if the next operation can be performed immediately
|
| + */
|
| + Future perform(Operation op) {
|
| + return op.perform(this);
|
| + }
|
| +
|
| + /**
|
| + * Send a command to the server. An 'id' will be automatically assigned.
|
| + * The returned [Future] will be completed when the server acknowledges the
|
| + * command with a response. If the server acknowledges the command with a
|
| + * normal (non-error) response, the future will be completed with the 'result'
|
| + * field from the response. If the server acknowledges the command with an
|
| + * error response, the future will be completed with an error.
|
| + */
|
| + Future send(String method, Map<String, dynamic> params) {
|
| + return server.send(method, params);
|
| + }
|
| +
|
| + /**
|
| + * Launch the analysis server.
|
| + * Return a [Future] that completes when analysis server has started.
|
| + */
|
| + Future startServer() async {
|
| + logger.log(Level.FINE, 'starting server');
|
| + initializeInttestMixin();
|
| + server = new Server();
|
| + Completer serverConnected = new Completer();
|
| + onServerConnected.listen((_) {
|
| + logger.log(Level.FINE, 'connected to server');
|
| + serverConnected.complete();
|
| + });
|
| + running = true;
|
| + return server.start(/*profileServer: true*/).then((params) {
|
| + server.listenToOutput(dispatchNotification);
|
| + server.exitCode.then((_) {
|
| + logger.log(Level.FINE, 'server stopped');
|
| + running = false;
|
| + _resultsReady();
|
| + });
|
| + return serverConnected.future;
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Shutdown the analysis server if it is running.
|
| + */
|
| + Future stopServer() async {
|
| + if (running) {
|
| + logger.log(Level.FINE, 'requesting server shutdown');
|
| + // Give the server a short time to comply with the shutdown request; if it
|
| + // doesn't exit, then forcibly terminate it.
|
| + sendServerShutdown();
|
| + await server.exitCode.timeout(SHUTDOWN_TIMEOUT, onTimeout: () {
|
| + return server.kill();
|
| + });
|
| + }
|
| + _resultsReady();
|
| + }
|
| +
|
| + /**
|
| + * If not already complete, signal the completer with the collected results.
|
| + */
|
| + void _resultsReady() {
|
| + if (!_runCompleter.isCompleted) {
|
| + _runCompleter.complete(results);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * [Results] contains information gathered by [Driver]
|
| + * while running the analysis server
|
| + */
|
| +class Results {
|
| + Map<String, Measurement> measurements = new Map<String, Measurement>();
|
| +
|
| + /**
|
| + * Display results on stdout.
|
| + */
|
| + void printResults() {
|
| + print('==================================================================');
|
| + print('Results:');
|
| + for (String tag in measurements.keys.toList()..sort()) {
|
| + measurements[tag].printResults();
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Record the elapsed time for the given operation.
|
| + */
|
| + void record(String tag, Duration elapsed) {
|
| + Measurement measurement = measurements[tag];
|
| + if (measurement == null) {
|
| + measurement = new Measurement(tag);
|
| + measurements[tag] = measurement;
|
| + }
|
| + measurement.record(elapsed);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * [Measurement] tracks elapsed time for a given operation.
|
| + */
|
| +class Measurement {
|
| + final String tag;
|
| + final List<Duration> elapsedTimes = new List<Duration>();
|
| +
|
| + Measurement(this.tag);
|
| +
|
| + void record(Duration elapsed) {
|
| + elapsedTimes.add(elapsed);
|
| + }
|
| +
|
| + void printResults() {
|
| + if (elapsedTimes.length == 0) {
|
| + return;
|
| + }
|
| + print('=== $tag');
|
| + for (Duration elapsed in elapsedTimes) {
|
| + print(elapsed);
|
| + }
|
| + }
|
| +}
|
|
|