Chromium Code Reviews| Index: tools/testing/dart/reset_safari.dart |
| diff --git a/tools/testing/dart/reset_safari.dart b/tools/testing/dart/reset_safari.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e1886a5442ca861c7248ae4a9daffe21f0bbd88a |
| --- /dev/null |
| +++ b/tools/testing/dart/reset_safari.dart |
| @@ -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); |
|
Bill Hesse
2016/04/25 08:20:19
I'm surprised this is so small. But I grew up wit
ahe
2016/05/24 08:44:42
Acknowledged.
|
| + |
| +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(); |
|
Bill Hesse
2016/04/25 08:20:19
Could we pass in "print" to killAndResetSafari her
ahe
2016/05/24 08:44:42
Acknowledged.
|
| +} |