diff options
| -rw-r--r-- | Makefile | 5 | ||||
| -rwxr-xr-x | psutil/tests/test_misc.py | 140 | ||||
| -rw-r--r-- | psutil/tests/test_unicode.py | 202 | ||||
| -rwxr-xr-x | scripts/internal/winmake.py | 7 |
4 files changed, 215 insertions, 139 deletions
@@ -134,6 +134,11 @@ test-misc: ${MAKE} install $(PYTHON) psutil/tests/test_misc.py +# Test misc. +test-unicode: + ${MAKE} install + $(PYTHON) psutil/tests/test_unicode.py + # Test POSIX. test-posix: ${MAKE} install diff --git a/psutil/tests/test_misc.py b/psutil/tests/test_misc.py index cfcf89d7..ee796d81 100755 --- a/psutil/tests/test_misc.py +++ b/psutil/tests/test_misc.py @@ -15,12 +15,10 @@ import imp import json import os import pickle -import psutil import socket import stat import sys -from psutil import BSD from psutil import LINUX from psutil import NETBSD from psutil import OPENBSD @@ -32,9 +30,7 @@ from psutil._common import memoize_when_activated from psutil._common import supports_ipv6 from psutil._compat import PY3 from psutil.tests import APPVEYOR -from psutil.tests import ASCII_FS from psutil.tests import chdir -from psutil.tests import create_exe from psutil.tests import create_proc_children_pair from psutil.tests import get_test_subprocess from psutil.tests import importlib @@ -47,12 +43,12 @@ from psutil.tests import safe_rmpath from psutil.tests import SCRIPTS_DIR from psutil.tests import sh from psutil.tests import TESTFN -from psutil.tests import TESTFN_UNICODE from psutil.tests import TOX from psutil.tests import TRAVIS from psutil.tests import unittest from psutil.tests import wait_for_file from psutil.tests import wait_for_pid +import psutil import psutil.tests @@ -681,139 +677,5 @@ class TestProcessUtils(unittest.TestCase): assert not psutil.tests._subprocesses_started -# =================================================================== -# --- Unicode tests -# =================================================================== - - -class _UnicodeFilesystemAPIS(unittest.TestCase): - """ - Make sure that fs-related APIs returning a string are able to - handle unicode, see: - https://github.com/giampaolo/psutil/issues/655 - This is a base class which is tested by the mixins below. - Note that on Python 2 we do not check whether the returned paths - match in case os.* functions are not able to so. - We just assume correct path handling on Python 2 is broken. In fact - it is broken for most os.* functions, see: - http://bugs.python.org/issue18695 - """ - funky_name = TESTFN_UNICODE - - @classmethod - def setUpClass(cls): - safe_rmpath(cls.funky_name) - - tearDownClass = setUpClass - - def setUp(self): - safe_rmpath(self.funky_name) - - def tearDown(self): - reap_children() - safe_rmpath(self.funky_name) - - @classmethod - def expect_exact_path_match(cls): - # Do not expect psutil to correctly handle unicode paths on - # Python 2 if os.listdir() is not able either. - return PY3 or cls.funky_name in os.listdir('.') - - def test_proc_exe(self): - create_exe(self.funky_name) - subp = get_test_subprocess(cmd=[self.funky_name]) - p = psutil.Process(subp.pid) - exe = p.exe() - self.assertIsInstance(exe, str) - if self.expect_exact_path_match(): - self.assertEqual(exe, self.funky_name) - - def test_proc_name(self): - create_exe(self.funky_name) - subp = get_test_subprocess(cmd=[self.funky_name]) - if WINDOWS: - # On Windows name() is determined from exe() first, because - # it's faster; we want to overcome the internal optimization - # and test name() instead of exe(). - from psutil._pswindows import py2_strencode - name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)) - else: - name = psutil.Process(subp.pid).name() - self.assertIsInstance(name, str) - if self.expect_exact_path_match(): - self.assertEqual(name, os.path.basename(self.funky_name)) - - def test_proc_cmdline(self): - create_exe(self.funky_name) - subp = get_test_subprocess(cmd=[self.funky_name]) - p = psutil.Process(subp.pid) - cmdline = p.cmdline() - if self.expect_exact_path_match(): - self.assertEqual(cmdline, [self.funky_name]) - - def test_proc_cwd(self): - os.mkdir(self.funky_name) - with chdir(self.funky_name): - p = psutil.Process() - cwd = p.cwd() - self.assertIsInstance(p.cwd(), str) - if self.expect_exact_path_match(): - self.assertEqual(cwd, self.funky_name) - - # @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") - def test_proc_open_files(self): - p = psutil.Process() - start = set(p.open_files()) - with open(self.funky_name, 'wb'): - new = set(p.open_files()) - path = (new - start).pop().path - if BSD and not path: - # XXX - # see https://github.com/giampaolo/psutil/issues/595 - return self.skipTest("open_files on BSD is broken") - self.assertIsInstance(path, str) - if self.expect_exact_path_match(): - self.assertEqual(os.path.normcase(path), - os.path.normcase(self.funky_name)) - - def test_disk_usage(self): - os.mkdir(self.funky_name) - psutil.disk_usage(self.funky_name) - - -@unittest.skipIf(ASCII_FS, "ASCII fs") -class TestUnicodeFilesystemAPISMixin(_UnicodeFilesystemAPIS): - funky_name = TESTFN_UNICODE - - -class TestInvalidUnicodeFilesystemAPISMixin(_UnicodeFilesystemAPIS): - """Like above but uses an invalid UTF8 file name.""" - if PY3: - funky_name = (TESTFN.encode('utf8') + b"f\xc0\x80").decode( - 'utf8', 'surrogateescape') - else: - funky_name = TESTFN + b"f\xc0\x80" - - -class TestUnicodeNonFsAPIS(unittest.TestCase): - """Unicode tests for non fs-related APIs.""" - - @unittest.skipUnless(hasattr(psutil.Process, "environ"), - "platform not supported") - def test_proc_environ(self): - # Note: differently from others, this test does not deal - # with fs paths. On Python 2 subprocess module is broken as - # it's not able to handle with non-ASCII env vars, so - # we use "è", which is part of the extended ASCII table - # (unicode point <= 255). - env = os.environ.copy() - funny_str = TESTFN_UNICODE if PY3 else 'è' - env['FUNNY_ARG'] = funny_str - sproc = get_test_subprocess(env=env) - p = psutil.Process(sproc.pid) - env = p.environ() - self.assertEqual(env['FUNNY_ARG'], funny_str) - - if __name__ == '__main__': run_test_module_by_name(__file__) diff --git a/psutil/tests/test_unicode.py b/psutil/tests/test_unicode.py new file mode 100644 index 00000000..882cc8a2 --- /dev/null +++ b/psutil/tests/test_unicode.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Notes about unicode handling in psutil +====================================== + +In psutil these are the APIs returning or dealing with a string: + +- disk_io_counters() (not tested) +- disk_partitions() (not tested) +- disk_usage(str) +- net_if_addrs() (not tested) +- net_if_stats() (not tested) +- net_io_counters() (not tested) +- Process.cmdline() +- Process.connections('unix') (not tested) +- Process.cwd() +- Process.environ() +- Process.exe() +- Process.memory_maps() (not tested) +- Process.name() +- Process.open_files() +- Process.username() (not tested) +- sensors_fans() +- sensors_temperatures() +- users() (not tested) +- WindowsService (not tested) + +In here we create a unicode path with a funky non-ASCII name and (where +possible) make psutil return it back (e.g. on name(), exe(), +open_files(), etc.) and make sure it doesn't crash with +UnicodeDecodeError. + +On Python 3 the returned path is supposed to match 100% (and this +is tested). +Not on Python 2 though, where we assume correct unicode path handling +is broken. In fact it is broken for most os.* functions, see: +http://bugs.python.org/issue18695 +There really is no way for psutil to handle unicode correctly on +Python 2 unless we make such APIs return a unicode type in certain +circumstances. +I'd rather have unicode support broken on Python 2 than having APIs +returning variable str/unicode types, see: +https://github.com/giampaolo/psutil/issues/655#issuecomment-136131180 +""" + +import os + +from psutil import BSD +from psutil import WINDOWS +from psutil._compat import PY3 +from psutil.tests import ASCII_FS +from psutil.tests import chdir +from psutil.tests import create_exe +from psutil.tests import get_test_subprocess +from psutil.tests import reap_children +from psutil.tests import run_test_module_by_name +from psutil.tests import safe_rmpath +from psutil.tests import TESTFN +from psutil.tests import TESTFN_UNICODE +from psutil.tests import unittest +import psutil +import psutil.tests + + +# =================================================================== +# FS APIs +# =================================================================== + + +class _BaseFSAPIsTests(object): + + funky_name = None + + @classmethod + def setUpClass(cls): + safe_rmpath(cls.funky_name) + + tearDownClass = setUpClass + + def setUp(self): + safe_rmpath(self.funky_name) + + def tearDown(self): + reap_children() + safe_rmpath(self.funky_name) + + @classmethod + def expect_exact_path_match(cls): + # Do not expect psutil to correctly handle unicode paths on + # Python 2 if os.listdir() is not able either. + return PY3 or cls.funky_name in os.listdir('.') + + def test_proc_exe(self): + create_exe(self.funky_name) + subp = get_test_subprocess(cmd=[self.funky_name]) + p = psutil.Process(subp.pid) + exe = p.exe() + self.assertIsInstance(exe, str) + if self.expect_exact_path_match(): + self.assertEqual(exe, self.funky_name) + + def test_proc_name(self): + create_exe(self.funky_name) + subp = get_test_subprocess(cmd=[self.funky_name]) + if WINDOWS: + # On Windows name() is determined from exe() first, because + # it's faster; we want to overcome the internal optimization + # and test name() instead of exe(). + from psutil._pswindows import py2_strencode + name = py2_strencode(psutil._psplatform.cext.proc_name(subp.pid)) + else: + name = psutil.Process(subp.pid).name() + self.assertIsInstance(name, str) + if self.expect_exact_path_match(): + self.assertEqual(name, os.path.basename(self.funky_name)) + + def test_proc_cmdline(self): + create_exe(self.funky_name) + subp = get_test_subprocess(cmd=[self.funky_name]) + p = psutil.Process(subp.pid) + cmdline = p.cmdline() + if self.expect_exact_path_match(): + self.assertEqual(cmdline, [self.funky_name]) + + def test_proc_cwd(self): + os.mkdir(self.funky_name) + with chdir(self.funky_name): + p = psutil.Process() + cwd = p.cwd() + self.assertIsInstance(p.cwd(), str) + if self.expect_exact_path_match(): + self.assertEqual(cwd, self.funky_name) + + # @unittest.skipIf(APPVEYOR, "unreliable on APPVEYOR") + def test_proc_open_files(self): + p = psutil.Process() + start = set(p.open_files()) + with open(self.funky_name, 'wb'): + new = set(p.open_files()) + path = (new - start).pop().path + if BSD and not path: + # XXX + # see https://github.com/giampaolo/psutil/issues/595 + return self.skipTest("open_files on BSD is broken") + self.assertIsInstance(path, str) + if self.expect_exact_path_match(): + self.assertEqual(os.path.normcase(path), + os.path.normcase(self.funky_name)) + + def test_disk_usage(self): + os.mkdir(self.funky_name) + psutil.disk_usage(self.funky_name) + + +@unittest.skipIf(ASCII_FS, "ASCII fs") +class TestFSAPIs(_BaseFSAPIsTests, unittest.TestCase): + """Test FS APIs with a funky, valid, UTF8 path name.""" + funky_name = TESTFN_UNICODE + + +class TestFSAPIsWithInvalidPath(_BaseFSAPIsTests, unittest.TestCase): + """Test FS APIs with a funky, invalid path name.""" + if PY3: + funky_name = (TESTFN.encode('utf8') + b"f\xc0\x80").decode( + 'utf8', 'surrogateescape') + else: + funky_name = TESTFN + "f\xc0\x80" + + +# =================================================================== +# FS APIs +# =================================================================== + + +class TestOtherAPIS(unittest.TestCase): + """Unicode tests for non fs-related APIs.""" + + @unittest.skipUnless(hasattr(psutil.Process, "environ"), + "platform not supported") + def test_proc_environ(self): + # Note: differently from others, this test does not deal + # with fs paths. On Python 2 subprocess module is broken as + # it's not able to handle with non-ASCII env vars, so + # we use "è", which is part of the extended ASCII table + # (unicode point <= 255). + env = os.environ.copy() + funny_str = TESTFN_UNICODE if PY3 else 'è' + env['FUNNY_ARG'] = funny_str + sproc = get_test_subprocess(env=env) + p = psutil.Process(sproc.pid) + env = p.environ() + self.assertEqual(env['FUNNY_ARG'], funny_str) + + +if __name__ == '__main__': + run_test_module_by_name(__file__) diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 7215560d..c2db0fe3 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -346,6 +346,13 @@ def test_misc(): @cmd +def test_unicode(): + """Run unicode tests""" + install() + sh("%s -m unittest -v psutil.tests.test_unicode" % PYTHON) + + +@cmd def test_by_name(): """Run test by name""" try: |
