Skip to content

Commit 24b8d43

Browse files
Pi Songkunthamgrouma
authored andcommitted
Created a debugger for Dart Debug Extension. (#501)
* Created a debugger for Dart Debug Extension.
1 parent cc72d14 commit 24b8d43

File tree

3 files changed

+208
-0
lines changed

3 files changed

+208
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
8+
import 'package:built_collection/built_collection.dart';
9+
import 'package:dwds/data/extension_request.dart';
10+
import 'package:sse/server/sse_handler.dart';
11+
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
12+
13+
import '../../data/serializers.dart';
14+
15+
/// A debugger backed by the Dart Debug Extension.
16+
class ExtensionDebugger implements WipDebugger {
17+
@override
18+
WipConnection get connection => throw UnimplementedError();
19+
20+
/// A connection between the debugger and the background of
21+
/// Dart Debug Extension
22+
final SseConnection _connection;
23+
24+
/// A map from id to a completer associated with an [ExtensionRequest]
25+
final _completers = <int, Completer>{};
26+
27+
var _completerId = 0;
28+
29+
ExtensionDebugger(this._connection) {
30+
_connection.stream.listen((data) {
31+
var message = serializers.deserialize(jsonDecode(data));
32+
if (message is ExtensionResponse) {
33+
var encodedResult = json.decode(message.result);
34+
if (_completers[message.id] == null) {
35+
throw StateError('Missing completer.');
36+
}
37+
_completers[message.id]
38+
.complete(WipResponse(encodedResult as Map<String, dynamic>));
39+
}
40+
}, onError: (e) {
41+
close();
42+
});
43+
}
44+
45+
/// Sends a [command] with optional [params] to Dart Debug Extension
46+
/// over the SSE connection.
47+
@override
48+
Future<WipResponse> sendCommand(String command,
49+
{Map<String, dynamic> params}) {
50+
var completer = Completer<WipResponse>();
51+
var id = newId();
52+
_completers[id] = completer;
53+
_connection.sink.add(jsonEncode(serializers.serialize(ExtensionRequest(
54+
(b) => b
55+
..id = id
56+
..command = command
57+
..commandParams =
58+
BuiltMap<String, Object>(params ?? {}).toBuilder()))));
59+
return completer.future;
60+
}
61+
62+
int newId() => _completerId++;
63+
64+
Future<void> close() async {
65+
await _connection.sink.close();
66+
}
67+
68+
@override
69+
Future disable() => throw UnimplementedError();
70+
71+
@override
72+
Future enable() => throw UnimplementedError();
73+
74+
@override
75+
Stream<T> eventStream<T>(String method, WipEventTransformer<T> transformer) =>
76+
throw UnimplementedError();
77+
78+
@override
79+
Future<String> getScriptSource(String scriptId) => throw UnimplementedError();
80+
81+
@override
82+
Stream<WipDomain> get onClosed => throw UnimplementedError();
83+
84+
@override
85+
Stream<GlobalObjectClearedEvent> get onGlobalObjectCleared =>
86+
throw UnimplementedError();
87+
88+
@override
89+
Stream<DebuggerPausedEvent> get onPaused => throw UnimplementedError();
90+
91+
@override
92+
Stream<DebuggerResumedEvent> get onResumed => throw UnimplementedError();
93+
94+
@override
95+
Stream<ScriptParsedEvent> get onScriptParsed => throw UnimplementedError();
96+
97+
@override
98+
Future pause() => throw UnimplementedError();
99+
100+
@override
101+
Future resume() => throw UnimplementedError();
102+
103+
@override
104+
Map<String, WipScript> get scripts => throw UnimplementedError();
105+
106+
@override
107+
Future setPauseOnExceptions(PauseState state) => throw UnimplementedError();
108+
109+
@override
110+
Future stepInto() => throw UnimplementedError();
111+
112+
@override
113+
Future stepOut() => throw UnimplementedError();
114+
115+
@override
116+
Future stepOver() => throw UnimplementedError();
117+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
8+
import 'package:dwds/data/extension_request.dart';
9+
import 'package:dwds/data/serializers.dart';
10+
import 'package:dwds/src/servers/extension_debugger.dart';
11+
import 'package:pedantic/pedantic.dart';
12+
import 'package:test/test.dart';
13+
14+
import 'fixtures/fakes.dart';
15+
16+
FakeSseConnection connection;
17+
ExtensionDebugger extensionDebugger;
18+
19+
void main() async {
20+
setUp(() async {
21+
connection = FakeSseConnection();
22+
extensionDebugger = ExtensionDebugger(connection);
23+
});
24+
25+
test('can send a command & receive a response', () async {
26+
var extensionResponse = ExtensionResponse((b) => b
27+
..result = jsonEncode({
28+
'result': {'value': 3.14}
29+
})
30+
..id = 0
31+
..success = true);
32+
var resultCompleter = Completer();
33+
unawaited(extensionDebugger.sendCommand('Runtime.evaluate',
34+
params: {'expression': '\$pi'}).then((response) {
35+
resultCompleter.complete(response);
36+
}));
37+
connection.controller
38+
.add(jsonEncode(serializers.serialize(extensionResponse)));
39+
var response = await resultCompleter.future;
40+
expect(response.result['value'], 3.14);
41+
});
42+
}

dwds/test/fixtures/fakes.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
6+
7+
import 'package:async/src/stream_sink_transformer.dart';
58
import 'package:dwds/src/debugging/inspector.dart';
69
import 'package:dwds/src/utilities/domain.dart';
10+
import 'package:sse/server/sse_handler.dart';
11+
import 'package:stream_channel/src/stream_channel_transformer.dart';
12+
import 'package:stream_channel/stream_channel.dart';
713

814
/// A library of fake/stub implementations of our classes and their supporting
915
/// classes (e.g. WipConnection) for unit testing.
@@ -89,3 +95,46 @@ class FakeRuntime extends WipRuntime {
8995
return results[resultsReturned++];
9096
}
9197
}
98+
99+
class FakeSseConnection implements SseConnection {
100+
@override
101+
StreamChannel<S> cast<S>() => null;
102+
103+
final _controller = StreamController<String>();
104+
105+
StreamController<String> get controller => _controller;
106+
107+
@override
108+
StreamChannel<String> changeSink(
109+
StreamSink<String> Function(StreamSink<String> sink) change) =>
110+
null;
111+
112+
@override
113+
StreamChannel<String> changeStream(
114+
Stream<String> Function(Stream<String> stream) change) =>
115+
null;
116+
117+
@override
118+
void pipe(StreamChannel<String> other) {}
119+
120+
@override
121+
StreamSink<String> get sink => controller.sink;
122+
123+
@override
124+
Stream<String> get stream => controller.stream;
125+
126+
@override
127+
StreamChannel<S> transform<S>(
128+
StreamChannelTransformer<S, String> transformer) =>
129+
null;
130+
131+
@override
132+
StreamChannel<String> transformSink(
133+
StreamSinkTransformer<String, String> transformer) =>
134+
null;
135+
136+
@override
137+
StreamChannel<String> transformStream(
138+
StreamTransformer<String, String> transformer) =>
139+
null;
140+
}

0 commit comments

Comments
 (0)