| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |