| OLD | NEW |
| 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'; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 * Data associated with the "server.connected" notification that was received | 53 * Data associated with the "server.connected" notification that was received |
| 54 * when the server started up. | 54 * when the server started up. |
| 55 */ | 55 */ |
| 56 var serverConnectedParams; | 56 var serverConnectedParams; |
| 57 | 57 |
| 58 /** | 58 /** |
| 59 * True if we are currently subscribed to [SERVER_STATUS] updates. | 59 * True if we are currently subscribed to [SERVER_STATUS] updates. |
| 60 */ | 60 */ |
| 61 bool _subscribedToServerStatus = false; | 61 bool _subscribedToServerStatus = false; |
| 62 | 62 |
| 63 AbstractAnalysisServerIntegrationTest() { |
| 64 initializeInttestMixin(); |
| 65 } |
| 66 |
| 63 /** | 67 /** |
| 64 * Write a source file with the given absolute [pathname] and [contents]. | 68 * Write a source file with the given absolute [pathname] and [contents]. |
| 65 * | 69 * |
| 66 * If the file didn't previously exist, it is created. If it did, it is | 70 * If the file didn't previously exist, it is created. If it did, it is |
| 67 * overwritten. | 71 * overwritten. |
| 68 * | 72 * |
| 69 * Parent directories are created as necessary. | 73 * Parent directories are created as necessary. |
| 70 */ | 74 */ |
| 71 void writeFile(String pathname, String contents) { | 75 void writeFile(String pathname, String contents) { |
| 72 new Directory(dirname(pathname)).createSync(recursive: true); | 76 new Directory(dirname(pathname)).createSync(recursive: true); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 * received after this function call. So it is safe to use this getter | 109 * received after this function call. So it is safe to use this getter |
| 106 * multiple times in one test; each time it is used it will wait afresh for | 110 * multiple times in one test; each time it is used it will wait afresh for |
| 107 * analysis to finish. | 111 * analysis to finish. |
| 108 */ | 112 */ |
| 109 Future get analysisFinished { | 113 Future get analysisFinished { |
| 110 Completer completer = new Completer(); | 114 Completer completer = new Completer(); |
| 111 StreamSubscription subscription; | 115 StreamSubscription subscription; |
| 112 // This will only work if the caller has already subscribed to | 116 // This will only work if the caller has already subscribed to |
| 113 // SERVER_STATUS (e.g. using sendServerSetSubscriptions(['STATUS'])) | 117 // SERVER_STATUS (e.g. using sendServerSetSubscriptions(['STATUS'])) |
| 114 expect(_subscribedToServerStatus, isTrue); | 118 expect(_subscribedToServerStatus, isTrue); |
| 115 subscription = server.onNotification(SERVER_STATUS).listen((params) { | 119 subscription = onServerStatus.listen((params) { |
| 116 bool analysisComplete = false; | 120 bool analysisComplete = false; |
| 117 try { | 121 try { |
| 118 analysisComplete = !params['analysis']['analyzing']; | 122 analysisComplete = !params['analysis']['analyzing']; |
| 119 } catch (_) { | 123 } catch (_) { |
| 120 // Status message was mal-formed or missing optional parameters. That's | 124 // Status message was mal-formed or missing optional parameters. That's |
| 121 // fine, since we'll detect a mal-formed status message below. | 125 // fine, since we'll detect a mal-formed status message below. |
| 122 } | 126 } |
| 123 if (analysisComplete) { | 127 if (analysisComplete) { |
| 124 completer.complete(params); | 128 completer.complete(params); |
| 125 subscription.cancel(); | 129 subscription.cancel(); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 145 checkTypes); | 149 checkTypes); |
| 146 } | 150 } |
| 147 | 151 |
| 148 /** | 152 /** |
| 149 * The server is automatically started before every test, and a temporary | 153 * The server is automatically started before every test, and a temporary |
| 150 * [sourceDirectory] is created. | 154 * [sourceDirectory] is created. |
| 151 */ | 155 */ |
| 152 Future setUp() { | 156 Future setUp() { |
| 153 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); | 157 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); |
| 154 | 158 |
| 155 server.onNotification(ANALYSIS_ERRORS).listen((params) { | 159 onAnalysisErrors.listen((params) { |
| 156 expect(params, isMap); | |
| 157 expect(params['file'], isString); | |
| 158 currentAnalysisErrors[params['file']] = params['errors']; | 160 currentAnalysisErrors[params['file']] = params['errors']; |
| 159 }); | 161 }); |
| 160 Completer serverConnected = new Completer(); | 162 Completer serverConnected = new Completer(); |
| 161 server.onNotification(SERVER_CONNECTED).listen((_) { | 163 onServerConnected.listen((_) { |
| 162 expect(serverConnected.isCompleted, isFalse); | 164 expect(serverConnected.isCompleted, isFalse); |
| 163 serverConnected.complete(); | 165 serverConnected.complete(); |
| 164 }); | 166 }); |
| 165 return server.start().then((params) { | 167 return server.start(dispatchNotification).then((params) { |
| 166 serverConnectedParams = params; | 168 serverConnectedParams = params; |
| 167 server.exitCode.then((_) { | 169 server.exitCode.then((_) { |
| 168 skipShutdown = true; | 170 skipShutdown = true; |
| 169 }); | 171 }); |
| 170 return serverConnected.future; | 172 return serverConnected.future; |
| 171 }); | 173 }); |
| 172 } | 174 } |
| 173 | 175 |
| 174 /** | 176 /** |
| 175 * After every test, the server is stopped and [sourceDirectory] is deleted. | 177 * After every test, the server is stopped and [sourceDirectory] is deleted. |
| (...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 | 487 |
| 486 @override | 488 @override |
| 487 Description describe(Description description) => description.add('Map from ' | 489 Description describe(Description description) => description.add('Map from ' |
| 488 ).addDescriptionOf(keyMatcher).add(' to ').addDescriptionOf(valueMatcher); | 490 ).addDescriptionOf(keyMatcher).add(' to ').addDescriptionOf(valueMatcher); |
| 489 } | 491 } |
| 490 | 492 |
| 491 Matcher isMapOf(Matcher keyMatcher, Matcher valueMatcher) => new _MapOf( | 493 Matcher isMapOf(Matcher keyMatcher, Matcher valueMatcher) => new _MapOf( |
| 492 keyMatcher, valueMatcher); | 494 keyMatcher, valueMatcher); |
| 493 | 495 |
| 494 /** | 496 /** |
| 497 * Type of callbacks used to process notifications. |
| 498 */ |
| 499 typedef void NotificationProcessor(String event, params); |
| 500 |
| 501 /** |
| 495 * Instances of the class [Server] manage a connection to a server process, and | 502 * Instances of the class [Server] manage a connection to a server process, and |
| 496 * facilitate communication to and from the server. | 503 * facilitate communication to and from the server. |
| 497 */ | 504 */ |
| 498 class Server { | 505 class Server { |
| 499 /** | 506 /** |
| 500 * Server process object, or null if server hasn't been started yet. | 507 * Server process object, or null if server hasn't been started yet. |
| 501 */ | 508 */ |
| 502 Process _process = null; | 509 Process _process = null; |
| 503 | 510 |
| 504 /** | 511 /** |
| 505 * Commands that have been sent to the server but not yet acknowledged, and | 512 * Commands that have been sent to the server but not yet acknowledged, and |
| 506 * the [Completer] objects which should be completed when acknowledgement is | 513 * the [Completer] objects which should be completed when acknowledgement is |
| 507 * received. | 514 * received. |
| 508 */ | 515 */ |
| 509 final HashMap<String, Completer> _pendingCommands = <String, Completer> {}; | 516 final HashMap<String, Completer> _pendingCommands = <String, Completer> {}; |
| 510 | 517 |
| 511 /** | 518 /** |
| 512 * Number which should be used to compute the 'id' to send in the next command | 519 * Number which should be used to compute the 'id' to send in the next command |
| 513 * sent to the server. | 520 * sent to the server. |
| 514 */ | 521 */ |
| 515 int _nextId = 0; | 522 int _nextId = 0; |
| 516 | 523 |
| 517 /** | 524 /** |
| 518 * [StreamController]s to which notifications should be sent, organized by | 525 * [StreamController] to which notifications will be sent. |
| 519 * event type. | |
| 520 */ | 526 */ |
| 521 final HashMap<String, StreamController> _notificationControllers = | 527 final StreamController _notifications = new StreamController(); |
| 522 new HashMap<String, StreamController>(); | |
| 523 | |
| 524 /** | |
| 525 * [Stream]s associated with the controllers in [_notificationControllers], | |
| 526 * but converted to broadcast streams. | |
| 527 */ | |
| 528 final HashMap<String, Stream> _notificationStreams = new HashMap<String, | |
| 529 Stream>(); | |
| 530 | 528 |
| 531 /** | 529 /** |
| 532 * Messages which have been exchanged with the server; we buffer these | 530 * Messages which have been exchanged with the server; we buffer these |
| 533 * up until the test finishes, so that they can be examined in the debugger | 531 * up until the test finishes, so that they can be examined in the debugger |
| 534 * or printed out in response to a call to [debugStdio]. | 532 * or printed out in response to a call to [debugStdio]. |
| 535 */ | 533 */ |
| 536 final List<String> _recordedStdio = <String>[]; | 534 final List<String> _recordedStdio = <String>[]; |
| 537 | 535 |
| 538 /** | 536 /** |
| 539 * True if we are currently printing out messages exchanged with the server. | 537 * True if we are currently printing out messages exchanged with the server. |
| 540 */ | 538 */ |
| 541 bool _debuggingStdio = false; | 539 bool _debuggingStdio = false; |
| 542 | 540 |
| 543 /** | 541 /** |
| 544 * True if we've received bad data from the server, and we are aborting the | 542 * True if we've received bad data from the server, and we are aborting the |
| 545 * test. | 543 * test. |
| 546 */ | 544 */ |
| 547 bool _receivedBadDataFromServer = false; | 545 bool _receivedBadDataFromServer = false; |
| 548 | 546 |
| 549 /** | 547 /** |
| 550 * Stopwatch that we use to generate timing information for debug output. | 548 * Stopwatch that we use to generate timing information for debug output. |
| 551 */ | 549 */ |
| 552 Stopwatch _time = new Stopwatch(); | 550 Stopwatch _time = new Stopwatch(); |
| 553 | 551 |
| 554 /** | 552 /** |
| 555 * Get a stream which will receive notifications of the given event type. | |
| 556 * The values delivered to the stream will be the contents of the 'params' | |
| 557 * field of the notification message. | |
| 558 */ | |
| 559 Stream onNotification(String event) { | |
| 560 Stream notificationStream = _notificationStreams[event]; | |
| 561 if (notificationStream == null) { | |
| 562 StreamController notificationController = new StreamController(); | |
| 563 _notificationControllers[event] = notificationController; | |
| 564 notificationStream = notificationController.stream.asBroadcastStream(); | |
| 565 _notificationStreams[event] = notificationStream; | |
| 566 } | |
| 567 return notificationStream; | |
| 568 } | |
| 569 | |
| 570 /** | |
| 571 * Start the server. If [debugServer] is true, the server will be started | 553 * Start the server. If [debugServer] is true, the server will be started |
| 572 * with "--debug", allowing a debugger to be attached. | 554 * with "--debug", allowing a debugger to be attached. |
| 573 */ | 555 */ |
| 574 Future start({bool debugServer: false}) { | 556 Future start(NotificationProcessor notificationProcessor, {bool debugServer: |
| 557 false}) { |
| 575 if (_process != null) { | 558 if (_process != null) { |
| 576 throw new Exception('Process already started'); | 559 throw new Exception('Process already started'); |
| 577 } | 560 } |
| 578 _time.start(); | 561 _time.start(); |
| 579 // TODO(paulberry): move the logic for finding the script, the dart | 562 // TODO(paulberry): move the logic for finding the script, the dart |
| 580 // executable, and the package root into a shell script. | 563 // executable, and the package root into a shell script. |
| 581 String dartBinary = Platform.executable; | 564 String dartBinary = Platform.executable; |
| 582 String scriptDir = dirname(Platform.script.toFilePath(windows: | 565 String scriptDir = dirname(Platform.script.toFilePath(windows: |
| 583 Platform.isWindows)); | 566 Platform.isWindows)); |
| 584 String serverPath = normalize(join(scriptDir, '..', '..', 'bin', | 567 String serverPath = normalize(join(scriptDir, '..', '..', 'bin', |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 625 } | 608 } |
| 626 // Check that the message is well-formed. We do this after calling | 609 // Check that the message is well-formed. We do this after calling |
| 627 // completer.complete() or completer.completeError() so that we don't | 610 // completer.complete() or completer.completeError() so that we don't |
| 628 // stall the test in the event of an error. | 611 // stall the test in the event of an error. |
| 629 expect(message, isResponse); | 612 expect(message, isResponse); |
| 630 } else { | 613 } else { |
| 631 // Message is a notification. It should have an event and possibly | 614 // Message is a notification. It should have an event and possibly |
| 632 // params. | 615 // params. |
| 633 expect(messageAsMap, contains('event')); | 616 expect(messageAsMap, contains('event')); |
| 634 expect(messageAsMap['event'], isString); | 617 expect(messageAsMap['event'], isString); |
| 635 String event = messageAsMap['event']; | 618 notificationProcessor(messageAsMap['event'], messageAsMap['params']); |
| 636 StreamController notificationController = | |
| 637 _notificationControllers[event]; | |
| 638 if (notificationController != null) { | |
| 639 notificationController.add(messageAsMap['params']); | |
| 640 } | |
| 641 // Check that the message is well-formed. We do this after calling | 619 // Check that the message is well-formed. We do this after calling |
| 642 // notificationController.add() so that we don't stall the test in the | 620 // notificationController.add() so that we don't stall the test in the |
| 643 // event of an error. | 621 // event of an error. |
| 644 expect(message, isNotification); | 622 expect(message, isNotification); |
| 645 } | 623 } |
| 646 }); | 624 }); |
| 647 process.stderr.transform((new Utf8Codec()).decoder).transform( | 625 process.stderr.transform((new Utf8Codec()).decoder).transform( |
| 648 new LineSplitter()).listen((String line) { | 626 new LineSplitter()).listen((String line) { |
| 649 String trimmedLine = line.trim(); | 627 String trimmedLine = line.trim(); |
| 650 _recordStdio('ERR: $trimmedLine'); | 628 _recordStdio('ERR: $trimmedLine'); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 739 */ | 717 */ |
| 740 void _recordStdio(String line) { | 718 void _recordStdio(String line) { |
| 741 double elapsedTime = _time.elapsedTicks / _time.frequency; | 719 double elapsedTime = _time.elapsedTicks / _time.frequency; |
| 742 line = "$elapsedTime: $line"; | 720 line = "$elapsedTime: $line"; |
| 743 if (_debuggingStdio) { | 721 if (_debuggingStdio) { |
| 744 print(line); | 722 print(line); |
| 745 } | 723 } |
| 746 _recordedStdio.add(line); | 724 _recordedStdio.add(line); |
| 747 } | 725 } |
| 748 } | 726 } |
| OLD | NEW |