summaryrefslogtreecommitdiffstats
path: root/WebKitTools/Scripts/webkitpy/common/system
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2010-10-22 13:02:20 +0100
committerBen Murdoch <benm@google.com>2010-10-26 15:21:41 +0100
commita94275402997c11dd2e778633dacf4b7e630a35d (patch)
treee66f56c67e3b01f22c9c23cd932271ee9ac558ed /WebKitTools/Scripts/webkitpy/common/system
parent09e26c78506587b3f5d930d7bc72a23287ffbec0 (diff)
downloadexternal_webkit-a94275402997c11dd2e778633dacf4b7e630a35d.zip
external_webkit-a94275402997c11dd2e778633dacf4b7e630a35d.tar.gz
external_webkit-a94275402997c11dd2e778633dacf4b7e630a35d.tar.bz2
Merge WebKit at r70209: Initial merge by Git
Change-Id: Id23a68efa36e9d1126bcce0b137872db00892c8e
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/common/system')
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/executive.py14
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/outputcapture.py27
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/path.py134
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/path_unittest.py105
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user.py7
-rw-r--r--WebKitTools/Scripts/webkitpy/common/system/user_unittest.py12
6 files changed, 292 insertions, 7 deletions
diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive.py b/WebKitTools/Scripts/webkitpy/common/system/executive.py
index 7c00f22..216cf58 100644
--- a/WebKitTools/Scripts/webkitpy/common/system/executive.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/executive.py
@@ -103,6 +103,13 @@ class Executive(object):
def _run_command_with_teed_output(self, args, teed_output):
args = map(unicode, args) # Popen will throw an exception if args are non-strings (like int())
+ if sys.platform == 'cygwin':
+ # Cygwin's Python's os.execv doesn't support unicode command
+ # arguments, and neither does Cygwin's execv itself.
+ # FIXME: Using UTF-8 here will confuse Windows-native commands
+ # which will expect arguments to be encoded using the current code
+ # page.
+ args = [arg.encode('utf-8') for arg in args]
child_process = subprocess.Popen(args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
@@ -281,6 +288,13 @@ class Executive(object):
assert(isinstance(args, list) or isinstance(args, tuple))
start_time = time.time()
args = map(unicode, args) # Popen will throw an exception if args are non-strings (like int())
+ if sys.platform == 'cygwin':
+ # Cygwin's Python's os.execv doesn't support unicode command
+ # arguments, and neither does Cygwin's execv itself.
+ # FIXME: Using UTF-8 here will confuse Windows-native commands
+ # which will expect arguments to be encoded using the current code
+ # page.
+ args = [arg.encode('utf-8') for arg in args]
stdin, string_to_communicate = self._compute_stdin(input)
stderr = subprocess.STDOUT if return_stderr else None
diff --git a/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py b/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
index 68a3919..45e0e3f 100644
--- a/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/outputcapture.py
@@ -29,6 +29,7 @@
# Class for unittest support. Used for capturing stderr/stdout.
import sys
+import unittest
from StringIO import StringIO
class OutputCapture(object):
@@ -37,7 +38,9 @@ class OutputCapture(object):
def _capture_output_with_name(self, output_name):
self.saved_outputs[output_name] = getattr(sys, output_name)
- setattr(sys, output_name, StringIO())
+ captured_output = StringIO()
+ setattr(sys, output_name, captured_output)
+ return captured_output
def _restore_output_with_name(self, output_name):
captured_output = getattr(sys, output_name).getvalue()
@@ -46,8 +49,7 @@ class OutputCapture(object):
return captured_output
def capture_output(self):
- self._capture_output_with_name("stdout")
- self._capture_output_with_name("stderr")
+ return (self._capture_output_with_name("stdout"), self._capture_output_with_name("stderr"))
def restore_output(self):
return (self._restore_output_with_name("stdout"), self._restore_output_with_name("stderr"))
@@ -63,3 +65,22 @@ class OutputCapture(object):
testcase.assertEqual(stderr_string, expected_stderr)
# This is a little strange, but I don't know where else to return this information.
return return_value
+
+
+class OutputCaptureTestCaseBase(unittest.TestCase):
+ def setUp(self):
+ unittest.TestCase.setUp(self)
+ self.output_capture = OutputCapture()
+ (self.__captured_stdout, self.__captured_stderr) = self.output_capture.capture_output()
+
+ def tearDown(self):
+ del self.__captured_stdout
+ del self.__captured_stderr
+ self.output_capture.restore_output()
+ unittest.TestCase.tearDown(self)
+
+ def assertStdout(self, expected_stdout):
+ self.assertEquals(expected_stdout, self.__captured_stdout.getvalue())
+
+ def assertStderr(self, expected_stderr):
+ self.assertEquals(expected_stderr, self.__captured_stderr.getvalue())
diff --git a/WebKitTools/Scripts/webkitpy/common/system/path.py b/WebKitTools/Scripts/webkitpy/common/system/path.py
new file mode 100644
index 0000000..43c6410
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/path.py
@@ -0,0 +1,134 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""generic routines to convert platform-specific paths to URIs."""
+from __future__ import with_statement
+
+import atexit
+import subprocess
+import sys
+import threading
+import urllib
+
+
+def abspath_to_uri(path, platform=None):
+ """Converts a platform-specific absolute path to a file: URL."""
+ if platform is None:
+ platform = sys.platform
+ return "file:" + _escape(_convert_path(path, platform))
+
+
+def cygpath(path):
+ """Converts a cygwin path to Windows path."""
+ return _CygPath.convert_using_singleton(path)
+
+
+# Note that this object is not threadsafe and must only be called
+# from multiple threads under protection of a lock (as is done in cygpath())
+class _CygPath(object):
+ """Manages a long-running 'cygpath' process for file conversion."""
+ _lock = None
+ _singleton = None
+
+ @staticmethod
+ def stop_cygpath_subprocess():
+ if not _CygPath._lock:
+ return
+
+ with _CygPath._lock:
+ if _CygPath._singleton:
+ _CygPath._singleton.stop()
+
+ @staticmethod
+ def convert_using_singleton(path):
+ if not _CygPath._lock:
+ _CygPath._lock = threading.Lock()
+
+ with _CygPath._lock:
+ if not _CygPath._singleton:
+ _CygPath._singleton = _CygPath()
+ # Make sure the cygpath subprocess always gets shutdown cleanly.
+ atexit.register(_CygPath.stop_cygpath_subprocess)
+
+ return _CygPath._singleton.convert(path)
+
+ def __init__(self):
+ self._child_process = None
+
+ def start(self):
+ assert(self._child_process is None)
+ args = ['cygpath', '-f', '-', '-wa']
+ self._child_process = subprocess.Popen(args,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ def is_running(self):
+ if not self._child_process:
+ return False
+ return self._child_process.returncode is None
+
+ def stop(self):
+ if self._child_process:
+ self._child_process.stdin.close()
+ self._child_process.wait()
+ self._child_process = None
+
+ def convert(self, path):
+ if not self.is_running():
+ self.start()
+ self._child_process.stdin.write("%s\r\n" % path)
+ self._child_process.stdin.flush()
+ return self._child_process.stdout.readline().rstrip()
+
+
+def _escape(path):
+ """Handle any characters in the path that should be escaped."""
+ # FIXME: web browsers don't appear to blindly quote every character
+ # when converting filenames to files. Instead of using urllib's default
+ # rules, we allow a small list of other characters through un-escaped.
+ # It's unclear if this is the best possible solution.
+ return urllib.quote(path, safe='/+:')
+
+
+def _convert_path(path, platform):
+ """Handles any os-specific path separators, mappings, etc."""
+ if platform == 'win32':
+ return _winpath_to_uri(path)
+ if platform == 'cygwin':
+ return _winpath_to_uri(cygpath(path))
+ return _unixypath_to_uri(path)
+
+
+def _winpath_to_uri(path):
+ """Converts a window absolute path to a file: URL."""
+ return "///" + path.replace("\\", "/")
+
+
+def _unixypath_to_uri(path):
+ """Converts a unix-style path to a file: URL."""
+ return "//" + path
diff --git a/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py
new file mode 100644
index 0000000..4dbd38a
--- /dev/null
+++ b/WebKitTools/Scripts/webkitpy/common/system/path_unittest.py
@@ -0,0 +1,105 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+import sys
+
+import path
+
+class AbspathTest(unittest.TestCase):
+ def assertMatch(self, test_path, expected_uri,
+ platform=None):
+ if platform == 'cygwin' and sys.platform != 'cygwin':
+ return
+ self.assertEqual(path.abspath_to_uri(test_path, platform=platform),
+ expected_uri)
+
+ def test_abspath_to_uri_cygwin(self):
+ if sys.platform != 'cygwin':
+ return
+
+ self.assertMatch('/cygdrive/c/foo/bar.html',
+ 'file:///C:/foo/bar.html',
+ platform='cygwin')
+ self.assertEqual(path.abspath_to_uri('/cygdrive/c/foo/bar.html',
+ platform='cygwin'),
+ 'file:///C:/foo/bar.html')
+
+ def test_abspath_to_uri_darwin(self):
+ self.assertMatch('/foo/bar.html',
+ 'file:///foo/bar.html',
+ platform='darwin')
+ self.assertEqual(path.abspath_to_uri("/foo/bar.html",
+ platform='darwin'),
+ "file:///foo/bar.html")
+
+ def test_abspath_to_uri_linux2(self):
+ self.assertMatch('/foo/bar.html',
+ 'file:///foo/bar.html',
+ platform='darwin')
+ self.assertEqual(path.abspath_to_uri("/foo/bar.html",
+ platform='linux2'),
+ "file:///foo/bar.html")
+
+ def test_abspath_to_uri_win(self):
+ self.assertMatch('c:\\foo\\bar.html',
+ 'file:///c:/foo/bar.html',
+ platform='win32')
+ self.assertEqual(path.abspath_to_uri("c:\\foo\\bar.html",
+ platform='win32'),
+ "file:///c:/foo/bar.html")
+
+ def test_abspath_to_uri_escaping(self):
+ self.assertMatch('/foo/bar + baz%?.html',
+ 'file:///foo/bar%20+%20baz%25%3F.html',
+ platform='darwin')
+ self.assertMatch('/foo/bar + baz%?.html',
+ 'file:///foo/bar%20+%20baz%25%3F.html',
+ platform='linux2')
+
+ # Note that you can't have '?' in a filename on windows.
+ self.assertMatch('/cygdrive/c/foo/bar + baz%.html',
+ 'file:///C:/foo/bar%20+%20baz%25.html',
+ platform='cygwin')
+
+ def test_stop_cygpath_subprocess(self):
+ if sys.platform != 'cygwin':
+ return
+
+ # Call cygpath to ensure the subprocess is running.
+ path.cygpath("/cygdrive/c/foo.txt")
+ self.assertTrue(path._CygPath._singleton.is_running())
+
+ # Stop it.
+ path._CygPath.stop_cygpath_subprocess()
+
+ # Ensure that it is stopped.
+ self.assertFalse(path._CygPath._singleton.is_running())
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/WebKitTools/Scripts/webkitpy/common/system/user.py b/WebKitTools/Scripts/webkitpy/common/system/user.py
index 240b67b..8917137 100644
--- a/WebKitTools/Scripts/webkitpy/common/system/user.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/user.py
@@ -96,6 +96,10 @@ class User(object):
# Note: Not thread safe: http://bugs.python.org/issue2320
subprocess.call(args + files)
+ def _warn_if_application_is_xcode(self, edit_application):
+ if "Xcode" in edit_application:
+ print "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\"."
+
def edit_changelog(self, files):
edit_application = os.environ.get("CHANGE_LOG_EDIT_APPLICATION")
if edit_application and sys.platform == "darwin":
@@ -103,8 +107,7 @@ class User(object):
args = shlex.split(edit_application)
print "Using editor in the CHANGE_LOG_EDIT_APPLICATION environment variable."
print "Please quit the editor application when done editing."
- if edit_application.find("Xcode.app"):
- print "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\"."
+ self._warn_if_application_is_xcode(edit_application)
subprocess.call(["open", "-W", "-n", "-a"] + args + files)
return
self.edit(files)
diff --git a/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
index ae1bad5..7ec9b34 100644
--- a/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
+++ b/WebKitTools/Scripts/webkitpy/common/system/user_unittest.py
@@ -97,5 +97,13 @@ class UserTest(unittest.TestCase):
raw_input=mock_raw_input)
self.assertEquals(expected[1], result)
-if __name__ == '__main__':
- unittest.main()
+ def test_warn_if_application_is_xcode(self):
+ output = OutputCapture()
+ user = User()
+ output.assert_outputs(self, user._warn_if_application_is_xcode, ["TextMate"])
+ output.assert_outputs(self, user._warn_if_application_is_xcode, ["/Applications/TextMate.app"])
+ output.assert_outputs(self, user._warn_if_application_is_xcode, ["XCode"]) # case sensitive matching
+
+ xcode_warning = "Instead of using Xcode.app, consider using EDITOR=\"xed --wait\".\n"
+ output.assert_outputs(self, user._warn_if_application_is_xcode, ["Xcode"], expected_stdout=xcode_warning)
+ output.assert_outputs(self, user._warn_if_application_is_xcode, ["/Developer/Applications/Xcode.app"], expected_stdout=xcode_warning)