Revert "Revert "Make Safari tests more robust.""

This reverts commit b9cdd0e1b2.
This change should now work, because the buildbot recipes use the
renamed flag --reset-browser-configuration.

BUG=
R=ahe@google.com

Review URL: https://codereview.chromium.org/2070513002 .
This commit is contained in:
William Hesse 2016-06-15 04:29:33 -07:00
parent b9cdd0e1b2
commit f76236db74
8 changed files with 512 additions and 176 deletions

View file

@ -3296,7 +3296,6 @@ LayoutTests/fast/animation/request-animation-frame-within-callback_t01: Skip # T
LayoutTests/fast/backgrounds/background-repeat-computed-style_t01: RuntimeError # Please triage this failure
LayoutTests/fast/backgrounds/repeat/parsing-background-repeat_t01: RuntimeError # Please triage this failure
LayoutTests/fast/borders/border-image-width-numbers-computed-style_t01: RuntimeError # Please triage this failure
LayoutTests/fast/borders/border-radius-child_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/2d.fillText.gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/2d.text.draw.fill.maxWidth.gradient_t01: RuntimeError # Please triage this failure
LayoutTests/fast/canvas/2d.text.draw.fill.maxWidth.negative_t01: RuntimeError # Please triage this failure
@ -3420,7 +3419,6 @@ LayoutTests/fast/css/aspect-ratio-parsing-tests_t01: RuntimeError # Please triag
LayoutTests/fast/css/background-serialize_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/border-image-style-length_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/checked-pseudo-selector_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/collapsed-whitespace-reattach-in-style-recalc_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/computed-offset-with-zoom_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/content/content-none_t01: RuntimeError # Please triage this failure
LayoutTests/fast/css/content/content-normal_t01: RuntimeError # Please triage this failure
@ -3548,7 +3546,6 @@ LayoutTests/fast/dom/HTMLDialogElement/synthetic-click-inert_t01: RuntimeError #
LayoutTests/fast/dom/HTMLDialogElement/top-layer-position-relative_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLDialogElement/top-layer-position-static_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLDocument/active-element-gets-unforcusable_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/dom/HTMLDocument/set-focus-on-valid-element_t01: RuntimeError, Timeout # Please triage this failure
LayoutTests/fast/dom/HTMLElement/insertAdjacentHTML-errors_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLElement/set-inner-outer-optimization_t01: RuntimeError # Please triage this failure
LayoutTests/fast/dom/HTMLElement/spellcheck_t01: RuntimeError # Please triage this failure
@ -3910,7 +3907,6 @@ LayoutTests/fast/multicol/hit-test-above-or-below_t01: RuntimeError # Please tri
LayoutTests/fast/multicol/hit-test-end-of-column-with-line-height_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/hit-test-end-of-column_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/hit-test-float_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/hit-test-gap-between-pages-flipped_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/newmulticol/balance-images_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/orphans-relayout_t01: RuntimeError # Please triage this failure
LayoutTests/fast/multicol/vertical-lr/float-truncation_t01: RuntimeError # Please triage this failure
@ -3957,7 +3953,6 @@ LayoutTests/fast/storage/storage-disallowed-in-data-url_t01: RuntimeError # Plea
LayoutTests/fast/sub-pixel/cssom-subpixel-precision_t01: RuntimeError # Please triage this failure
LayoutTests/fast/sub-pixel/float-containing-block-with-margin_t01: RuntimeError # Please triage this failure
LayoutTests/fast/sub-pixel/shadows-computed-style_t01: RuntimeError # Please triage this failure
LayoutTests/fast/sub-pixel/table-rows-have-stable-height_t01: RuntimeError # Please triage this failure
LayoutTests/fast/svg/tabindex-focus_t01: RuntimeError # Please triage this failure
LayoutTests/fast/svg/whitespace-angle_t01: RuntimeError # Please triage this failure
LayoutTests/fast/svg/whitespace-integer_t01: RuntimeError # Please triage this failure
@ -4427,8 +4422,43 @@ WebPlatformTest/webstorage/event_local_key_t01: RuntimeError # Please triage thi
WebPlatformTest/webstorage/event_session_key_t01: RuntimeError # Please triage this failure
WebPlatformTest/webstorage/event_session_storagearea_t01: Pass, RuntimeError # Fails on 7.1. Please triage this failure
WebPlatformTest/webstorage/event_session_url_t01: Skip # Times out. Please triage this failure
WebPlatformTest/webstorage/storage_local_setitem_quotaexceedederr_t01: Skip # Makes the following test time out.
WebPlatformTest/webstorage/storage_session_setitem_quotaexceedederr_t01: Skip # Times out. Please triage this failure
LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video_t01: Skip
LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video-rgb565_t01: Skip
LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video-rgba5551_t01: Skip
LayoutTests/fast/dom/HTMLDocument/set-focus-on-valid-element_t01: Skip
LayoutTests/fast/canvas/webgl/context-lost_t01: Skip
LayoutTests/fast/canvas/canvas-strokePath-gradient-shadow_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/canvas/canvas-strokeRect-gradient-shadow_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/css-selector-text_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/ex-unit-with-no-x-height_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/font-property-priority_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/inherited-properties-rare-text_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/invalid-not-with-simple-selector-sequence_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/parsing-font-variant-ligatures_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/css/pseudo-valid-unapplied_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/dom/HTMLLinkElement/resolve-url-on-insertion_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/dom/css-selectorText_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/forms/datalist/datalist-child-validation_t01: RuntimeError # Fails 10 out of 10.
LayoutTests/fast/overflow/scrollbar-restored_t01: RuntimeError # Fails 10 out of 10.
LibTest/html/Element/isContentEditable_A01_t01: RuntimeError # Fails 10 out of 10.
LibTest/html/Element/isContentEditable_A02_t01: RuntimeError # Fails 10 out of 10.
LibTest/html/IFrameElement/isContentEditable_A01_t01: RuntimeError, Pass # Fails 19 out of 20.
LayoutTests/fast/flexbox/repaint-scrollbar_t01: Pass, RuntimeError # Fails 2 out of 10.
LibTest/html/Element/getBoundingClientRect_A01_t02: Pass, RuntimeError # Fails 3 out of 10.
LayoutTests/fast/css/focus-display-block-inline_t01: Pass, RuntimeError # Fails 5 out of 10.
LayoutTests/fast/css/pseudo-any_t01: Pass, RuntimeError # Fails 4 out of 10.
LayoutTests/fast/dom/partial-layout-overlay-scrollbars_t01: Pass, RuntimeError # Fails 8 out of 10.
LayoutTests/fast/forms/autofocus-input-css-style-change_t01: Pass, RuntimeError # Fails 7 out of 10.
LayoutTests/fast/forms/date-multiple-fields/date-multiple-fields-onblur-setvalue-onfocusremoved_t01: Pass, RuntimeError # Fails 6 out of 10.
LayoutTests/fast/canvas/webgl/glsl-conformance_t01: Skip # Times out 1 out of 20.
[ $compiler == dart2js && $runtime == safarimobilesim ]
LayoutTests/fast/alignment/parse-align-items_t01: RuntimeError # Please triage this failure
LayoutTests/fast/alignment/parse-align-self_t01: RuntimeError # Please triage this failure

