Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: tools/testing/dart/android.dart

Issue 15567002: Added support for running dart2js tests on android devices (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 library android;
6
7 import "dart:io";
8 import "dart:async";
9 import "dart:core";
10 import "dart:utf";
11
12 import "utils.dart";
13
14 Future _executeCommand(String executable,
15 List<String> args,
16 [String stdin = ""]) {
17 return _executeCommandRaw(executable, args, stdin).then((results) => null);
18 }
19
20 Future _executeCommandGetOutput(String executable,
21 List<String> args,
22 [String stdin = ""]) {
23 return _executeCommandRaw(executable, args, stdin)
24 .then((results) => results[0]);
25 }
26
27 /**
28 * [_executeCommandRaw] will write [stdin] to the standard input of the created
29 * process and will return a tuple (stdout, stderr).
30 *
31 * If the exit code of the process was nonzero it will complete with an error.
32 * If starting the process failed, it will complete with an error as well.
33 */
34 Future _executeCommandRaw(String executable,
35 List<String> args,
36 [String stdin = ""]) {
37 Future<String> getOutput(Stream<List<int>> stream) {
38 return stream.transform(new StringDecoder())
39 .reduce(new StringBuffer(), (buf, data) {
40 buf.write(data);
ricow1 2013/05/21 18:30:43 indentation seems off here, hard to follow flow
kustermann 2013/05/22 09:30:58 Done.
41 return buf;
42 }).then((buf) => buf.toString());
43 }
44
45 DebugLogger.info("Running: '\$ $executable ${args.join(' ')}'");
46 return Process.start(executable, args).then((Process process) {
47 if (stdin != null && stdin != '') {
48 process.stdin.write(stdin);
49 }
50 process.stdin.close();
51
52 var futures = [getOutput(process.stdout),
53 getOutput(process.stderr),
54 process.exitCode];
55 return Future.wait(futures).then((results) {
56 bool success = results[2] == 0;
57 if (!success) {
58 var error = """Running: '\$ $executable ${args.join(' ')}' failed:
59 stdout: \n ${results[0]}
ricow1 2013/05/21 18:30:43 this may look sort of strange due to the whitespac
kustermann 2013/05/22 09:30:58 Done.
60 stderr: \n ${results[1]}
61 exitCode: \n ${results[2]}
62 """;
63 throw new Exception(error);
64 }
65 return [results[0], results[1]];
ricow1 2013/05/21 18:30:43 how about a ProcessResult like class for returning
kustermann 2013/05/22 09:30:58 I think there is no public ProcessResult construct
ricow1 2013/05/22 13:13:36 That is why I wrote ProcessResult _like_ class :-)
66 });
67 });
68 }
69
70 /**
71 * Helper class to loop through all adb ports.
72 *
73 * The ports come in pairs:
74 * - even number: console connection
75 * - odd number: adb connection
76 * Note that this code doesn't check if the ports are used.
77 */
78 class AdbServerPortPool {
79 static int MIN_PORT = 5554;
80 static int MAX_PORT = 5584;
81
82 static int _nextPort = MIN_PORT;
83
84 static int next() {
85 var port = _nextPort;
86 if (port > MAX_PORT) {
87 throw new Exception("All ports are used.");
88 }
89 _nextPort += 2;
90 return port;
91 }
92 }
93
94 /**
95 * Represents the interface to the emulator.
96 * New emulators can be launched by calling the static [launchNewEmulator]
97 * method.
98 */
99 class AndroidEmulator {
100 int _port;
101 Process _emulatorProcess;
102 AdbDevice _adbDevice;
103
104 int get port => _port;
105
106 AdbDevice get adbDevice => _adbDevice;
107
108 static Future<AndroidEmulator> launchNewEmulator(String avdName) {
109 var portNumber = AdbServerPortPool.next();
110 var args = ['-avd', '$avdName', '-port', "$portNumber" /*, '-gpu', 'on'*/];
111 return Process.start("emulator64-arm", args).then((Process process) {
112 var adbDevice = new AdbDevice('emulator-$portNumber');
113 return new AndroidEmulator(portNumber, adbDevice, process);
114 });
115 }
116
117 AndroidEmulator(this._port, this._adbDevice, this._emulatorProcess) {
ricow1 2013/05/21 18:30:43 since you are using private methods and instance v
kustermann 2013/05/22 09:30:58 Done.
118 Stream<String> getLines(Stream s) {
119 return s.transform(new StringDecoder()).transform(new LineTransformer());
120 }
121
122 getLines(_emulatorProcess.stdout).listen((line) {
123 log("stdout: ${line.trim()}");
124 });
125 getLines(_emulatorProcess.stderr).listen((line) {
126 log("stderr: ${line.trim()}");
127 });
128 _emulatorProcess.exitCode.then((exitCode) {
129 log("emulator exited with exitCode: $exitCode.");
130 });
131 }
132
133 Future<bool> kill() {
134 var completer = new Completer();
135 if (_emulatorProcess.kill()) {
136 _emulatorProcess.exitCode.then((exitCode) {
137 completer.complete(true);
138 });
139 } else {
140 log("Sending kill signal to emulator process failed");
141 completer.complete(false);
142 }
143 return completer.future;
144 }
145
146 void log(String msg) {
147 DebugLogger.info("AndroidEmulator(${_adbDevice.deviceId}): $msg");
148 }
149 }
150
151 /**
152 * Helper class to create avd device configurations.
153 */
154 class AndroidHelper {
155 static Future createAvd(String name, String target) {
156 var args = ['--silent', 'create', 'avd', '--name', '$name',
157 '--target', '$target', '--force', '--abi', 'armeabi-v7a'];
158 return _executeCommand("android", args, "\n\n\n\n");
ricow1 2013/05/21 18:30:43 add a comment stating why we add 4 new lines to st
kustermann 2013/05/22 09:30:58 Done. I don't remember if we actually need 4 of th
159 }
160 }
161
162 /**
163 * Used for communicating with an emulator or with a real device.
164 */
165 class AdbDevice {
166 static const _adbServerStartupTime = const Duration(seconds: 3);
167 String _deviceId;
168
169 String get deviceId => _deviceId;
170
171 AdbDevice(this._deviceId);
172
173 /**
174 * Blocks execution until the device is online
175 */
176 Future waitForDevice() {
177 return _adbCommand(['wait-for-device']);
178 }
179
180 /**
181 * Polls the 'sys.boot_completed' property. Returns as soon as the property is
182 * 1.
183 */
184 Future waitForBootCompleted() {
185 var timeout = const Duration(seconds: 2);
186 var completer = new Completer();
187
188 checkUntilBooted() {
189 _adbCommandGetOutput(['shell', 'getprop', 'sys.boot_completed'])
190 .then((String stdout) {
191 stdout = stdout.trim();
192 if (stdout == '1') {
193 completer.complete();
194 } else {
195 new Timer(timeout, checkUntilBooted);
196 }
197 }).catchError((error) {
198 new Timer(timeout, checkUntilBooted);
199 });
200 }
201 checkUntilBooted();
202 return completer.future;
203 }
204
205 /**
206 * Put adb in root mode.
207 */
208 Future adbRoot() {
209 var adbRootCompleter = new Completer();
210 return _adbCommand(['root']).then((_) {
211 new Timer(_adbServerStartupTime, () => adbRootCompleter.complete(true));
212 }).catchError((error) => adbRootCompleter.completeError(error));
213 return adbRootCompleter.future;
214 }
215
216 /**
217 * Download data form the device.
218 */
219 Future pullData(Path remote, Path local) {
220 return _adbCommand(['pull', '$remote', '$local']);
221 }
222
223 /**
224 * Upload data to the device.
225 */
226 Future pushData(Path local, Path remote) {
227 return _adbCommand(['push', '$local', '$remote']);
228 }
229
230 /**
231 * Change permission of directory recursively.
232 */
233 Future chmod(String mode, Path directory) {
234 var arguments = ['shell', 'chmod', '-R', mode, '$directory'];
235 return _adbCommand(arguments);
236 }
237
238 /**
239 * Install an application on the device.
240 */
241 Future installApk(Path filename) {
242 return _adbCommand(
243 ['install', '-i', 'com.google.android.feedback', '-r', '$filename']);
244 }
245
246 /**
247 * Start the given intent on the device.
248 */
249 Future startActivity(Intent intent) {
250 var arguments = ['shell', 'am', 'start', '-W',
251 '-a', intent.action,
252 '-n', "${intent.package}/${intent.activity}"];
253 if (intent.dataUri != null) {
254 arguments.addAll(['-d', intent.dataUri]);
255 }
256 return _adbCommand(arguments);
257 }
258
259 /**
260 * Force to stop everything associated with [package].
261 */
262 Future forceStop(String package) {
263 var arguments = ['shell', 'am', 'force-stop', package];
264 return _adbCommand(arguments);
265 }
266
267 /**
268 * Kill all background processes.
269 */
270 Future killAll() {
271 var arguments = ['shell', 'am', 'kill-all'];
272 return _adbCommand(arguments);
273 }
274
275 Future _adbCommand(List<String> adbArgs) {
276 if (_deviceId != null) {
277 var extendedAdbArgs = ['-s', _deviceId];
278 extendedAdbArgs.addAll(adbArgs);
279 adbArgs = extendedAdbArgs;
280 }
281 return _executeCommand("adb", adbArgs);
282 }
283
284 Future<String> _adbCommandGetOutput(List<String> adbArgs) {
285 if (_deviceId != null) {
286 var extendedAdbArgs = ['-s', _deviceId];
287 extendedAdbArgs.addAll(adbArgs);
288 adbArgs = extendedAdbArgs;
289 }
290 return _executeCommandGetOutput("adb", adbArgs);
291 }
292 }
293
294 /**
295 * Helper to list all adb devicess available.
296 */
297 class AdbHelper {
298 static RegExp _deviceLineRegexp =
299 new RegExp(r'^([a-zA-Z0-9_-]+)[ \t]+device$', multiLine: true);
300
301 static Future<List<String>> listDevices() {
302 return Process.run('adb', ['devices']).then((ProcessResult result) {
303 if (result.exitCode != 0) {
304 throw new Exception("Could not list devices [stdout: ${result.stdout},"
305 "stderr: ${result.stderr}]");
306 }
307 return _deviceLineRegexp.allMatches(result.stdout).map((Match m) => m.grou p(1)).toList();
ricow1 2013/05/21 18:30:43 long line
kustermann 2013/05/22 09:30:58 Done.
308 });
309 }
310 }
311
312 /**
313 * Represents an android intent.
314 */
315 class Intent {
316 String action;
317 String package;
318 String activity;
319 String dataUri;
320
321 Intent(this.action, this.package, this.activity, [this.dataUri]);
322 }
323
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698