Skip to content

Commit 985a43c

Browse files
authored
start work on a testing plan for devtools (#5)
* start work on a testing plan for devtools * add initial two end-to-end integration tests * fix pubspec * review comments
1 parent 86d41cd commit 985a43c

File tree

12 files changed

+907
-5
lines changed

12 files changed

+907
-5
lines changed

analysis_options.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ analyzer:
2020
missing_required_param: warning
2121
# treat missing returns as a warning (not a hint)
2222
missing_return: warning
23+
exclude:
24+
- build/**
2325

2426
linter:
2527
rules:

lib/device/device.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,13 @@ class ExtensionTracker {
171171
if (result is Isolate) {
172172
final Isolate isolate = result;
173173

174-
for (String rpc in isolate.extensionRPCs) {
175-
if (!extensionToIsolatesMap.containsKey(rpc)) {
176-
extensionToIsolatesMap[rpc] = new Set<IsolateRef>();
174+
if (isolate.extensionRPCs != null) {
175+
for (String rpc in isolate.extensionRPCs) {
176+
if (!extensionToIsolatesMap.containsKey(rpc)) {
177+
extensionToIsolatesMap[rpc] = new Set<IsolateRef>();
178+
}
179+
extensionToIsolatesMap[rpc].add(isolateRef);
177180
}
178-
extensionToIsolatesMap[rpc].add(isolateRef);
179181
}
180182
}
181183

lib/main.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'framework/framework.dart';
1111
import 'globals.dart';
1212
import 'logging/logging.dart';
1313
import 'memory/memory.dart';
14+
import 'model/model.dart';
1415
import 'performance/performance.dart';
1516
import 'service.dart';
1617
import 'timeline/timeline.dart';
@@ -39,6 +40,8 @@ class PerfToolFramework extends Framework {
3940
addScreen(new LoggingScreen());
4041

4142
initGlobalUI();
43+
44+
initTestingModel();
4245
}
4346

4447
void initGlobalUI() {
@@ -80,6 +83,10 @@ class PerfToolFramework extends Framework {
8083
.listen(_rebuildIsolateSelect);
8184
}
8285

86+
void initTestingModel() {
87+
App.register(this);
88+
}
89+
8390
IsolateRef get currentIsolate => serviceInfo.isolateManager.selectedIsolate;
8491

8592
void _handleIsolateSelect() {

lib/model/model.dart

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
/// An application model used to programmatically query and drive the UI.
6+
///
7+
/// This is largely intended to aid in testing.
8+
9+
library model;
10+
11+
import 'dart:async';
12+
import 'dart:convert';
13+
import 'dart:js' as js;
14+
15+
import 'package:devtools/main.dart';
16+
17+
import '../framework/framework.dart';
18+
import '../logging/logging.dart';
19+
20+
// TODO(devoncarew): Only enable logging after enabled by the client.
21+
22+
class App {
23+
App(this.framework) {
24+
_register<void>('echo', echo);
25+
_register<void>('switchPage', switchPage);
26+
_register<String>('currentPageId', currentPageId);
27+
28+
// LoggingScreen
29+
_register<void>('logs.clearLogs', logsClearLogs);
30+
_register<int>('logs.logCount', logsLogCount);
31+
}
32+
33+
static void register(PerfToolFramework framework) {
34+
final App app = new App(framework);
35+
app._bind();
36+
}
37+
38+
final PerfToolFramework framework;
39+
40+
void _bind() {
41+
final js.JsObject binding = new js.JsObject.jsify(<dynamic, dynamic>{});
42+
binding['send'] = (String method, int id, dynamic arg) {
43+
try {
44+
final dynamic result = _dispatch(method, id, arg);
45+
Future<dynamic>.value(result).then((dynamic result) {
46+
_sendResponseResult(id, result);
47+
}).catchError((dynamic error, StackTrace stackTrace) {
48+
_sendReponseError(id, error, stackTrace);
49+
});
50+
} catch (error, stackTrace) {
51+
_sendReponseError(id, error, stackTrace);
52+
}
53+
};
54+
55+
js.context['devtools'] = binding;
56+
57+
_sendNotification('app.inited');
58+
}
59+
60+
Future<void> echo(dynamic message) async {
61+
_sendNotification('app.echo', message);
62+
}
63+
64+
Future<void> switchPage(dynamic pageId) async {
65+
final Screen screen = framework.getScreen(pageId);
66+
if (screen == null) {
67+
throw 'page $pageId not found';
68+
}
69+
framework.load(screen);
70+
71+
// TODO(devoncarew): Listen for and log framework page change events?
72+
}
73+
74+
Future<String> currentPageId([dynamic _]) async {
75+
return framework.current?.id;
76+
}
77+
78+
Future<void> logsClearLogs([dynamic _]) async {
79+
final LoggingScreen screen = framework.getScreen('logs');
80+
screen.loggingTable.setRows(<LogData>[]);
81+
}
82+
83+
Future<int> logsLogCount([dynamic _]) async {
84+
final LoggingScreen screen = framework.getScreen('logs');
85+
return screen.loggingTable.rows.length;
86+
}
87+
88+
void _sendNotification(String event, [dynamic params]) {
89+
final Map<String, dynamic> map = <String, dynamic>{
90+
'event': event,
91+
};
92+
if (params != null) {
93+
map['params'] = params;
94+
}
95+
print('[${jsonEncode(map)}]');
96+
}
97+
98+
void _sendResponseResult(int id, [dynamic result]) {
99+
final Map<String, dynamic> map = <String, dynamic>{
100+
'id': id,
101+
};
102+
if (result != null) {
103+
map['result'] = result;
104+
}
105+
print('[${jsonEncode(map)}]');
106+
}
107+
108+
void _sendReponseError(int id, dynamic error, StackTrace stackTrace) {
109+
final Map<String, dynamic> map = <String, dynamic>{
110+
'id': id,
111+
'error': <String, String>{
112+
'message': error.toString(),
113+
'stackTrace': stackTrace.toString(),
114+
},
115+
};
116+
print('[${jsonEncode(map)}]');
117+
}
118+
119+
dynamic _dispatch(String method, int id, dynamic arg) {
120+
final Handler<dynamic> handler = _handlers[method];
121+
if (handler != null) {
122+
return handler(arg);
123+
} else {
124+
print('handler not found for $method()');
125+
throw 'no handler found for $method()';
126+
}
127+
}
128+
129+
final Map<String, Handler<dynamic>> _handlers = <String, Handler<dynamic>>{};
130+
131+
void _register<T>(String idMethod, Handler<T> fn) {
132+
_handlers[idMethod] = fn;
133+
}
134+
}
135+
136+
typedef Future<T> Handler<T>(dynamic arg);

lib/utils.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
import 'dart:async';
66
import 'dart:convert';
7+
import 'dart:io';
78
import 'dart:math';
89

910
import 'package:intl/intl.dart';
11+
import 'package:path/path.dart' as path;
1012
import 'package:vm_service_lib/vm_service_lib.dart';
1113

1214
const String loremIpsum = '''
@@ -90,3 +92,19 @@ class Property<T> {
9092

9193
Stream<T> get onValueChange => _changeController.stream;
9294
}
95+
96+
/// The directory used to store per-user settings for Dart tooling.
97+
Directory getDartPrefsDirectory() {
98+
return new Directory(path.join(getUserHomeDir(), '.dart'));
99+
}
100+
101+
/// Return the user's home directory.
102+
String getUserHomeDir() {
103+
final String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME';
104+
final String value = Platform.environment[envKey];
105+
return value == null ? '.' : value;
106+
}
107+
108+
/// A typedef to represent a function taking no arguments and with no return
109+
/// value.
110+
typedef void VoidFunction();

pubspec.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ dependencies:
1010
intl: ^0.15.0
1111
logging: ^0.11.0
1212
meta: ^1.1.0
13+
path: ^1.6.0
1314
vm_service_lib: ^0.3.9
1415

1516
dev_dependencies:
1617
build_runner: any
1718
build_web_compilers: any
1819
test: ^1.0.0
20+
webdev: 0.2.4
21+
webkit_inspection_protocol: ^0.3.6

test/fixtures/logging_app.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:developer';
7+
import 'dart:io';
8+
9+
// Allow a test driver to communicate with this app through the controller.
10+
final Controller controller = new Controller();
11+
12+
void main() {
13+
print('starting app');
14+
log('starting app');
15+
16+
// Don't exit until it's indicated we should by the controller.
17+
new Timer(const Duration(days: 1), () {});
18+
}
19+
20+
class Controller {
21+
int count = 0;
22+
23+
void emitLog() {
24+
count++;
25+
26+
print('emitLog called');
27+
log('emit log $count', name: 'logging');
28+
}
29+
30+
void shutdown() {
31+
print('stopping app');
32+
log('stopping app');
33+
34+
exit(0);
35+
}
36+
}

0 commit comments

Comments
 (0)