View file

@ -292,13 +292,15 @@ input_element_test/supported_date: Fail
input_element_test/supported_datetime-local: Fail
touchevent_test/supported: Fail # Safari does not support TouchEvents
notification_test/constructors: Fail # Safari doesn't let us access the fields of the Notification to verify them.
[ $runtime == safari ]
indexeddb_1_test/functional: Skip # Times out. Issue 21433
indexeddb_2_test: RuntimeError # Issue 21433
indexeddb_4_test: RuntimeError # Issue 21433
indexeddb_5_test: RuntimeError # Issue 21433
js_test/JsArray: RuntimeError # Fails 10 out of 10.
indexeddb_3_test: Skip # Times out 1 out of 10.
[ $runtime == opera ]
blob_constructor_test: Fail
canvas_test: Fail

View file

@ -202,7 +202,7 @@ def TestStep(name, mode, system, compiler, runtime, targets, flags, arch):
else:
cmd.extend(['--progress=buildbot', '-v'])
cmd.append('--clear_browser_cache')
cmd.append('--reset-browser-configuration')
global IsFirstTestStepCall
if IsFirstTestStepCall:

27
tools/safari_factory_reset.py Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env python
# Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE file.
import os
import string
import subprocess
import sys
import utils
def Main():
args = sys.argv[1:]
tools_dir = os.path.dirname(os.path.realpath(__file__))
dart_script_name = os.path.join(
tools_dir, 'testing', 'dart', 'reset_safari.dart');
command = [utils.CheckedInSdkExecutable(),
'--checked', dart_script_name] + args
exit_code = subprocess.call(command)
utils.DiagnoseExitCode(exit_code, command)
return exit_code
if __name__ == '__main__':
sys.exit(Main())

