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

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

Issue 1859973002: Autoformat tools/testing/dart (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Format whole directory Created 4 years, 8 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
« no previous file with comments | « tools/testing/dart/android.dart ('k') | tools/testing/dart/browser_perf_testing/pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 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 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 library browser; 4 library browser;
5 5
6 import "dart:async"; 6 import "dart:async";
7 import "dart:convert" show LineSplitter, UTF8, JSON; 7 import "dart:convert" show LineSplitter, UTF8, JSON;
8 import "dart:core"; 8 import "dart:core";
9 import "dart:io"; 9 import "dart:io";
10 import "dart:math" show max, min; 10 import "dart:math" show max, min;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 60
61 /** Print everything (stdout, stderr, usageLog) whenever we add to it */ 61 /** Print everything (stdout, stderr, usageLog) whenever we add to it */
62 bool debugPrint = false; 62 bool debugPrint = false;
63 63
64 // This future returns when the process exits. It is also the return value 64 // This future returns when the process exits. It is also the return value
65 // of close() 65 // of close()
66 Future done; 66 Future done;
67 67
68 Browser(); 68 Browser();
69 69
70 factory Browser.byName(String name, 70 factory Browser.byName(String name, String executablePath,
71 String executablePath, 71 [bool checkedMode = false]) {
72 [bool checkedMode = false]) {
73 var browser; 72 var browser;
74 if (name == 'firefox') { 73 if (name == 'firefox') {
75 browser = new Firefox(); 74 browser = new Firefox();
76 } else if (name == 'chrome') { 75 } else if (name == 'chrome') {
77 browser = new Chrome(); 76 browser = new Chrome();
78 } else if (name == 'dartium') { 77 } else if (name == 'dartium') {
79 browser = new Dartium(checkedMode); 78 browser = new Dartium(checkedMode);
80 } else if (name == 'safari') { 79 } else if (name == 'safari') {
81 browser = new Safari(); 80 browser = new Safari();
82 } else if (name == 'safarimobilesim') { 81 } else if (name == 'safarimobilesim') {
83 browser = new SafariMobileSimulator(); 82 browser = new SafariMobileSimulator();
84 } else if (name.startsWith('ie')) { 83 } else if (name.startsWith('ie')) {
85 browser = new IE(); 84 browser = new IE();
86 } else { 85 } else {
87 throw "Non supported browser"; 86 throw "Non supported browser";
88 } 87 }
89 browser._binary = executablePath; 88 browser._binary = executablePath;
90 return browser; 89 return browser;
91 } 90 }
92 91
93 static const List<String> SUPPORTED_BROWSERS = 92 static const List<String> SUPPORTED_BROWSERS = const [
94 const ['safari', 'ff', 'firefox', 'chrome', 'ie9', 'ie10', 93 'safari',
95 'ie11', 'dartium']; 94 'ff',
95 'firefox',
96 'chrome',
97 'ie9',
98 'ie10',
99 'ie11',
100 'dartium'
101 ];
96 102
97 static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = 103 static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const [
98 const ['ie11', 'ie10']; 104 'ie11',
105 'ie10'
106 ];
99 107
100 // TODO(kustermann): add standard support for chrome on android 108 // TODO(kustermann): add standard support for chrome on android
101 static bool supportedBrowser(String name) { 109 static bool supportedBrowser(String name) {
102 return SUPPORTED_BROWSERS.contains(name); 110 return SUPPORTED_BROWSERS.contains(name);
103 } 111 }
104 112
105 void _logEvent(String event) { 113 void _logEvent(String event) {
106 String toLog = "$this ($id) - $event \n"; 114 String toLog = "$this ($id) - $event \n";
107 if (debugPrint) print("usageLog: $toLog"); 115 if (debugPrint) print("usageLog: $toLog");
108 if (logger != null) logger(toLog); 116 if (logger != null) logger(toLog);
(...skipping 28 matching lines...) Expand all
137 } else { 145 } else {
138 _logEvent("The process is already dead."); 146 _logEvent("The process is already dead.");
139 return new Future.value(true); 147 return new Future.value(true);
140 } 148 }
141 } 149 }
142 150
143 /** 151 /**
144 * Start the browser using the supplied argument. 152 * Start the browser using the supplied argument.
145 * This sets up the error handling and usage logging. 153 * This sets up the error handling and usage logging.
146 */ 154 */
147 Future<bool> startBrowserProcess(String command, 155 Future<bool> startBrowserProcess(String command, List<String> arguments,
148 List<String> arguments, 156 {Map<String, String> environment}) {
149 {Map<String,String> environment}) { 157 return Process
150 return Process.start(command, arguments, environment: environment) 158 .start(command, arguments, environment: environment)
151 .then((startedProcess) { 159 .then((startedProcess) {
152 _logEvent("Started browser using $command ${arguments.join(' ')}"); 160 _logEvent("Started browser using $command ${arguments.join(' ')}");
153 process = startedProcess; 161 process = startedProcess;
154 // Used to notify when exiting, and as a return value on calls to 162 // Used to notify when exiting, and as a return value on calls to
155 // close(). 163 // close().
156 var doneCompleter = new Completer(); 164 var doneCompleter = new Completer();
157 done = doneCompleter.future; 165 done = doneCompleter.future;
158 166
159 Completer stdoutDone = new Completer(); 167 Completer stdoutDone = new Completer();
160 Completer stderrDone = new Completer(); 168 Completer stderrDone = new Completer();
161 169
162 bool stdoutIsDone = false; 170 bool stdoutIsDone = false;
163 bool stderrIsDone = false; 171 bool stderrIsDone = false;
164 StreamSubscription stdoutSubscription; 172 StreamSubscription stdoutSubscription;
165 StreamSubscription stderrSubscription; 173 StreamSubscription stderrSubscription;
166 174
167 // This timer is used to close stdio to the subprocess once we got 175 // This timer is used to close stdio to the subprocess once we got
168 // the exitCode. Sometimes descendants of the subprocess keep stdio 176 // the exitCode. Sometimes descendants of the subprocess keep stdio
169 // handles alive even though the direct subprocess is dead. 177 // handles alive even though the direct subprocess is dead.
170 Timer watchdogTimer; 178 Timer watchdogTimer;
171 179
172 void closeStdout([_]){ 180 void closeStdout([_]) {
173 if (!stdoutIsDone) { 181 if (!stdoutIsDone) {
174 stdoutDone.complete(); 182 stdoutDone.complete();
175 stdoutIsDone = true; 183 stdoutIsDone = true;
176 184
177 if (stderrIsDone && watchdogTimer != null) { 185 if (stderrIsDone && watchdogTimer != null) {
178 watchdogTimer.cancel(); 186 watchdogTimer.cancel();
179 } 187 }
180 } 188 }
181 } 189 }
182 190
183 void closeStderr([_]) { 191 void closeStderr([_]) {
184 if (!stderrIsDone) { 192 if (!stderrIsDone) {
185 stderrDone.complete(); 193 stderrDone.complete();
186 stderrIsDone = true; 194 stderrIsDone = true;
187 195
188 if (stdoutIsDone && watchdogTimer != null) { 196 if (stdoutIsDone && watchdogTimer != null) {
189 watchdogTimer.cancel(); 197 watchdogTimer.cancel();
190 } 198 }
191 } 199 }
192 } 200 }
193 201
194 stdoutSubscription = 202 stdoutSubscription =
195 process.stdout.transform(UTF8.decoder).listen((data) { 203 process.stdout.transform(UTF8.decoder).listen((data) {
196 _addStdout(data); 204 _addStdout(data);
197 }, onError: (error) { 205 }, onError: (error) {
198 // This should _never_ happen, but we really want this in the log 206 // This should _never_ happen, but we really want this in the log
199 // if it actually does due to dart:io or vm bug. 207 // if it actually does due to dart:io or vm bug.
200 _logEvent("An error occured in the process stdout handling: $error"); 208 _logEvent("An error occured in the process stdout handling: $error");
201 }, onDone: closeStdout); 209 }, onDone: closeStdout);
202 210
203 stderrSubscription = 211 stderrSubscription =
204 process.stderr.transform(UTF8.decoder).listen((data) { 212 process.stderr.transform(UTF8.decoder).listen((data) {
205 _addStderr(data); 213 _addStderr(data);
206 }, onError: (error) { 214 }, onError: (error) {
207 // This should _never_ happen, but we really want this in the log 215 // This should _never_ happen, but we really want this in the log
208 // if it actually does due to dart:io or vm bug. 216 // if it actually does due to dart:io or vm bug.
209 _logEvent("An error occured in the process stderr handling: $error"); 217 _logEvent("An error occured in the process stderr handling: $error");
210 }, onDone: closeStderr); 218 }, onDone: closeStderr);
211 219
212 process.exitCode.then((exitCode) { 220 process.exitCode.then((exitCode) {
213 _logEvent("Browser closed with exitcode $exitCode"); 221 _logEvent("Browser closed with exitcode $exitCode");
214 222
215 if (!stdoutIsDone || !stderrIsDone) { 223 if (!stdoutIsDone || !stderrIsDone) {
216 watchdogTimer = new Timer(MAX_STDIO_DELAY, () { 224 watchdogTimer = new Timer(MAX_STDIO_DELAY, () {
217 DebugLogger.warning( 225 DebugLogger
218 "$MAX_STDIO_DELAY_PASSED_MESSAGE (browser: $this)"); 226 .warning("$MAX_STDIO_DELAY_PASSED_MESSAGE (browser: $this)");
219 watchdogTimer = null; 227 watchdogTimer = null;
220 stdoutSubscription.cancel(); 228 stdoutSubscription.cancel();
221 stderrSubscription.cancel(); 229 stderrSubscription.cancel();
222 closeStdout(); 230 closeStdout();
223 closeStderr(); 231 closeStderr();
224 }); 232 });
225 } 233 }
226 234
227 Future.wait([stdoutDone.future, stderrDone.future]).then((_) { 235 Future.wait([stdoutDone.future, stderrDone.future]).then((_) {
228 process = null; 236 process = null;
(...skipping 19 matching lines...) Expand all
248 256
249 void resetTestBrowserOutput() { 257 void resetTestBrowserOutput() {
250 _testBrowserOutput = new BrowserOutput(); 258 _testBrowserOutput = new BrowserOutput();
251 } 259 }
252 260
253 /** 261 /**
254 * Add useful info about the browser to the _testBrowserOutput.stdout, 262 * Add useful info about the browser to the _testBrowserOutput.stdout,
255 * where it will be reported for failing tests. Used to report which 263 * where it will be reported for failing tests. Used to report which
256 * android device a failing test is running on. 264 * android device a failing test is running on.
257 */ 265 */
258 void logBrowserInfoToTestBrowserOutput() { } 266 void logBrowserInfoToTestBrowserOutput() {}
259 267
260 String toString(); 268 String toString();
261 269
262 /** Starts the browser loading the given url */ 270 /** Starts the browser loading the given url */
263 Future<bool> start(String url); 271 Future<bool> start(String url);
264 } 272 }
265 273
266 class Safari extends Browser { 274 class Safari extends Browser {
267 /** 275 /**
268 * We get the safari version by parsing a version file 276 * We get the safari version by parsing a version file
269 */ 277 */
270 static const String versionFile = 278 static const String versionFile =
271 "/Applications/Safari.app/Contents/version.plist"; 279 "/Applications/Safari.app/Contents/version.plist";
272 280
273 /** 281 /**
274 * Directories where safari stores state. We delete these if the deleteCache 282 * Directories where safari stores state. We delete these if the deleteCache
275 * is set 283 * is set
276 */ 284 */
277 static const List<String> CACHE_DIRECTORIES = 285 static const List<String> CACHE_DIRECTORIES = const [
278 const ["Library/Caches/com.apple.Safari", 286 "Library/Caches/com.apple.Safari",
279 "Library/Safari", 287 "Library/Safari",
280 "Library/Saved Application State/com.apple.Safari.savedState", 288 "Library/Saved Application State/com.apple.Safari.savedState",
281 "Library/Caches/Metadata/Safari"]; 289 "Library/Caches/Metadata/Safari"
282 290 ];
283 291
284 Future<bool> allowPopUps() { 292 Future<bool> allowPopUps() {
285 var command = "defaults"; 293 var command = "defaults";
286 var args = ["write", "com.apple.safari", 294 var args = [
287 "com.apple.Safari.ContentPageGroupIdentifier." 295 "write",
288 "WebKit2JavaScriptCanOpenWindowsAutomatically", 296 "com.apple.safari",
289 "1"]; 297 "com.apple.Safari.ContentPageGroupIdentifier."
298 "WebKit2JavaScriptCanOpenWindowsAutomatically",
299 "1"
300 ];
290 return Process.run(command, args).then((result) { 301 return Process.run(command, args).then((result) {
291 if (result.exitCode != 0) { 302 if (result.exitCode != 0) {
292 _logEvent("Could not disable pop-up blocking for safari"); 303 _logEvent("Could not disable pop-up blocking for safari");
293 return false; 304 return false;
294 } 305 }
295 return true; 306 return true;
296 }); 307 });
297 } 308 }
298 309
299 Future<bool> deleteIfExists(Iterator<String> paths) { 310 Future<bool> deleteIfExists(Iterator<String> paths) {
300 if (!paths.moveNext()) return new Future.value(true); 311 if (!paths.moveNext()) return new Future.value(true);
301 Directory directory = new Directory(paths.current); 312 Directory directory = new Directory(paths.current);
302 return directory.exists().then((exists) { 313 return directory.exists().then((exists) {
303 if (exists) { 314 if (exists) {
304 _logEvent("Deleting ${paths.current}"); 315 _logEvent("Deleting ${paths.current}");
305 return directory.delete(recursive: true) 316 return directory
306 » .then((_) => deleteIfExists(paths)) 317 .delete(recursive: true)
307 » .catchError((error) { 318 .then((_) => deleteIfExists(paths))
308 » _logEvent("Failure trying to delete ${paths.current}: $error"); 319 .catchError((error) {
309 » return false; 320 _logEvent("Failure trying to delete ${paths.current}: $error");
310 » }); 321 return false;
322 });
311 } else { 323 } else {
312 _logEvent("${paths.current} is not present"); 324 _logEvent("${paths.current} is not present");
313 return deleteIfExists(paths); 325 return deleteIfExists(paths);
314 } 326 }
315 }); 327 });
316 } 328 }
317 329
318 // Clears the cache if the static deleteCache flag is set. 330 // Clears the cache if the static deleteCache flag is set.
319 // Returns false if the command to actually clear the cache did not complete. 331 // Returns false if the command to actually clear the cache did not complete.
320 Future<bool> clearCache() { 332 Future<bool> clearCache() {
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 } 385 }
374 return clearCache().then((cleared) { 386 return clearCache().then((cleared) {
375 if (!cleared) { 387 if (!cleared) {
376 _logEvent("Could not clear cache"); 388 _logEvent("Could not clear cache");
377 return false; 389 return false;
378 } 390 }
379 // Get the version and log that. 391 // Get the version and log that.
380 return getVersion().then((version) { 392 return getVersion().then((version) {
381 _logEvent("Got version: $version"); 393 _logEvent("Got version: $version");
382 return Directory.systemTemp.createTemp().then((userDir) { 394 return Directory.systemTemp.createTemp().then((userDir) {
383 _cleanup = () { userDir.deleteSync(recursive: true); }; 395 _cleanup = () {
396 userDir.deleteSync(recursive: true);
397 };
384 _createLaunchHTML(userDir.path, url); 398 _createLaunchHTML(userDir.path, url);
385 var args = ["${userDir.path}/launch.html"]; 399 var args = ["${userDir.path}/launch.html"];
386 return startBrowserProcess(_binary, args); 400 return startBrowserProcess(_binary, args);
387 }); 401 });
388 }).catchError((error) { 402 }).catchError((error) {
389 _logEvent("Running $_binary --version failed with $error"); 403 _logEvent("Running $_binary --version failed with $error");
390 return false; 404 return false;
391 }); 405 });
392 }); 406 });
393 }); 407 });
394 } 408 }
395 409
396 String toString() => "Safari"; 410 String toString() => "Safari";
397 } 411 }
398 412
399
400 class Chrome extends Browser { 413 class Chrome extends Browser {
401 String _version = "Version not found yet"; 414 String _version = "Version not found yet";
402 415
403 Map<String, String> _getEnvironment() => null; 416 Map<String, String> _getEnvironment() => null;
404 417
405 Future<bool> _getVersion() { 418 Future<bool> _getVersion() {
406 if (Platform.isWindows) { 419 if (Platform.isWindows) {
407 // The version flag does not work on windows. 420 // The version flag does not work on windows.
408 // See issue: 421 // See issue:
409 // https://code.google.com/p/chromium/issues/detail?id=158372 422 // https://code.google.com/p/chromium/issues/detail?id=158372
(...skipping 13 matching lines...) Expand all
423 if (versionResult.exitCode != 0) { 436 if (versionResult.exitCode != 0) {
424 _logEvent("Failed to chrome get version"); 437 _logEvent("Failed to chrome get version");
425 _logEvent("Make sure $_binary is a valid program for running chrome"); 438 _logEvent("Make sure $_binary is a valid program for running chrome");
426 return false; 439 return false;
427 } 440 }
428 _version = versionResult.stdout; 441 _version = versionResult.stdout;
429 return true; 442 return true;
430 }); 443 });
431 } 444 }
432 445
433
434 Future<bool> start(String url) { 446 Future<bool> start(String url) {
435 _logEvent("Starting chrome browser on: $url"); 447 _logEvent("Starting chrome browser on: $url");
436 // Get the version and log that. 448 // Get the version and log that.
437 return _getVersion().then((success) { 449 return _getVersion().then((success) {
438 if (!success) return false; 450 if (!success) return false;
439 _logEvent("Got version: $_version"); 451 _logEvent("Got version: $_version");
440 452
441 return Directory.systemTemp.createTemp().then((userDir) { 453 return Directory.systemTemp.createTemp().then((userDir) {
442 _cleanup = () { userDir.deleteSync(recursive: true); }; 454 _cleanup = () {
443 var args = ["--user-data-dir=${userDir.path}", url, 455 userDir.deleteSync(recursive: true);
444 "--disable-extensions", "--disable-popup-blocking", 456 };
445 "--bwsi", "--no-first-run"]; 457 var args = [
446 return startBrowserProcess(_binary, args, environment: _getEnvironment() ); 458 "--user-data-dir=${userDir.path}",
459 url,
460 "--disable-extensions",
461 "--disable-popup-blocking",
462 "--bwsi",
463 "--no-first-run"
464 ];
465 return startBrowserProcess(_binary, args,
466 environment: _getEnvironment());
447 }); 467 });
448 }).catchError((e) { 468 }).catchError((e) {
449 _logEvent("Running $_binary --version failed with $e"); 469 _logEvent("Running $_binary --version failed with $e");
450 return false; 470 return false;
451 }); 471 });
452 } 472 }
453 473
454 String toString() => "Chrome"; 474 String toString() => "Chrome";
455 } 475 }
456 476
457
458 class SafariMobileSimulator extends Safari { 477 class SafariMobileSimulator extends Safari {
459 /** 478 /**
460 * Directories where safari simulator stores state. We delete these if the 479 * Directories where safari simulator stores state. We delete these if the
461 * deleteCache is set 480 * deleteCache is set
462 */ 481 */
463 static const List<String> CACHE_DIRECTORIES = 482 static const List<String> CACHE_DIRECTORIES = const [
464 const ["Library/Application Support/iPhone Simulator/7.1/Applications"]; 483 "Library/Application Support/iPhone Simulator/7.1/Applications"
484 ];
465 485
466 // Clears the cache if the static deleteCache flag is set. 486 // Clears the cache if the static deleteCache flag is set.
467 // Returns false if the command to actually clear the cache did not complete. 487 // Returns false if the command to actually clear the cache did not complete.
468 Future<bool> clearCache() { 488 Future<bool> clearCache() {
469 if (!Browser.deleteCache) return new Future.value(true); 489 if (!Browser.deleteCache) return new Future.value(true);
470 var home = Platform.environment['HOME']; 490 var home = Platform.environment['HOME'];
471 Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator; 491 Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator;
472 return deleteIfExists(iterator); 492 return deleteIfExists(iterator);
473 } 493 }
474 494
475 Future<bool> start(String url) { 495 Future<bool> start(String url) {
476 _logEvent("Starting safari mobile simulator browser on: $url"); 496 _logEvent("Starting safari mobile simulator browser on: $url");
477 return clearCache().then((success) { 497 return clearCache().then((success) {
478 if (!success) { 498 if (!success) {
479 _logEvent("Could not clear cache, exiting"); 499 _logEvent("Could not clear cache, exiting");
480 » return false; 500 return false;
481 } 501 }
482 var args = ["-SimulateApplication", 502 var args = [
483 "/Applications/Xcode.app/Contents/Developer/Platforms/" 503 "-SimulateApplication",
484 "iPhoneSimulator.platform/Developer/SDKs/" 504 "/Applications/Xcode.app/Contents/Developer/Platforms/"
485 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/" 505 "iPhoneSimulator.platform/Developer/SDKs/"
486 "MobileSafari", 506 "iPhoneSimulator7.1.sdk/Applications/MobileSafari.app/"
487 "-u", url]; 507 "MobileSafari",
488 return startBrowserProcess(_binary, args) 508 "-u",
489 .catchError((e) { 509 url
490 _logEvent("Running $_binary --version failed with $e"); 510 ];
491 return false; 511 return startBrowserProcess(_binary, args).catchError((e) {
492 }); 512 _logEvent("Running $_binary --version failed with $e");
513 return false;
514 });
493 }); 515 });
494 } 516 }
495 517
496 String toString() => "SafariMobileSimulator"; 518 String toString() => "SafariMobileSimulator";
497 } 519 }
498 520
499
500 class Dartium extends Chrome { 521 class Dartium extends Chrome {
501 final bool checkedMode; 522 final bool checkedMode;
502 523
503 Dartium(this.checkedMode); 524 Dartium(this.checkedMode);
504 525
505 Map<String, String> _getEnvironment() { 526 Map<String, String> _getEnvironment() {
506 var environment = new Map<String,String>.from(Platform.environment); 527 var environment = new Map<String, String>.from(Platform.environment);
507 // By setting this environment variable, dartium will forward "print()" 528 // By setting this environment variable, dartium will forward "print()"
508 // calls in dart to the top-level javascript function "dartPrint()" if 529 // calls in dart to the top-level javascript function "dartPrint()" if
509 // available. 530 // available.
510 environment['DART_FORWARDING_PRINT'] = '1'; 531 environment['DART_FORWARDING_PRINT'] = '1';
511 if (checkedMode) { 532 if (checkedMode) {
512 environment['DART_FLAGS'] = '--checked'; 533 environment['DART_FLAGS'] = '--checked';
513 } 534 }
514 return environment; 535 return environment;
515 } 536 }
516 537
517 String toString() => "Dartium"; 538 String toString() => "Dartium";
518 } 539 }
519 540
520 class IE extends Browser { 541 class IE extends Browser {
521 Future<String> getVersion() { 542 Future<String> getVersion() {
522 var args = ["query", 543 var args = [
523 "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer", 544 "query",
524 "/v", 545 "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Internet Explorer",
525 "svcVersion"]; 546 "/v",
547 "svcVersion"
548 ];
526 return Process.run("reg", args).then((result) { 549 return Process.run("reg", args).then((result) {
527 if (result.exitCode == 0) { 550 if (result.exitCode == 0) {
528 // The string we get back looks like this: 551 // The string we get back looks like this:
529 // HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer 552 // HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer
530 // version REG_SZ 9.0.8112.16421 553 // version REG_SZ 9.0.8112.16421
531 var findString = "REG_SZ"; 554 var findString = "REG_SZ";
532 var index = result.stdout.indexOf(findString); 555 var index = result.stdout.indexOf(findString);
533 if (index > 0) { 556 if (index > 0) {
534 return result.stdout.substring(index + findString.length).trim(); 557 return result.stdout.substring(index + findString.length).trim();
535 } 558 }
536 } 559 }
537 return "Could not get the version of internet explorer"; 560 return "Could not get the version of internet explorer";
538 }); 561 });
539 } 562 }
540 563
541 // Clears the recovery cache if the static deleteCache flag is set. 564 // Clears the recovery cache if the static deleteCache flag is set.
542 Future<bool> clearCache() { 565 Future<bool> clearCache() {
543 if (!Browser.deleteCache) return new Future.value(true); 566 if (!Browser.deleteCache) return new Future.value(true);
544 var localAppData = Platform.environment['LOCALAPPDATA']; 567 var localAppData = Platform.environment['LOCALAPPDATA'];
545 568
546 Directory dir = new Directory("$localAppData\\Microsoft\\" 569 Directory dir = new Directory("$localAppData\\Microsoft\\"
547 "Internet Explorer\\Recovery"); 570 "Internet Explorer\\Recovery");
548 return dir.delete(recursive: true) 571 return dir.delete(recursive: true).then((_) {
549 .then((_) { return true; }) 572 return true;
550 .catchError((error) { 573 }).catchError((error) {
551 _logEvent("Deleting recovery dir failed with $error"); 574 _logEvent("Deleting recovery dir failed with $error");
552 return false; 575 return false;
553 }); 576 });
554 } 577 }
555 578
556 Future<bool> start(String url) { 579 Future<bool> start(String url) {
557 _logEvent("Starting ie browser on: $url"); 580 _logEvent("Starting ie browser on: $url");
558 return clearCache().then((_) => getVersion()).then((version) { 581 return clearCache().then((_) => getVersion()).then((version) {
559 _logEvent("Got version: $version"); 582 _logEvent("Got version: $version");
560 return startBrowserProcess(_binary, [url]); 583 return startBrowserProcess(_binary, [url]);
561 }); 584 });
562 } 585 }
586
563 String toString() => "IE"; 587 String toString() => "IE";
564
565 } 588 }
566 589
567
568 class AndroidBrowserConfig { 590 class AndroidBrowserConfig {
569 final String name; 591 final String name;
570 final String package; 592 final String package;
571 final String activity; 593 final String activity;
572 final String action; 594 final String action;
573 AndroidBrowserConfig(this.name, this.package, this.activity, this.action); 595 AndroidBrowserConfig(this.name, this.package, this.activity, this.action);
574 } 596 }
575 597
576
577 final contentShellOnAndroidConfig = new AndroidBrowserConfig( 598 final contentShellOnAndroidConfig = new AndroidBrowserConfig(
578 'ContentShellOnAndroid', 599 'ContentShellOnAndroid',
579 'org.chromium.content_shell_apk', 600 'org.chromium.content_shell_apk',
580 '.ContentShellActivity', 601 '.ContentShellActivity',
581 'android.intent.action.VIEW'); 602 'android.intent.action.VIEW');
582 603
583 604 final dartiumOnAndroidConfig = new AndroidBrowserConfig('DartiumOnAndroid',
584 final dartiumOnAndroidConfig = new AndroidBrowserConfig( 605 'com.google.android.apps.chrome', '.Main', 'android.intent.action.VIEW');
585 'DartiumOnAndroid',
586 'com.google.android.apps.chrome',
587 '.Main',
588 'android.intent.action.VIEW');
589
590 606
591 class AndroidBrowser extends Browser { 607 class AndroidBrowser extends Browser {
592 final bool checkedMode; 608 final bool checkedMode;
593 AdbDevice _adbDevice; 609 AdbDevice _adbDevice;
594 AndroidBrowserConfig _config; 610 AndroidBrowserConfig _config;
595 611
596 AndroidBrowser(this._adbDevice, this._config, this.checkedMode, apkPath) { 612 AndroidBrowser(this._adbDevice, this._config, this.checkedMode, apkPath) {
597 _binary = apkPath; 613 _binary = apkPath;
598 } 614 }
599 615
600 Future<bool> start(String url) { 616 Future<bool> start(String url) {
601 var intent = new Intent( 617 var intent =
602 _config.action, _config.package, _config.activity, url); 618 new Intent(_config.action, _config.package, _config.activity, url);
603 return _adbDevice.waitForBootCompleted().then((_) { 619 return _adbDevice.waitForBootCompleted().then((_) {
604 return _adbDevice.forceStop(_config.package); 620 return _adbDevice.forceStop(_config.package);
605 }).then((_) { 621 }).then((_) {
606 return _adbDevice.killAll(); 622 return _adbDevice.killAll();
607 }).then((_) { 623 }).then((_) {
608 return _adbDevice.adbRoot(); 624 return _adbDevice.adbRoot();
609 }).then((_) { 625 }).then((_) {
610 return _adbDevice.setProp("DART_FORWARDING_PRINT", "1"); 626 return _adbDevice.setProp("DART_FORWARDING_PRINT", "1");
611 }).then((_) { 627 }).then((_) {
612 if (checkedMode) { 628 if (checkedMode) {
(...skipping 11 matching lines...) Expand all
624 Future<bool> close() { 640 Future<bool> close() {
625 if (_adbDevice != null) { 641 if (_adbDevice != null) {
626 return _adbDevice.forceStop(_config.package).then((_) { 642 return _adbDevice.forceStop(_config.package).then((_) {
627 return _adbDevice.killAll().then((_) => true); 643 return _adbDevice.killAll().then((_) => true);
628 }); 644 });
629 } 645 }
630 return new Future.value(true); 646 return new Future.value(true);
631 } 647 }
632 648
633 void logBrowserInfoToTestBrowserOutput() { 649 void logBrowserInfoToTestBrowserOutput() {
634 _testBrowserOutput.stdout.write( 650 _testBrowserOutput.stdout
635 'Android device id: ${_adbDevice.deviceId}\n'); 651 .write('Android device id: ${_adbDevice.deviceId}\n');
636 } 652 }
637 653
638 String toString() => _config.name; 654 String toString() => _config.name;
639 } 655 }
640 656
641
642 class AndroidChrome extends Browser { 657 class AndroidChrome extends Browser {
643 static const String viewAction = 'android.intent.action.VIEW'; 658 static const String viewAction = 'android.intent.action.VIEW';
644 static const String mainAction = 'android.intent.action.MAIN'; 659 static const String mainAction = 'android.intent.action.MAIN';
645 static const String chromePackage = 'com.android.chrome'; 660 static const String chromePackage = 'com.android.chrome';
646 static const String browserPackage = 'com.android.browser'; 661 static const String browserPackage = 'com.android.browser';
647 static const String firefoxPackage = 'org.mozilla.firefox'; 662 static const String firefoxPackage = 'org.mozilla.firefox';
648 static const String turnScreenOnPackage = 'com.google.dart.turnscreenon'; 663 static const String turnScreenOnPackage = 'com.google.dart.turnscreenon';
649 664
650 AdbDevice _adbDevice; 665 AdbDevice _adbDevice;
651 666
652 AndroidChrome(this._adbDevice); 667 AndroidChrome(this._adbDevice);
653 668
654 Future<bool> start(String url) { 669 Future<bool> start(String url) {
655 var browserIntent = new Intent( 670 var browserIntent =
656 viewAction, browserPackage, '.BrowserActivity', url); 671 new Intent(viewAction, browserPackage, '.BrowserActivity', url);
657 var chromeIntent = new Intent(viewAction, chromePackage, '.Main', url); 672 var chromeIntent = new Intent(viewAction, chromePackage, '.Main', url);
658 var firefoxIntent = new Intent(viewAction, firefoxPackage, '.App', url); 673 var firefoxIntent = new Intent(viewAction, firefoxPackage, '.App', url);
659 var turnScreenOnIntent = 674 var turnScreenOnIntent =
660 new Intent(mainAction, turnScreenOnPackage, '.Main'); 675 new Intent(mainAction, turnScreenOnPackage, '.Main');
661 676
662 var testing_resources_dir = 677 var testing_resources_dir =
663 new Path('third_party/android_testing_resources'); 678 new Path('third_party/android_testing_resources');
664 if (!new Directory(testing_resources_dir.toNativePath()).existsSync()) { 679 if (!new Directory(testing_resources_dir.toNativePath()).existsSync()) {
665 DebugLogger.error("$testing_resources_dir doesn't exist. Exiting now."); 680 DebugLogger.error("$testing_resources_dir doesn't exist. Exiting now.");
666 exit(1); 681 exit(1);
(...skipping 28 matching lines...) Expand all
695 Future<bool> close() { 710 Future<bool> close() {
696 if (_adbDevice != null) { 711 if (_adbDevice != null) {
697 return _adbDevice.forceStop(chromePackage).then((_) { 712 return _adbDevice.forceStop(chromePackage).then((_) {
698 return _adbDevice.killAll().then((_) => true); 713 return _adbDevice.killAll().then((_) => true);
699 }); 714 });
700 } 715 }
701 return new Future.value(true); 716 return new Future.value(true);
702 } 717 }
703 718
704 void logBrowserInfoToTestBrowserOutput() { 719 void logBrowserInfoToTestBrowserOutput() {
705 _testBrowserOutput.stdout.write( 720 _testBrowserOutput.stdout
706 'Android device id: ${_adbDevice.deviceId}\n'); 721 .write('Android device id: ${_adbDevice.deviceId}\n');
707 } 722 }
708 723
709 String toString() => "chromeOnAndroid"; 724 String toString() => "chromeOnAndroid";
710 } 725 }
711 726
712
713 class Firefox extends Browser { 727 class Firefox extends Browser {
714 static const String enablePopUp = 728 static const String enablePopUp =
715 'user_pref("dom.disable_open_during_load", false);'; 729 'user_pref("dom.disable_open_during_load", false);';
716 static const String disableDefaultCheck = 730 static const String disableDefaultCheck =
717 'user_pref("browser.shell.checkDefaultBrowser", false);'; 731 'user_pref("browser.shell.checkDefaultBrowser", false);';
718 static const String disableScriptTimeLimit = 732 static const String disableScriptTimeLimit =
719 'user_pref("dom.max_script_run_time", 0);'; 733 'user_pref("dom.max_script_run_time", 0);';
720 734
721 void _createPreferenceFile(var path) { 735 void _createPreferenceFile(var path) {
722 var file = new File("${path.toString()}/user.js"); 736 var file = new File("${path.toString()}/user.js");
(...skipping 11 matching lines...) Expand all
734 if (versionResult.exitCode != 0) { 748 if (versionResult.exitCode != 0) {
735 _logEvent("Failed to firefox get version"); 749 _logEvent("Failed to firefox get version");
736 _logEvent("Make sure $_binary is a valid program for running firefox"); 750 _logEvent("Make sure $_binary is a valid program for running firefox");
737 return new Future.value(false); 751 return new Future.value(false);
738 } 752 }
739 version = versionResult.stdout; 753 version = versionResult.stdout;
740 _logEvent("Got version: $version"); 754 _logEvent("Got version: $version");
741 755
742 return Directory.systemTemp.createTemp().then((userDir) { 756 return Directory.systemTemp.createTemp().then((userDir) {
743 _createPreferenceFile(userDir.path); 757 _createPreferenceFile(userDir.path);
744 _cleanup = () { userDir.deleteSync(recursive: true); }; 758 _cleanup = () {
745 var args = ["-profile", "${userDir.path}", 759 userDir.deleteSync(recursive: true);
746 "-no-remote", "-new-instance", url]; 760 };
747 var environment = new Map<String,String>.from(Platform.environment); 761 var args = [
762 "-profile",
763 "${userDir.path}",
764 "-no-remote",
765 "-new-instance",
766 url
767 ];
768 var environment = new Map<String, String>.from(Platform.environment);
748 environment["MOZ_CRASHREPORTER_DISABLE"] = "1"; 769 environment["MOZ_CRASHREPORTER_DISABLE"] = "1";
749 return startBrowserProcess(_binary, args, environment: environment); 770 return startBrowserProcess(_binary, args, environment: environment);
750
751 }); 771 });
752 }).catchError((e) { 772 }).catchError((e) {
753 _logEvent("Running $_binary --version failed with $e"); 773 _logEvent("Running $_binary --version failed with $e");
754 return false; 774 return false;
755 }); 775 });
756 } 776 }
757 777
758 String toString() => "Firefox"; 778 String toString() => "Firefox";
759 } 779 }
760 780
761
762 /** 781 /**
763 * Describes the current state of a browser used for testing. 782 * Describes the current state of a browser used for testing.
764 */ 783 */
765 class BrowserStatus { 784 class BrowserStatus {
766 Browser browser; 785 Browser browser;
767 BrowserTest currentTest; 786 BrowserTest currentTest;
768 787
769 // This is currently not used for anything except for error reporting. 788 // This is currently not used for anything except for error reporting.
770 // Given the usefulness of this in debugging issues this should not be 789 // Given the usefulness of this in debugging issues this should not be
771 // removed even when we have a really stable system. 790 // removed even when we have a really stable system.
772 BrowserTest lastTest; 791 BrowserTest lastTest;
773 bool timeout = false; 792 bool timeout = false;
774 Timer nextTestTimeout; 793 Timer nextTestTimeout;
775 Stopwatch timeSinceRestart = new Stopwatch()..start(); 794 Stopwatch timeSinceRestart = new Stopwatch()..start();
776 795
777 BrowserStatus(Browser this.browser); 796 BrowserStatus(Browser this.browser);
778 } 797 }
779 798
780
781 /** 799 /**
782 * Describes a single test to be run in the browser. 800 * Describes a single test to be run in the browser.
783 */ 801 */
784 class BrowserTest { 802 class BrowserTest {
785 // TODO(ricow): Add timeout callback instead of the string passing hack. 803 // TODO(ricow): Add timeout callback instead of the string passing hack.
786 Function doneCallback; 804 Function doneCallback;
787 String url; 805 String url;
788 int timeout; 806 int timeout;
789 String lastKnownMessage = ''; 807 String lastKnownMessage = '';
790 Stopwatch stopwatch; 808 Stopwatch stopwatch;
791 809
792 // This might be null 810 // This might be null
793 Duration delayUntilTestStarted; 811 Duration delayUntilTestStarted;
794 812
795 // We store this here for easy access when tests time out (instead of 813 // We store this here for easy access when tests time out (instead of
796 // capturing this in a closure) 814 // capturing this in a closure)
797 Timer timeoutTimer; 815 Timer timeoutTimer;
798 816
799 // Used for debugging, this is simply a unique identifier assigned to each 817 // Used for debugging, this is simply a unique identifier assigned to each
800 // test. 818 // test.
801 int id; 819 int id;
802 static int _idCounter = 0; 820 static int _idCounter = 0;
803 821
804 BrowserTest(this.url, this.doneCallback, this.timeout) { 822 BrowserTest(this.url, this.doneCallback, this.timeout) {
805 id = _idCounter++; 823 id = _idCounter++;
806 } 824 }
807 825
808 String toJSON() => JSON.encode({'url': url, 826 String toJSON() => JSON.encode({'url': url, 'id': id, 'isHtmlTest': false});
809 'id': id,
810 'isHtmlTest': false});
811 } 827 }
812 828
813
814 /** 829 /**
815 * Describes a test with a custom HTML page to be run in the browser. 830 * Describes a test with a custom HTML page to be run in the browser.
816 */ 831 */
817 class HtmlTest extends BrowserTest { 832 class HtmlTest extends BrowserTest {
818 List<String> expectedMessages; 833 List<String> expectedMessages;
819 834
820 HtmlTest(url, doneCallback, timeout, this.expectedMessages) 835 HtmlTest(url, doneCallback, timeout, this.expectedMessages)
821 : super(url, doneCallback, timeout) { } 836 : super(url, doneCallback, timeout) {}
822 837
823 String toJSON() => JSON.encode({'url': url, 838 String toJSON() => JSON.encode({
824 'id': id, 839 'url': url,
825 'isHtmlTest': true, 840 'id': id,
826 'expectedMessages': expectedMessages}); 841 'isHtmlTest': true,
842 'expectedMessages': expectedMessages
843 });
827 } 844 }
828 845
829
830 /* Describes the output of running the test in a browser */ 846 /* Describes the output of running the test in a browser */
831 class BrowserTestOutput { 847 class BrowserTestOutput {
832 final Duration delayUntilTestStarted; 848 final Duration delayUntilTestStarted;
833 final Duration duration; 849 final Duration duration;
834 850
835 final String lastKnownMessage; 851 final String lastKnownMessage;
836 852
837 final BrowserOutput browserOutput; 853 final BrowserOutput browserOutput;
838 final bool didTimeout; 854 final bool didTimeout;
839 855
840 BrowserTestOutput( 856 BrowserTestOutput(this.delayUntilTestStarted, this.duration,
841 this.delayUntilTestStarted, this.duration, this.lastKnownMessage, 857 this.lastKnownMessage, this.browserOutput,
842 this.browserOutput, {this.didTimeout: false}); 858 {this.didTimeout: false});
843 } 859 }
844 860
845
846 /// Encapsulates all the functionality for running tests in browsers. 861 /// Encapsulates all the functionality for running tests in browsers.
847 /// Tests are added to the queue and the supplied callbacks are called 862 /// Tests are added to the queue and the supplied callbacks are called
848 /// when a test completes. 863 /// when a test completes.
849 /// BrowserTestRunner starts up to maxNumBrowser instances of the browser, 864 /// BrowserTestRunner starts up to maxNumBrowser instances of the browser,
850 /// to run the tests, starting them sequentially, as needed, so only 865 /// to run the tests, starting them sequentially, as needed, so only
851 /// one is starting up at a time. 866 /// one is starting up at a time.
852 /// BrowserTestRunner starts a BrowserTestingServer, which serves a 867 /// BrowserTestRunner starts a BrowserTestingServer, which serves a
853 /// driver page to the browsers, serves tests, and receives results and 868 /// driver page to the browsers, serves tests, and receives results and
854 /// requests back from the browsers. 869 /// requests back from the browsers.
855 class BrowserTestRunner { 870 class BrowserTestRunner {
(...skipping 16 matching lines...) Expand all
872 Function logger; 887 Function logger;
873 888
874 int browserIdCounter = 1; 889 int browserIdCounter = 1;
875 890
876 bool testingServerStarted = false; 891 bool testingServerStarted = false;
877 bool underTermination = false; 892 bool underTermination = false;
878 int numBrowserGetTestTimeouts = 0; 893 int numBrowserGetTestTimeouts = 0;
879 DateTime lastEmptyTestQueueTime = new DateTime.now(); 894 DateTime lastEmptyTestQueueTime = new DateTime.now();
880 String _currentStartingBrowserId; 895 String _currentStartingBrowserId;
881 List<BrowserTest> testQueue = new List<BrowserTest>(); 896 List<BrowserTest> testQueue = new List<BrowserTest>();
882 Map<String, BrowserStatus> browserStatus = 897 Map<String, BrowserStatus> browserStatus = new Map<String, BrowserStatus>();
883 new Map<String, BrowserStatus>();
884 898
885 var adbDeviceMapping = new Map<String, AdbDevice>(); 899 var adbDeviceMapping = new Map<String, AdbDevice>();
886 List<AdbDevice> idleAdbDevices; 900 List<AdbDevice> idleAdbDevices;
887 901
888 // This cache is used to guarantee that we never see double reporting. 902 // This cache is used to guarantee that we never see double reporting.
889 // If we do we need to provide developers with this information. 903 // If we do we need to provide developers with this information.
890 // We don't add urls to the cache until we have run it. 904 // We don't add urls to the cache until we have run it.
891 Map<int, String> testCache = new Map<int, String>(); 905 Map<int, String> testCache = new Map<int, String>();
892 906
893 Map<int, String> doubleReportingOutputs = new Map<int, String>(); 907 Map<int, String> doubleReportingOutputs = new Map<int, String>();
894 List<String> timedOut = []; 908 List<String> timedOut = [];
895 909
896 // We will start a new browser when the test queue hasn't been empty 910 // We will start a new browser when the test queue hasn't been empty
897 // recently, we have fewer than maxNumBrowsers browsers, and there is 911 // recently, we have fewer than maxNumBrowsers browsers, and there is
898 // no other browser instance currently starting up. 912 // no other browser instance currently starting up.
899 bool get queueWasEmptyRecently { 913 bool get queueWasEmptyRecently {
900 return testQueue.isEmpty || 914 return testQueue.isEmpty ||
901 new DateTime.now().difference(lastEmptyTestQueueTime) < 915 new DateTime.now().difference(lastEmptyTestQueueTime) <
902 MIN_NONEMPTY_QUEUE_TIME; 916 MIN_NONEMPTY_QUEUE_TIME;
903 } 917 }
904 918
905 // While a browser is starting, but has not requested its first test, its 919 // While a browser is starting, but has not requested its first test, its
906 // browserId is stored in _currentStartingBrowserId. 920 // browserId is stored in _currentStartingBrowserId.
907 // When no browser is currently starting, _currentStartingBrowserId is null. 921 // When no browser is currently starting, _currentStartingBrowserId is null.
908 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null; 922 bool get aBrowserIsCurrentlyStarting => _currentStartingBrowserId != null;
909 void markCurrentlyStarting(String id) { 923 void markCurrentlyStarting(String id) {
910 _currentStartingBrowserId = id; 924 _currentStartingBrowserId = id;
911 } 925 }
926
912 void markNotCurrentlyStarting(String id) { 927 void markNotCurrentlyStarting(String id) {
913 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null; 928 if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
914 } 929 }
915 930
916 // If [browserName] doesn't support opening new windows, we use new iframes 931 // If [browserName] doesn't support opening new windows, we use new iframes
917 // instead. 932 // instead.
918 bool get useIframe => 933 bool get useIframe =>
919 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName); 934 !Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
920 935
921 /// The optional testingServer parameter allows callers to pass in 936 /// The optional testingServer parameter allows callers to pass in
922 /// a testing server with different behavior than the default 937 /// a testing server with different behavior than the default
923 /// BrowserTestServer. The url handlers of the testingServer are 938 /// BrowserTestServer. The url handlers of the testingServer are
924 /// overwritten, so an existing handler can't be shared between instances. 939 /// overwritten, so an existing handler can't be shared between instances.
925 BrowserTestRunner(this.configuration, 940 BrowserTestRunner(
926 this.localIp, 941 this.configuration, this.localIp, this.browserName, this.maxNumBrowsers,
927 this.browserName, 942 {BrowserTestingServer this.testingServer}) {
928 this.maxNumBrowsers,
929 {BrowserTestingServer this.testingServer}) {
930 checkedMode = configuration['checked']; 943 checkedMode = configuration['checked'];
931 if (browserName == 'ff') browserName = 'firefox'; 944 if (browserName == 'ff') browserName = 'firefox';
932 } 945 }
933 946
934 Future start() async { 947 Future start() async {
935 if (testingServer == null) { 948 if (testingServer == null) {
936 testingServer = new BrowserTestingServer( 949 testingServer =
937 configuration, localIp, useIframe); 950 new BrowserTestingServer(configuration, localIp, useIframe);
938 } 951 }
939 await testingServer.start(); 952 await testingServer.start();
940 testingServer 953 testingServer
941 ..testDoneCallBack = handleResults 954 ..testDoneCallBack = handleResults
942 ..testStatusUpdateCallBack = handleStatusUpdate 955 ..testStatusUpdateCallBack = handleStatusUpdate
943 ..testStartedCallBack = handleStarted 956 ..testStartedCallBack = handleStarted
944 ..nextTestCallBack = getNextTest; 957 ..nextTestCallBack = getNextTest;
945 if (browserName == 'chromeOnAndroid') { 958 if (browserName == 'chromeOnAndroid') {
946 var idbNames = await AdbHelper.listDevices(); 959 var idbNames = await AdbHelper.listDevices();
947 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id))); 960 idleAdbDevices = new List.from(idbNames.map((id) => new AdbDevice(id)));
948 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length); 961 maxNumBrowsers = min(maxNumBrowsers, idleAdbDevices.length);
949 } 962 }
950 testingServerStarted = true; 963 testingServerStarted = true;
951 requestBrowser(); 964 requestBrowser();
952 } 965 }
953 966
954 /// requestBrowser() is called whenever we might want to start an additional 967 /// requestBrowser() is called whenever we might want to start an additional
(...skipping 12 matching lines...) Expand all
967 createBrowser(); 980 createBrowser();
968 } 981 }
969 982
970 String getNextBrowserId() => "BROWSER${browserIdCounter++}"; 983 String getNextBrowserId() => "BROWSER${browserIdCounter++}";
971 984
972 void createBrowser() { 985 void createBrowser() {
973 final String id = getNextBrowserId(); 986 final String id = getNextBrowserId();
974 final String url = testingServer.getDriverUrl(id); 987 final String url = testingServer.getDriverUrl(id);
975 Browser browser; 988 Browser browser;
976 if (browserName == 'chromeOnAndroid') { 989 if (browserName == 'chromeOnAndroid') {
977 AdbDevice device = idleAdbDevices.removeLast(); 990 AdbDevice device = idleAdbDevices.removeLast();
978 adbDeviceMapping[id] = device; 991 adbDeviceMapping[id] = device;
979 browser = new AndroidChrome(device); 992 browser = new AndroidChrome(device);
980 } else { 993 } else {
981 String path = Locations.getBrowserLocation(browserName, configuration); 994 String path = Locations.getBrowserLocation(browserName, configuration);
982 browser = new Browser.byName(browserName, path, checkedMode); 995 browser = new Browser.byName(browserName, path, checkedMode);
983 browser.logger = logger; 996 browser.logger = logger;
984 } 997 }
985 browser.id = id; 998 browser.id = id;
986 markCurrentlyStarting(id); 999 markCurrentlyStarting(id);
987 final status = new BrowserStatus(browser); 1000 final status = new BrowserStatus(browser);
(...skipping 13 matching lines...) Expand all
1001 if (status == null || status.timeout) { 1014 if (status == null || status.timeout) {
1002 // We don't do anything, this browser is currently being killed and 1015 // We don't do anything, this browser is currently being killed and
1003 // replaced. The browser here can be null if we decided to kill the 1016 // replaced. The browser here can be null if we decided to kill the
1004 // browser. 1017 // browser.
1005 } else if (status.currentTest != null) { 1018 } else if (status.currentTest != null) {
1006 status.currentTest.timeoutTimer.cancel(); 1019 status.currentTest.timeoutTimer.cancel();
1007 status.currentTest.stopwatch.stop(); 1020 status.currentTest.stopwatch.stop();
1008 1021
1009 if (status.currentTest.id != testId) { 1022 if (status.currentTest.id != testId) {
1010 print("Expected test id ${status.currentTest.id} for" 1023 print("Expected test id ${status.currentTest.id} for"
1011 "${status.currentTest.url}"); 1024 "${status.currentTest.url}");
1012 print("Got test id ${testId}"); 1025 print("Got test id ${testId}");
1013 print("Last test id was ${status.lastTest.id} for " 1026 print("Last test id was ${status.lastTest.id} for "
1014 "${status.currentTest.url}"); 1027 "${status.currentTest.url}");
1015 throw("This should never happen, wrong test id"); 1028 throw ("This should never happen, wrong test id");
1016 } 1029 }
1017 testCache[testId] = status.currentTest.url; 1030 testCache[testId] = status.currentTest.url;
1018 1031
1019 // Report that the test is finished now 1032 // Report that the test is finished now
1020 var browserTestOutput = new BrowserTestOutput( 1033 var browserTestOutput = new BrowserTestOutput(
1021 status.currentTest.delayUntilTestStarted, 1034 status.currentTest.delayUntilTestStarted,
1022 status.currentTest.stopwatch.elapsed, 1035 status.currentTest.stopwatch.elapsed,
1023 output, 1036 output,
1024 status.browser.testBrowserOutput); 1037 status.browser.testBrowserOutput);
1025 status.currentTest.doneCallback(browserTestOutput); 1038 status.currentTest.doneCallback(browserTestOutput);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
1058 createTimeoutTimer(status.currentTest, status); 1071 createTimeoutTimer(status.currentTest, status);
1059 status.currentTest.delayUntilTestStarted = 1072 status.currentTest.delayUntilTestStarted =
1060 status.currentTest.stopwatch.elapsed; 1073 status.currentTest.stopwatch.elapsed;
1061 } 1074 }
1062 } 1075 }
1063 1076
1064 void handleTimeout(BrowserStatus status) { 1077 void handleTimeout(BrowserStatus status) {
1065 // We simply kill the browser and starts up a new one! 1078 // We simply kill the browser and starts up a new one!
1066 // We could be smarter here, but it does not seems like it is worth it. 1079 // We could be smarter here, but it does not seems like it is worth it.
1067 if (status.timeout) { 1080 if (status.timeout) {
1068 DebugLogger.error( 1081 DebugLogger.error("Got test timeout for an already restarting browser");
1069 "Got test timeout for an already restarting browser");
1070 return; 1082 return;
1071 } 1083 }
1072 status.timeout = true; 1084 status.timeout = true;
1073 timedOut.add(status.currentTest.url); 1085 timedOut.add(status.currentTest.url);
1074 var id = status.browser.id; 1086 var id = status.browser.id;
1075 1087
1076 status.currentTest.stopwatch.stop(); 1088 status.currentTest.stopwatch.stop();
1077 status.browser.close().then((_) { 1089 status.browser.close().then((_) {
1078 var lastKnownMessage = 1090 var lastKnownMessage =
1079 'Dom could not be fetched, since the test timed out.'; 1091 'Dom could not be fetched, since the test timed out.';
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1120 status.nextTestTimeout = null; 1132 status.nextTestTimeout = null;
1121 } 1133 }
1122 if (testQueue.isEmpty) return null; 1134 if (testQueue.isEmpty) return null;
1123 1135
1124 // We are currently terminating this browser, don't start a new test. 1136 // We are currently terminating this browser, don't start a new test.
1125 if (status.timeout) return null; 1137 if (status.timeout) return null;
1126 1138
1127 // Restart Internet Explorer if it has been 1139 // Restart Internet Explorer if it has been
1128 // running for longer than RESTART_BROWSER_INTERVAL. The tests have 1140 // running for longer than RESTART_BROWSER_INTERVAL. The tests have
1129 // had flaky timeouts, and this may help. 1141 // had flaky timeouts, and this may help.
1130 if ((browserName == 'ie10' || 1142 if ((browserName == 'ie10' || browserName == 'ie11') &&
1131 browserName == 'ie11') &&
1132 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) { 1143 status.timeSinceRestart.elapsed > RESTART_BROWSER_INTERVAL) {
1133 var id = status.browser.id; 1144 var id = status.browser.id;
1134 // Reset stopwatch so we don't trigger again before restarting. 1145 // Reset stopwatch so we don't trigger again before restarting.
1135 status.timeout = true; 1146 status.timeout = true;
1136 status.browser.close().then((_) { 1147 status.browser.close().then((_) {
1137 // We don't want to start a new browser if we are terminating. 1148 // We don't want to start a new browser if we are terminating.
1138 if (underTermination) return; 1149 if (underTermination) return;
1139 removeBrowser(id); 1150 removeBrowser(id);
1140 requestBrowser(); 1151 requestBrowser();
1141 }); 1152 });
(...skipping 29 matching lines...) Expand all
1171 1182
1172 // Reset the test specific output information (stdout, stderr) on the 1183 // Reset the test specific output information (stdout, stderr) on the
1173 // browser, since a new test is being started. 1184 // browser, since a new test is being started.
1174 status.browser.resetTestBrowserOutput(); 1185 status.browser.resetTestBrowserOutput();
1175 status.browser.logBrowserInfoToTestBrowserOutput(); 1186 status.browser.logBrowserInfoToTestBrowserOutput();
1176 return test; 1187 return test;
1177 } 1188 }
1178 1189
1179 /// Creates a timer that is active while a test is running on a browser. 1190 /// Creates a timer that is active while a test is running on a browser.
1180 Timer createTimeoutTimer(BrowserTest test, BrowserStatus status) { 1191 Timer createTimeoutTimer(BrowserTest test, BrowserStatus status) {
1181 return new Timer(new Duration(seconds: test.timeout), 1192 return new Timer(new Duration(seconds: test.timeout), () {
1182 () { handleTimeout(status); }); 1193 handleTimeout(status);
1194 });
1183 } 1195 }
1184 1196
1185 /// Creates a timer that is active while no test is running on the 1197 /// Creates a timer that is active while no test is running on the
1186 /// browser. It has finished one test, and it has not requested a new test. 1198 /// browser. It has finished one test, and it has not requested a new test.
1187 Timer createNextTestTimer(BrowserStatus status) { 1199 Timer createNextTestTimer(BrowserStatus status) {
1188 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT, 1200 return new Timer(BrowserTestRunner.NEXT_TEST_TIMEOUT, () {
1189 () { handleNextTestTimeout(status); }); 1201 handleNextTestTimeout(status);
1202 });
1190 } 1203 }
1191 1204
1192 void handleNextTestTimeout(status) { 1205 void handleNextTestTimeout(status) {
1193 DebugLogger.warning( 1206 DebugLogger
1194 "Browser timed out before getting next test. Restarting"); 1207 .warning("Browser timed out before getting next test. Restarting");
1195 if (status.timeout) return; 1208 if (status.timeout) return;
1196 numBrowserGetTestTimeouts++; 1209 numBrowserGetTestTimeouts++;
1197 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) { 1210 if (numBrowserGetTestTimeouts >= MAX_NEXT_TEST_TIMEOUTS) {
1198 DebugLogger.error( 1211 DebugLogger.error(
1199 "Too many browser timeouts before getting next test. Terminating"); 1212 "Too many browser timeouts before getting next test. Terminating");
1200 terminate().then((_) => exit(1)); 1213 terminate().then((_) => exit(1));
1201 } else { 1214 } else {
1202 status.timeout = true; 1215 status.timeout = true;
1203 status.browser.close().then((_) { 1216 status.browser.close().then((_) {
1204 removeBrowser(status.browser.id); 1217 removeBrowser(status.browser.id);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1248 for (Browser b in browsers) { 1261 for (Browser b in browsers) {
1249 await b.close(); 1262 await b.close();
1250 } 1263 }
1251 testingServer.errorReportingServer.close(); 1264 testingServer.errorReportingServer.close();
1252 printDoubleReportingTests(); 1265 printDoubleReportingTests();
1253 } 1266 }
1254 } 1267 }
1255 1268
1256 class BrowserTestingServer { 1269 class BrowserTestingServer {
1257 final Map configuration; 1270 final Map configuration;
1271
1258 /// Interface of the testing server: 1272 /// Interface of the testing server:
1259 /// 1273 ///
1260 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch 1274 /// GET /driver/BROWSER_ID -- This will get the driver page to fetch
1261 /// and run tests ... 1275 /// and run tests ...
1262 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id" 1276 /// GET /next_test/BROWSER_ID -- returns "WAIT" "TERMINATE" or "url#id"
1263 /// where url is the test to run, and id is the id of the test. 1277 /// where url is the test to run, and id is the id of the test.
1264 /// If there are currently no available tests the waitSignal is send 1278 /// If there are currently no available tests the waitSignal is send
1265 /// back. If we are in the process of terminating the terminateSignal 1279 /// back. If we are in the process of terminating the terminateSignal
1266 /// is send back and the browser will stop requesting new tasks. 1280 /// is send back and the browser will stop requesting new tasks.
1267 /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed 1281 /// POST /report/BROWSER_ID?id=NUM -- sends back the dom of the executed
(...skipping 16 matching lines...) Expand all
1284 1298
1285 Function testDoneCallBack; 1299 Function testDoneCallBack;
1286 Function testStatusUpdateCallBack; 1300 Function testStatusUpdateCallBack;
1287 Function testStartedCallBack; 1301 Function testStartedCallBack;
1288 Function nextTestCallBack; 1302 Function nextTestCallBack;
1289 1303
1290 BrowserTestingServer(this.configuration, this.localIp, this.useIframe); 1304 BrowserTestingServer(this.configuration, this.localIp, this.useIframe);
1291 1305
1292 Future start() { 1306 Future start() {
1293 var test_driver_error_port = configuration['test_driver_error_port']; 1307 var test_driver_error_port = configuration['test_driver_error_port'];
1294 return HttpServer.bind(localIp, test_driver_error_port) 1308 return HttpServer
1295 .then(setupErrorServer) 1309 .bind(localIp, test_driver_error_port)
1296 .then(setupDispatchingServer); 1310 .then(setupErrorServer)
1311 .then(setupDispatchingServer);
1297 } 1312 }
1298 1313
1299 void setupErrorServer(HttpServer server) { 1314 void setupErrorServer(HttpServer server) {
1300 errorReportingServer = server; 1315 errorReportingServer = server;
1301 void errorReportingHandler(HttpRequest request) { 1316 void errorReportingHandler(HttpRequest request) {
1302 StringBuffer buffer = new StringBuffer(); 1317 StringBuffer buffer = new StringBuffer();
1303 request.transform(UTF8.decoder).listen((data) { 1318 request.transform(UTF8.decoder).listen((data) {
1304 buffer.write(data); 1319 buffer.write(data);
1305 }, onDone: () { 1320 }, onDone: () {
1306 String back = buffer.toString(); 1321 String back = buffer.toString();
1307 request.response.headers.set("Access-Control-Allow-Origin", "*"); 1322 request.response.headers.set("Access-Control-Allow-Origin", "*");
1308 request.response.done.catchError((error) { 1323 request.response.done.catchError((error) {
1309 DebugLogger.error("Error getting error from browser" 1324 DebugLogger.error("Error getting error from browser"
1310 "on uri ${request.uri.path}: $error"); 1325 "on uri ${request.uri.path}: $error");
1311 }); 1326 });
1312 request.response.close(); 1327 request.response.close();
1313 DebugLogger.error("Error from browser on : " 1328 DebugLogger.error("Error from browser on : "
1314 "${request.uri.path}, data: $back"); 1329 "${request.uri.path}, data: $back");
1315 }, onError: (error) { print(error); }); 1330 }, onError: (error) {
1331 print(error);
1332 });
1316 } 1333 }
1317 void errorHandler(e) { 1334 void errorHandler(e) {
1318 if (!underTermination) print("Error occured in httpserver: $e"); 1335 if (!underTermination) print("Error occured in httpserver: $e");
1319 } 1336 }
1320 errorReportingServer.listen(errorReportingHandler, onError: errorHandler); 1337 errorReportingServer.listen(errorReportingHandler, onError: errorHandler);
1321 } 1338 }
1322 1339
1323 void setupDispatchingServer(_) { 1340 void setupDispatchingServer(_) {
1324 DispatchingServer server = configuration['_servers_'].server; 1341 DispatchingServer server = configuration['_servers_'].server;
1325 void noCache(request) { 1342 void noCache(request) {
1326 request.response.headers.set("Cache-Control", 1343 request.response.headers
1327 "no-cache, no-store, must-revalidate"); 1344 .set("Cache-Control", "no-cache, no-store, must-revalidate");
1328 } 1345 }
1329 int testId(request) => 1346 int testId(request) => int.parse(request.uri.queryParameters["id"]);
1330 int.parse(request.uri.queryParameters["id"]);
1331 String browserId(request, prefix) => 1347 String browserId(request, prefix) =>
1332 request.uri.path.substring(prefix.length + 1); 1348 request.uri.path.substring(prefix.length + 1);
1333 1349
1334
1335 server.addHandler(reportPath, (HttpRequest request) { 1350 server.addHandler(reportPath, (HttpRequest request) {
1336 noCache(request); 1351 noCache(request);
1337 handleReport(request, browserId(request, reportPath), 1352 handleReport(request, browserId(request, reportPath), testId(request),
1338 testId(request), isStatusUpdate: false); 1353 isStatusUpdate: false);
1339 }); 1354 });
1340 server.addHandler(statusUpdatePath, (HttpRequest request) { 1355 server.addHandler(statusUpdatePath, (HttpRequest request) {
1341 noCache(request); 1356 noCache(request);
1342 handleReport(request, browserId(request, statusUpdatePath), 1357 handleReport(
1343 testId(request), isStatusUpdate: true); 1358 request, browserId(request, statusUpdatePath), testId(request),
1359 isStatusUpdate: true);
1344 }); 1360 });
1345 server.addHandler(startedPath, (HttpRequest request) { 1361 server.addHandler(startedPath, (HttpRequest request) {
1346 noCache(request); 1362 noCache(request);
1347 handleStarted(request, browserId(request, startedPath), 1363 handleStarted(request, browserId(request, startedPath), testId(request));
1348 testId(request));
1349 }); 1364 });
1350 1365
1351 makeSendPageHandler(String prefix) => (HttpRequest request) { 1366 makeSendPageHandler(String prefix) => (HttpRequest request) {
1352 noCache(request); 1367 noCache(request);
1353 var textResponse = ""; 1368 var textResponse = "";
1354 if (prefix == driverPath) { 1369 if (prefix == driverPath) {
1355 textResponse = getDriverPage(browserId(request, prefix)); 1370 textResponse = getDriverPage(browserId(request, prefix));
1356 request.response.headers.set('Content-Type', 'text/html'); 1371 request.response.headers.set('Content-Type', 'text/html');
1357 } 1372 }
1358 if (prefix == nextTestPath) { 1373 if (prefix == nextTestPath) {
1359 textResponse = getNextTest(browserId(request, prefix)); 1374 textResponse = getNextTest(browserId(request, prefix));
1360 request.response.headers.set('Content-Type', 'text/plain'); 1375 request.response.headers.set('Content-Type', 'text/plain');
1361 } 1376 }
1362 request.response.write(textResponse); 1377 request.response.write(textResponse);
1363 request.listen((_) {}, onDone: request.response.close); 1378 request.listen((_) {}, onDone: request.response.close);
1364 request.response.done.catchError((error) { 1379 request.response.done.catchError((error) {
1365 if (!underTermination) { 1380 if (!underTermination) {
1366 print("URI ${request.uri}"); 1381 print("URI ${request.uri}");
1367 print("Textresponse $textResponse"); 1382 print("Textresponse $textResponse");
1368 throw "Error returning content to browser: $error"; 1383 throw "Error returning content to browser: $error";
1369 } 1384 }
1370 }); 1385 });
1371 }; 1386 };
1372 server.addHandler(driverPath, makeSendPageHandler(driverPath)); 1387 server.addHandler(driverPath, makeSendPageHandler(driverPath));
1373 server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath)); 1388 server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath));
1374 } 1389 }
1375 1390
1376 void handleReport(HttpRequest request, String browserId, var testId, 1391 void handleReport(HttpRequest request, String browserId, var testId,
1377 {bool isStatusUpdate}) { 1392 {bool isStatusUpdate}) {
1378 StringBuffer buffer = new StringBuffer(); 1393 StringBuffer buffer = new StringBuffer();
1379 request.transform(UTF8.decoder).listen((data) { 1394 request.transform(UTF8.decoder).listen((data) {
1380 buffer.write(data); 1395 buffer.write(data);
1381 }, onDone: () { 1396 }, onDone: () {
1382 String back = buffer.toString(); 1397 String back = buffer.toString();
1383 request.response.close(); 1398 request.response.close();
1384 if (isStatusUpdate) { 1399 if (isStatusUpdate) {
1385 testStatusUpdateCallBack(browserId, back, testId); 1400 testStatusUpdateCallBack(browserId, back, testId);
1386 } else { 1401 } else {
1387 testDoneCallBack(browserId, back, testId); 1402 testDoneCallBack(browserId, back, testId);
1388 } 1403 }
1389 // TODO(ricow): We should do something smart if we get an error here. 1404 // TODO(ricow): We should do something smart if we get an error here.
1390 }, onError: (error) { DebugLogger.error("$error"); }); 1405 }, onError: (error) {
1406 DebugLogger.error("$error");
1407 });
1391 } 1408 }
1392 1409
1393 void handleStarted(HttpRequest request, String browserId, var testId) { 1410 void handleStarted(HttpRequest request, String browserId, var testId) {
1394 StringBuffer buffer = new StringBuffer(); 1411 StringBuffer buffer = new StringBuffer();
1395 // If an error occurs while receiving the data from the request stream, 1412 // If an error occurs while receiving the data from the request stream,
1396 // we don't handle it specially. We can safely ignore it, since the started 1413 // we don't handle it specially. We can safely ignore it, since the started
1397 // events are not crucial. 1414 // events are not crucial.
1398 request.transform(UTF8.decoder).listen((data) { 1415 request.transform(UTF8.decoder).listen((data) {
1399 buffer.write(data); 1416 buffer.write(data);
1400 }, onDone: () { 1417 }, onDone: () {
1401 String back = buffer.toString(); 1418 String back = buffer.toString();
1402 request.response.close(); 1419 request.response.close();
1403 testStartedCallBack(browserId, back, testId); 1420 testStartedCallBack(browserId, back, testId);
1404 }, onError: (error) { DebugLogger.error("$error"); }); 1421 }, onError: (error) {
1422 DebugLogger.error("$error");
1423 });
1405 } 1424 }
1406 1425
1407 String getNextTest(String browserId) { 1426 String getNextTest(String browserId) {
1408 var nextTest = nextTestCallBack(browserId); 1427 var nextTest = nextTestCallBack(browserId);
1409 if (underTermination) { 1428 if (underTermination) {
1410 // Browsers will be killed shortly, send them a terminate signal so 1429 // Browsers will be killed shortly, send them a terminate signal so
1411 // that they stop pulling. 1430 // that they stop pulling.
1412 return terminateSignal; 1431 return terminateSignal;
1413 } 1432 }
1414 return nextTest == null ? waitSignal : nextTest.toJSON(); 1433 return nextTest == null ? waitSignal : nextTest.toJSON();
1415 } 1434 }
1416 1435
1417 String getDriverUrl(String browserId) { 1436 String getDriverUrl(String browserId) {
1418 if (errorReportingServer == null) { 1437 if (errorReportingServer == null) {
1419 print("Bad browser testing server, you are not started yet. Can't " 1438 print("Bad browser testing server, you are not started yet. Can't "
1420 "produce driver url"); 1439 "produce driver url");
1421 exit(1); 1440 exit(1);
1422 // This should never happen - exit immediately; 1441 // This should never happen - exit immediately;
1423 } 1442 }
1424 var port = configuration['_servers_'].port; 1443 var port = configuration['_servers_'].port;
1425 return "http://$localIp:$port/driver/$browserId"; 1444 return "http://$localIp:$port/driver/$browserId";
1426 } 1445 }
1427 1446
1428
1429 String getDriverPage(String browserId) { 1447 String getDriverPage(String browserId) {
1430 var errorReportingUrl = 1448 var errorReportingUrl =
1431 "http://$localIp:${errorReportingServer.port}/$browserId"; 1449 "http://$localIp:${errorReportingServer.port}/$browserId";
1432 String driverContent = """ 1450 String driverContent = """
1433 <!DOCTYPE html><html> 1451 <!DOCTYPE html><html>
1434 <head> 1452 <head>
1435 <style> 1453 <style>
1436 body { 1454 body {
1437 margin: 0; 1455 margin: 0;
1438 } 1456 }
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after
1761 </div> 1779 </div>
1762 <div id="embedded_iframe_div" class="test box"> 1780 <div id="embedded_iframe_div" class="test box">
1763 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe> 1781 <iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe>
1764 </div> 1782 </div>
1765 </body> 1783 </body>
1766 </html> 1784 </html>
1767 """; 1785 """;
1768 return driverContent; 1786 return driverContent;
1769 } 1787 }
1770 } 1788 }
OLDNEW
« no previous file with comments | « tools/testing/dart/android.dart ('k') | tools/testing/dart/browser_perf_testing/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698