| 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'; |
| 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 |
| 22 * forcibly terminating it. |
| 23 */ |
| 24 static const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 5); |
| 25 |
| 26 /** |
| 21 * Connection to the analysis server. | 27 * Connection to the analysis server. |
| 22 */ | 28 */ |
| 23 Server server; | 29 Server server; |
| 24 | 30 |
| 25 /** | 31 /** |
| 26 * Temporary directory in which source files can be stored. | 32 * Temporary directory in which source files can be stored. |
| 27 */ | 33 */ |
| 28 Directory sourceDirectory; | 34 Directory sourceDirectory; |
| 29 | 35 |
| 30 /** | 36 /** |
| 31 * 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 |
| 32 * been received for the file. | 38 * been received for the file. |
| 33 */ | 39 */ |
| 34 HashMap<String, dynamic> currentAnalysisErrors = new HashMap<String, dynamic>( | 40 HashMap<String, dynamic> currentAnalysisErrors = new HashMap<String, dynamic>( |
| 35 ); | 41 ); |
| 36 | 42 |
| 37 /** | 43 /** |
| 44 * True if the teardown process should skip sending a "server.shutdown" |
| 45 * request (e.g. because the server is known to have already shutdown). |
| 46 */ |
| 47 bool skipShutdown = false; |
| 48 |
| 49 /** |
| 38 * Write a source file with the given contents. [relativePath] | 50 * Write a source file with the given contents. [relativePath] |
| 39 * is relative to [sourceDirectory]; on Windows any forward slashes it | 51 * is relative to [sourceDirectory]; on Windows any forward slashes it |
| 40 * contains are converted to backslashes. | 52 * contains are converted to backslashes. |
| 41 * | 53 * |
| 42 * If the file didn't previously exist, it is created. If it did, it is | 54 * If the file didn't previously exist, it is created. If it did, it is |
| 43 * overwritten. | 55 * overwritten. |
| 44 */ | 56 */ |
| 45 void writeFile(String relativePath, String contents) { | 57 void writeFile(String relativePath, String contents) { |
| 46 String absolutePath = normalizePath(relativePath); | 58 String absolutePath = normalizePath(relativePath); |
| 47 new Directory(dirname(absolutePath)).createSync(recursive: true); | 59 new Directory(dirname(absolutePath)).createSync(recursive: true); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 119 */ | 131 */ |
| 120 Future setUp() { | 132 Future setUp() { |
| 121 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); | 133 sourceDirectory = Directory.systemTemp.createTempSync('analysisServer'); |
| 122 return Server.start().then((Server server) { | 134 return Server.start().then((Server server) { |
| 123 this.server = server; | 135 this.server = server; |
| 124 server.onNotification(ANALYSIS_ERRORS).listen((params) { | 136 server.onNotification(ANALYSIS_ERRORS).listen((params) { |
| 125 expect(params, isMap); | 137 expect(params, isMap); |
| 126 expect(params['file'], isString); | 138 expect(params['file'], isString); |
| 127 currentAnalysisErrors[params['file']] = params['errors']; | 139 currentAnalysisErrors[params['file']] = params['errors']; |
| 128 }); | 140 }); |
| 141 server.exitCode.then((_) { skipShutdown = true; }); |
| 129 }); | 142 }); |
| 130 } | 143 } |
| 131 | 144 |
| 132 /** | 145 /** |
| 133 * After every test, the server stopped and [sourceDirectory] is deleted. | 146 * After every test, the server is stopped and [sourceDirectory] is deleted. |
| 134 */ | 147 */ |
| 135 Future tearDown() { | 148 Future tearDown() { |
| 136 return server.kill().then((_) { | 149 return _shutdownIfNeeded().then((_) { |
| 137 sourceDirectory.deleteSync(recursive: true); | 150 sourceDirectory.deleteSync(recursive: true); |
| 138 }); | 151 }); |
| 139 } | 152 } |
| 153 |
| 154 /** |
| 155 * If [skipShutdown] is not set, shut down the server. |
| 156 */ |
| 157 Future _shutdownIfNeeded() { |
| 158 if (skipShutdown) { |
| 159 return new Future.value(); |
| 160 } |
| 161 // Give the server a short time to comply with the shutdown request; if it |
| 162 // doesn't exit, then forcibly terminate it. |
| 163 Completer processExited = new Completer(); |
| 164 server.send(SERVER_SHUTDOWN, null); |
| 165 server.exitCode.whenComplete(() { |
| 166 processExited.complete(); |
| 167 }); |
| 168 new Future.delayed(SHUTDOWN_TIMEOUT).then((_) { |
| 169 if (!processExited.isCompleted) { |
| 170 server.kill(); |
| 171 } |
| 172 }); |
| 173 return processExited.future; |
| 174 } |
| 140 } | 175 } |
| 141 | 176 |
| 142 // Matchers for data types defined in the analysis server API | 177 // Matchers for data types defined in the analysis server API |
| 143 // ========================================================== | 178 // ========================================================== |
| 144 // TODO(paulberry): add more matchers. | 179 // TODO(paulberry): add more matchers. |
| 145 | 180 |
| 146 // Matchers common to all domains | 181 // Matchers common to all domains |
| 147 // ------------------------------ | 182 // ------------------------------ |
| 148 | 183 |
| 149 const Matcher isResponse = const MatchesJsonObject('response', const { | 184 const Matcher isResponse = const MatchesJsonObject('response', const { |
| (...skipping 421 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 571 // event of an error. | 606 // event of an error. |
| 572 expect(message, isNotification); | 607 expect(message, isNotification); |
| 573 } | 608 } |
| 574 }); | 609 }); |
| 575 process.stderr.transform((new Utf8Codec()).decoder).transform( | 610 process.stderr.transform((new Utf8Codec()).decoder).transform( |
| 576 new LineSplitter()).listen((String line) { | 611 new LineSplitter()).listen((String line) { |
| 577 String trimmedLine = line.trim(); | 612 String trimmedLine = line.trim(); |
| 578 server._recordStdio('ERR: $trimmedLine'); | 613 server._recordStdio('ERR: $trimmedLine'); |
| 579 server._badDataFromServer(); | 614 server._badDataFromServer(); |
| 580 }); | 615 }); |
| 616 process.exitCode.then((int code) { |
| 617 server._recordStdio('TERMINATED WITH EXIT CODE $code'); |
| 618 if (code != 0) { |
| 619 server._badDataFromServer(); |
| 620 } |
| 621 }); |
| 581 return server; | 622 return server; |
| 582 }); | 623 }); |
| 583 } | 624 } |
| 584 | 625 |
| 585 /** | 626 /** |
| 627 * Future that completes when the server process exits. |
| 628 */ |
| 629 Future<int> get exitCode => _process.exitCode; |
| 630 |
| 631 /** |
| 586 * Stop the server. | 632 * Stop the server. |
| 587 */ | 633 */ |
| 588 Future kill() { | 634 Future kill() { |
| 635 debugStdio(); |
| 636 _recordStdio('PROCESS FORCIBLY TERMINATED'); |
| 589 _process.kill(); | 637 _process.kill(); |
| 590 return _process.exitCode; | 638 return _process.exitCode; |
| 591 } | 639 } |
| 592 | 640 |
| 593 /** | 641 /** |
| 594 * Send a command to the server. An 'id' will be automatically assigned. | 642 * Send a command to the server. An 'id' will be automatically assigned. |
| 595 * The returned [Future] will be completed when the server acknowledges the | 643 * The returned [Future] will be completed when the server acknowledges the |
| 596 * command with a response. If the server acknowledges the command with a | 644 * command with a response. If the server acknowledges the command with a |
| 597 * normal (non-error) response, the future will be completed with the 'result' | 645 * normal (non-error) response, the future will be completed with the 'result' |
| 598 * field from the response. If the server acknowledges the command with an | 646 * field from the response. If the server acknowledges the command with an |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 653 * Record a message that was exchanged with the server, and print it out if | 701 * Record a message that was exchanged with the server, and print it out if |
| 654 * [debugStdio] has been called. | 702 * [debugStdio] has been called. |
| 655 */ | 703 */ |
| 656 void _recordStdio(String line) { | 704 void _recordStdio(String line) { |
| 657 if (_debuggingStdio) { | 705 if (_debuggingStdio) { |
| 658 print(line); | 706 print(line); |
| 659 } | 707 } |
| 660 _recordedStdio.add(line); | 708 _recordedStdio.add(line); |
| 661 } | 709 } |
| 662 } | 710 } |
| OLD | NEW |