diff options
author | Teng-Hui Zhu <ztenghui@google.com> | 2010-11-10 15:31:59 -0800 |
---|---|---|
committer | Teng-Hui Zhu <ztenghui@google.com> | 2010-11-17 13:35:59 -0800 |
commit | 28040489d744e0c5d475a88663056c9040ed5320 (patch) | |
tree | c463676791e4a63e452a95f0a12b2a8519730693 /WebKitTools/Scripts/webkitpy/common | |
parent | eff9be92c41913c92fb1d3b7983c071f3e718678 (diff) | |
download | external_webkit-28040489d744e0c5d475a88663056c9040ed5320.zip external_webkit-28040489d744e0c5d475a88663056c9040ed5320.tar.gz external_webkit-28040489d744e0c5d475a88663056c9040ed5320.tar.bz2 |
Merge WebKit at r71558: Initial merge by git.
Change-Id: Ib345578fa29df7e4bc72b4f00e4a6fddcb754c4c
Diffstat (limited to 'WebKitTools/Scripts/webkitpy/common')
9 files changed, 463 insertions, 48 deletions
diff --git a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py index 9b602c3..8aadcb8 100644 --- a/WebKitTools/Scripts/webkitpy/common/checkout/scm.py +++ b/WebKitTools/Scripts/webkitpy/common/checkout/scm.py @@ -64,7 +64,7 @@ def default_scm(): cwd = os.getcwd() scm_system = detect_scm_system(cwd) if not scm_system: - script_directory = os.path.abspath(sys.path[0]) + script_directory = os.path.dirname(os.path.abspath(__file__)) scm_system = detect_scm_system(script_directory) if scm_system: log("The current directory (%s) is not a WebKit checkout, using %s" % (cwd, scm_system.checkout_root)) diff --git a/WebKitTools/Scripts/webkitpy/common/config/committers.py b/WebKitTools/Scripts/webkitpy/common/config/committers.py index 71d764c..446c2b1 100644 --- a/WebKitTools/Scripts/webkitpy/common/config/committers.py +++ b/WebKitTools/Scripts/webkitpy/common/config/committers.py @@ -112,6 +112,7 @@ committers_unable_to_review = [ Committer("Girish Ramakrishnan", ["girish@forwardbias.in", "ramakrishnan.girish@gmail.com"]), Committer("Graham Dennis", ["Graham.Dennis@gmail.com", "gdennis@webkit.org"]), Committer("Greg Bolsinga", "bolsinga@apple.com"), + Committer("Gyuyoung Kim", ["gyuyoung.kim@samsung.com", "gyuyoung@gmail.com", "gyuyoung@webkit.org"], "gyuyoung"), Committer("Hans Wennborg", "hans@chromium.org", "hwennborg"), Committer("Hayato Ito", "hayato@chromium.org", "hayato"), Committer("Hin-Chung Lam", ["hclam@google.com", "hclam@chromium.org"]), @@ -147,7 +148,7 @@ committers_unable_to_review = [ Committer("Luiz Agostini", ["luiz@webkit.org", "luiz.agostini@openbossa.org"], "lca"), Committer("Mads Ager", "ager@chromium.org"), Committer("Marcus Voltis Bulach", "bulach@chromium.org"), - Committer("Mario Sanchez Prada", ["msanchez@igalia.com", "mario@webkit.org"]), + Committer("Mario Sanchez Prada", ["msanchez@igalia.com", "mario@webkit.org"], "msanchez"), Committer("Matt Delaney", "mdelaney@apple.com"), Committer("Matt Lilek", ["webkit@mattlilek.com", "pewtermoose@webkit.org"]), Committer("Matt Perry", "mpcomplete@chromium.org"), @@ -171,6 +172,7 @@ committers_unable_to_review = [ Committer("Philippe Normand", ["pnormand@igalia.com", "philn@webkit.org"], "philn-tp"), Committer("Pierre d'Herbemont", ["pdherbemont@free.fr", "pdherbemont@apple.com"], "pdherbemont"), Committer("Pierre-Olivier Latour", "pol@apple.com", "pol"), + Committer("Renata Hodovan", "reni@webkit.org", "reni"), Committer("Robert Hogan", ["robert@webkit.org", "robert@roberthogan.net", "lists@roberthogan.net"], "mwenge"), Committer("Roland Steiner", "rolandsteiner@chromium.org"), Committer("Ryosuke Niwa", "rniwa@webkit.org", "rniwa"), diff --git a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py index 1cc8e2e..a7dc1b7 100644 --- a/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py +++ b/WebKitTools/Scripts/webkitpy/common/net/bugzilla.py @@ -281,7 +281,7 @@ class BugzillaQueries(object): return sum([self._fetch_bug(bug_id).commit_queued_patches() for bug_id in self.fetch_bug_ids_from_commit_queue()], []) - def _fetch_bug_ids_from_review_queue(self): + def fetch_bug_ids_from_review_queue(self): review_queue_url = "buglist.cgi?query_format=advanced&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&field0-0-0=flagtypes.name&type0-0-0=equals&value0-0-0=review?" return self._fetch_bug_ids_advanced_query(review_queue_url) @@ -289,7 +289,7 @@ class BugzillaQueries(object): def fetch_patches_from_review_queue(self, limit=None): # [:None] returns the whole array. return sum([self._fetch_bug(bug_id).unreviewed_patches() - for bug_id in self._fetch_bug_ids_from_review_queue()[:limit]], []) + for bug_id in self.fetch_bug_ids_from_review_queue()[:limit]], []) # NOTE: This is the only client of _fetch_attachment_ids_request_query # This method only makes one request to bugzilla. diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive.py b/WebKitTools/Scripts/webkitpy/common/system/executive.py index 216cf58..37f4e53 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/executive.py +++ b/WebKitTools/Scripts/webkitpy/common/system/executive.py @@ -33,6 +33,7 @@ try: except ImportError: multiprocessing = None +import ctypes import errno import logging import os @@ -44,6 +45,7 @@ import sys import time from webkitpy.common.system.deprecated_logging import tee +from webkitpy.python24 import versioning _log = logging.getLogger("webkitpy.common.system") @@ -103,13 +105,8 @@ 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] + args = map(self._encode_argument_if_needed, args) + child_process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, @@ -148,9 +145,8 @@ class Executive(object): child_output = child_out_file.getvalue() child_out_file.close() - # We assume the child process output utf-8 if decode_output: - child_output = child_output.decode("utf-8") + child_output = child_output.decode(self._child_process_encoding()) if exit_code: raise ScriptError(script_args=args, @@ -205,6 +201,55 @@ class Executive(object): return raise + def _win32_check_running_pid(self): + + class PROCESSENTRY32(ctypes.Structure): + _fields_ = [("dwSize", ctypes.c_ulong), + ("cntUsage", ctypes.c_ulong), + ("th32ProcessID", ctypes.c_ulong), + ("th32DefaultHeapID", ctypes.c_ulong), + ("th32ModuleID", ctypes.c_ulong), + ("cntThreads", ctypes.c_ulong), + ("th32ParentProcessID", ctypes.c_ulong), + ("pcPriClassBase", ctypes.c_ulong), + ("dwFlags", ctypes.c_ulong), + ("szExeFile", ctypes.c_char * 260)] + + CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot + Process32First = ctypes.windll.kernel32.Process32First + Process32Next = ctypes.windll.kernel32.Process32Next + CloseHandle = ctypes.windll.kernel32.CloseHandle + TH32CS_SNAPPROCESS = 0x00000002 # win32 magic number + hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) + pe32 = PROCESSENTRY32() + pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) + result = False + if not Process32First(hProcessSnap, ctypes.byref(pe32)): + _log.debug("Failed getting first process.") + CloseHandle(hProcessSnap) + return result + while True: + if pe32.th32ProcessID == pid: + result = True + break + if not Process32Next(hProcessSnap, ctypes.byref(pe32)): + break + CloseHandle(hProcessSnap) + return result + + def check_running_pid(self, pid): + """Return True if pid is alive, otherwise return False.""" + if sys.platform in ('darwin', 'linux2', 'cygwin'): + try: + os.kill(pid, 0) + return True + except OSError: + return False + elif sys.platform == 'win32': + return self._win32_check_running_pid() + + assert(False) + def _windows_image_name(self, process_name): name, extension = os.path.splitext(process_name) if not extension: @@ -260,7 +305,7 @@ class Executive(object): # for an example of a regresion caused by passing a unicode string directly. # FIXME: We may need to encode differently on different platforms. if isinstance(input, unicode): - input = input.encode("utf-8") + input = input.encode(self._child_process_encoding()) return (subprocess.PIPE, input) def _command_for_printing(self, args): @@ -288,13 +333,8 @@ 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] + args = map(self._encode_argument_if_needed, args) + stdin, string_to_communicate = self._compute_stdin(input) stderr = subprocess.STDOUT if return_stderr else None @@ -305,9 +345,11 @@ class Executive(object): cwd=cwd, close_fds=self._should_close_fds()) output = process.communicate(string_to_communicate)[0] + # run_command automatically decodes to unicode() unless explicitly told not to. if decode_output: - output = output.decode("utf-8") + output = output.decode(self._child_process_encoding()) + # wait() is not threadsafe and can throw OSError due to: # http://bugs.python.org/issue1731717 exit_code = process.wait() @@ -324,3 +366,34 @@ class Executive(object): cwd=cwd) (error_handler or self.default_error_handler)(script_error) return output + + def _child_process_encoding(self): + # Win32 Python 2.x uses CreateProcessA rather than CreateProcessW + # to launch subprocesses, so we have to encode arguments using the + # current code page. + if sys.platform == 'win32' and versioning.compare_version(sys, '3.0')[0] < 0: + return 'mbcs' + # All other platforms use UTF-8. + # FIXME: Using UTF-8 on Cygwin will confuse Windows-native commands + # which will expect arguments to be encoded using the current code + # page. + return 'utf-8' + + def _should_encode_child_process_arguments(self): + # Cygwin's Python's os.execv doesn't support unicode command + # arguments, and neither does Cygwin's execv itself. + if sys.platform == 'cygwin': + return True + + # Win32 Python 2.x uses CreateProcessA rather than CreateProcessW + # to launch subprocesses, so we have to encode arguments using the + # current code page. + if sys.platform == 'win32' and versioning.compare_version(sys, '3.0')[0] < 0: + return True + + return False + + def _encode_argument_if_needed(self, argument): + if not self._should_encode_child_process_arguments(): + return argument + return argument.encode(self._child_process_encoding()) diff --git a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py index 32f8f51..b8fd82e 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py +++ b/WebKitTools/Scripts/webkitpy/common/system/executive_unittest.py @@ -27,12 +27,23 @@ # (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 os import signal import subprocess import sys import unittest from webkitpy.common.system.executive import Executive, run_command, ScriptError +from webkitpy.test import cat, echo + + +def never_ending_command(): + """Arguments for a command that will never end (useful for testing process + killing). It should be a process that is unlikely to already be running + because all instances will be killed.""" + if sys.platform == 'win32': + return ['wmic'] + return ['yes'] class ExecutiveTest(unittest.TestCase): @@ -46,46 +57,56 @@ class ExecutiveTest(unittest.TestCase): executive = Executive() self.assertRaises(AssertionError, executive.run_command, "echo") self.assertRaises(AssertionError, executive.run_command, u"echo") - executive.run_command(["echo", "foo"]) - executive.run_command(("echo", "foo")) + executive.run_command(echo.command_arguments('foo')) + executive.run_command(tuple(echo.command_arguments('foo'))) def test_run_command_with_unicode(self): """Validate that it is safe to pass unicode() objects to Executive.run* methods, and they will return unicode() objects by default unless decode_output=False""" + unicode_tor_input = u"WebKit \u2661 Tor Arne Vestb\u00F8!" + if sys.platform == 'win32': + encoding = 'mbcs' + else: + encoding = 'utf-8' + encoded_tor = unicode_tor_input.encode(encoding) + # On Windows, we expect the unicode->mbcs->unicode roundtrip to be + # lossy. On other platforms, we expect a lossless roundtrip. + if sys.platform == 'win32': + unicode_tor_output = encoded_tor.decode(encoding) + else: + unicode_tor_output = unicode_tor_input + executive = Executive() - unicode_tor = u"WebKit \u2661 Tor Arne Vestb\u00F8!" - utf8_tor = unicode_tor.encode("utf-8") - output = executive.run_command(["cat"], input=unicode_tor) - self.assertEquals(output, unicode_tor) + output = executive.run_command(cat.command_arguments(), input=unicode_tor_input) + self.assertEquals(output, unicode_tor_output) - output = executive.run_command(["echo", "-n", unicode_tor]) - self.assertEquals(output, unicode_tor) + output = executive.run_command(echo.command_arguments("-n", unicode_tor_input)) + self.assertEquals(output, unicode_tor_output) - output = executive.run_command(["echo", "-n", unicode_tor], decode_output=False) - self.assertEquals(output, utf8_tor) + output = executive.run_command(echo.command_arguments("-n", unicode_tor_input), decode_output=False) + self.assertEquals(output, encoded_tor) # Make sure that str() input also works. - output = executive.run_command(["cat"], input=utf8_tor, decode_output=False) - self.assertEquals(output, utf8_tor) + output = executive.run_command(cat.command_arguments(), input=encoded_tor, decode_output=False) + self.assertEquals(output, encoded_tor) # FIXME: We should only have one run* method to test - output = executive.run_and_throw_if_fail(["echo", "-n", unicode_tor], quiet=True) - self.assertEquals(output, unicode_tor) + output = executive.run_and_throw_if_fail(echo.command_arguments("-n", unicode_tor_input), quiet=True) + self.assertEquals(output, unicode_tor_output) - output = executive.run_and_throw_if_fail(["echo", "-n", unicode_tor], quiet=True, decode_output=False) - self.assertEquals(output, utf8_tor) + output = executive.run_and_throw_if_fail(echo.command_arguments("-n", unicode_tor_input), quiet=True, decode_output=False) + self.assertEquals(output, encoded_tor) def test_kill_process(self): executive = Executive() - # We use "yes" because it loops forever. - process = subprocess.Popen(["yes"], stdout=subprocess.PIPE) + process = subprocess.Popen(never_ending_command(), stdout=subprocess.PIPE) self.assertEqual(process.poll(), None) # Process is running executive.kill_process(process.pid) # Note: Can't use a ternary since signal.SIGKILL is undefined for sys.platform == "win32" if sys.platform == "win32": - expected_exit_code = 0 # taskkill.exe results in exit(0) + expected_exit_code = 1 else: expected_exit_code = -signal.SIGKILL self.assertEqual(process.wait(), expected_exit_code) @@ -109,14 +130,22 @@ class ExecutiveTest(unittest.TestCase): def test_kill_all(self): executive = Executive() # We use "yes" because it loops forever. - process = subprocess.Popen(["yes"], stdout=subprocess.PIPE) + process = subprocess.Popen(never_ending_command(), stdout=subprocess.PIPE) self.assertEqual(process.poll(), None) # Process is running - executive.kill_all("yes") + executive.kill_all(never_ending_command()[0]) # Note: Can't use a ternary since signal.SIGTERM is undefined for sys.platform == "win32" - if sys.platform in ("win32", "cygwin"): - expected_exit_code = 0 # taskkill.exe results in exit(0) + if sys.platform == "cygwin": + expected_exit_code = 0 # os.kill results in exit(0) for this process. + elif sys.platform == "win32": + expected_exit_code = 1 else: expected_exit_code = -signal.SIGTERM self.assertEqual(process.wait(), expected_exit_code) # Killing again should fail silently. - executive.kill_all("yes") + executive.kill_all(never_ending_command()[0]) + + def test_check_running_pid(self): + executive = Executive() + self.assertTrue(executive.check_running_pid(os.getpid())) + # Maximum pid number on Linux is 32768 by default + self.assertFalse(executive.check_running_pid(100000)) diff --git a/WebKitTools/Scripts/webkitpy/common/system/filesystem.py b/WebKitTools/Scripts/webkitpy/common/system/filesystem.py new file mode 100644 index 0000000..c7efde3 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/common/system/filesystem.py @@ -0,0 +1,117 @@ +# 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. + +"""Wrapper object for the file system / source tree.""" + +from __future__ import with_statement + +import codecs +import errno +import os +import tempfile + + +class FileSystem(object): + """FileSystem interface for webkitpy. + + Unless otherwise noted, all paths are allowed to be either absolute + or relative.""" + + def exists(self, path): + """Return whether the path exists in the filesystem.""" + return os.path.exists(path) + + def isdir(self, path): + """Return whether the path refers to a directory.""" + return os.path.isdir(path) + + def join(self, *comps): + """Return the path formed by joining the components.""" + return os.path.join(*comps) + + def listdir(self, path): + """Return the contents of the directory pointed to by path.""" + return os.listdir(path) + + def mkdtemp(self, **kwargs): + """Create and return a uniquely named directory. + + This is like tempfile.mkdtemp, but if used in a with statement + the directory will self-delete at the end of the block (if the + directory is empty; non-empty directories raise errors). The + directory can be safely deleted inside the block as well, if so + desired.""" + class TemporaryDirectory(object): + def __init__(self, **kwargs): + self._kwargs = kwargs + self._directory_path = None + + def __enter__(self): + self._directory_path = tempfile.mkdtemp(**self._kwargs) + return self._directory_path + + def __exit__(self, type, value, traceback): + # Only self-delete if necessary. + + # FIXME: Should we delete non-empty directories? + if os.path.exists(self._directory_path): + os.rmdir(self._directory_path) + + return TemporaryDirectory(**kwargs) + + def maybe_make_directory(self, *path): + """Create the specified directory if it doesn't already exist.""" + try: + os.makedirs(os.path.join(*path)) + except OSError, e: + if e.errno != errno.EEXIST: + raise + + def read_binary_file(self, path): + """Return the contents of the file at the given path as a byte string.""" + with file(path, 'rb') as f: + return f.read() + + def read_text_file(self, path): + """Return the contents of the file at the given path as a Unicode string. + + The file is read assuming it is a UTF-8 encoded file with no BOM.""" + with codecs.open(path, 'r', 'utf8') as f: + return f.read() + + def write_binary_file(self, path, contents): + """Write the contents to the file at the given location.""" + with file(path, 'wb') as f: + f.write(contents) + + def write_text_file(self, path, contents): + """Write the contents to the file at the given location. + + The file is written encoded as UTF-8 with no BOM.""" + with codecs.open(path, 'w', 'utf8') as f: + f.write(contents) diff --git a/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py b/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py new file mode 100644 index 0000000..95684b7 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/common/system/filesystem_unittest.py @@ -0,0 +1,157 @@ +# vim: set fileencoding=utf-8 : +# 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. + +# NOTE: The fileencoding comment on the first line of the file is +# important; without it, Python will choke while trying to parse the file, +# since it includes non-ASCII characters. + +from __future__ import with_statement + +import os +import stat +import sys +import tempfile +import unittest + +from filesystem import FileSystem + + +class FileSystemTest(unittest.TestCase): + def setUp(self): + self._this_dir = os.path.dirname(os.path.abspath(__file__)) + self._missing_file = os.path.join(self._this_dir, 'missing_file.py') + self._this_file = os.path.join(self._this_dir, 'filesystem_unittest.py') + + def test_exists__true(self): + fs = FileSystem() + self.assertTrue(fs.exists(self._this_file)) + + def test_exists__false(self): + fs = FileSystem() + self.assertFalse(fs.exists(self._missing_file)) + + def test_isdir__true(self): + fs = FileSystem() + self.assertTrue(fs.isdir(self._this_dir)) + + def test_isdir__false(self): + fs = FileSystem() + self.assertFalse(fs.isdir(self._this_file)) + + def test_join(self): + fs = FileSystem() + self.assertEqual(fs.join('foo', 'bar'), + os.path.join('foo', 'bar')) + + def test_listdir(self): + fs = FileSystem() + with fs.mkdtemp(prefix='filesystem_unittest_') as d: + self.assertEqual(fs.listdir(d), []) + new_file = os.path.join(d, 'foo') + fs.write_text_file(new_file, u'foo') + self.assertEqual(fs.listdir(d), ['foo']) + os.remove(new_file) + + def test_maybe_make_directory__success(self): + fs = FileSystem() + + with fs.mkdtemp(prefix='filesystem_unittest_') as base_path: + sub_path = os.path.join(base_path, "newdir") + self.assertFalse(os.path.exists(sub_path)) + self.assertFalse(fs.isdir(sub_path)) + + fs.maybe_make_directory(sub_path) + self.assertTrue(os.path.exists(sub_path)) + self.assertTrue(fs.isdir(sub_path)) + + # Make sure we can re-create it. + fs.maybe_make_directory(sub_path) + self.assertTrue(os.path.exists(sub_path)) + self.assertTrue(fs.isdir(sub_path)) + + # Clean up. + os.rmdir(sub_path) + + self.assertFalse(os.path.exists(base_path)) + self.assertFalse(fs.isdir(base_path)) + + def test_maybe_make_directory__failure(self): + # FIXME: os.chmod() doesn't work on Windows to set directories + # as readonly, so we skip this test for now. + if sys.platform in ('win32', 'cygwin'): + return + + fs = FileSystem() + with fs.mkdtemp(prefix='filesystem_unittest_') as d: + # Remove write permissions on the parent directory. + os.chmod(d, stat.S_IRUSR) + + # Now try to create a sub directory - should fail. + sub_dir = fs.join(d, 'subdir') + self.assertRaises(OSError, fs.maybe_make_directory, sub_dir) + + # Clean up in case the test failed and we did create the + # directory. + if os.path.exists(sub_dir): + os.rmdir(sub_dir) + + def test_read_and_write_file(self): + fs = FileSystem() + text_path = None + binary_path = None + + unicode_text_string = u'Ūnĭcōde̽' + hex_equivalent = '\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD' + try: + text_path = tempfile.mktemp(prefix='tree_unittest_') + binary_path = tempfile.mktemp(prefix='tree_unittest_') + fs.write_text_file(text_path, unicode_text_string) + contents = fs.read_binary_file(text_path) + self.assertEqual(contents, hex_equivalent) + + fs.write_text_file(binary_path, hex_equivalent) + text_contents = fs.read_text_file(binary_path) + self.assertEqual(text_contents, unicode_text_string) + except: + if text_path: + os.remove(text_path) + if binary_path: + os.remove(binary_path) + + def test_read_binary_file__missing(self): + fs = FileSystem() + self.assertRaises(IOError, fs.read_binary_file, self._missing_file) + + def test_read_text_file__missing(self): + fs = FileSystem() + self.assertRaises(IOError, fs.read_text_file, self._missing_file) + + +if __name__ == '__main__': + unittest.main() diff --git a/WebKitTools/Scripts/webkitpy/common/system/fileutils.py b/WebKitTools/Scripts/webkitpy/common/system/fileutils.py new file mode 100644 index 0000000..55821f8 --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/common/system/fileutils.py @@ -0,0 +1,33 @@ +# Copyright (C) 2010 Apple 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: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# +# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 sys + + +def make_stdout_binary(): + """Puts sys.stdout into binary mode (on platforms that have a distinction + between text and binary mode).""" + if sys.platform != 'win32' or not hasattr(sys.stdout, 'fileno'): + return + import msvcrt + import os + msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) diff --git a/WebKitTools/Scripts/webkitpy/common/system/path.py b/WebKitTools/Scripts/webkitpy/common/system/path.py index 43c6410..09787d7 100644 --- a/WebKitTools/Scripts/webkitpy/common/system/path.py +++ b/WebKitTools/Scripts/webkitpy/common/system/path.py @@ -44,7 +44,7 @@ def abspath_to_uri(path, platform=None): def cygpath(path): - """Converts a cygwin path to Windows path.""" + """Converts an absolute cygwin path to an absolute Windows path.""" return _CygPath.convert_using_singleton(path) @@ -103,7 +103,11 @@ class _CygPath(object): self.start() self._child_process.stdin.write("%s\r\n" % path) self._child_process.stdin.flush() - return self._child_process.stdout.readline().rstrip() + windows_path = self._child_process.stdout.readline().rstrip() + # Some versions of cygpath use lowercase drive letters while others + # use uppercase. We always convert to uppercase for consistency. + windows_path = '%s%s' % (windows_path[0].upper(), windows_path[1:]) + return windows_path def _escape(path): |