mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
Add analysis_server_client without analysis_server.
BUG= R=brianwilkerson@google.com, scheglov@google.com Review-Url: https://codereview.chromium.org/2994723003 .
This commit is contained in:
parent
91662ce579
commit
4e42dbc1f9
|
@ -7,6 +7,7 @@
|
|||
# Please update this file if you add a package to DEPS or /pkg
|
||||
#
|
||||
analysis_server:pkg/analysis_server/lib
|
||||
analysis_server_client:pkg/analysis_server_client/lib
|
||||
analyzer:pkg/analyzer/lib
|
||||
analyzer_cli:pkg/analyzer_cli/lib
|
||||
analyzer_plugin:pkg/analyzer_plugin/lib
|
||||
|
|
5
pkg/analysis_server_client/CHANGELOG.md
Normal file
5
pkg/analysis_server_client/CHANGELOG.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
#1.0.1
|
||||
* Update path on homepage
|
||||
|
||||
#1.0.0
|
||||
* Initial version
|
26
pkg/analysis_server_client/LICENSE
Normal file
26
pkg/analysis_server_client/LICENSE
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright 2017, the Dart project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
13
pkg/analysis_server_client/README.md
Normal file
13
pkg/analysis_server_client/README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Analysis Server Client
|
||||
|
||||
A client wrapper over Analysis Server. Instances of this client manages
|
||||
connection to Analysis Server and process and faciliates JSON protocol
|
||||
communication to and from the server.
|
||||
|
||||
Current implementation has no knowledge of the Analysis Server library yet.
|
||||
Future updates will allow for full class-access of Analysis Server protocol
|
||||
objects.
|
||||
|
||||
Analysis Server process must be instantiated separately and loaded into
|
||||
Analysis Server Client. To learn how to generate an Analysis Server Process,
|
||||
refer to the [Analysis Server page.](https://github.com/dart-lang/sdk/tree/master/pkg/analysis_server)
|
101
pkg/analysis_server_client/lib/analysis_server_client.dart
Normal file
101
pkg/analysis_server_client/lib/analysis_server_client.dart
Normal file
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
/// Type of callbacks used to process notification.
|
||||
typedef void NotificationProcessor(String event, Map<String, Object> params);
|
||||
|
||||
/// Instances of the class [AnalysisServerClient] manage a connection to an
|
||||
/// [AnalysisServer] process, and facilitate communication to and from the
|
||||
/// client/user.
|
||||
class AnalysisServerClient {
|
||||
/// AnalysisServer process object, or null if the server has been shut down.
|
||||
final Process _process;
|
||||
|
||||
/// Commands that have been sent to the server but not yet acknowledged,
|
||||
/// and the [Completer] objects which should be completed when
|
||||
/// acknowledgement is received.
|
||||
final Map<String, Completer> _pendingCommands = <String, Completer>{};
|
||||
|
||||
/// Number which should be used to compute the 'id' to send to the next
|
||||
/// command sent to the server.
|
||||
int _nextId = 0;
|
||||
|
||||
AnalysisServerClient(this._process);
|
||||
|
||||
/// Return a future that will complete when all commands that have been
|
||||
/// sent to the server so far have been flushed to the OS buffer.
|
||||
Future<Null> flushCommands() {
|
||||
return _process.stdin.flush();
|
||||
}
|
||||
|
||||
void listenToOutput({NotificationProcessor notificationProcessor}) {
|
||||
_process.stdout
|
||||
.transform((new Utf8Codec()).decoder)
|
||||
.transform(new LineSplitter())
|
||||
.listen((String line) {
|
||||
String trimmedLine = line.trim();
|
||||
if (trimmedLine.startsWith('Observatory listening on ')) {
|
||||
return;
|
||||
}
|
||||
final result = JSON.decoder.convert(trimmedLine) as Map;
|
||||
if (result.containsKey('id')) {
|
||||
final id = result['id'] as String;
|
||||
final completer = _pendingCommands.remove(id);
|
||||
|
||||
if (result.containsKey('error')) {
|
||||
completer.completeError(new ServerErrorMessage(result['error']));
|
||||
} else {
|
||||
completer.complete(result['result']);
|
||||
}
|
||||
} else if (notificationProcessor != null && result.containsKey('event')) {
|
||||
// Message is a notification. It should have an event and possibly
|
||||
// params.
|
||||
notificationProcessor(result['event'], result['params']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Sends 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) {
|
||||
String id = '${_nextId++}';
|
||||
Map<String, dynamic> command = <String, dynamic>{
|
||||
'id': id,
|
||||
'method': method
|
||||
};
|
||||
if (params != null) {
|
||||
command['params'] = params;
|
||||
}
|
||||
Completer completer = new Completer();
|
||||
_pendingCommands[id] = completer;
|
||||
String commandAsJson = JSON.encode(command);
|
||||
_process.stdin.add(UTF8.encoder.convert('$commandAsJson\n'));
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
/// Force kill the server. Returns exit code future.
|
||||
Future<int> kill() {
|
||||
_process.kill();
|
||||
return _process.exitCode;
|
||||
}
|
||||
}
|
||||
|
||||
class ServerErrorMessage {
|
||||
final Map errorJson;
|
||||
|
||||
ServerErrorMessage(this.errorJson);
|
||||
|
||||
String get code => errorJson['code'].toString();
|
||||
String get message => errorJson['message'];
|
||||
String get stackTrace => errorJson['stackTrace'];
|
||||
}
|
13
pkg/analysis_server_client/pubspec.yaml
Normal file
13
pkg/analysis_server_client/pubspec.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
name: analysis_server_client
|
||||
version: 1.0.1
|
||||
author: Dart Team <misc@dartlang.org>
|
||||
description:
|
||||
A client wrapper over analysis_server. Instances of this client
|
||||
manage a connection to the analysis_server process and
|
||||
facilitates communication to and from the server.
|
||||
homepage: http://github.com/dart-lang/sdk/pkg/analysis_server_client
|
||||
dev_dependencies:
|
||||
test: ">=0.12.0 <0.13.0"
|
||||
mockito: ^2.0.2
|
||||
environment:
|
||||
sdk: ">=1.0.0 < 2.0.0-dev.infinity"
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analysis_server_client/analysis_server_client.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
Process _process;
|
||||
AnalysisServerClient serverWrapper;
|
||||
|
||||
setUp(() async {
|
||||
_process = new MockProcess();
|
||||
serverWrapper = new AnalysisServerClient(_process);
|
||||
when(_process.stdin).thenReturn(<int>[]);
|
||||
});
|
||||
|
||||
test('test_listenToOutput_good', () async {
|
||||
when(_process.stdout).thenReturn(_goodMessage());
|
||||
|
||||
final future = serverWrapper.send('blahMethod', null);
|
||||
serverWrapper.listenToOutput();
|
||||
|
||||
final response = await future;
|
||||
expect(response, new isInstanceOf<Map>());
|
||||
final responseAsMap = response as Map;
|
||||
expect(responseAsMap['foo'], 'bar');
|
||||
});
|
||||
|
||||
test('test_listenToOutput_error', () async {
|
||||
when(_process.stdout).thenReturn(_badMessage());
|
||||
final future = serverWrapper.send('blahMethod', null);
|
||||
future.catchError((e) {
|
||||
expect(e, new isInstanceOf<ServerErrorMessage>());
|
||||
final e2 = e as ServerErrorMessage;
|
||||
expect(e2.code, 'someErrorCode');
|
||||
expect(e2.message, 'something went wrong');
|
||||
expect(e2.stackTrace, 'some long stack trace');
|
||||
});
|
||||
serverWrapper.listenToOutput();
|
||||
});
|
||||
|
||||
test('test_listenToOutput_event', () async {
|
||||
when(_process.stdout).thenReturn(_eventMessage());
|
||||
|
||||
void eventHandler(String event, Map<String, Object> params) {
|
||||
expect(event, 'fooEvent');
|
||||
expect(params.length, 2);
|
||||
expect(params['foo'] as String, 'bar');
|
||||
expect(params['baz'] as String, 'bang');
|
||||
}
|
||||
|
||||
serverWrapper.send('blahMethod', null);
|
||||
serverWrapper.listenToOutput(notificationProcessor: eventHandler);
|
||||
});
|
||||
}
|
||||
|
||||
Stream<List<int>> _goodMessage() async* {
|
||||
yield UTF8.encoder.convert('Observatory listening on foo bar\n');
|
||||
final sampleJson = {
|
||||
'id': '0',
|
||||
'result': {'foo': 'bar'}
|
||||
};
|
||||
yield UTF8.encoder.convert(JSON.encode(sampleJson));
|
||||
}
|
||||
|
||||
final _badErrorMessage = {
|
||||
'code': 'someErrorCode',
|
||||
'message': 'something went wrong',
|
||||
'stackTrace': 'some long stack trace'
|
||||
};
|
||||
|
||||
Stream<List<int>> _badMessage() async* {
|
||||
yield UTF8.encoder.convert('Observatory listening on foo bar\n');
|
||||
final sampleJson = {'id': '0', 'error': _badErrorMessage};
|
||||
yield UTF8.encoder.convert(JSON.encode(sampleJson));
|
||||
}
|
||||
|
||||
Stream<List<int>> _eventMessage() async* {
|
||||
yield UTF8.encoder.convert('Observatory listening on foo bar\n');
|
||||
final sampleJson = {
|
||||
'event': 'fooEvent',
|
||||
'params': {'foo': 'bar', 'baz': 'bang'}
|
||||
};
|
||||
yield UTF8.encoder.convert(JSON.encode(sampleJson));
|
||||
}
|
||||
|
||||
class MockProcess extends Mock implements Process {}
|
Loading…
Reference in a new issue