diff options
author | Ben Murdoch <benm@google.com> | 2010-10-22 13:02:20 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-10-26 15:21:41 +0100 |
commit | a94275402997c11dd2e778633dacf4b7e630a35d (patch) | |
tree | e66f56c67e3b01f22c9c23cd932271ee9ac558ed /WebKitTools/Scripts/webkitpy/common/system | |
parent | 09e26c78506587b3f5d930d7bc72a23287ffbec0 (diff) | |
download | external_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')
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) |