Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(200)

Side by Side Diff: pkg/analysis_server/test/integration/integration_tests.dart

Issue 429383002: Wait for "server.connected" notification before beginning integration tests. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/analysis_server/test/integration/server_domain_int_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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 test.integration.analysis; 5 library test.integration.analysis;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 import 'dart:convert'; 9 import 'dart:convert';
10 import 'dart:io'; 10 import 'dart:io';
11 11
12 import 'package:analysis_server/src/constants.dart'; 12 import 'package:analysis_server/src/constants.dart';
13 import 'package:path/path.dart'; 13 import 'package:path/path.dart';
14 import 'package:unittest/unittest.dart'; 14 import 'package:unittest/unittest.dart';
15 15
16 /** 16 /**
17 * Base class for analysis server integration tests. 17 * Base class for analysis server integration tests.
18 */ 18 */
19 abstract class AbstractAnalysisServerIntegrationTest { 19 abstract class AbstractAnalysisServerIntegrationTest {
20 /** 20 /**
21 * Amount of time to give the server to respond to a shutdown request before 21 * Amount of time to give the server to respond to a shutdown request before
22 * forcibly terminating it. 22 * forcibly terminating it.
23 *
24 * TODO(paulberry): the extra-long timeout (20s) is because sometimes the
25 * buildbots are slow to spawn a new analysis server process. It would be
26 * better to wait for the initial "server.connected" message with a long
27 * timeout, and keep this timeout short.
28 *
29 */ 23 */
30 static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 20); 24 static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5);
31 25
32 /** 26 /**
33 * Connection to the analysis server. 27 * Connection to the analysis server.
34 */ 28 */
35 Server server; 29 final Server server = new Server();
36 30
37 /** 31 /**
38 * Temporary directory in which source files can be stored. 32 * Temporary directory in which source files can be stored.
39 */ 33 */
40 Directory sourceDirectory; 34 Directory sourceDirectory;
41 35
42 /** 36 /**
43 * Map from file path to the list of analysis errors which have most recently 37 * Map from file path to the list of analysis errors which have most recently
44 * been received for the file. 38 * been received for the file.
45 */ 39 */
46 HashMap<String, dynamic> currentAnalysisErrors = new HashMap<String, dynamic>( 40 HashMap<String, dynamic> currentAnalysisErrors = new HashMap<String, dynamic>(
47 ); 41 );
48 42
49 /** 43 /**
50 * True if the teardown process should skip sending a "server.shutdown" 44 * True if the teardown process should skip sending a "server.shutdown"
51 * request (e.g. because the server is known to have already shutdown). 45 * request (e.g. because the server is known to have already shutdown).
52 */ 46 */
53 bool skipShutdown = false; 47 bool skipShutdown = false;
54 48
55 /** 49 /**
50 * Data associated with the "server.connected" notification that was received
51 * when the server started up.
52 */
53 var serverConnectedParams;
54
55 /**
56 * Write a source file with the given contents. [relativePath] 56 * Write a source file with the given contents. [relativePath]
57 * is relative to [sourceDirectory]; on Windows any forward slashes it 57 * is relative to [sourceDirectory]; on Windows any forward slashes it
58 * contains are converted to backslashes. 58 * contains are converted to backslashes.
59 * 59 *
60 * If the file didn't previously exist, it is created. If it did, it is 60 * If the file didn't previously exist, it is created. If it did, it is
61 * overwritten. 61 * overwritten.
62 */ 62 */
63 void writeFile(String relativePath, String contents) { 63 void writeFile(String relativePath, String contents) {
64 String absolutePath = normalizePath(relativePath); 64 String absolutePath = normalizePath(relativePath);
65 new Directory(dirname(absolutePath)).createSync(recursive: true); 65 new Directory(dirname(absolutePath)).createSync(recursive: true);
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 void debugStdio() { 130 void debugStdio() {
131 server.debugStdio(); 131 server.debugStdio();
132 } 132 }
133 133
134 /** 134 /**
135 * The server is automatically started before every test, and a temporary 135 * The server is automatically started before every test, and a temporary
136 * [sourceDirectory] is created. 136 * [sourceDirectory] is created.
137 */ 137 */
138 Future setUp() { 138 Future setUp() {
139 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); 139 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer');
140 return Server.start().then((Server server) { 140
141 this.server = server; 141 server.onNotification(ANALYSIS_ERRORS).listen((params) {
142 server.onNotification(ANALYSIS_ERRORS).listen((params) { 142 expect(params, isMap);
143 expect(params, isMap); 143 expect(params['file'], isString);
144 expect(params['file'], isString); 144 currentAnalysisErrors[params['file']] = params['errors'];
145 currentAnalysisErrors[params['file']] = params['errors']; 145 });
146 }); 146 Completer serverConnected = new Completer();
147 server.onNotification(SERVER_CONNECTED).listen((_) {
148 expect(serverConnected.isCompleted, isFalse);
149 serverConnected.complete();
150 });
151 return server.start().then((params) {
152 serverConnectedParams = params;
147 server.exitCode.then((_) { skipShutdown = true; }); 153 server.exitCode.then((_) { skipShutdown = true; });
154 return serverConnected.future;
148 }); 155 });
149 } 156 }
150 157
151 /** 158 /**
152 * After every test, the server is stopped and [sourceDirectory] is deleted. 159 * After every test, the server is stopped and [sourceDirectory] is deleted.
153 */ 160 */
154 Future tearDown() { 161 Future tearDown() {
155 return _shutdownIfNeeded().then((_) { 162 return _shutdownIfNeeded().then((_) {
156 sourceDirectory.deleteSync(recursive: true); 163 sourceDirectory.deleteSync(recursive: true);
157 }); 164 });
(...skipping 303 matching lines...) Expand 10 before | Expand all | Expand 10 after
461 } 468 }
462 469
463 Matcher isListOf(Matcher elementMatcher) => new _ListOf(elementMatcher); 470 Matcher isListOf(Matcher elementMatcher) => new _ListOf(elementMatcher);
464 471
465 /** 472 /**
466 * Instances of the class [Server] manage a connection to a server process, and 473 * Instances of the class [Server] manage a connection to a server process, and
467 * facilitate communication to and from the server. 474 * facilitate communication to and from the server.
468 */ 475 */
469 class Server { 476 class Server {
470 /** 477 /**
471 * Server process object. 478 * Server process object, or null if server hasn't been started yet.
472 */ 479 */
473 Process _process; 480 Process _process = null;
474 481
475 /** 482 /**
476 * Commands that have been sent to the server but not yet acknowledged, and 483 * Commands that have been sent to the server but not yet acknowledged, and
477 * the [Completer] objects which should be completed when acknowledgement is 484 * the [Completer] objects which should be completed when acknowledgement is
478 * received. 485 * received.
479 */ 486 */
480 final HashMap<String, Completer> _pendingCommands = <String, Completer> {}; 487 final HashMap<String, Completer> _pendingCommands = <String, Completer> {};
481 488
482 /** 489 /**
483 * Number which should be used to compute the 'id' to send in the next command 490 * Number which should be used to compute the 'id' to send in the next command
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
515 * True if we've received bad data from the server, and we are aborting the 522 * True if we've received bad data from the server, and we are aborting the
516 * test. 523 * test.
517 */ 524 */
518 bool _receivedBadDataFromServer = false; 525 bool _receivedBadDataFromServer = false;
519 526
520 /** 527 /**
521 * Stopwatch that we use to generate timing information for debug output. 528 * Stopwatch that we use to generate timing information for debug output.
522 */ 529 */
523 Stopwatch _time = new Stopwatch(); 530 Stopwatch _time = new Stopwatch();
524 531
525 Server._(this._process) {
526 _time.start();
527 }
528
529 /** 532 /**
530 * Get a stream which will receive notifications of the given event type. 533 * Get a stream which will receive notifications of the given event type.
531 * The values delivered to the stream will be the contents of the 'params' 534 * The values delivered to the stream will be the contents of the 'params'
532 * field of the notification message. 535 * field of the notification message.
533 */ 536 */
534 Stream onNotification(String event) { 537 Stream onNotification(String event) {
535 Stream notificationStream = _notificationStreams[event]; 538 Stream notificationStream = _notificationStreams[event];
536 if (notificationStream == null) { 539 if (notificationStream == null) {
537 StreamController notificationController = new StreamController(); 540 StreamController notificationController = new StreamController();
538 _notificationControllers[event] = notificationController; 541 _notificationControllers[event] = notificationController;
539 notificationStream = notificationController.stream.asBroadcastStream(); 542 notificationStream = notificationController.stream.asBroadcastStream();
540 _notificationStreams[event] = notificationStream; 543 _notificationStreams[event] = notificationStream;
541 } 544 }
542 return notificationStream; 545 return notificationStream;
543 } 546 }
544 547
545 /** 548 /**
546 * Start the server. If [debugServer] is true, the server will be started 549 * Start the server. If [debugServer] is true, the server will be started
547 * with "--debug", allowing a debugger to be attached. 550 * with "--debug", allowing a debugger to be attached.
548 */ 551 */
549 static Future<Server> start({bool debugServer: false}) { 552 Future start({bool debugServer: false}) {
553 if (_process != null) {
554 throw new Exception('Process already started');
555 }
556 _time.start();
550 // TODO(paulberry): move the logic for finding the script, the dart 557 // TODO(paulberry): move the logic for finding the script, the dart
551 // executable, and the package root into a shell script. 558 // executable, and the package root into a shell script.
552 String dartBinary = Platform.executable; 559 String dartBinary = Platform.executable;
553 String scriptDir = dirname(Platform.script.toFilePath(windows: 560 String scriptDir = dirname(Platform.script.toFilePath(windows:
554 Platform.isWindows)); 561 Platform.isWindows));
555 String serverPath = normalize(join(scriptDir, '..', '..', 'bin', 562 String serverPath = normalize(join(scriptDir, '..', '..', 'bin',
556 'server.dart')); 563 'server.dart'));
557 List<String> arguments = []; 564 List<String> arguments = [];
558 if (debugServer) { 565 if (debugServer) {
559 arguments.add('--debug'); 566 arguments.add('--debug');
560 } 567 }
561 if (Platform.packageRoot.isNotEmpty) { 568 if (Platform.packageRoot.isNotEmpty) {
562 arguments.add('--package-root=${Platform.packageRoot}'); 569 arguments.add('--package-root=${Platform.packageRoot}');
563 } 570 }
564 arguments.add(serverPath); 571 arguments.add(serverPath);
565 return Process.start(dartBinary, arguments).then((Process process) { 572 return Process.start(dartBinary, arguments).then((Process process) {
566 Server server = new Server._(process); 573 _process = process;
567 process.stdout.transform((new Utf8Codec()).decoder).transform( 574 process.stdout.transform((new Utf8Codec()).decoder).transform(
568 new LineSplitter()).listen((String line) { 575 new LineSplitter()).listen((String line) {
569 String trimmedLine = line.trim(); 576 String trimmedLine = line.trim();
570 server._recordStdio('RECV: $trimmedLine'); 577 _recordStdio('RECV: $trimmedLine');
571 var message; 578 var message;
572 try { 579 try {
573 message = JSON.decoder.convert(trimmedLine); 580 message = JSON.decoder.convert(trimmedLine);
574 } catch (exception) { 581 } catch (exception) {
575 server._badDataFromServer(); 582 _badDataFromServer();
576 return; 583 return;
577 } 584 }
578 expect(message, isMap); 585 expect(message, isMap);
579 Map messageAsMap = message; 586 Map messageAsMap = message;
580 if (messageAsMap.containsKey('id')) { 587 if (messageAsMap.containsKey('id')) {
581 expect(messageAsMap['id'], isString); 588 expect(messageAsMap['id'], isString);
582 String id = message['id']; 589 String id = message['id'];
583 Completer completer = server._pendingCommands[id]; 590 Completer completer = _pendingCommands[id];
584 if (completer == null) { 591 if (completer == null) {
585 fail('Unexpected response from server: id=$id'); 592 fail('Unexpected response from server: id=$id');
586 } else { 593 } else {
587 server._pendingCommands.remove(id); 594 _pendingCommands.remove(id);
588 } 595 }
589 if (messageAsMap.containsKey('error')) { 596 if (messageAsMap.containsKey('error')) {
590 // TODO(paulberry): propagate the error info to the completer. 597 // TODO(paulberry): propagate the error info to the completer.
591 completer.completeError(new UnimplementedError( 598 completer.completeError(new UnimplementedError(
592 'Server responded with an error')); 599 'Server responded with an error'));
593 } else { 600 } else {
594 completer.complete(messageAsMap['result']); 601 completer.complete(messageAsMap['result']);
595 } 602 }
596 // Check that the message is well-formed. We do this after calling 603 // Check that the message is well-formed. We do this after calling
597 // completer.complete() or completer.completeError() so that we don't 604 // completer.complete() or completer.completeError() so that we don't
598 // stall the test in the event of an error. 605 // stall the test in the event of an error.
599 expect(message, isResponse); 606 expect(message, isResponse);
600 } else { 607 } else {
601 // Message is a notification. It should have an event and possibly 608 // Message is a notification. It should have an event and possibly
602 // params. 609 // params.
603 expect(messageAsMap, contains('event')); 610 expect(messageAsMap, contains('event'));
604 expect(messageAsMap['event'], isString); 611 expect(messageAsMap['event'], isString);
605 String event = messageAsMap['event']; 612 String event = messageAsMap['event'];
606 StreamController notificationController = 613 StreamController notificationController =
607 server._notificationControllers[event]; 614 _notificationControllers[event];
608 if (notificationController != null) { 615 if (notificationController != null) {
609 notificationController.add(messageAsMap['params']); 616 notificationController.add(messageAsMap['params']);
610 } 617 }
611 // Check that the message is well-formed. We do this after calling 618 // Check that the message is well-formed. We do this after calling
612 // notificationController.add() so that we don't stall the test in the 619 // notificationController.add() so that we don't stall the test in the
613 // event of an error. 620 // event of an error.
614 expect(message, isNotification); 621 expect(message, isNotification);
615 } 622 }
616 }); 623 });
617 process.stderr.transform((new Utf8Codec()).decoder).transform( 624 process.stderr.transform((new Utf8Codec()).decoder).transform(
618 new LineSplitter()).listen((String line) { 625 new LineSplitter()).listen((String line) {
619 String trimmedLine = line.trim(); 626 String trimmedLine = line.trim();
620 server._recordStdio('ERR: $trimmedLine'); 627 _recordStdio('ERR: $trimmedLine');
621 server._badDataFromServer(); 628 _badDataFromServer();
622 }); 629 });
623 process.exitCode.then((int code) { 630 process.exitCode.then((int code) {
624 server._recordStdio('TERMINATED WITH EXIT CODE $code'); 631 _recordStdio('TERMINATED WITH EXIT CODE $code');
625 if (code != 0) { 632 if (code != 0) {
626 server._badDataFromServer(); 633 _badDataFromServer();
627 } 634 }
628 }); 635 });
629 return server;
630 }); 636 });
631 } 637 }
632 638
633 /** 639 /**
634 * Future that completes when the server process exits. 640 * Future that completes when the server process exits.
635 */ 641 */
636 Future<int> get exitCode => _process.exitCode; 642 Future<int> get exitCode => _process.exitCode;
637 643
638 /** 644 /**
639 * Stop the server. 645 * Stop the server.
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
710 */ 716 */
711 void _recordStdio(String line) { 717 void _recordStdio(String line) {
712 double elapsedTime = _time.elapsedTicks / _time.frequency; 718 double elapsedTime = _time.elapsedTicks / _time.frequency;
713 line = "$elapsedTime: $line"; 719 line = "$elapsedTime: $line";
714 if (_debuggingStdio) { 720 if (_debuggingStdio) {
715 print(line); 721 print(line);
716 } 722 }
717 _recordedStdio.add(line); 723 _recordedStdio.add(line);
718 } 724 }
719 } 725 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/integration/server_domain_int_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698