View file

@ -14,6 +14,9 @@ import 'http_server.dart';
import 'path.dart';
import 'utils.dart';
import 'reset_safari.dart' show
killAndResetSafari;
class BrowserOutput {
final StringBuffer stdout = new StringBuffer();
final StringBuffer stderr = new StringBuffer();
@ -53,10 +56,10 @@ abstract class Browser {
String id;
/**
* Delete the browser specific caches on startup.
* Reset the browser to a known configuration on start-up.
* Browser specific implementations are free to ignore this.
*/
static bool deleteCache = false;
static bool resetBrowserConfiguration = false;
/** Print everything (stdout, stderr, usageLog) whenever we add to it */
bool debugPrint = false;
@ -105,6 +108,15 @@ abstract class Browser {
'ie10'
];
/// If [browserName] doesn't support Window.open, we use iframes instead.
static bool requiresIframe(String browserName) {
return !BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
}
static bool requiresFocus(String browserName) {
return browserName == "safari";
}
// TODO(kustermann): add standard support for chrome on android
static bool supportedBrowser(String name) {
return SUPPORTED_BROWSERS.contains(name);
@ -269,6 +281,13 @@ abstract class Browser {
/** Starts the browser loading the given url */
Future<bool> start(String url);
/// Called when the driver page is requested, that is, when the browser first
/// contacts the test server. At this time, it's safe to assume that the
/// browser process has started and opened its first window.
///
/// This is used by [Safari] to ensure the browser window has focus.
Future<Null> onDriverPageRequested() => new Future<Null>.value();
}
class Safari extends Browser {
@ -278,62 +297,48 @@ class Safari extends Browser {
static const String versionFile =
"/Applications/Safari.app/Contents/version.plist";
/**
* Directories where safari stores state. We delete these if the deleteCache
* is set
*/
static const List<String> CACHE_DIRECTORIES = const [
"Library/Caches/com.apple.Safari",
"Library/Safari",
"Library/Saved Application State/com.apple.Safari.savedState",
"Library/Caches/Metadata/Safari"
];
static const String safariBundleLocation = "/Applications/Safari.app/";
Future<bool> allowPopUps() {
var command = "defaults";
var args = [
"write",
"com.apple.safari",
"com.apple.Safari.ContentPageGroupIdentifier."
"WebKit2JavaScriptCanOpenWindowsAutomatically",
"1"
];
return Process.run(command, args).then((result) {
if (result.exitCode != 0) {
_logEvent("Could not disable pop-up blocking for safari");
return false;
}
return true;
});
}
Future<bool> deleteIfExists(Iterator<String> paths) {
if (!paths.moveNext()) return new Future.value(true);
Directory directory = new Directory(paths.current);
return directory.exists().then((exists) {
if (exists) {
_logEvent("Deleting ${paths.current}");
return directory
.delete(recursive: true)
.then((_) => deleteIfExists(paths))
.catchError((error) {
_logEvent("Failure trying to delete ${paths.current}: $error");
return false;
});
} else {
_logEvent("${paths.current} is not present");
return deleteIfExists(paths);
}
});
}
// Clears the cache if the static deleteCache flag is set.
// Clears the cache if the static resetBrowserConfiguration flag is set.
// Returns false if the command to actually clear the cache did not complete.
Future<bool> clearCache() {
if (!Browser.deleteCache) return new Future.value(true);
var home = Platform.environment['HOME'];
Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator;
return deleteIfExists(iterator);
Future<bool> resetConfiguration() async {
if (!Browser.resetBrowserConfiguration) return true;
Completer completer = new Completer();
handleUncaughtError(error, StackTrace stackTrace) {
if (!completer.isCompleted) {
completer.completeError(error, stackTrace);
} else {
throw new AsyncError(error, stackTrace);
}
}
Zone parent = Zone.current;
ZoneSpecification specification = new ZoneSpecification(
print: (Zone self, ZoneDelegate delegate, Zone zone, String line) {
delegate.run(parent, () {
_logEvent(line);
});
});
Future zoneWrapper() {
Uri safariUri = Uri.base.resolve(safariBundleLocation);
return new Future(() => killAndResetSafari(bundle: safariUri))
.then(completer.complete);
}
// We run killAndResetSafari in a Zone as opposed to running an external
// process. The Zone allows us to collect its output, and protect the rest
// of the test infrastructure against errors in it.
runZoned(
zoneWrapper, zoneSpecification: specification,
onError: handleUncaughtError);
try {
await completer.future;
return true;
} catch (error, st) {
_logEvent("Unable to reset Safari: $error$st");
return false;
}
}
Future<String> getVersion() {
@ -369,42 +374,58 @@ class Safari extends Browser {
});
}
void _createLaunchHTML(var path, var url) {
Future<Null> _createLaunchHTML(var path, var url) async {
var file = new File("${path}/launch.html");
var randomFile = file.openSync(mode: FileMode.WRITE);
var randomFile = await file.open(mode: FileMode.WRITE);
var content = '<script language="JavaScript">location = "$url"</script>';
randomFile.writeStringSync(content);
randomFile.close();
await randomFile.writeString(content);
await randomFile.close();
}
Future<bool> start(String url) {
Future<bool> start(String url) async {
_logEvent("Starting Safari browser on: $url");
return allowPopUps().then((success) {
if (!success) {
return false;
}
return clearCache().then((cleared) {
if (!cleared) {
_logEvent("Could not clear cache");
return false;
}
// Get the version and log that.
return getVersion().then((version) {
_logEvent("Got version: $version");
return Directory.systemTemp.createTemp().then((userDir) {
_cleanup = () {
userDir.deleteSync(recursive: true);
};
_createLaunchHTML(userDir.path, url);
var args = ["${userDir.path}/launch.html"];
return startBrowserProcess(_binary, args);
});
}).catchError((error) {
_logEvent("Running $_binary --version failed with $error");
return false;
});
});
});
if (!await resetConfiguration()) {
_logEvent("Could not clear cache");
return false;
}
String version;
try {
version = await getVersion();
} catch (error) {
_logEvent("Running $_binary --version failed with $error");
return false;
}
_logEvent("Got version: $version");
Directory userDir;
try {
userDir = await Directory.systemTemp.createTemp();
} catch (error) {
_logEvent("Error creating temporary directory: $error");
return false;
}
_cleanup = () {
userDir.deleteSync(recursive: true);
};
try {
await _createLaunchHTML(userDir.path, url);
} catch (error) {
_logEvent("Error creating launch HTML: $error");
return false;
}
var args = [
"-d", "-i", "-m", "-s", "-u", _binary,
"${userDir.path}/launch.html"];
try {
return startBrowserProcess("/usr/bin/caffeinate", args);
} catch (error) {
_logEvent("Error starting browser process: $error");
return false;
}
}
Future<Null> onDriverPageRequested() async {
await Process.run("/usr/bin/osascript",
['-e', 'tell application "Safari" to activate']);
}
String toString() => "Safari";
@ -477,16 +498,16 @@ class Chrome extends Browser {
class SafariMobileSimulator extends Safari {
/**
* Directories where safari simulator stores state. We delete these if the
* deleteCache is set
* resetBrowserConfiguration is set
*/
static const List<String> CACHE_DIRECTORIES = const [
"Library/Application Support/iPhone Simulator/7.1/Applications"
];
// Clears the cache if the static deleteCache flag is set.
// Clears the cache if the static resetBrowserConfiguration flag is set.
// Returns false if the command to actually clear the cache did not complete.
Future<bool> clearCache() {
if (!Browser.deleteCache) return new Future.value(true);
Future<bool> resetConfiguration() {
if (!Browser.resetBrowserConfiguration) return new Future.value(true);
var home = Platform.environment['HOME'];
Iterator iterator = CACHE_DIRECTORIES.map((s) => "$home/$s").iterator;
return deleteIfExists(iterator);
@ -494,7 +515,7 @@ class SafariMobileSimulator extends Safari {
Future<bool> start(String url) {
_logEvent("Starting safari mobile simulator browser on: $url");
return clearCache().then((success) {
return resetConfiguration().then((success) {
if (!success) {
_logEvent("Could not clear cache, exiting");
return false;
@ -561,9 +582,10 @@ class IE extends Browser {
});
}
// Clears the recovery cache if the static deleteCache flag is set.
Future<bool> clearCache() {
if (!Browser.deleteCache) return new Future.value(true);
// Clears the recovery cache if the static resetBrowserConfiguration flag is
// set.
Future<bool> resetConfiguration() {
if (!Browser.resetBrowserConfiguration) return new Future.value(true);
var localAppData = Platform.environment['LOCALAPPDATA'];
Directory dir = new Directory("$localAppData\\Microsoft\\"
@ -578,7 +600,7 @@ class IE extends Browser {
Future<bool> start(String url) {
_logEvent("Starting ie browser on: $url");
return clearCache().then((_) => getVersion()).then((version) {
return resetConfiguration().then((_) => getVersion()).then((version) {
_logEvent("Got version: $version");
return startBrowserProcess(_binary, [url]);
});
@ -876,12 +898,12 @@ class BrowserTestRunner {
static const Duration MIN_NONEMPTY_QUEUE_TIME = const Duration(seconds: 1);
final Map configuration;
BrowserTestingServer testingServer;
final BrowserTestingServer testingServer;
final String localIp;
String browserName;
int maxNumBrowsers;
bool checkedMode;
final String browserName;
final int maxNumBrowsers;
final bool checkedMode;
int numBrowsers = 0;
// Used to send back logs from the browser (start, stop etc)
Function logger;
@ -928,27 +950,23 @@ class BrowserTestRunner {
if (_currentStartingBrowserId == id) _currentStartingBrowserId = null;
}
// If [browserName] doesn't support opening new windows, we use new iframes
// instead.
bool get useIframe =>
!Browser.BROWSERS_WITH_WINDOW_SUPPORT.contains(browserName);
/// The optional testingServer parameter allows callers to pass in
/// a testing server with different behavior than the default
/// BrowserTestServer. The url handlers of the testingServer are
/// overwritten, so an existing handler can't be shared between instances.
BrowserTestRunner(
this.configuration, this.localIp, this.browserName, this.maxNumBrowsers,
{BrowserTestingServer this.testingServer}) {
checkedMode = configuration['checked'];
if (browserName == 'ff') browserName = 'firefox';
Map configuration,
String localIp,
String browserName,
this.maxNumBrowsers)
: configuration = configuration,
localIp = localIp,
browserName = (browserName == 'ff') ? 'firefox' : browserName,
checkedMode = configuration['checked'],
testingServer = new BrowserTestingServer(
configuration, localIp,
Browser.requiresIframe(browserName),
Browser.requiresFocus(browserName)) {
testingServer.testRunner = this;
}
Future start() async {
if (testingServer == null) {
testingServer =
new BrowserTestingServer(configuration, localIp, useIframe);
}
await testingServer.start();
testingServer
..testDoneCallBack = handleResults
@ -1285,6 +1303,9 @@ class BrowserTestingServer {
/// test
final String localIp;
final bool useIframe;
final bool requiresFocus;
BrowserTestRunner testRunner;
static const String driverPath = "/driver";
static const String nextTestPath = "/next_test";
@ -1297,14 +1318,14 @@ class BrowserTestingServer {
var testCount = 0;
var errorReportingServer;
bool underTermination = false;
bool useIframe = false;
Function testDoneCallBack;
Function testStatusUpdateCallBack;
Function testStartedCallBack;
Function nextTestCallBack;
BrowserTestingServer(this.configuration, this.localIp, this.useIframe);
BrowserTestingServer(
this.configuration, this.localIp, this.useIframe, this.requiresFocus);
Future start() {
var test_driver_error_port = configuration['test_driver_error_port'];
@ -1366,29 +1387,41 @@ class BrowserTestingServer {
handleStarted(request, browserId(request, startedPath), testId(request));
});
makeSendPageHandler(String prefix) => (HttpRequest request) {
noCache(request);
var textResponse = "";
if (prefix == driverPath) {
textResponse = getDriverPage(browserId(request, prefix));
request.response.headers.set('Content-Type', 'text/html');
}
if (prefix == nextTestPath) {
textResponse = getNextTest(browserId(request, prefix));
request.response.headers.set('Content-Type', 'text/plain');
}
request.response.write(textResponse);
request.listen((_) {}, onDone: request.response.close);
request.response.done.catchError((error) {
if (!underTermination) {
print("URI ${request.uri}");
print("Textresponse $textResponse");
throw "Error returning content to browser: $error";
}
void sendPageHandler(HttpRequest request) {
// Do NOT make this method async. We need to call catchError below
// synchronously to avoid unhandled asynchronous errors.
noCache(request);
Future<String> textResponse;
if (request.uri.path.startsWith(driverPath)) {
textResponse = getDriverPage(browserId(request, driverPath));
request.response.headers.set('Content-Type', 'text/html');
} else if (request.uri.path.startsWith(nextTestPath)) {
textResponse = new Future<String>.value(
getNextTest(browserId(request, nextTestPath)));
request.response.headers.set('Content-Type', 'text/plain');
} else {
textResponse = new Future<String>.value("");
}
request.response.done.catchError((error) {
if (!underTermination) {
return textResponse.then((String text) {
print("URI ${request.uri}");
print("textResponse $textResponse");
throw "Error returning content to browser: $error";
});
};
server.addHandler(driverPath, makeSendPageHandler(driverPath));
server.addHandler(nextTestPath, makeSendPageHandler(nextTestPath));
}
});
textResponse.then((String text) async {
request.response.write(text);
await request.listen(null).asFuture();
// Ignoring the returned closure as it returns the 'done' future
// which alread has catchError installed above.
request.response.close();
});
}
server.addHandler(driverPath, sendPageHandler);
server.addHandler(nextTestPath, sendPageHandler);
}
void handleReport(HttpRequest request, String browserId, var testId,
@ -1447,33 +1480,34 @@ class BrowserTestingServer {
return "http://$localIp:$port/driver/$browserId";
}
String getDriverPage(String browserId) {
Future<String> getDriverPage(String browserId) async {
await testRunner.browserStatus[browserId].browser.onDriverPageRequested();
var errorReportingUrl =
"http://$localIp:${errorReportingServer.port}/$browserId";
String driverContent = """
<!DOCTYPE html><html>
<head>
<style>
body {
margin: 0;
}
.box {
overflow: hidden;
overflow-y: auto;
position: absolute;
left: 0;
right: 0;
}
.controller.box {
height: 75px;
top: 0;
}
.test.box {
top: 75px;
bottom: 0;
}
</style>
<title>Driving page</title>
<style>
.big-notice {
background-color: red;
color: white;
font-weight: bold;
font-size: xx-large;
text-align: center;
}
.controller.box {
white-space: nowrap;
overflow: scroll;
height: 6em;
}
body {
font-family: sans-serif;
}
body div {
padding-top: 10px;
}
</style>
<script type='text/javascript'>
var STATUS_UPDATE_INTERVAL = 10000;
@ -1603,7 +1637,8 @@ class BrowserTestingServer {
embedded_iframe_div.removeChild(embedded_iframe);
embedded_iframe = document.createElement('iframe');
embedded_iframe.id = "embedded_iframe";
embedded_iframe.style="width:100%;height:100%";
embedded_iframe.width='800px';
embedded_iframe.height='600px';
embedded_iframe_div.appendChild(embedded_iframe);
embedded_iframe.src = url;
} else {
@ -1775,13 +1810,26 @@ class BrowserTestingServer {
</script>
</head>
<body onload="startTesting()">
<div class='big-notice'>
Please keep this window in focus at all times.
</div>
<div>
Some browsers, Safari, in particular, may pause JavaScript when not
visible to conserve power consumption and CPU resources. In addition,
some tests of focus events will not work correctly if this window doesn't
have focus. It's also advisable to close any other programs that may open
modal dialogs, for example, Chrome with Calendar open.
</div>
<div class="controller box">
Dart test driver, number of tests: <span id="number"></span><br>
Currently executing: <span id="currently_executing"></span><br>
Unhandled error: <span id="unhandled_error"></span>
</div>
<div id="embedded_iframe_div" class="test box">
<iframe style="width:100%;height:100%;" id="embedded_iframe"></iframe>
<iframe id="embedded_iframe"></iframe>
</div>
</body>
</html>

View file

@ -0,0 +1,227 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Helper program for killing and resetting all Safari settings to a known
/// state that works well for testing dart2js output in Safari.
///
/// Warning: this will delete all your Safari settings and bookmarks.
library testing.reset_safari;
import 'dart:async' show
Future,
Timer;
import 'dart:io' show
Directory,
File,
Platform,
Process,
ProcessResult;
const String defaultSafariBundleLocation = "/Applications/Safari.app/";
const String relativeSafariLocation = "Contents/MacOS/Safari";
const String lsofLocation = "/usr/sbin/lsof";
const String killLocation = "/bin/kill";
const String pkillLocation = "/usr/bin/pkill";
const String safari = "com.apple.Safari";
const String defaultsLocation = "/usr/bin/defaults";
final List<String> safariSettings = <String>[
"Library/Caches/$safari",
"Library/Safari",
"Library/Saved Application State/$safari.savedState",
"Library/Caches/Metadata/Safari",
"Library/Preferences/$safari.plist",
];
const Duration defaultPollDelay = const Duration(milliseconds: 1);
final String cpgi = "$safari.ContentPageGroupIdentifier";
final String knownSafariPreference = '''
{
DefaultBrowserPromptingState2 = 2;
StartPageViewControllerMode = 0;
TestDriveOriginBrowser = 1;
TestDriveUserDecision = 2;
TestDriveState = 3;
AlwaysRestoreSessionAtLaunch = 0;
NewTabBehavior = 1;
NewWindowBehavior = 1;
LastSafariVersionWithWelcomePage = "9.0";
OpenNewTabsInFront = 0;
TabCreationPolicy = 0;
IncludeDevelopMenu = 1;
WebKitDeveloperExtrasEnabledPreferenceKey = 1;
"$cpgi.WebKit2DeveloperExtrasEnabled" = 1;
AutoFillCreditCardData = 0;
AutoFillMiscellaneousForms = 0;
AutoFillPasswords = 0;
SuppressSearchSuggestions = 1;
PreloadTopHit = 0;
ShowFavoritesUnderSmartSearchField = 0;
WebsiteSpecificSearchEnabled = 0;
WarnAboutFraudulentWebsites = 0;
WebKitJavaScriptEnabled = 1;
"$cpgi.WebKit2JavaScriptEnabled" = 1;
WebKitJavaScriptCanOpenWindowsAutomatically = 1;
"$cpgi.WebKit2JavaScriptCanOpenWindowsAutomatically" = 1;
"$cpgi.WebKit2WebGLEnabled" = 1;
WebGLDefaultLoadPolicy = WebGLPolicyAllowNoSecurityRestrictions;
"$cpgi.WebKit2PluginsEnabled" = 0;
BlockStoragePolicy = 1;
WebKitStorageBlockingPolicy = 0;
"$cpgi.WebKit2StorageBlockingPolicy" = 0;
SafariGeolocationPermissionPolicy = 0;
CanPromptForPushNotifications = 0;
InstallExtensionUpdatesAutomatically = 0;
ShowFullURLInSmartSearchField = 1;
"$cpgi.WebKit2PlugInSnapshottingEnabled" = 0;
}
''';
Future<Null> get pollDelay => new Future.delayed(defaultPollDelay);
String signalArgument(
String defaultSignal,
{bool force: false,
bool testOnly: false}) {
if (force && testOnly) {
throw new ArgumentError("[force] and [testOnly] can't both be true.");
}
if (force) return "-KILL";
if (testOnly) return "-0";
return defaultSignal;
}
Future<int> kill(
List<String> pids,
{bool force: false,
bool testOnly: false}) async {
List<String> arguments =
<String>[signalArgument("-TERM", force: force, testOnly: testOnly)]
..addAll(pids);
ProcessResult result = await Process.run(killLocation, arguments);
return result.exitCode;
}
Future<int> pkill(
String pattern,
{bool force: false,
bool testOnly: false}) async {
List<String> arguments = <String>[
signalArgument("-HUP", force: force, testOnly: testOnly),
pattern];
ProcessResult result = await Process.run(pkillLocation, arguments);
return result.exitCode;
}
Uri validatedBundleName(Uri bundle) {
if (bundle == null) return Uri.base.resolve(defaultSafariBundleLocation);
if (!bundle.path.endsWith("/")) {
throw new ArgumentError(
"Bundle ('$bundle') must end with a slash ('/').");
}
return bundle;
}
Future<Null> killSafari({Uri bundle}) async {
bundle = validatedBundleName(bundle);
Uri safariBinary = bundle.resolve(relativeSafariLocation);
ProcessResult result = await Process.run(
lsofLocation, ["-t", safariBinary.toFilePath()]);
if (result.exitCode == 0) {
String stdout = result.stdout;
List<String> pids = new List<String>.from(
stdout.split("\n").where((String line) => !line.isEmpty));
Timer timer = new Timer(const Duration(seconds: 10), () {
print("Kill -9 Safari $pids");
kill(pids, force: true);
});
int exitCode = await kill(pids);
while (exitCode == 0) {
await pollDelay;
print("Polling Safari $pids");
exitCode = await kill(pids, testOnly: true);
}
timer.cancel();
}
Timer timer = new Timer(const Duration(seconds: 10), () {
print("Kill -9 $safari");
pkill(safari, force: true);
});
int exitCode = await pkill(safari);
while (exitCode == 0) {
await pollDelay;
print("Polling $safari");
exitCode = await pkill(safari, testOnly: true);
}
timer.cancel();
}
Future<Null> deleteIfExists(Uri uri) async {
Directory directory = new Directory.fromUri(uri);
if (await directory.exists()) {
print("Deleting directory '$uri'.");
await directory.delete(recursive: true);
} else {
File file = new File.fromUri(uri);
if (await file.exists()) {
print("Deleting file '$uri'.");
await file.delete();
} else {
print("File '$uri' not found.");
}
}
}
Future<Null> resetSafariSettings() async {
String home = Platform.environment["HOME"];
if (!home.endsWith("/")) {
home = "$home/";
}
Uri homeDirectory = Uri.base.resolve(home);
for (String setting in safariSettings) {
await deleteIfExists(homeDirectory.resolve(setting));
}
ProcessResult result = await Process.run(
defaultsLocation, <String>["write", safari, knownSafariPreference]);
if (result.exitCode != 0) {
throw "Unable to reset Safari settings: ${result.stdout}${result.stderr}";
}
}
Future<Null> killAndResetSafari({Uri bundle}) async {
bundle = validatedBundleName(bundle);
await killSafari(bundle: bundle);
await resetSafariSettings();
}
Future<Null> main() async {
await killAndResetSafari();
}

View file

@ -69,7 +69,7 @@ Future testConfigurations(List<Map> configurations) async {
var recordingPath = firstConf['record_to_file'];
var recordingOutputPath = firstConf['replay_from_file'];
Browser.deleteCache = firstConf['clear_browser_cache'];
Browser.resetBrowserConfiguration = firstConf['reset_browser_configuration'];
if (recordingPath != null && recordingOutputPath != null) {
print("Fatal: Can't have the '--record_to_file' and '--replay_from_file'"

View file

@ -338,9 +338,11 @@ Note: currently only implemented for dart2js.''',
false,
type: 'bool'),
new _TestOptionSpecification(
'clear_browser_cache',
'Browser specific clearing of caches(i.e., delete it).',
['--clear_browser_cache'],
'reset_browser_configuration',
'Browser specific reset of configuration. '
'WARNING: Using this option may remove your bookmarks and '
'other settings.',
['--reset-browser-configuration'],
[],
false,
type: 'bool'),