From f31f60755d39899b46ea7958b0fe59453cc125d1 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Thu, 16 Apr 2015 21:11:55 +0200 Subject: psutil_net_io_counters: fix swapped send and receive On SunOS/Solaris the bytes and packets send/received were swapped. Fixed it. --- CREDITS | 5 ++++- HISTORY.rst | 1 + psutil/_psutil_sunos.c | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CREDITS b/CREDITS index a1ec04e2..f7e95596 100644 --- a/CREDITS +++ b/CREDITS @@ -289,4 +289,7 @@ I: 578, 581, 587 N: spacewanderlzx C: Guangzhou,China E: spacewanderlzx@gmail.com -I: 555 \ No newline at end of file +I: 555 + +N: Fabian Groffen +I: 611 diff --git a/HISTORY.rst b/HISTORY.rst index f8098ea8..9065f436 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -36,6 +36,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues number is provided. - #593: [FreeBSD] Process().memory_maps() segfaults. - #606: Process.parent() may swallow NoSuchProcess exceptions. +- #611L [SunOS] net_io_counters has send and received swapped 2.2.1 - 2015-02-02 diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c index fb80e3ef..0cb6978f 100644 --- a/psutil/_psutil_sunos.c +++ b/psutil/_psutil_sunos.c @@ -736,18 +736,18 @@ psutil_net_io_counters(PyObject *self, PyObject *args) #if defined(_INT64_TYPE) py_ifc_info = Py_BuildValue("(KKKKkkii)", - rbytes->value.ui64, wbytes->value.ui64, - rpkts->value.ui64, + rbytes->value.ui64, wpkts->value.ui64, + rpkts->value.ui64, ierrs->value.ui32, oerrs->value.ui32, #else py_ifc_info = Py_BuildValue("(kkkkkkii)", - rbytes->value.ui32, wbytes->value.ui32, - rpkts->value.ui32, + rbytes->value.ui32, wpkts->value.ui32, + rpkts->value.ui32, ierrs->value.ui32, oerrs->value.ui32, #endif -- cgit v1.2.1 From d59a0da7aad75e0f5ebc7bf39d5d69b130022d81 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 16 Apr 2015 15:50:41 -0400 Subject: make flake8 happy --- test/_linux.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/_linux.py b/test/_linux.py index 1ba8fb94..8ac0608c 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -7,7 +7,6 @@ """Linux specific tests. These are implicitly run by test_psutil.py.""" from __future__ import division -import array import contextlib import fcntl import os -- cgit v1.2.1 From df3d5271b9153c6510c6178194bb33cbd1a7b732 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Fri, 17 Apr 2015 09:15:44 +0200 Subject: swap: fix finding the 'swap' utility on SunOS This is a bit of a kludge, but allows to find the swap utility when invoced by normal users which shouldn't have the sbin paths in PATH by default. One can argue the paths should be appended instead of prepended, but I doubt whether one wants to use another swap utility, since the code relies on its output format. --- CREDITS | 2 +- HISTORY.rst | 3 ++- psutil/_pssunos.py | 3 ++- test/_sunos.py | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CREDITS b/CREDITS index f7e95596..5efcd099 100644 --- a/CREDITS +++ b/CREDITS @@ -292,4 +292,4 @@ E: spacewanderlzx@gmail.com I: 555 N: Fabian Groffen -I: 611 +I: 611, 618 diff --git a/HISTORY.rst b/HISTORY.rst index 9065f436..a598e9b0 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -36,7 +36,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues number is provided. - #593: [FreeBSD] Process().memory_maps() segfaults. - #606: Process.parent() may swallow NoSuchProcess exceptions. -- #611L [SunOS] net_io_counters has send and received swapped +- #611: [SunOS] net_io_counters has send and received swapped +- #618: [SunOS] swap tests fail on Solaris when run as normal user 2.2.1 - 2015-02-02 diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index bb47fd29..b438697d 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -96,7 +96,8 @@ def swap_memory(): # usr/src/cmd/swap/swap.c # ...nevertheless I can't manage to obtain the same numbers as 'swap' # cmdline utility, so let's parse its output (sigh!) - p = subprocess.Popen(['swap', '-l', '-k'], stdout=subprocess.PIPE) + p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' % + os.environ['PATH'], 'swap', '-l', '-k'], stdout=subprocess.PIPE) stdout, stderr = p.communicate() if PY3: stdout = stdout.decode(sys.stdout.encoding) diff --git a/test/_sunos.py b/test/_sunos.py index 7fdc50b6..b1943373 100644 --- a/test/_sunos.py +++ b/test/_sunos.py @@ -7,6 +7,7 @@ """Sun OS specific tests. These are implicitly run by test_psutil.py.""" import sys +import os from test_psutil import sh, unittest import psutil @@ -15,7 +16,7 @@ import psutil class SunOSSpecificTestCase(unittest.TestCase): def test_swap_memory(self): - out = sh('swap -l -k') + out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) lines = out.strip().split('\n')[1:] if not lines: raise ValueError('no swap device(s) configured') -- cgit v1.2.1 From b68eb581483c5b243c4a0bc1365a90e3e85e6a2c Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Fri, 17 Apr 2015 16:40:51 +0200 Subject: swap_memory: fix flake8 complaint --- psutil/_pssunos.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index b438697d..bc35a718 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -97,7 +97,8 @@ def swap_memory(): # ...nevertheless I can't manage to obtain the same numbers as 'swap' # cmdline utility, so let's parse its output (sigh!) p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' % - os.environ['PATH'], 'swap', '-l', '-k'], stdout=subprocess.PIPE) + os.environ['PATH'], 'swap', '-l', '-k'], + stdout=subprocess.PIPE) stdout, stderr = p.communicate() if PY3: stdout = stdout.decode(sys.stdout.encoding) -- cgit v1.2.1 From 8a72a5498a77b8054a7555d2ad86182dce859c33 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 20 Apr 2015 14:40:52 -0400 Subject: git pre-commit hook: turn it into a py script --- .git-pre-commit | 66 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/.git-pre-commit b/.git-pre-commit index da628495..2af81d19 100644 --- a/.git-pre-commit +++ b/.git-pre-commit @@ -1,25 +1,41 @@ -#!/bin/bash - -# This script gets executed on "git commit -am '...'". -# You can install it with "make install-git-hooks". - -# pdb -pdb=`git diff --cached --name-only | \ - grep -E '\.py$' | \ - xargs grep -n -H -E '(pdb\.set_trace\(\))'` -if [ ! -z "$pdb" ] ; then - echo "commit aborted: you forgot a pdb in your python code" - echo $pdb - exit 1 -fi - -# flake8 -flake8=`git diff --cached --name-only | grep -E '\.py$'` -if [ ! -z "$flake8" ] ; then - flake8=`echo "$flake8" | xargs flake8` - if [ ! -z "$flake8" ] ; then - echo "commit aborted: python code is not flake8-compliant" - echo $flake8 - exit 1 - fi -fi +#!/usr/bin/env python + +import os +import subprocess +import sys + + +def main(): + out = subprocess.check_output("git diff --cached --name-only", shell=True) + files = [x for x in out.split('\n') if x.endswith('.py') and + os.path.exists(x)] + + for path in files: + with open(path) as f: + data = f.read() + + # pdb + if "pdb.set_trace" in data: + for lineno, line in enumerate(data.split('\n'), 1): + line = line.rstrip() + if "pdb.set_trace" in line: + print("%s: %s" % (lineno, line)) + sys.exit( + "commit aborted: you forgot a pdb in your python code") + + # bare except clause + if "except:" in data: + for lineno, line in enumerate(data.split('\n'), 1): + line = line.rstrip() + if "except:" in line and not line.endswith("# NOQA"): + print("%s: %s" % (lineno, line)) + sys.exit("commit aborted: bare except clause") + + # flake8 + for path in files: + ret = subprocess.call("flake8 %s" % path, shell=True) + if ret != 0: + print("commit aborted: python code is not flake8-compliant") + sys.exit(ret) + +main() -- cgit v1.2.1 From b861f4000ae14e9a084a4d4b9026c29c72c3209c Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 20 Apr 2015 14:52:12 -0400 Subject: xxx --- .git-pre-commit | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-pre-commit b/.git-pre-commit index 2af81d19..d5fd018f 100644 --- a/.git-pre-commit +++ b/.git-pre-commit @@ -39,3 +39,5 @@ def main(): sys.exit(ret) main() + +1 / 0 -- cgit v1.2.1 From 31862a02eebd5a29408d3767c06ae52cd6bdcc2a Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 22 Apr 2015 01:05:26 -0400 Subject: have Error define __str__ for all of its subclasses --- psutil/__init__.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/psutil/__init__.py b/psutil/__init__.py index 608db167..2bb5daba 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -190,6 +190,12 @@ class Error(Exception): from this one. """ + def __init__(self, msg=""): + self.msg = msg + + def __str__(self): + return self.msg + class NoSuchProcess(Error): """Exception raised when a process with a certain PID doesn't @@ -197,7 +203,7 @@ class NoSuchProcess(Error): """ def __init__(self, pid, name=None, msg=None): - Error.__init__(self) + Error.__init__(self, msg) self.pid = pid self.name = name self.msg = msg @@ -208,9 +214,6 @@ class NoSuchProcess(Error): details = "(pid=%s)" % self.pid self.msg = "process no longer exists " + details - def __str__(self): - return self.msg - class ZombieProcess(NoSuchProcess): """Exception raised when querying a zombie process. This is @@ -221,7 +224,7 @@ class ZombieProcess(NoSuchProcess): """ def __init__(self, pid, name=None, ppid=None, msg=None): - Error.__init__(self) + Error.__init__(self, msg) self.pid = pid self.ppid = ppid self.name = name @@ -236,15 +239,12 @@ class ZombieProcess(NoSuchProcess): details = "(pid=%s)" % self.pid self.msg = "process still exists but it's a zombie " + details - def __str__(self): - return self.msg - class AccessDenied(Error): """Exception raised when permission to perform an action is denied.""" def __init__(self, pid=None, name=None, msg=None): - Error.__init__(self) + Error.__init__(self, msg) self.pid = pid self.name = name self.msg = msg @@ -256,9 +256,6 @@ class AccessDenied(Error): else: self.msg = "" - def __str__(self): - return self.msg - class TimeoutExpired(Error): """Raised on Process.wait(timeout) if timeout expires and process @@ -266,18 +263,15 @@ class TimeoutExpired(Error): """ def __init__(self, seconds, pid=None, name=None): - Error.__init__(self) + Error.__init__(self, "timeout after %s seconds" % seconds) self.seconds = seconds self.pid = pid self.name = name - self.msg = "timeout after %s seconds" % seconds if (pid is not None) and (name is not None): self.msg += " (pid=%s, name=%s)" % (pid, repr(name)) elif (pid is not None): self.msg += " (pid=%s)" % self.pid - def __str__(self): - return self.msg # push exception classes into platform specific module namespace _psplatform.NoSuchProcess = NoSuchProcess -- cgit v1.2.1 From 5d47d656ab4a2bab1378a092070cff06db1bcfb0 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 15 May 2015 00:42:29 -0400 Subject: makefile: use the proper python interpreter for running all cmds --- Makefile | 6 +++--- psutil/__init__.py | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index dead5612..49d1b744 100644 --- a/Makefile +++ b/Makefile @@ -61,16 +61,16 @@ test-memleaks-by-name: install # requires "pip install pep8" pep8: - @git ls-files | grep \\.py$ | xargs pep8 + @git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8 # requires "pip install pyflakes" pyflakes: @export PYFLAKES_NODOCTEST=1 && \ - git ls-files | grep \\.py$ | xargs pyflakes + git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes # requires "pip install flake8" flake8: - @git ls-files | grep \\.py$ | xargs flake8 + @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 # Upload source tarball on https://pypi.python.org/pypi/psutil. diff --git a/psutil/__init__.py b/psutil/__init__.py index 2bb5daba..b8e6b690 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -284,6 +284,7 @@ _psplatform.TimeoutExpired = TimeoutExpired # --- Process class # ===================================================================== + def _assert_pid_not_reused(fun): """Decorator which raises NoSuchProcess in case a process is no longer running or its PID has been reused. @@ -1099,6 +1100,7 @@ class Process(object): # --- Popen class # ===================================================================== + class Popen(Process): """A more convenient interface to stdlib subprocess module. It starts a sub process and deals with it exactly as when using @@ -1166,6 +1168,7 @@ class Popen(Process): # --- system processes related functions # ===================================================================== + def pids(): """Return a list of current running PIDs.""" return _psplatform.pids() @@ -1332,6 +1335,7 @@ def wait_procs(procs, timeout=None, callback=None): # --- CPU related functions # ===================================================================== + @memoize def cpu_count(logical=True): """Return the number of logical CPUs in the system (same as @@ -1542,6 +1546,7 @@ def cpu_times_percent(interval=None, percpu=False): # --- system memory related functions # ===================================================================== + def virtual_memory(): """Return statistics about system memory usage as a namedtuple including the following fields, expressed in bytes: @@ -1622,6 +1627,7 @@ def swap_memory(): # --- disks/paritions related functions # ===================================================================== + def disk_usage(path): """Return disk usage statistics about the given path as a namedtuple including total, used and free space expressed in bytes plus the @@ -1676,6 +1682,7 @@ def disk_io_counters(perdisk=False): # --- network related functions # ===================================================================== + def net_io_counters(pernic=False): """Return network I/O statistics as a namedtuple including the following fields: -- cgit v1.2.1 From f0d199e87930b6e5d5f9edfc0b077e21b2487766 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 15 May 2015 00:53:07 -0400 Subject: add test for setup.py script --- test/test_psutil.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_psutil.py b/test/test_psutil.py index b53ff680..fa2b62c2 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -22,6 +22,7 @@ import contextlib import datetime import errno import functools +import imp import json import os import pickle @@ -2635,6 +2636,12 @@ class TestMisc(unittest.TestCase): check(psutil.disk_usage(os.getcwd())) check(psutil.users()) + def test_setup_script(self): + here = os.path.abspath(os.path.dirname(__file__)) + setup_py = os.path.realpath(os.path.join(here, '..', 'setup.py')) + module = imp.load_source('setup', setup_py) + self.assertRaises(SystemExit, module.setup) + # =================================================================== # --- Example script tests -- cgit v1.2.1 From fba759ed1c422515dd6337d79ea1bc2422039c71 Mon Sep 17 00:00:00 2001 From: Weilu Jia Date: Fri, 15 May 2015 14:46:50 -0700 Subject: Remove deprecated attributes --- psutil/_pslinux.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 40132794..413f539c 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -40,10 +40,7 @@ __extra__all__ = [ # connection status constants "CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1", "CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT", - "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", - # other - "phymem_buffers", "cached_phymem"] - + "CONN_LAST_ACK", "CONN_LISTEN", "CONN_CLOSING", ] # --- constants -- cgit v1.2.1 From 05a8806db9334ba9991928f817fbe66fc3b51687 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 18 May 2015 21:53:04 -0400 Subject: fix make install-git-hooks --- .git-pre-commit | 8 ++++---- Makefile | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) mode change 100644 => 100755 .git-pre-commit diff --git a/.git-pre-commit b/.git-pre-commit old mode 100644 new mode 100755 index d5fd018f..ce209f43 --- a/.git-pre-commit +++ b/.git-pre-commit @@ -32,12 +32,12 @@ def main(): sys.exit("commit aborted: bare except clause") # flake8 + failed = False for path in files: ret = subprocess.call("flake8 %s" % path, shell=True) if ret != 0: - print("commit aborted: python code is not flake8-compliant") - sys.exit(ret) + failed = True + if failed: + sys.exit("commit aborted: python code is not flake8-compliant") main() - -1 / 0 diff --git a/Makefile b/Makefile index 49d1b744..62a93036 100644 --- a/Makefile +++ b/Makefile @@ -90,5 +90,6 @@ git-tag-release: # install GIT pre-commit hook install-git-hooks: - cp .git-pre-commit .git/hooks/pre-commit + rm -f .git/hooks/pre-commit + ln -s ../../.git-pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit -- cgit v1.2.1 From 85d0d4592881b0c301b04b9024b9a2ea31040b40 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 18 May 2015 21:58:41 -0400 Subject: git-hooks: include the new file in MANIFEST.in and use --force when creating the symlink (instead of removing it first) --- .git-pre-commit | 4 ++++ MANIFEST.in | 3 ++- Makefile | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.git-pre-commit b/.git-pre-commit index ce209f43..3a6161f4 100755 --- a/.git-pre-commit +++ b/.git-pre-commit @@ -1,5 +1,9 @@ #!/usr/bin/env python +# This gets executed on 'git commit' and rejects the commit in case the +# submitted code does not pass validation. +# Install it with "make install-git-hooks" + import os import subprocess import sys diff --git a/MANIFEST.in b/MANIFEST.in index 91f24647..de1e8b7d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ include .git-pre-commit include .gitignore include .travis.yml +include .git-pre-commit include CREDITS include HISTORY.rst include INSTALL.rst @@ -16,4 +17,4 @@ recursive-include docs * recursive-exclude docs/_build * recursive-include examples *.py recursive-include psutil *.py *.c *.h -recursive-include test *.py README \ No newline at end of file +recursive-include test *.py README diff --git a/Makefile b/Makefile index 62a93036..7d9c9d63 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,5 @@ git-tag-release: # install GIT pre-commit hook install-git-hooks: - rm -f .git/hooks/pre-commit - ln -s ../../.git-pre-commit .git/hooks/pre-commit + ln -sf ../../.git-pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit -- cgit v1.2.1 From 9007757dc40e8b8702b81a2c6ce8303d8506733d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Mon, 18 May 2015 22:09:56 -0400 Subject: fix travis --- .travis.yml | 5 +++-- Makefile | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 202d4877..693829a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,13 @@ install: - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install ipaddress; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install ipaddress; fi script: - - pip install flake8 + - pip install flake8 pep8 - python setup.py build - python setup.py install - python test/test_psutil.py - python test/test_memory_leaks.py - - make flake8 + - flake8 + - pep8 os: - linux - osx diff --git a/Makefile b/Makefile index 7d9c9d63..510d7313 100644 --- a/Makefile +++ b/Makefile @@ -72,7 +72,6 @@ pyflakes: flake8: @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 - # Upload source tarball on https://pypi.python.org/pypi/psutil. upload-src: clean $(PYTHON) setup.py sdist upload -- cgit v1.2.1 From 58711c2db81d437fdcf5b8aedf68bfd00fc089fd Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 28 May 2015 09:28:46 +0200 Subject: update doc --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index efbab674..000ac20d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -568,7 +568,7 @@ Functions import psutil def on_terminate(proc): - print("process {} terminated".format(proc)) + print("process {} terminated with exit code {}".format(proc, proc.returncode)) procs = [...] # a list of Process instances for p in procs: -- cgit v1.2.1 From d3239a650eaa2595fdd8c2eca9cda9a599b17a5b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 3 Jun 2015 11:48:31 +0200 Subject: fix #628: Process.name() on Linux truncates spaces and ')' --- CREDITS | 5 +++++ HISTORY.rst | 2 ++ docs/index.rst | 15 ++++++++------- psutil/_pslinux.py | 5 +++-- test/test_psutil.py | 20 ++++++++++++++++++++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/CREDITS b/CREDITS index 5efcd099..de2a5fa6 100644 --- a/CREDITS +++ b/CREDITS @@ -293,3 +293,8 @@ I: 555 N: Fabian Groffen I: 611, 618 + +N: desbma +W: https://github.com/desbma +C: France +I: 628 diff --git a/HISTORY.rst b/HISTORY.rst index a598e9b0..0f5eed06 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -38,6 +38,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #606: Process.parent() may swallow NoSuchProcess exceptions. - #611: [SunOS] net_io_counters has send and received swapped - #618: [SunOS] swap tests fail on Solaris when run as normal user +- #628: [Linux] Process.name() truncates process name in case it contains + spaces or parentheses. 2.2.1 - 2015-02-02 diff --git a/docs/index.rst b/docs/index.rst index 000ac20d..9d527ebb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -769,6 +769,13 @@ Process class 10 >>> + Starting from `Python 3.3 `__ this + functionality is also available as + `os.getpriority() `__ + and + `os.setpriority() `__ + (UNIX only). + On Windows this is available as well by using `GetPriorityClass `__ and `SetPriorityClass `__ @@ -779,12 +786,6 @@ Process class >>> p.nice(psutil.HIGH_PRIORITY_CLASS) - Starting from `Python 3.3 `__ this - same functionality is available as - `os.getpriority() `__ - and - `os.setpriority() `__. - .. method:: ionice(ioclass=None, value=None) Get or set @@ -1225,7 +1226,7 @@ Popen class :meth:`send_signal() `, :meth:`terminate() ` and :meth:`kill() ` - so that you don't accidentally terminate another process, fixing + so that you can't accidentally terminate another process, fixing http://bugs.python.org/issue6973. >>> import psutil diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 413f539c..a7cac31c 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -713,8 +713,9 @@ class Process(object): fname = "/proc/%s/stat" % self.pid kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() with open(fname, "rt", **kw) as f: - # XXX - gets changed later and probably needs refactoring - return f.read().split(' ')[1].replace('(', '').replace(')', '') + data = f.read() + # XXX - gets changed later and probably needs refactoring + return data[data.find('(') + 1:data.rfind(')')] def exe(self): try: diff --git a/test/test_psutil.py b/test/test_psutil.py index fa2b62c2..75bc20de 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1543,6 +1543,26 @@ class TestProcess(unittest.TestCase): pyexe = os.path.basename(os.path.realpath(sys.executable)).lower() assert pyexe.startswith(name), (pyexe, name) + @unittest.skipUnless(POSIX, "posix only") + # TODO: add support for other compilers + @unittest.skipUnless(which("gcc"), "gcc not available") + def test_prog_w_funky_name(self): + # Test that name(), exe() and cmdline() correctly handle programs + # with funky chars such as spaces and ")", see: + # https://github.com/giampaolo/psutil/issues/628 + funky_name = "/tmp/foo bar )" + _, c_file = tempfile.mkstemp(prefix='psutil-', suffix='.c', dir="/tmp") + self.addCleanup(lambda: safe_remove(c_file)) + with open(c_file, "w") as f: + f.write("void main() { pause(); }") + subprocess.check_call(["gcc", c_file, "-o", funky_name]) + self.addCleanup(lambda: safe_remove(funky_name)) + sproc = get_test_subprocess([funky_name, "arg1", "arg2"]) + p = psutil.Process(sproc.pid) + self.assertEqual(p.name(), "foo bar )") + self.assertEqual(p.exe(), "/tmp/foo bar )") + self.assertEqual(p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2"]) + @unittest.skipUnless(POSIX, 'posix only') def test_uids(self): p = psutil.Process() -- cgit v1.2.1 From 636fb7eaf042aaa303aaae60159c103f7bd8e293 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 3 Jun 2015 11:51:12 +0200 Subject: addCleanup sooner rather than later --- test/test_psutil.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_psutil.py b/test/test_psutil.py index 75bc20de..470dafc4 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1553,10 +1553,10 @@ class TestProcess(unittest.TestCase): funky_name = "/tmp/foo bar )" _, c_file = tempfile.mkstemp(prefix='psutil-', suffix='.c', dir="/tmp") self.addCleanup(lambda: safe_remove(c_file)) + self.addCleanup(lambda: safe_remove(funky_name)) with open(c_file, "w") as f: f.write("void main() { pause(); }") subprocess.check_call(["gcc", c_file, "-o", funky_name]) - self.addCleanup(lambda: safe_remove(funky_name)) sproc = get_test_subprocess([funky_name, "arg1", "arg2"]) p = psutil.Process(sproc.pid) self.assertEqual(p.name(), "foo bar )") -- cgit v1.2.1 From 97fd107b0f1f87dcec3037de4ec02b9cd669c321 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 3 Jun 2015 13:30:29 +0200 Subject: fix #614: [Linux] return the num of physical cores instead of physical CPUs --- CREDITS | 5 +++++ HISTORY.rst | 2 ++ psutil/__init__.py | 2 +- psutil/_pslinux.py | 24 +++++++++++++++++++----- test/_linux.py | 11 +++++++++++ 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CREDITS b/CREDITS index de2a5fa6..22f604c4 100644 --- a/CREDITS +++ b/CREDITS @@ -298,3 +298,8 @@ N: desbma W: https://github.com/desbma C: France I: 628 + +N: John Burnett +W: http://www.johnburnett.com/ +C: Irvine, CA, US +I: 614 diff --git a/HISTORY.rst b/HISTORY.rst index 0f5eed06..683b7d09 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -37,6 +37,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #593: [FreeBSD] Process().memory_maps() segfaults. - #606: Process.parent() may swallow NoSuchProcess exceptions. - #611: [SunOS] net_io_counters has send and received swapped +- #614: [Linux]: cpu_count(logical=False) return the number of physical CPUs + instead of physical cores. - #618: [SunOS] swap tests fail on Solaris when run as normal user - #628: [Linux] Process.name() truncates process name in case it contains spaces or parentheses. diff --git a/psutil/__init__.py b/psutil/__init__.py index b8e6b690..9e8cb740 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1342,7 +1342,7 @@ def cpu_count(logical=True): os.cpu_count() in Python 3.4). If logical is False return the number of physical cores only - (hyper thread CPUs are excluded). + (e.g. hyper thread CPUs are excluded). Return None if undetermined. diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index a7cac31c..668154bb 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -276,14 +276,28 @@ def cpu_count_logical(): def cpu_count_physical(): - """Return the number of physical CPUs in the system.""" + """Return the number of physical cores in the system.""" + mapping = {} + current_info = {} with open('/proc/cpuinfo', 'rb') as f: - found = set() for line in f: - if line.lower().startswith(b'physical id'): - found.add(line.strip()) + line = line.strip().lower() + if not line: + # new section + if (b'physical id' in current_info and + b'cpu cores' in current_info): + mapping[current_info[b'physical id']] = \ + current_info[b'cpu cores'] + current_info = {} + else: + # ongoing section + if (line.startswith(b'physical id') or + line.startswith(b'cpu cores')): + key, value = line.split(b'\t:', 1) + current_info[key] = int(value) + # mimic os.cpu_count() - return len(found) if found else None + return sum(mapping.values()) or None # --- other system functions diff --git a/test/_linux.py b/test/_linux.py index 8ac0608c..a6cb9e41 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -204,6 +204,17 @@ class LinuxSpecificTestCase(unittest.TestCase): self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( pprint.pformat(nics), out)) + @unittest.skipUnless(which("nproc"), "nproc utility not available") + def test_cpu_count_logical_w_nproc(self): + num = int(sh("nproc --all")) + self.assertEqual(psutil.cpu_count(logical=True), num) + + @unittest.skipUnless(which("lscpu"), "lscpu utility not available") + def test_cpu_count_logical_w_lscpu(self): + out = sh("lscpu -p") + num = len([x for x in out.split('\n') if not x.startswith('#')]) + self.assertEqual(psutil.cpu_count(logical=True), num) + # --- tests for specific kernel versions @unittest.skipUnless( -- cgit v1.2.1 From 5224920f72434e2306f83898c01dd2db9f41e744 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 3 Jun 2015 16:01:36 +0200 Subject: add mocked tests for linux (also include mock as a dep on py < 3.4) --- .travis.yml | 6 ++-- MANIFEST.in | 2 +- test/README | 15 -------- test/README.rst | 21 ++++++++++++ test/_linux.py | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 3 ++ 6 files changed, 133 insertions(+), 19 deletions(-) delete mode 100644 test/README create mode 100644 test/README.rst diff --git a/.travis.yml b/.travis.yml index 693829a3..aaca6dbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,9 @@ python: - 3.4 # - pypy install: - - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install ipaddress unittest2; fi - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install ipaddress; fi - - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install ipaddress; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install ipaddress unittest2 mock; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install ipaddress mock; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install ipaddress mock; fi - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install ipaddress; fi script: - pip install flake8 pep8 diff --git a/MANIFEST.in b/MANIFEST.in index de1e8b7d..18136122 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -17,4 +17,4 @@ recursive-include docs * recursive-exclude docs/_build * recursive-include examples *.py recursive-include psutil *.py *.c *.h -recursive-include test *.py README +recursive-include test *.py README* diff --git a/test/README b/test/README deleted file mode 100644 index 801f93d1..00000000 --- a/test/README +++ /dev/null @@ -1,15 +0,0 @@ -- The recommended way to run tests (also on Windows) is to cd into parent - directory and run: - - make test - -- If you're on Python < 2.7 unittest2 module must be installed first: - https://pypi.python.org/pypi/unittest2 - -- The main test script is test_psutil.py, which also imports platform-specific - _*.py scripts (which should be ignored). - -- test_memory_leaks.py looks for memory leaks into C extension modules and must - be run separately with: - - make memtest diff --git a/test/README.rst b/test/README.rst new file mode 100644 index 00000000..c79c69a6 --- /dev/null +++ b/test/README.rst @@ -0,0 +1,21 @@ +- The recommended way to run tests (also on Windows) is to cd into parent + directory and run ``make test`` + +- Dependencies for running tests: + - python 2.6: ipaddress, mock, unittest2 + - python 2.7: ipaddress, mock + - python 3.2: ipaddress, mock + - python 3.3: ipaddress + - python >= 3.4: no deps required + +- The main test script is ``test_psutil.py``, which also imports platform-specific + ``_*.py`` scripts (which should be ignored). + +- ``test_memory_leaks.py`` looks for memory leaks into C extension modules and must + be run separately with ``make memtest``. + +- To run tests on all supported Python version install tox (pip install tox) + then run ``tox``. + +- Every time a commit is pushed tests are automatically run on Travis: + https://travis-ci.org/giampaolo/psutil/ diff --git a/test/_linux.py b/test/_linux.py index a6cb9e41..97a5493e 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -16,6 +16,12 @@ import socket import struct import sys import time +import warnings + +try: + from unittest import mock # py3 +except ImportError: + import mock # requires "pip install mock" from test_psutil import POSIX, TOLERANCE, TRAVIS from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, @@ -23,6 +29,7 @@ from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, which) import psutil +import psutil._pslinux from psutil._compat import PY3 @@ -215,6 +222,104 @@ class LinuxSpecificTestCase(unittest.TestCase): num = len([x for x in out.split('\n') if not x.startswith('#')]) self.assertEqual(psutil.cpu_count(logical=True), num) + # --- mocked tests + + def test_virtual_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.virtual_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'cached', 'active' and 'inactive' memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.cached, 0) + self.assertEqual(ret.active, 0) + self.assertEqual(ret.inactive, 0) + + def test_swap_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.swap_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'sin' and 'sout' swap memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.sin, 0) + self.assertEqual(ret.sout, 0) + + def test_cpu_count_logical_mocked(self): + import psutil._pslinux + original = psutil._pslinux.cpu_count_logical() + with mock.patch( + 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: + # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in + # order to test /proc/cpuinfo parsing. + # We might also test /proc/stat parsing but mocking open() + # like that is too difficult. + self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert m.called + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_logical()) + assert m.called + + def test_cpu_count_physical_mocked(self): + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_physical()) + assert m.called + + def test_proc_terminal_mocked(self): + with mock.patch('psutil._pslinux._psposix._get_terminal_map', + return_value={}) as m: + self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) + assert m.called + + def test_proc_num_ctx_switches_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_ctx_switches) + assert m.called + + def test_proc_num_threads_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_threads) + assert m.called + + def test_proc_ppid_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).ppid) + assert m.called + + def test_proc_uids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).uids) + assert m.called + + def test_proc_gids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).gids) + assert m.called + # --- tests for specific kernel versions @unittest.skipUnless( diff --git a/tox.ini b/tox.ini index 25cee381..cf60729f 100644 --- a/tox.ini +++ b/tox.ini @@ -12,9 +12,12 @@ deps = flake8 pytest py26: ipaddress + py26: mock py26: unittest2 py27: ipaddress + py27: mock py32: ipaddress + py32: mock py33: ipaddress setenv = -- cgit v1.2.1 From 82da82a6bb94ed5c6faf9d762ef4ad0fec18f01b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jun 2015 02:45:50 +0200 Subject: add 'make coverage' and 'make install-dev-deps' --- .travis.yml | 8 ++++---- Makefile | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index aaca6dbe..c4085bc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,10 +7,10 @@ python: - 3.4 # - pypy install: - - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install ipaddress unittest2 mock; fi - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install ipaddress mock; fi - - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install ipaddress mock; fi - - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install ipaddress; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install -U ipaddress unittest2 mock; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install -U ipaddress mock; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install -U ipaddress mock; fi + - if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then pip install -U ipaddress; fi script: - pip install flake8 pep8 - python setup.py build diff --git a/Makefile b/Makefile index 510d7313..b13547c8 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,9 @@ PYTHON = python TSCRIPT = test/test_psutil.py +# Private vars +COVERAGE_OPTS = --include="*psutil*" --omit="test/*,*setup*" + all: test clean: @@ -19,10 +22,12 @@ clean: rm -rf *.core rm -rf *.egg-info rm -rf *\$testfile* + rm -rf .coverage rm -rf .tox rm -rf build rm -rf dist rm -rf docs/_build + rm -rf htmlcov build: clean $(PYTHON) setup.py build @@ -31,6 +36,11 @@ build: clean @# this directory. $(PYTHON) setup.py build_ext -i +install-dev-deps: + $(PYTHON) -m pip install --user --upgrade pip + $(PYTHON) -m pip install --user --upgrade \ + ipaddress unittest2 mock ipdb coverage nose + install: build $(PYTHON) setup.py install --user; \ @@ -59,6 +69,14 @@ test-by-name: install test-memleaks-by-name: install @$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) +coverage: install + rm -rf .coverage htmlcov + $(PYTHON) -m coverage run $(TSCRIPT) $(COVERAGE_OPTS) + $(PYTHON) -m coverage report $(COVERAGE_OPTS) + @echo "writing results to htmlcov/index.html" + $(PYTHON) -m coverage html $(COVERAGE_OPTS) + $(PYTHON) -m webbrowser -t htmlcov/index.html + # requires "pip install pep8" pep8: @git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8 -- cgit v1.2.1 From d9f580538224d63f1a1273854293e2c87b0144dd Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jun 2015 03:37:02 +0200 Subject: #629: 'skip' platorm specific tests if we're on another platform (instead of failing); rename test_main to main otherwise pytest / nose will execute it as a test case --- HISTORY.rst | 1 + test/_bsd.py | 7 ++++--- test/_linux.py | 7 ++++--- test/_osx.py | 9 +++++---- test/_posix.py | 7 ++++--- test/_sunos.py | 7 ++++--- test/_windows.py | 18 +++++++++++------- test/test_memory_leaks.py | 4 ++-- 8 files changed, 35 insertions(+), 25 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 683b7d09..faeb0c9e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -21,6 +21,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #599: [Windows] process name() can now be determined for all processes even when running as a limited user. - #602: pre-commit GIT hook. +- #629: enhanced support for py.test and nose test discovery and tests run. **Bug fixes** diff --git a/test/_bsd.py b/test/_bsd.py index 9dfeb493..76f12442 100644 --- a/test/_bsd.py +++ b/test/_bsd.py @@ -16,7 +16,7 @@ import time import psutil from psutil._compat import PY3 -from test_psutil import (TOLERANCE, sh, get_test_subprocess, which, +from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, retry_before_failing, reap_children, unittest) @@ -50,6 +50,7 @@ def muse(field): return int(line.split()[1]) +@unittest.skipUnless(BSD, "not a BSD system") class BSDSpecificTestCase(unittest.TestCase): @classmethod @@ -236,12 +237,12 @@ class BSDSpecificTestCase(unittest.TestCase): delta=TOLERANCE) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/test/_linux.py b/test/_linux.py index 97a5493e..5d7f0521 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -23,7 +23,7 @@ try: except ImportError: import mock # requires "pip install mock" -from test_psutil import POSIX, TOLERANCE, TRAVIS +from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, retry_before_failing, get_kernel_version, unittest, which) @@ -67,6 +67,7 @@ def get_mac_address(ifname): return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] +@unittest.skipUnless(LINUX, "not a Linux system") class LinuxSpecificTestCase(unittest.TestCase): @unittest.skipIf( @@ -356,12 +357,12 @@ class LinuxSpecificTestCase(unittest.TestCase): self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/test/_osx.py b/test/_osx.py index 784f00e0..6e6e4380 100644 --- a/test/_osx.py +++ b/test/_osx.py @@ -15,8 +15,8 @@ import time import psutil from psutil._compat import PY3 -from test_psutil import (TOLERANCE, sh, get_test_subprocess, reap_children, - retry_before_failing, unittest) +from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, + reap_children, retry_before_failing, unittest) PAGESIZE = os.sysconf("SC_PAGE_SIZE") @@ -47,6 +47,7 @@ def vm_stat(field): return int(re.search('\d+', line).group(0)) * PAGESIZE +@unittest.skipUnless(OSX, "not an OSX system") class OSXSpecificTestCase(unittest.TestCase): @classmethod @@ -148,12 +149,12 @@ class OSXSpecificTestCase(unittest.TestCase): self.assertEqual(tot1, tot2) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/test/_posix.py b/test/_posix.py index 4439f2c7..2a263a3f 100644 --- a/test/_posix.py +++ b/test/_posix.py @@ -15,7 +15,7 @@ import time import psutil from psutil._compat import PY3, callable -from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON +from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX from test_psutil import (get_test_subprocess, skip_on_access_denied, retry_before_failing, reap_children, sh, unittest, get_kernel_version, wait_for_pid) @@ -42,6 +42,7 @@ def ps(cmd): return output +@unittest.skipUnless(POSIX, "not a POSIX system") class PosixSpecificTestCase(unittest.TestCase): """Compare psutil results against 'ps' command line utility.""" @@ -243,12 +244,12 @@ class PosixSpecificTestCase(unittest.TestCase): self.fail('\n' + '\n'.join(failures)) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/test/_sunos.py b/test/_sunos.py index b1943373..3d54ccd8 100644 --- a/test/_sunos.py +++ b/test/_sunos.py @@ -9,10 +9,11 @@ import sys import os -from test_psutil import sh, unittest +from test_psutil import SUNOS, sh, unittest import psutil +@unittest.skipUnless(SUNOS, "not a SunOS system") class SunOSSpecificTestCase(unittest.TestCase): def test_swap_memory(self): @@ -36,12 +37,12 @@ class SunOSSpecificTestCase(unittest.TestCase): self.assertEqual(psutil_swap.free, free) -def test_main(): +def main(): test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) result = unittest.TextTestRunner(verbosity=2).run(test_suite) return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) diff --git a/test/_windows.py b/test/_windows.py index a3699398..a0a22052 100644 --- a/test/_windows.py +++ b/test/_windows.py @@ -15,7 +15,7 @@ import sys import time import traceback -from test_psutil import (get_test_subprocess, reap_children, unittest) +from test_psutil import WINDOWS, get_test_subprocess, reap_children, unittest try: import wmi @@ -28,16 +28,18 @@ except ImportError: win32api = win32con = None from psutil._compat import PY3, callable, long -from psutil._pswindows import ACCESS_DENIED_SET -import psutil._psutil_windows as _psutil_windows import psutil +cext = psutil._psplatform.cext + + def wrap_exceptions(fun): def wrapper(self, *args, **kwargs): try: return fun(self, *args, **kwargs) except OSError as err: + from psutil._pswindows import ACCESS_DENIED_SET if err.errno in ACCESS_DENIED_SET: raise psutil.AccessDenied(None, None) if err.errno == errno.ESRCH: @@ -46,6 +48,7 @@ def wrap_exceptions(fun): return wrapper +@unittest.skipUnless(WINDOWS, "not a Windows system") class WindowsSpecificTestCase(unittest.TestCase): @classmethod @@ -281,6 +284,7 @@ class WindowsSpecificTestCase(unittest.TestCase): self.fail('\n' + '\n'.join(failures)) +@unittest.skipUnless(WINDOWS, "not a Windows system") class TestDualProcessImplementation(unittest.TestCase): fun_names = [ # function name, tolerance @@ -324,7 +328,7 @@ class TestDualProcessImplementation(unittest.TestCase): failures = [] for p in psutil.process_iter(): try: - nt = ntpinfo(*_psutil_windows.proc_info(p.pid)) + nt = ntpinfo(*cext.proc_info(p.pid)) except psutil.NoSuchProcess: continue assert_ge_0(nt) @@ -334,7 +338,7 @@ class TestDualProcessImplementation(unittest.TestCase): continue if name == 'proc_create_time' and p.pid in (0, 4): continue - meth = wrap_exceptions(getattr(_psutil_windows, name)) + meth = wrap_exceptions(getattr(cext, name)) try: ret = meth(p.pid) except (psutil.NoSuchProcess, psutil.AccessDenied): @@ -356,7 +360,7 @@ class TestDualProcessImplementation(unittest.TestCase): compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) elif name == 'proc_memory_info': try: - rawtupl = _psutil_windows.proc_memory_info_2(p.pid) + rawtupl = cext.proc_memory_info_2(p.pid) except psutil.NoSuchProcess: continue compare_with_tolerance(ret, rawtupl, tolerance) @@ -385,7 +389,7 @@ class TestDualProcessImplementation(unittest.TestCase): # process no longer exists ZOMBIE_PID = max(psutil.pids()) + 5000 for name, _ in self.fun_names: - meth = wrap_exceptions(getattr(_psutil_windows, name)) + meth = wrap_exceptions(getattr(cext, name)) self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py index 5a31ac1f..802e20ab 100644 --- a/test/test_memory_leaks.py +++ b/test/test_memory_leaks.py @@ -417,7 +417,7 @@ class TestModuleFunctionsLeaks(Base): self.execute('net_if_stats') -def test_main(): +def main(): test_suite = unittest.TestSuite() tests = [TestProcessObjectLeaksZombie, TestProcessObjectLeaks, @@ -428,5 +428,5 @@ def test_main(): return result.wasSuccessful() if __name__ == '__main__': - if not test_main(): + if not main(): sys.exit(1) -- cgit v1.2.1 From 870e00f71c15ef39f8259ff16b682e495ea5d6eb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jun 2015 03:42:11 +0200 Subject: #629: rename test/_linux.py (and others) to test/test_pslinux.py in order to ease test discovery for nose and pytest --- test/_bsd.py | 248 ------------------------------- test/_linux.py | 368 ---------------------------------------------- test/_osx.py | 160 -------------------- test/_posix.py | 255 -------------------------------- test/_sunos.py | 48 ------ test/_windows.py | 405 --------------------------------------------------- test/test_bsd.py | 248 +++++++++++++++++++++++++++++++ test/test_linux.py | 368 ++++++++++++++++++++++++++++++++++++++++++++++ test/test_osx.py | 160 ++++++++++++++++++++ test/test_posix.py | 255 ++++++++++++++++++++++++++++++++ test/test_psutil.py | 14 +- test/test_sunos.py | 48 ++++++ test/test_windows.py | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 1491 insertions(+), 1491 deletions(-) delete mode 100644 test/_bsd.py delete mode 100644 test/_linux.py delete mode 100644 test/_osx.py delete mode 100644 test/_posix.py delete mode 100644 test/_sunos.py delete mode 100644 test/_windows.py create mode 100644 test/test_bsd.py create mode 100644 test/test_linux.py create mode 100644 test/test_osx.py create mode 100644 test/test_posix.py create mode 100644 test/test_sunos.py create mode 100644 test/test_windows.py diff --git a/test/_bsd.py b/test/_bsd.py deleted file mode 100644 index 76f12442..00000000 --- a/test/_bsd.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python - -# 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. - -# TODO: add test for comparing connections with 'sockstat' cmd - -"""BSD specific tests. These are implicitly run by test_psutil.py.""" - -import os -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3 -from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, - retry_before_failing, reap_children, unittest) - - -PAGESIZE = os.sysconf("SC_PAGE_SIZE") -if os.getuid() == 0: # muse requires root privileges - MUSE_AVAILABLE = which('muse') -else: - MUSE_AVAILABLE = False - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - result = sh("sysctl " + cmdline) - result = result[result.find(": ") + 2:] - try: - return int(result) - except ValueError: - return result - - -def muse(field): - """Thin wrapper around 'muse' cmdline utility.""" - out = sh('muse') - for line in out.split('\n'): - if line.startswith(field): - break - else: - raise ValueError("line not found") - return int(line.split()[1]) - - -@unittest.skipUnless(BSD, "not a BSD system") -class BSDSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_boot_time(self): - s = sysctl('sysctl kern.boottime') - s = s[s.find(" sec = ") + 7:] - s = s[:s.find(',')] - btime = int(s) - self.assertEqual(btime, psutil.boot_time()) - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" % self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if PY3: - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -k "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.free, free)) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.used, used)) - - @retry_before_failing() - def test_memory_maps(self): - out = sh('procstat -v %s' % self.pid) - maps = psutil.Process(self.pid).memory_maps(grouped=False) - lines = out.split('\n')[1:] - while lines: - line = lines.pop() - fields = line.split() - _, start, stop, perms, res = fields[:5] - map = maps.pop() - self.assertEqual("%s-%s" % (start, stop), map.addr) - self.assertEqual(int(res), map.rss) - if not map.path.startswith('['): - self.assertEqual(fields[10], map.path) - - def test_exe(self): - out = sh('procstat -b %s' % self.pid) - self.assertEqual(psutil.Process(self.pid).exe(), - out.split('\n')[1].split()[-1]) - - def test_cmdline(self): - out = sh('procstat -c %s' % self.pid) - self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), - ' '.join(out.split('\n')[1].split()[2:])) - - def test_uids_gids(self): - out = sh('procstat -s %s' % self.pid) - euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] - p = psutil.Process(self.pid) - uids = p.uids() - gids = p.gids() - self.assertEqual(uids.real, int(ruid)) - self.assertEqual(uids.effective, int(euid)) - self.assertEqual(uids.saved, int(suid)) - self.assertEqual(gids.real, int(rgid)) - self.assertEqual(gids.effective, int(egid)) - self.assertEqual(gids.saved, int(sgid)) - - # --- virtual_memory(); tests against sysctl - - def test_vmem_total(self): - syst = sysctl("sysctl vm.stats.vm.v_page_count") * PAGESIZE - self.assertEqual(psutil.virtual_memory().total, syst) - - @retry_before_failing() - def test_vmem_active(self): - syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().active, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_inactive(self): - syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_wired(self): - syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().wired, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_cached(self): - syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().cached, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_free(self): - syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().free, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_buffers(self): - syst = sysctl("vfs.bufspace") - self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, - delta=TOLERANCE) - - # --- virtual_memory(); tests against muse - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - def test_total(self): - num = muse('Total') - self.assertEqual(psutil.virtual_memory().total, num) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_active(self): - num = muse('Active') - self.assertAlmostEqual(psutil.virtual_memory().active, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_inactive(self): - num = muse('Inactive') - self.assertAlmostEqual(psutil.virtual_memory().inactive, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_wired(self): - num = muse('Wired') - self.assertAlmostEqual(psutil.virtual_memory().wired, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_cached(self): - num = muse('Cache') - self.assertAlmostEqual(psutil.virtual_memory().cached, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_free(self): - num = muse('Free') - self.assertAlmostEqual(psutil.virtual_memory().free, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_buffers(self): - num = muse('Buffer') - self.assertAlmostEqual(psutil.virtual_memory().buffers, num, - delta=TOLERANCE) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/_linux.py b/test/_linux.py deleted file mode 100644 index 5d7f0521..00000000 --- a/test/_linux.py +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Linux specific tests. These are implicitly run by test_psutil.py.""" - -from __future__ import division -import contextlib -import fcntl -import os -import pprint -import re -import socket -import struct -import sys -import time -import warnings - -try: - from unittest import mock # py3 -except ImportError: - import mock # requires "pip install mock" - -from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX -from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, - retry_before_failing, get_kernel_version, unittest, - which) - -import psutil -import psutil._pslinux -from psutil._compat import PY3 - - -SIOCGIFADDR = 0x8915 -SIOCGIFCONF = 0x8912 -SIOCGIFHWADDR = 0x8927 - - -def get_ipv4_address(ifname): - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): - return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), - SIOCGIFADDR, - struct.pack('256s', ifname))[20:24]) - - -def get_mac_address(ifname): - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): - info = fcntl.ioctl( - s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) - if PY3: - def ord(x): - return x - else: - import __builtin__ - ord = __builtin__.ord - return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] - - -@unittest.skipUnless(LINUX, "not a Linux system") -class LinuxSpecificTestCase(unittest.TestCase): - - @unittest.skipIf( - POSIX and not hasattr(os, 'statvfs'), - reason="os.statvfs() function not available on this platform") - @skip_on_not_implemented() - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -P -B 1 "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total, used, free = int(total), int(used), int(free) - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.free, free)) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.used, used)) - - def test_memory_maps(self): - sproc = get_test_subprocess() - time.sleep(1) - p = psutil.Process(sproc.pid) - maps = p.memory_maps(grouped=False) - pmap = sh('pmap -x %s' % p.pid).split('\n') - # get rid of header - del pmap[0] - del pmap[0] - while maps and pmap: - this = maps.pop(0) - other = pmap.pop(0) - addr, _, rss, dirty, mode, path = other.split(None, 5) - if not path.startswith('[') and not path.endswith(']'): - self.assertEqual(path, os.path.basename(this.path)) - self.assertEqual(int(rss) * 1024, this.rss) - # test only rwx chars, ignore 's' and 'p' - self.assertEqual(mode[:3], this.perms[:3]) - - def test_vmem_total(self): - lines = sh('free').split('\n')[1:] - total = int(lines[0].split()[1]) * 1024 - self.assertEqual(total, psutil.virtual_memory().total) - - @retry_before_failing() - def test_vmem_used(self): - lines = sh('free').split('\n')[1:] - used = int(lines[0].split()[2]) * 1024 - self.assertAlmostEqual(used, psutil.virtual_memory().used, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_free(self): - lines = sh('free').split('\n')[1:] - free = int(lines[0].split()[3]) * 1024 - self.assertAlmostEqual(free, psutil.virtual_memory().free, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_buffers(self): - lines = sh('free').split('\n')[1:] - buffers = int(lines[0].split()[5]) * 1024 - self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_cached(self): - lines = sh('free').split('\n')[1:] - cached = int(lines[0].split()[6]) * 1024 - self.assertAlmostEqual(cached, psutil.virtual_memory().cached, - delta=TOLERANCE) - - def test_swapmem_total(self): - lines = sh('free').split('\n')[1:] - total = int(lines[2].split()[1]) * 1024 - self.assertEqual(total, psutil.swap_memory().total) - - @retry_before_failing() - def test_swapmem_used(self): - lines = sh('free').split('\n')[1:] - used = int(lines[2].split()[2]) * 1024 - self.assertAlmostEqual(used, psutil.swap_memory().used, - delta=TOLERANCE) - - @retry_before_failing() - def test_swapmem_free(self): - lines = sh('free').split('\n')[1:] - free = int(lines[2].split()[3]) * 1024 - self.assertAlmostEqual(free, psutil.swap_memory().free, - delta=TOLERANCE) - - @unittest.skipIf(TRAVIS, "unknown failure on travis") - def test_cpu_times(self): - fields = psutil.cpu_times()._fields - kernel_ver = re.findall('\d+\.\d+\.\d+', os.uname()[2])[0] - kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) - if kernel_ver_info >= (2, 6, 11): - self.assertIn('steal', fields) - else: - self.assertNotIn('steal', fields) - if kernel_ver_info >= (2, 6, 24): - self.assertIn('guest', fields) - else: - self.assertNotIn('guest', fields) - if kernel_ver_info >= (3, 2, 0): - self.assertIn('guest_nice', fields) - else: - self.assertNotIn('guest_nice', fields) - - def test_net_if_addrs_ips(self): - for name, addrs in psutil.net_if_addrs().items(): - for addr in addrs: - if addr.family == psutil.AF_LINK: - self.assertEqual(addr.address, get_mac_address(name)) - elif addr.family == socket.AF_INET: - self.assertEqual(addr.address, get_ipv4_address(name)) - # TODO: test for AF_INET6 family - - @unittest.skipUnless(which('ip'), "'ip' utility not available") - @unittest.skipIf(TRAVIS, "skipped on Travis") - def test_net_if_names(self): - out = sh("ip addr").strip() - nics = psutil.net_if_addrs() - found = 0 - for line in out.split('\n'): - line = line.strip() - if re.search("^\d+:", line): - found += 1 - name = line.split(':')[1].strip() - self.assertIn(name, nics.keys()) - self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( - pprint.pformat(nics), out)) - - @unittest.skipUnless(which("nproc"), "nproc utility not available") - def test_cpu_count_logical_w_nproc(self): - num = int(sh("nproc --all")) - self.assertEqual(psutil.cpu_count(logical=True), num) - - @unittest.skipUnless(which("lscpu"), "lscpu utility not available") - def test_cpu_count_logical_w_lscpu(self): - out = sh("lscpu -p") - num = len([x for x in out.split('\n') if not x.startswith('#')]) - self.assertEqual(psutil.cpu_count(logical=True), num) - - # --- mocked tests - - def test_virtual_memory_mocked_warnings(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil._pslinux.virtual_memory() - assert m.called - self.assertEqual(len(ws), 1) - w = ws[0] - self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) - self.assertIn( - "'cached', 'active' and 'inactive' memory stats couldn't " - "be determined", str(w.message)) - self.assertEqual(ret.cached, 0) - self.assertEqual(ret.active, 0) - self.assertEqual(ret.inactive, 0) - - def test_swap_memory_mocked_warnings(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil._pslinux.swap_memory() - assert m.called - self.assertEqual(len(ws), 1) - w = ws[0] - self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) - self.assertIn( - "'sin' and 'sout' swap memory stats couldn't " - "be determined", str(w.message)) - self.assertEqual(ret.sin, 0) - self.assertEqual(ret.sout, 0) - - def test_cpu_count_logical_mocked(self): - import psutil._pslinux - original = psutil._pslinux.cpu_count_logical() - with mock.patch( - 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: - # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in - # order to test /proc/cpuinfo parsing. - # We might also test /proc/stat parsing but mocking open() - # like that is too difficult. - self.assertEqual(psutil._pslinux.cpu_count_logical(), original) - assert m.called - # Have open() return emtpy data and make sure None is returned - # ('cause we want to mimick os.cpu_count()) - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_logical()) - assert m.called - - def test_cpu_count_physical_mocked(self): - # Have open() return emtpy data and make sure None is returned - # ('cause we want to mimick os.cpu_count()) - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_physical()) - assert m.called - - def test_proc_terminal_mocked(self): - with mock.patch('psutil._pslinux._psposix._get_terminal_map', - return_value={}) as m: - self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) - assert m.called - - def test_proc_num_ctx_switches_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).num_ctx_switches) - assert m.called - - def test_proc_num_threads_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).num_threads) - assert m.called - - def test_proc_ppid_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).ppid) - assert m.called - - def test_proc_uids_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).uids) - assert m.called - - def test_proc_gids_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).gids) - assert m.called - - # --- tests for specific kernel versions - - @unittest.skipUnless( - get_kernel_version() >= (2, 6, 36), - "prlimit() not available on this Linux kernel version") - def test_prlimit_availability(self): - # prlimit() should be available starting from kernel 2.6.36 - p = psutil.Process(os.getpid()) - p.rlimit(psutil.RLIMIT_NOFILE) - # if prlimit() is supported *at least* these constants should - # be available - self.assertTrue(hasattr(psutil, "RLIM_INFINITY")) - self.assertTrue(hasattr(psutil, "RLIMIT_AS")) - self.assertTrue(hasattr(psutil, "RLIMIT_CORE")) - self.assertTrue(hasattr(psutil, "RLIMIT_CPU")) - self.assertTrue(hasattr(psutil, "RLIMIT_DATA")) - self.assertTrue(hasattr(psutil, "RLIMIT_FSIZE")) - self.assertTrue(hasattr(psutil, "RLIMIT_LOCKS")) - self.assertTrue(hasattr(psutil, "RLIMIT_MEMLOCK")) - self.assertTrue(hasattr(psutil, "RLIMIT_NOFILE")) - self.assertTrue(hasattr(psutil, "RLIMIT_NPROC")) - self.assertTrue(hasattr(psutil, "RLIMIT_RSS")) - self.assertTrue(hasattr(psutil, "RLIMIT_STACK")) - - @unittest.skipUnless( - get_kernel_version() >= (3, 0), - "prlimit constants not available on this Linux kernel version") - def test_resource_consts_kernel_v(self): - # more recent constants - self.assertTrue(hasattr(psutil, "RLIMIT_MSGQUEUE")) - self.assertTrue(hasattr(psutil, "RLIMIT_NICE")) - self.assertTrue(hasattr(psutil, "RLIMIT_RTPRIO")) - self.assertTrue(hasattr(psutil, "RLIMIT_RTTIME")) - self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/_osx.py b/test/_osx.py deleted file mode 100644 index 6e6e4380..00000000 --- a/test/_osx.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""OSX specific tests. These are implicitly run by test_psutil.py.""" - -import os -import re -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3 -from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, - reap_children, retry_before_failing, unittest) - - -PAGESIZE = os.sysconf("SC_PAGE_SIZE") - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip().split()[1] - if PY3: - result = str(result, sys.stdout.encoding) - try: - return int(result) - except ValueError: - return result - - -def vm_stat(field): - """Wrapper around 'vm_stat' cmdline utility.""" - out = sh('vm_stat') - for line in out.split('\n'): - if field in line: - break - else: - raise ValueError("line not found") - return int(re.search('\d+', line).group(0)) * PAGESIZE - - -@unittest.skipUnless(OSX, "not an OSX system") -class OSXSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" % self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if PY3: - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -k "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % usage.free, free) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % usage.used, used) - - # --- virtual mem - - def test_vmem_total(self): - sysctl_hwphymem = sysctl('sysctl hw.memsize') - self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) - - @retry_before_failing() - def test_vmem_free(self): - num = vm_stat("free") - self.assertAlmostEqual(psutil.virtual_memory().free, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_active(self): - num = vm_stat("active") - self.assertAlmostEqual(psutil.virtual_memory().active, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_inactive(self): - num = vm_stat("inactive") - self.assertAlmostEqual(psutil.virtual_memory().inactive, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_wired(self): - num = vm_stat("wired") - self.assertAlmostEqual(psutil.virtual_memory().wired, num, - delta=TOLERANCE) - - # --- swap mem - - def test_swapmem_sin(self): - num = vm_stat("Pageins") - self.assertEqual(psutil.swap_memory().sin, num) - - def test_swapmem_sout(self): - num = vm_stat("Pageouts") - self.assertEqual(psutil.swap_memory().sout, num) - - def test_swapmem_total(self): - tot1 = psutil.swap_memory().total - tot2 = 0 - # OSX uses multiple cache files: - # http://en.wikipedia.org/wiki/Paging#OS_X - for name in os.listdir("/var/vm/"): - file = os.path.join("/var/vm", name) - if os.path.isfile(file): - tot2 += os.path.getsize(file) - self.assertEqual(tot1, tot2) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/_posix.py b/test/_posix.py deleted file mode 100644 index 2a263a3f..00000000 --- a/test/_posix.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""POSIX specific tests. These are implicitly run by test_psutil.py.""" - -import datetime -import os -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3, callable -from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX -from test_psutil import (get_test_subprocess, skip_on_access_denied, - retry_before_failing, reap_children, sh, unittest, - get_kernel_version, wait_for_pid) - - -def ps(cmd): - """Expects a ps command with a -o argument and parse the result - returning only the value of interest. - """ - if not LINUX: - cmd = cmd.replace(" --no-headers ", " ") - if SUNOS: - cmd = cmd.replace("-o command", "-o comm") - cmd = cmd.replace("-o start", "-o stime") - p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - if not LINUX: - output = output.split('\n')[1].strip() - try: - return int(output) - except ValueError: - return output - - -@unittest.skipUnless(POSIX, "not a POSIX system") -class PosixSpecificTestCase(unittest.TestCase): - """Compare psutil results against 'ps' command line utility.""" - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess([PYTHON, "-E", "-O"], - stdin=subprocess.PIPE).pid - wait_for_pid(cls.pid) - - @classmethod - def tearDownClass(cls): - reap_children() - - # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps - - def test_process_parent_pid(self): - ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) - ppid_psutil = psutil.Process(self.pid).ppid() - self.assertEqual(ppid_ps, ppid_psutil) - - def test_process_uid(self): - uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) - uid_psutil = psutil.Process(self.pid).uids().real - self.assertEqual(uid_ps, uid_psutil) - - def test_process_gid(self): - gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) - gid_psutil = psutil.Process(self.pid).gids().real - self.assertEqual(gid_ps, gid_psutil) - - def test_process_username(self): - username_ps = ps("ps --no-headers -o user -p %s" % self.pid) - username_psutil = psutil.Process(self.pid).username() - self.assertEqual(username_ps, username_psutil) - - @skip_on_access_denied() - @retry_before_failing() - def test_process_rss_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) - rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 - self.assertEqual(rss_ps, rss_psutil) - - @skip_on_access_denied() - @retry_before_failing() - def test_process_vsz_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) - vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 - self.assertEqual(vsz_ps, vsz_psutil) - - def test_process_name(self): - # use command + arg since "comm" keyword not supported on all platforms - name_ps = ps("ps --no-headers -o command -p %s" % ( - self.pid)).split(' ')[0] - # remove path if there is any, from the command - name_ps = os.path.basename(name_ps).lower() - name_psutil = psutil.Process(self.pid).name().lower() - self.assertEqual(name_ps, name_psutil) - - @unittest.skipIf(OSX or BSD, - 'ps -o start not available') - def test_process_create_time(self): - time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] - time_psutil = psutil.Process(self.pid).create_time() - time_psutil_tstamp = datetime.datetime.fromtimestamp( - time_psutil).strftime("%H:%M:%S") - # sometimes ps shows the time rounded up instead of down, so we check - # for both possible values - round_time_psutil = round(time_psutil) - round_time_psutil_tstamp = datetime.datetime.fromtimestamp( - round_time_psutil).strftime("%H:%M:%S") - self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) - - def test_process_exe(self): - ps_pathname = ps("ps --no-headers -o command -p %s" % - self.pid).split(' ')[0] - psutil_pathname = psutil.Process(self.pid).exe() - try: - self.assertEqual(ps_pathname, psutil_pathname) - except AssertionError: - # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python2.7" - # ...instead of: - # "/usr/local/bin/python" - # We do not want to consider this difference in accuracy - # an error. - adjusted_ps_pathname = ps_pathname[:len(ps_pathname)] - self.assertEqual(ps_pathname, adjusted_ps_pathname) - - def test_process_cmdline(self): - ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) - psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) - if SUNOS: - # ps on Solaris only shows the first part of the cmdline - psutil_cmdline = psutil_cmdline.split(" ")[0] - self.assertEqual(ps_cmdline, psutil_cmdline) - - @retry_before_failing() - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - if SUNOS: - cmd = ["ps", "ax"] - else: - cmd = ["ps", "ax", "-o", "pid"] - p = get_test_subprocess(cmd, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - pids_ps = [] - for line in output.split('\n')[1:]: - if line: - pid = int(line.split()[0].strip()) - pids_ps.append(pid) - # remove ps subprocess pid which is supposed to be dead in meantime - pids_ps.remove(p.pid) - pids_psutil = psutil.pids() - pids_ps.sort() - pids_psutil.sort() - - # on OSX ps doesn't show pid 0 - if OSX and 0 not in pids_ps: - pids_ps.insert(0, 0) - - if pids_ps != pids_psutil: - difference = [x for x in pids_psutil if x not in pids_ps] + \ - [x for x in pids_ps if x not in pids_psutil] - self.fail("difference: " + str(difference)) - - # for some reason ifconfig -a does not report differente interfaces - # psutil does - @unittest.skipIf(SUNOS, "test not reliable on SUNOS") - def test_nic_names(self): - p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - for nic in psutil.net_io_counters(pernic=True).keys(): - for line in output.split(): - if line.startswith(nic): - break - else: - self.fail("couldn't find %s nic in 'ifconfig -a' output" % nic) - - @retry_before_failing() - def test_users(self): - out = sh("who") - lines = out.split('\n') - users = [x.split()[0] for x in lines] - self.assertEqual(len(users), len(psutil.users())) - terminals = [x.split()[1] for x in lines] - for u in psutil.users(): - self.assertTrue(u.name in users, u.name) - self.assertTrue(u.terminal in terminals, u.terminal) - - def test_fds_open(self): - # Note: this fails from time to time; I'm keen on thinking - # it doesn't mean something is broken - def call(p, attr): - args = () - attr = getattr(p, name, None) - if attr is not None and callable(attr): - if name == 'rlimit': - args = (psutil.RLIMIT_NOFILE,) - attr(*args) - else: - attr - - p = psutil.Process(os.getpid()) - failures = [] - ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', - 'send_signal', 'wait', 'children', 'as_dict'] - if LINUX and get_kernel_version() < (2, 6, 36): - ignored_names.append('rlimit') - if LINUX and get_kernel_version() < (2, 6, 23): - ignored_names.append('num_ctx_switches') - for name in dir(psutil.Process): - if (name.startswith('_') or name in ignored_names): - continue - else: - try: - num1 = p.num_fds() - for x in range(2): - call(p, name) - num2 = p.num_fds() - except psutil.AccessDenied: - pass - else: - if abs(num2 - num1) > 1: - fail = "failure while processing Process.%s method " \ - "(before=%s, after=%s)" % (name, num1, num2) - failures.append(fail) - if failures: - self.fail('\n' + '\n'.join(failures)) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/_sunos.py b/test/_sunos.py deleted file mode 100644 index 3d54ccd8..00000000 --- a/test/_sunos.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Sun OS specific tests. These are implicitly run by test_psutil.py.""" - -import sys -import os - -from test_psutil import SUNOS, sh, unittest -import psutil - - -@unittest.skipUnless(SUNOS, "not a SunOS system") -class SunOSSpecificTestCase(unittest.TestCase): - - def test_swap_memory(self): - out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) - lines = out.strip().split('\n')[1:] - if not lines: - raise ValueError('no swap device(s) configured') - total = free = 0 - for line in lines: - line = line.split() - t, f = line[-2:] - t = t.replace('K', '') - f = f.replace('K', '') - total += int(int(t) * 1024) - free += int(int(f) * 1024) - used = total - free - - psutil_swap = psutil.swap_memory() - self.assertEqual(psutil_swap.total, total) - self.assertEqual(psutil_swap.used, used) - self.assertEqual(psutil_swap.free, free) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/_windows.py b/test/_windows.py deleted file mode 100644 index a0a22052..00000000 --- a/test/_windows.py +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Windows specific tests. These are implicitly run by test_psutil.py.""" - -import errno -import os -import platform -import signal -import subprocess -import sys -import time -import traceback - -from test_psutil import WINDOWS, get_test_subprocess, reap_children, unittest - -try: - import wmi -except ImportError: - wmi = None -try: - import win32api - import win32con -except ImportError: - win32api = win32con = None - -from psutil._compat import PY3, callable, long -import psutil - - -cext = psutil._psplatform.cext - - -def wrap_exceptions(fun): - def wrapper(self, *args, **kwargs): - try: - return fun(self, *args, **kwargs) - except OSError as err: - from psutil._pswindows import ACCESS_DENIED_SET - if err.errno in ACCESS_DENIED_SET: - raise psutil.AccessDenied(None, None) - if err.errno == errno.ESRCH: - raise psutil.NoSuchProcess(None, None) - raise - return wrapper - - -@unittest.skipUnless(WINDOWS, "not a Windows system") -class WindowsSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_issue_24(self): - p = psutil.Process(0) - self.assertRaises(psutil.AccessDenied, p.kill) - - def test_special_pid(self): - p = psutil.Process(4) - self.assertEqual(p.name(), 'System') - # use __str__ to access all common Process properties to check - # that nothing strange happens - str(p) - p.username() - self.assertTrue(p.create_time() >= 0.0) - try: - rss, vms = p.memory_info() - except psutil.AccessDenied: - # expected on Windows Vista and Windows 7 - if not platform.uname()[1] in ('vista', 'win-7', 'win7'): - raise - else: - self.assertTrue(rss > 0) - - def test_send_signal(self): - p = psutil.Process(self.pid) - self.assertRaises(ValueError, p.send_signal, signal.SIGINT) - - def test_nic_names(self): - p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) - out = p.communicate()[0] - if PY3: - out = str(out, sys.stdout.encoding) - nics = psutil.net_io_counters(pernic=True).keys() - for nic in nics: - if "pseudo-interface" in nic.replace(' ', '-').lower(): - continue - if nic not in out: - self.fail( - "%r nic wasn't found in 'ipconfig /all' output" % nic) - - def test_exe(self): - for p in psutil.process_iter(): - try: - self.assertEqual(os.path.basename(p.exe()), p.name()) - except psutil.Error: - pass - - # --- Process class tests - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_name(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(p.name(), w.Caption) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_exe(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - # Note: wmi reports the exe as a lower case string. - # Being Windows paths case-insensitive we ignore that. - self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_cmdline(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(' '.join(p.cmdline()), - w.CommandLine.replace('"', '')) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_username(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - domain, _, username = w.GetOwner() - username = "%s\\%s" % (domain, username) - self.assertEqual(p.username(), username) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_rss_memory(self): - time.sleep(0.1) - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - rss = p.memory_info().rss - self.assertEqual(rss, int(w.WorkingSetSize)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_vms_memory(self): - time.sleep(0.1) - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - vms = p.memory_info().vms - # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx - # ...claims that PageFileUsage is represented in Kilo - # bytes but funnily enough on certain platforms bytes are - # returned instead. - wmi_usage = int(w.PageFileUsage) - if (vms != wmi_usage) and (vms != wmi_usage * 1024): - self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_create_time(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime("%Y%m%d%H%M%S", - time.localtime(p.create_time())) - self.assertEqual(wmic_create, psutil_create) - - # --- psutil namespace functions and constants tests - - @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, - 'NUMBER_OF_PROCESSORS env var is not available') - def test_cpu_count(self): - num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - self.assertEqual(num_cpus, psutil.cpu_count()) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_total_phymem(self): - w = wmi.WMI().Win32_ComputerSystem()[0] - self.assertEqual(int(w.TotalPhysicalMemory), - psutil.virtual_memory().total) - - # @unittest.skipIf(wmi is None, "wmi module is not installed") - # def test__UPTIME(self): - # # _UPTIME constant is not public but it is used internally - # # as value to return for pid 0 creation time. - # # WMI behaves the same. - # w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - # p = psutil.Process(0) - # wmic_create = str(w.CreationDate.split('.')[0]) - # psutil_create = time.strftime("%Y%m%d%H%M%S", - # time.localtime(p.create_time())) - # - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - w = wmi.WMI().Win32_Process() - wmi_pids = [x.ProcessId for x in w] - wmi_pids.sort() - psutil_pids = psutil.pids() - psutil_pids.sort() - if wmi_pids != psutil_pids: - difference = \ - filter(lambda x: x not in wmi_pids, psutil_pids) + \ - filter(lambda x: x not in psutil_pids, wmi_pids) - self.fail("difference: " + str(difference)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_disks(self): - ps_parts = psutil.disk_partitions(all=True) - wmi_parts = wmi.WMI().Win32_LogicalDisk() - for ps_part in ps_parts: - for wmi_part in wmi_parts: - if ps_part.device.replace('\\', '') == wmi_part.DeviceID: - if not ps_part.mountpoint: - # this is usually a CD-ROM with no disk inserted - break - try: - usage = psutil.disk_usage(ps_part.mountpoint) - except OSError as err: - if err.errno == errno.ENOENT: - # usually this is the floppy - break - else: - raise - self.assertEqual(usage.total, int(wmi_part.Size)) - wmi_free = int(wmi_part.FreeSpace) - self.assertEqual(usage.free, wmi_free) - # 10 MB tollerance - if abs(usage.free - wmi_free) > 10 * 1024 * 1024: - self.fail("psutil=%s, wmi=%s" % ( - usage.free, wmi_free)) - break - else: - self.fail("can't find partition %s" % repr(ps_part)) - - @unittest.skipIf(win32api is None, "pywin32 module is not installed") - def test_num_handles(self): - p = psutil.Process(os.getpid()) - before = p.num_handles() - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, os.getpid()) - after = p.num_handles() - self.assertEqual(after, before + 1) - win32api.CloseHandle(handle) - self.assertEqual(p.num_handles(), before) - - @unittest.skipIf(win32api is None, "pywin32 module is not installed") - def test_num_handles_2(self): - # Note: this fails from time to time; I'm keen on thinking - # it doesn't mean something is broken - def call(p, attr): - attr = getattr(p, name, None) - if attr is not None and callable(attr): - attr() - else: - attr - - p = psutil.Process(self.pid) - failures = [] - for name in dir(psutil.Process): - if name.startswith('_') \ - or name in ('terminate', 'kill', 'suspend', 'resume', - 'nice', 'send_signal', 'wait', 'children', - 'as_dict'): - continue - else: - try: - call(p, name) - num1 = p.num_handles() - call(p, name) - num2 = p.num_handles() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - else: - if num2 > num1: - fail = \ - "failure while processing Process.%s method " \ - "(before=%s, after=%s)" % (name, num1, num2) - failures.append(fail) - if failures: - self.fail('\n' + '\n'.join(failures)) - - -@unittest.skipUnless(WINDOWS, "not a Windows system") -class TestDualProcessImplementation(unittest.TestCase): - fun_names = [ - # function name, tolerance - ('proc_cpu_times', 0.2), - ('proc_create_time', 0.5), - ('proc_num_handles', 1), # 1 because impl #1 opens a handle - ('proc_memory_info', 1024), # KB - ('proc_io_counters', 0), - ] - - def test_compare_values(self): - # Certain APIs on Windows have 2 internal implementations, one - # based on documented Windows APIs, another one based - # NtQuerySystemInformation() which gets called as fallback in - # case the first fails because of limited permission error. - # Here we test that the two methods return the exact same value, - # see: - # https://github.com/giampaolo/psutil/issues/304 - def assert_ge_0(obj): - if isinstance(obj, tuple): - for value in obj: - self.assertGreaterEqual(value, 0, msg=obj) - elif isinstance(obj, (int, long, float)): - self.assertGreaterEqual(obj, 0) - else: - assert 0 # case not handled which needs to be fixed - - def compare_with_tolerance(ret1, ret2, tolerance): - if ret1 == ret2: - return - else: - if isinstance(ret2, (int, long, float)): - diff = abs(ret1 - ret2) - self.assertLessEqual(diff, tolerance) - elif isinstance(ret2, tuple): - for a, b in zip(ret1, ret2): - diff = abs(a - b) - self.assertLessEqual(diff, tolerance) - - from psutil._pswindows import ntpinfo - failures = [] - for p in psutil.process_iter(): - try: - nt = ntpinfo(*cext.proc_info(p.pid)) - except psutil.NoSuchProcess: - continue - assert_ge_0(nt) - - for name, tolerance in self.fun_names: - if name == 'proc_memory_info' and p.pid == os.getpid(): - continue - if name == 'proc_create_time' and p.pid in (0, 4): - continue - meth = wrap_exceptions(getattr(cext, name)) - try: - ret = meth(p.pid) - except (psutil.NoSuchProcess, psutil.AccessDenied): - continue - # compare values - try: - if name == 'proc_cpu_times': - compare_with_tolerance(ret[0], nt.user_time, tolerance) - compare_with_tolerance(ret[1], - nt.kernel_time, tolerance) - elif name == 'proc_create_time': - compare_with_tolerance(ret, nt.create_time, tolerance) - elif name == 'proc_num_handles': - compare_with_tolerance(ret, nt.num_handles, tolerance) - elif name == 'proc_io_counters': - compare_with_tolerance(ret[0], nt.io_rcount, tolerance) - compare_with_tolerance(ret[1], nt.io_wcount, tolerance) - compare_with_tolerance(ret[2], nt.io_rbytes, tolerance) - compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) - elif name == 'proc_memory_info': - try: - rawtupl = cext.proc_memory_info_2(p.pid) - except psutil.NoSuchProcess: - continue - compare_with_tolerance(ret, rawtupl, tolerance) - except AssertionError: - trace = traceback.format_exc() - msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % ( - trace, p.pid, name, ret, nt) - failures.append(msg) - break - - if failures: - self.fail('\n\n'.join(failures)) - - def test_compare_name_exe(self): - for p in psutil.process_iter(): - try: - a = os.path.basename(p.exe()) - b = p.name() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - else: - self.assertEqual(a, b) - - def test_zombies(self): - # test that NPS is raised by the 2nd implementation in case a - # process no longer exists - ZOMBIE_PID = max(psutil.pids()) + 5000 - for name, _ in self.fun_names: - meth = wrap_exceptions(getattr(cext, name)) - self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) - test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_bsd.py b/test/test_bsd.py new file mode 100644 index 00000000..76f12442 --- /dev/null +++ b/test/test_bsd.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python + +# 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. + +# TODO: add test for comparing connections with 'sockstat' cmd + +"""BSD specific tests. These are implicitly run by test_psutil.py.""" + +import os +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3 +from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, + retry_before_failing, reap_children, unittest) + + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") +if os.getuid() == 0: # muse requires root privileges + MUSE_AVAILABLE = which('muse') +else: + MUSE_AVAILABLE = False + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + result = sh("sysctl " + cmdline) + result = result[result.find(": ") + 2:] + try: + return int(result) + except ValueError: + return result + + +def muse(field): + """Thin wrapper around 'muse' cmdline utility.""" + out = sh('muse') + for line in out.split('\n'): + if line.startswith(field): + break + else: + raise ValueError("line not found") + return int(line.split()[1]) + + +@unittest.skipUnless(BSD, "not a BSD system") +class BSDSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_boot_time(self): + s = sysctl('sysctl kern.boottime') + s = s[s.find(" sec = ") + 7:] + s = s[:s.find(',')] + btime = int(s) + self.assertEqual(btime, psutil.boot_time()) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" % self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if PY3: + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time() + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -k "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total = int(total) * 1024 + used = int(used) * 1024 + free = int(free) * 1024 + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.free, free)) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.used, used)) + + @retry_before_failing() + def test_memory_maps(self): + out = sh('procstat -v %s' % self.pid) + maps = psutil.Process(self.pid).memory_maps(grouped=False) + lines = out.split('\n')[1:] + while lines: + line = lines.pop() + fields = line.split() + _, start, stop, perms, res = fields[:5] + map = maps.pop() + self.assertEqual("%s-%s" % (start, stop), map.addr) + self.assertEqual(int(res), map.rss) + if not map.path.startswith('['): + self.assertEqual(fields[10], map.path) + + def test_exe(self): + out = sh('procstat -b %s' % self.pid) + self.assertEqual(psutil.Process(self.pid).exe(), + out.split('\n')[1].split()[-1]) + + def test_cmdline(self): + out = sh('procstat -c %s' % self.pid) + self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), + ' '.join(out.split('\n')[1].split()[2:])) + + def test_uids_gids(self): + out = sh('procstat -s %s' % self.pid) + euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] + p = psutil.Process(self.pid) + uids = p.uids() + gids = p.gids() + self.assertEqual(uids.real, int(ruid)) + self.assertEqual(uids.effective, int(euid)) + self.assertEqual(uids.saved, int(suid)) + self.assertEqual(gids.real, int(rgid)) + self.assertEqual(gids.effective, int(egid)) + self.assertEqual(gids.saved, int(sgid)) + + # --- virtual_memory(); tests against sysctl + + def test_vmem_total(self): + syst = sysctl("sysctl vm.stats.vm.v_page_count") * PAGESIZE + self.assertEqual(psutil.virtual_memory().total, syst) + + @retry_before_failing() + def test_vmem_active(self): + syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().active, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_inactive(self): + syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_wired(self): + syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().wired, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_cached(self): + syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().cached, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_free(self): + syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().free, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_buffers(self): + syst = sysctl("vfs.bufspace") + self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, + delta=TOLERANCE) + + # --- virtual_memory(); tests against muse + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + def test_total(self): + num = muse('Total') + self.assertEqual(psutil.virtual_memory().total, num) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_active(self): + num = muse('Active') + self.assertAlmostEqual(psutil.virtual_memory().active, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_inactive(self): + num = muse('Inactive') + self.assertAlmostEqual(psutil.virtual_memory().inactive, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_wired(self): + num = muse('Wired') + self.assertAlmostEqual(psutil.virtual_memory().wired, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_cached(self): + num = muse('Cache') + self.assertAlmostEqual(psutil.virtual_memory().cached, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_free(self): + num = muse('Free') + self.assertAlmostEqual(psutil.virtual_memory().free, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_buffers(self): + num = muse('Buffer') + self.assertAlmostEqual(psutil.virtual_memory().buffers, num, + delta=TOLERANCE) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_linux.py b/test/test_linux.py new file mode 100644 index 00000000..5d7f0521 --- /dev/null +++ b/test/test_linux.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python + +# 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. + +"""Linux specific tests. These are implicitly run by test_psutil.py.""" + +from __future__ import division +import contextlib +import fcntl +import os +import pprint +import re +import socket +import struct +import sys +import time +import warnings + +try: + from unittest import mock # py3 +except ImportError: + import mock # requires "pip install mock" + +from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX +from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, + retry_before_failing, get_kernel_version, unittest, + which) + +import psutil +import psutil._pslinux +from psutil._compat import PY3 + + +SIOCGIFADDR = 0x8915 +SIOCGIFCONF = 0x8912 +SIOCGIFHWADDR = 0x8927 + + +def get_ipv4_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + return socket.inet_ntoa( + fcntl.ioctl(s.fileno(), + SIOCGIFADDR, + struct.pack('256s', ifname))[20:24]) + + +def get_mac_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + info = fcntl.ioctl( + s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) + if PY3: + def ord(x): + return x + else: + import __builtin__ + ord = __builtin__.ord + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + +@unittest.skipUnless(LINUX, "not a Linux system") +class LinuxSpecificTestCase(unittest.TestCase): + + @unittest.skipIf( + POSIX and not hasattr(os, 'statvfs'), + reason="os.statvfs() function not available on this platform") + @skip_on_not_implemented() + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -P -B 1 "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total, used, free = int(total), int(used), int(free) + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.free, free)) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.used, used)) + + def test_memory_maps(self): + sproc = get_test_subprocess() + time.sleep(1) + p = psutil.Process(sproc.pid) + maps = p.memory_maps(grouped=False) + pmap = sh('pmap -x %s' % p.pid).split('\n') + # get rid of header + del pmap[0] + del pmap[0] + while maps and pmap: + this = maps.pop(0) + other = pmap.pop(0) + addr, _, rss, dirty, mode, path = other.split(None, 5) + if not path.startswith('[') and not path.endswith(']'): + self.assertEqual(path, os.path.basename(this.path)) + self.assertEqual(int(rss) * 1024, this.rss) + # test only rwx chars, ignore 's' and 'p' + self.assertEqual(mode[:3], this.perms[:3]) + + def test_vmem_total(self): + lines = sh('free').split('\n')[1:] + total = int(lines[0].split()[1]) * 1024 + self.assertEqual(total, psutil.virtual_memory().total) + + @retry_before_failing() + def test_vmem_used(self): + lines = sh('free').split('\n')[1:] + used = int(lines[0].split()[2]) * 1024 + self.assertAlmostEqual(used, psutil.virtual_memory().used, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_free(self): + lines = sh('free').split('\n')[1:] + free = int(lines[0].split()[3]) * 1024 + self.assertAlmostEqual(free, psutil.virtual_memory().free, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_buffers(self): + lines = sh('free').split('\n')[1:] + buffers = int(lines[0].split()[5]) * 1024 + self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_cached(self): + lines = sh('free').split('\n')[1:] + cached = int(lines[0].split()[6]) * 1024 + self.assertAlmostEqual(cached, psutil.virtual_memory().cached, + delta=TOLERANCE) + + def test_swapmem_total(self): + lines = sh('free').split('\n')[1:] + total = int(lines[2].split()[1]) * 1024 + self.assertEqual(total, psutil.swap_memory().total) + + @retry_before_failing() + def test_swapmem_used(self): + lines = sh('free').split('\n')[1:] + used = int(lines[2].split()[2]) * 1024 + self.assertAlmostEqual(used, psutil.swap_memory().used, + delta=TOLERANCE) + + @retry_before_failing() + def test_swapmem_free(self): + lines = sh('free').split('\n')[1:] + free = int(lines[2].split()[3]) * 1024 + self.assertAlmostEqual(free, psutil.swap_memory().free, + delta=TOLERANCE) + + @unittest.skipIf(TRAVIS, "unknown failure on travis") + def test_cpu_times(self): + fields = psutil.cpu_times()._fields + kernel_ver = re.findall('\d+\.\d+\.\d+', os.uname()[2])[0] + kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) + if kernel_ver_info >= (2, 6, 11): + self.assertIn('steal', fields) + else: + self.assertNotIn('steal', fields) + if kernel_ver_info >= (2, 6, 24): + self.assertIn('guest', fields) + else: + self.assertNotIn('guest', fields) + if kernel_ver_info >= (3, 2, 0): + self.assertIn('guest_nice', fields) + else: + self.assertNotIn('guest_nice', fields) + + def test_net_if_addrs_ips(self): + for name, addrs in psutil.net_if_addrs().items(): + for addr in addrs: + if addr.family == psutil.AF_LINK: + self.assertEqual(addr.address, get_mac_address(name)) + elif addr.family == socket.AF_INET: + self.assertEqual(addr.address, get_ipv4_address(name)) + # TODO: test for AF_INET6 family + + @unittest.skipUnless(which('ip'), "'ip' utility not available") + @unittest.skipIf(TRAVIS, "skipped on Travis") + def test_net_if_names(self): + out = sh("ip addr").strip() + nics = psutil.net_if_addrs() + found = 0 + for line in out.split('\n'): + line = line.strip() + if re.search("^\d+:", line): + found += 1 + name = line.split(':')[1].strip() + self.assertIn(name, nics.keys()) + self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( + pprint.pformat(nics), out)) + + @unittest.skipUnless(which("nproc"), "nproc utility not available") + def test_cpu_count_logical_w_nproc(self): + num = int(sh("nproc --all")) + self.assertEqual(psutil.cpu_count(logical=True), num) + + @unittest.skipUnless(which("lscpu"), "lscpu utility not available") + def test_cpu_count_logical_w_lscpu(self): + out = sh("lscpu -p") + num = len([x for x in out.split('\n') if not x.startswith('#')]) + self.assertEqual(psutil.cpu_count(logical=True), num) + + # --- mocked tests + + def test_virtual_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.virtual_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'cached', 'active' and 'inactive' memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.cached, 0) + self.assertEqual(ret.active, 0) + self.assertEqual(ret.inactive, 0) + + def test_swap_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.swap_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'sin' and 'sout' swap memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.sin, 0) + self.assertEqual(ret.sout, 0) + + def test_cpu_count_logical_mocked(self): + import psutil._pslinux + original = psutil._pslinux.cpu_count_logical() + with mock.patch( + 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: + # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in + # order to test /proc/cpuinfo parsing. + # We might also test /proc/stat parsing but mocking open() + # like that is too difficult. + self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert m.called + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_logical()) + assert m.called + + def test_cpu_count_physical_mocked(self): + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_physical()) + assert m.called + + def test_proc_terminal_mocked(self): + with mock.patch('psutil._pslinux._psposix._get_terminal_map', + return_value={}) as m: + self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) + assert m.called + + def test_proc_num_ctx_switches_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_ctx_switches) + assert m.called + + def test_proc_num_threads_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_threads) + assert m.called + + def test_proc_ppid_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).ppid) + assert m.called + + def test_proc_uids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).uids) + assert m.called + + def test_proc_gids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).gids) + assert m.called + + # --- tests for specific kernel versions + + @unittest.skipUnless( + get_kernel_version() >= (2, 6, 36), + "prlimit() not available on this Linux kernel version") + def test_prlimit_availability(self): + # prlimit() should be available starting from kernel 2.6.36 + p = psutil.Process(os.getpid()) + p.rlimit(psutil.RLIMIT_NOFILE) + # if prlimit() is supported *at least* these constants should + # be available + self.assertTrue(hasattr(psutil, "RLIM_INFINITY")) + self.assertTrue(hasattr(psutil, "RLIMIT_AS")) + self.assertTrue(hasattr(psutil, "RLIMIT_CORE")) + self.assertTrue(hasattr(psutil, "RLIMIT_CPU")) + self.assertTrue(hasattr(psutil, "RLIMIT_DATA")) + self.assertTrue(hasattr(psutil, "RLIMIT_FSIZE")) + self.assertTrue(hasattr(psutil, "RLIMIT_LOCKS")) + self.assertTrue(hasattr(psutil, "RLIMIT_MEMLOCK")) + self.assertTrue(hasattr(psutil, "RLIMIT_NOFILE")) + self.assertTrue(hasattr(psutil, "RLIMIT_NPROC")) + self.assertTrue(hasattr(psutil, "RLIMIT_RSS")) + self.assertTrue(hasattr(psutil, "RLIMIT_STACK")) + + @unittest.skipUnless( + get_kernel_version() >= (3, 0), + "prlimit constants not available on this Linux kernel version") + def test_resource_consts_kernel_v(self): + # more recent constants + self.assertTrue(hasattr(psutil, "RLIMIT_MSGQUEUE")) + self.assertTrue(hasattr(psutil, "RLIMIT_NICE")) + self.assertTrue(hasattr(psutil, "RLIMIT_RTPRIO")) + self.assertTrue(hasattr(psutil, "RLIMIT_RTTIME")) + self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_osx.py b/test/test_osx.py new file mode 100644 index 00000000..6e6e4380 --- /dev/null +++ b/test/test_osx.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# 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. + +"""OSX specific tests. These are implicitly run by test_psutil.py.""" + +import os +import re +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3 +from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, + reap_children, retry_before_failing, unittest) + + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip().split()[1] + if PY3: + result = str(result, sys.stdout.encoding) + try: + return int(result) + except ValueError: + return result + + +def vm_stat(field): + """Wrapper around 'vm_stat' cmdline utility.""" + out = sh('vm_stat') + for line in out.split('\n'): + if field in line: + break + else: + raise ValueError("line not found") + return int(re.search('\d+', line).group(0)) * PAGESIZE + + +@unittest.skipUnless(OSX, "not an OSX system") +class OSXSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" % self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if PY3: + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time() + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -k "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total = int(total) * 1024 + used = int(used) * 1024 + free = int(free) * 1024 + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % usage.free, free) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % usage.used, used) + + # --- virtual mem + + def test_vmem_total(self): + sysctl_hwphymem = sysctl('sysctl hw.memsize') + self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) + + @retry_before_failing() + def test_vmem_free(self): + num = vm_stat("free") + self.assertAlmostEqual(psutil.virtual_memory().free, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_active(self): + num = vm_stat("active") + self.assertAlmostEqual(psutil.virtual_memory().active, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_inactive(self): + num = vm_stat("inactive") + self.assertAlmostEqual(psutil.virtual_memory().inactive, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_wired(self): + num = vm_stat("wired") + self.assertAlmostEqual(psutil.virtual_memory().wired, num, + delta=TOLERANCE) + + # --- swap mem + + def test_swapmem_sin(self): + num = vm_stat("Pageins") + self.assertEqual(psutil.swap_memory().sin, num) + + def test_swapmem_sout(self): + num = vm_stat("Pageouts") + self.assertEqual(psutil.swap_memory().sout, num) + + def test_swapmem_total(self): + tot1 = psutil.swap_memory().total + tot2 = 0 + # OSX uses multiple cache files: + # http://en.wikipedia.org/wiki/Paging#OS_X + for name in os.listdir("/var/vm/"): + file = os.path.join("/var/vm", name) + if os.path.isfile(file): + tot2 += os.path.getsize(file) + self.assertEqual(tot1, tot2) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_posix.py b/test/test_posix.py new file mode 100644 index 00000000..2a263a3f --- /dev/null +++ b/test/test_posix.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python + +# 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. + +"""POSIX specific tests. These are implicitly run by test_psutil.py.""" + +import datetime +import os +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3, callable +from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX +from test_psutil import (get_test_subprocess, skip_on_access_denied, + retry_before_failing, reap_children, sh, unittest, + get_kernel_version, wait_for_pid) + + +def ps(cmd): + """Expects a ps command with a -o argument and parse the result + returning only the value of interest. + """ + if not LINUX: + cmd = cmd.replace(" --no-headers ", " ") + if SUNOS: + cmd = cmd.replace("-o command", "-o comm") + cmd = cmd.replace("-o start", "-o stime") + p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + if not LINUX: + output = output.split('\n')[1].strip() + try: + return int(output) + except ValueError: + return output + + +@unittest.skipUnless(POSIX, "not a POSIX system") +class PosixSpecificTestCase(unittest.TestCase): + """Compare psutil results against 'ps' command line utility.""" + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess([PYTHON, "-E", "-O"], + stdin=subprocess.PIPE).pid + wait_for_pid(cls.pid) + + @classmethod + def tearDownClass(cls): + reap_children() + + # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps + + def test_process_parent_pid(self): + ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) + ppid_psutil = psutil.Process(self.pid).ppid() + self.assertEqual(ppid_ps, ppid_psutil) + + def test_process_uid(self): + uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) + uid_psutil = psutil.Process(self.pid).uids().real + self.assertEqual(uid_ps, uid_psutil) + + def test_process_gid(self): + gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) + gid_psutil = psutil.Process(self.pid).gids().real + self.assertEqual(gid_ps, gid_psutil) + + def test_process_username(self): + username_ps = ps("ps --no-headers -o user -p %s" % self.pid) + username_psutil = psutil.Process(self.pid).username() + self.assertEqual(username_ps, username_psutil) + + @skip_on_access_denied() + @retry_before_failing() + def test_process_rss_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) + rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 + self.assertEqual(rss_ps, rss_psutil) + + @skip_on_access_denied() + @retry_before_failing() + def test_process_vsz_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) + vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 + self.assertEqual(vsz_ps, vsz_psutil) + + def test_process_name(self): + # use command + arg since "comm" keyword not supported on all platforms + name_ps = ps("ps --no-headers -o command -p %s" % ( + self.pid)).split(' ')[0] + # remove path if there is any, from the command + name_ps = os.path.basename(name_ps).lower() + name_psutil = psutil.Process(self.pid).name().lower() + self.assertEqual(name_ps, name_psutil) + + @unittest.skipIf(OSX or BSD, + 'ps -o start not available') + def test_process_create_time(self): + time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] + time_psutil = psutil.Process(self.pid).create_time() + time_psutil_tstamp = datetime.datetime.fromtimestamp( + time_psutil).strftime("%H:%M:%S") + # sometimes ps shows the time rounded up instead of down, so we check + # for both possible values + round_time_psutil = round(time_psutil) + round_time_psutil_tstamp = datetime.datetime.fromtimestamp( + round_time_psutil).strftime("%H:%M:%S") + self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) + + def test_process_exe(self): + ps_pathname = ps("ps --no-headers -o command -p %s" % + self.pid).split(' ')[0] + psutil_pathname = psutil.Process(self.pid).exe() + try: + self.assertEqual(ps_pathname, psutil_pathname) + except AssertionError: + # certain platforms such as BSD are more accurate returning: + # "/usr/local/bin/python2.7" + # ...instead of: + # "/usr/local/bin/python" + # We do not want to consider this difference in accuracy + # an error. + adjusted_ps_pathname = ps_pathname[:len(ps_pathname)] + self.assertEqual(ps_pathname, adjusted_ps_pathname) + + def test_process_cmdline(self): + ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) + psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) + if SUNOS: + # ps on Solaris only shows the first part of the cmdline + psutil_cmdline = psutil_cmdline.split(" ")[0] + self.assertEqual(ps_cmdline, psutil_cmdline) + + @retry_before_failing() + def test_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + if SUNOS: + cmd = ["ps", "ax"] + else: + cmd = ["ps", "ax", "-o", "pid"] + p = get_test_subprocess(cmd, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + pids_ps = [] + for line in output.split('\n')[1:]: + if line: + pid = int(line.split()[0].strip()) + pids_ps.append(pid) + # remove ps subprocess pid which is supposed to be dead in meantime + pids_ps.remove(p.pid) + pids_psutil = psutil.pids() + pids_ps.sort() + pids_psutil.sort() + + # on OSX ps doesn't show pid 0 + if OSX and 0 not in pids_ps: + pids_ps.insert(0, 0) + + if pids_ps != pids_psutil: + difference = [x for x in pids_psutil if x not in pids_ps] + \ + [x for x in pids_ps if x not in pids_psutil] + self.fail("difference: " + str(difference)) + + # for some reason ifconfig -a does not report differente interfaces + # psutil does + @unittest.skipIf(SUNOS, "test not reliable on SUNOS") + def test_nic_names(self): + p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + for nic in psutil.net_io_counters(pernic=True).keys(): + for line in output.split(): + if line.startswith(nic): + break + else: + self.fail("couldn't find %s nic in 'ifconfig -a' output" % nic) + + @retry_before_failing() + def test_users(self): + out = sh("who") + lines = out.split('\n') + users = [x.split()[0] for x in lines] + self.assertEqual(len(users), len(psutil.users())) + terminals = [x.split()[1] for x in lines] + for u in psutil.users(): + self.assertTrue(u.name in users, u.name) + self.assertTrue(u.terminal in terminals, u.terminal) + + def test_fds_open(self): + # Note: this fails from time to time; I'm keen on thinking + # it doesn't mean something is broken + def call(p, attr): + args = () + attr = getattr(p, name, None) + if attr is not None and callable(attr): + if name == 'rlimit': + args = (psutil.RLIMIT_NOFILE,) + attr(*args) + else: + attr + + p = psutil.Process(os.getpid()) + failures = [] + ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', + 'send_signal', 'wait', 'children', 'as_dict'] + if LINUX and get_kernel_version() < (2, 6, 36): + ignored_names.append('rlimit') + if LINUX and get_kernel_version() < (2, 6, 23): + ignored_names.append('num_ctx_switches') + for name in dir(psutil.Process): + if (name.startswith('_') or name in ignored_names): + continue + else: + try: + num1 = p.num_fds() + for x in range(2): + call(p, name) + num2 = p.num_fds() + except psutil.AccessDenied: + pass + else: + if abs(num2 - num1) > 1: + fail = "failure while processing Process.%s method " \ + "(before=%s, after=%s)" % (name, num1, num2) + failures.append(fail) + if failures: + self.fail('\n' + '\n'.join(failures)) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_psutil.py b/test/test_psutil.py index 470dafc4..ed85708c 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2767,23 +2767,23 @@ def main(): tests.append(LimitedUserTestCase) if POSIX: - from _posix import PosixSpecificTestCase + from test_posix import PosixSpecificTestCase tests.append(PosixSpecificTestCase) # import the specific platform test suite stc = None if LINUX: - from _linux import LinuxSpecificTestCase as stc + from test_linux import LinuxSpecificTestCase as stc elif WINDOWS: - from _windows import WindowsSpecificTestCase as stc - from _windows import TestDualProcessImplementation + from test_windows import WindowsSpecificTestCase as stc + from test_windows import TestDualProcessImplementation tests.append(TestDualProcessImplementation) elif OSX: - from _osx import OSXSpecificTestCase as stc + from test_osx import OSXSpecificTestCase as stc elif BSD: - from _bsd import BSDSpecificTestCase as stc + from test_bsd import BSDSpecificTestCase as stc elif SUNOS: - from _sunos import SunOSSpecificTestCase as stc + from test_sunos import SunOSSpecificTestCase as stc if stc is not None: tests.append(stc) diff --git a/test/test_sunos.py b/test/test_sunos.py new file mode 100644 index 00000000..3d54ccd8 --- /dev/null +++ b/test/test_sunos.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# 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. + +"""Sun OS specific tests. These are implicitly run by test_psutil.py.""" + +import sys +import os + +from test_psutil import SUNOS, sh, unittest +import psutil + + +@unittest.skipUnless(SUNOS, "not a SunOS system") +class SunOSSpecificTestCase(unittest.TestCase): + + def test_swap_memory(self): + out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) + lines = out.strip().split('\n')[1:] + if not lines: + raise ValueError('no swap device(s) configured') + total = free = 0 + for line in lines: + line = line.split() + t, f = line[-2:] + t = t.replace('K', '') + f = f.replace('K', '') + total += int(int(t) * 1024) + free += int(int(f) * 1024) + used = total - free + + psutil_swap = psutil.swap_memory() + self.assertEqual(psutil_swap.total, total) + self.assertEqual(psutil_swap.used, used) + self.assertEqual(psutil_swap.free, free) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_windows.py b/test/test_windows.py new file mode 100644 index 00000000..a0a22052 --- /dev/null +++ b/test/test_windows.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python + +# 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. + +"""Windows specific tests. These are implicitly run by test_psutil.py.""" + +import errno +import os +import platform +import signal +import subprocess +import sys +import time +import traceback + +from test_psutil import WINDOWS, get_test_subprocess, reap_children, unittest + +try: + import wmi +except ImportError: + wmi = None +try: + import win32api + import win32con +except ImportError: + win32api = win32con = None + +from psutil._compat import PY3, callable, long +import psutil + + +cext = psutil._psplatform.cext + + +def wrap_exceptions(fun): + def wrapper(self, *args, **kwargs): + try: + return fun(self, *args, **kwargs) + except OSError as err: + from psutil._pswindows import ACCESS_DENIED_SET + if err.errno in ACCESS_DENIED_SET: + raise psutil.AccessDenied(None, None) + if err.errno == errno.ESRCH: + raise psutil.NoSuchProcess(None, None) + raise + return wrapper + + +@unittest.skipUnless(WINDOWS, "not a Windows system") +class WindowsSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_issue_24(self): + p = psutil.Process(0) + self.assertRaises(psutil.AccessDenied, p.kill) + + def test_special_pid(self): + p = psutil.Process(4) + self.assertEqual(p.name(), 'System') + # use __str__ to access all common Process properties to check + # that nothing strange happens + str(p) + p.username() + self.assertTrue(p.create_time() >= 0.0) + try: + rss, vms = p.memory_info() + except psutil.AccessDenied: + # expected on Windows Vista and Windows 7 + if not platform.uname()[1] in ('vista', 'win-7', 'win7'): + raise + else: + self.assertTrue(rss > 0) + + def test_send_signal(self): + p = psutil.Process(self.pid) + self.assertRaises(ValueError, p.send_signal, signal.SIGINT) + + def test_nic_names(self): + p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) + out = p.communicate()[0] + if PY3: + out = str(out, sys.stdout.encoding) + nics = psutil.net_io_counters(pernic=True).keys() + for nic in nics: + if "pseudo-interface" in nic.replace(' ', '-').lower(): + continue + if nic not in out: + self.fail( + "%r nic wasn't found in 'ipconfig /all' output" % nic) + + def test_exe(self): + for p in psutil.process_iter(): + try: + self.assertEqual(os.path.basename(p.exe()), p.name()) + except psutil.Error: + pass + + # --- Process class tests + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_name(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.name(), w.Caption) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_exe(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + # Note: wmi reports the exe as a lower case string. + # Being Windows paths case-insensitive we ignore that. + self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_cmdline(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(' '.join(p.cmdline()), + w.CommandLine.replace('"', '')) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_username(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + domain, _, username = w.GetOwner() + username = "%s\\%s" % (domain, username) + self.assertEqual(p.username(), username) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_rss_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + rss = p.memory_info().rss + self.assertEqual(rss, int(w.WorkingSetSize)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_vms_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + vms = p.memory_info().vms + # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx + # ...claims that PageFileUsage is represented in Kilo + # bytes but funnily enough on certain platforms bytes are + # returned instead. + wmi_usage = int(w.PageFileUsage) + if (vms != wmi_usage) and (vms != wmi_usage * 1024): + self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_create_time(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time())) + self.assertEqual(wmic_create, psutil_create) + + # --- psutil namespace functions and constants tests + + @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, + 'NUMBER_OF_PROCESSORS env var is not available') + def test_cpu_count(self): + num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) + self.assertEqual(num_cpus, psutil.cpu_count()) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_total_phymem(self): + w = wmi.WMI().Win32_ComputerSystem()[0] + self.assertEqual(int(w.TotalPhysicalMemory), + psutil.virtual_memory().total) + + # @unittest.skipIf(wmi is None, "wmi module is not installed") + # def test__UPTIME(self): + # # _UPTIME constant is not public but it is used internally + # # as value to return for pid 0 creation time. + # # WMI behaves the same. + # w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + # p = psutil.Process(0) + # wmic_create = str(w.CreationDate.split('.')[0]) + # psutil_create = time.strftime("%Y%m%d%H%M%S", + # time.localtime(p.create_time())) + # + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + w = wmi.WMI().Win32_Process() + wmi_pids = [x.ProcessId for x in w] + wmi_pids.sort() + psutil_pids = psutil.pids() + psutil_pids.sort() + if wmi_pids != psutil_pids: + difference = \ + filter(lambda x: x not in wmi_pids, psutil_pids) + \ + filter(lambda x: x not in psutil_pids, wmi_pids) + self.fail("difference: " + str(difference)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_disks(self): + ps_parts = psutil.disk_partitions(all=True) + wmi_parts = wmi.WMI().Win32_LogicalDisk() + for ps_part in ps_parts: + for wmi_part in wmi_parts: + if ps_part.device.replace('\\', '') == wmi_part.DeviceID: + if not ps_part.mountpoint: + # this is usually a CD-ROM with no disk inserted + break + try: + usage = psutil.disk_usage(ps_part.mountpoint) + except OSError as err: + if err.errno == errno.ENOENT: + # usually this is the floppy + break + else: + raise + self.assertEqual(usage.total, int(wmi_part.Size)) + wmi_free = int(wmi_part.FreeSpace) + self.assertEqual(usage.free, wmi_free) + # 10 MB tollerance + if abs(usage.free - wmi_free) > 10 * 1024 * 1024: + self.fail("psutil=%s, wmi=%s" % ( + usage.free, wmi_free)) + break + else: + self.fail("can't find partition %s" % repr(ps_part)) + + @unittest.skipIf(win32api is None, "pywin32 module is not installed") + def test_num_handles(self): + p = psutil.Process(os.getpid()) + before = p.num_handles() + handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, + win32con.FALSE, os.getpid()) + after = p.num_handles() + self.assertEqual(after, before + 1) + win32api.CloseHandle(handle) + self.assertEqual(p.num_handles(), before) + + @unittest.skipIf(win32api is None, "pywin32 module is not installed") + def test_num_handles_2(self): + # Note: this fails from time to time; I'm keen on thinking + # it doesn't mean something is broken + def call(p, attr): + attr = getattr(p, name, None) + if attr is not None and callable(attr): + attr() + else: + attr + + p = psutil.Process(self.pid) + failures = [] + for name in dir(psutil.Process): + if name.startswith('_') \ + or name in ('terminate', 'kill', 'suspend', 'resume', + 'nice', 'send_signal', 'wait', 'children', + 'as_dict'): + continue + else: + try: + call(p, name) + num1 = p.num_handles() + call(p, name) + num2 = p.num_handles() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + if num2 > num1: + fail = \ + "failure while processing Process.%s method " \ + "(before=%s, after=%s)" % (name, num1, num2) + failures.append(fail) + if failures: + self.fail('\n' + '\n'.join(failures)) + + +@unittest.skipUnless(WINDOWS, "not a Windows system") +class TestDualProcessImplementation(unittest.TestCase): + fun_names = [ + # function name, tolerance + ('proc_cpu_times', 0.2), + ('proc_create_time', 0.5), + ('proc_num_handles', 1), # 1 because impl #1 opens a handle + ('proc_memory_info', 1024), # KB + ('proc_io_counters', 0), + ] + + def test_compare_values(self): + # Certain APIs on Windows have 2 internal implementations, one + # based on documented Windows APIs, another one based + # NtQuerySystemInformation() which gets called as fallback in + # case the first fails because of limited permission error. + # Here we test that the two methods return the exact same value, + # see: + # https://github.com/giampaolo/psutil/issues/304 + def assert_ge_0(obj): + if isinstance(obj, tuple): + for value in obj: + self.assertGreaterEqual(value, 0, msg=obj) + elif isinstance(obj, (int, long, float)): + self.assertGreaterEqual(obj, 0) + else: + assert 0 # case not handled which needs to be fixed + + def compare_with_tolerance(ret1, ret2, tolerance): + if ret1 == ret2: + return + else: + if isinstance(ret2, (int, long, float)): + diff = abs(ret1 - ret2) + self.assertLessEqual(diff, tolerance) + elif isinstance(ret2, tuple): + for a, b in zip(ret1, ret2): + diff = abs(a - b) + self.assertLessEqual(diff, tolerance) + + from psutil._pswindows import ntpinfo + failures = [] + for p in psutil.process_iter(): + try: + nt = ntpinfo(*cext.proc_info(p.pid)) + except psutil.NoSuchProcess: + continue + assert_ge_0(nt) + + for name, tolerance in self.fun_names: + if name == 'proc_memory_info' and p.pid == os.getpid(): + continue + if name == 'proc_create_time' and p.pid in (0, 4): + continue + meth = wrap_exceptions(getattr(cext, name)) + try: + ret = meth(p.pid) + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + # compare values + try: + if name == 'proc_cpu_times': + compare_with_tolerance(ret[0], nt.user_time, tolerance) + compare_with_tolerance(ret[1], + nt.kernel_time, tolerance) + elif name == 'proc_create_time': + compare_with_tolerance(ret, nt.create_time, tolerance) + elif name == 'proc_num_handles': + compare_with_tolerance(ret, nt.num_handles, tolerance) + elif name == 'proc_io_counters': + compare_with_tolerance(ret[0], nt.io_rcount, tolerance) + compare_with_tolerance(ret[1], nt.io_wcount, tolerance) + compare_with_tolerance(ret[2], nt.io_rbytes, tolerance) + compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) + elif name == 'proc_memory_info': + try: + rawtupl = cext.proc_memory_info_2(p.pid) + except psutil.NoSuchProcess: + continue + compare_with_tolerance(ret, rawtupl, tolerance) + except AssertionError: + trace = traceback.format_exc() + msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % ( + trace, p.pid, name, ret, nt) + failures.append(msg) + break + + if failures: + self.fail('\n\n'.join(failures)) + + def test_compare_name_exe(self): + for p in psutil.process_iter(): + try: + a = os.path.basename(p.exe()) + b = p.name() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + self.assertEqual(a, b) + + def test_zombies(self): + # test that NPS is raised by the 2nd implementation in case a + # process no longer exists + ZOMBIE_PID = max(psutil.pids()) + 5000 + for name, _ in self.fun_names: + meth = wrap_exceptions(getattr(cext, name)) + self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) + test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) -- cgit v1.2.1 From 931432d83e203e757d0c6668c02386d7f762ea66 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 4 Jun 2015 03:59:48 +0200 Subject: Revert "#629: rename test/_linux.py (and others) to test/test_pslinux.py in order to ease test discovery for nose and pytest" This reverts commit 870e00f71c15ef39f8259ff16b682e495ea5d6eb. --- test/_bsd.py | 248 +++++++++++++++++++++++++++++++ test/_linux.py | 368 ++++++++++++++++++++++++++++++++++++++++++++++ test/_osx.py | 160 ++++++++++++++++++++ test/_posix.py | 255 ++++++++++++++++++++++++++++++++ test/_sunos.py | 48 ++++++ test/_windows.py | 405 +++++++++++++++++++++++++++++++++++++++++++++++++++ test/test_bsd.py | 248 ------------------------------- test/test_linux.py | 368 ---------------------------------------------- test/test_osx.py | 160 -------------------- test/test_posix.py | 255 -------------------------------- test/test_psutil.py | 14 +- test/test_sunos.py | 48 ------ test/test_windows.py | 405 --------------------------------------------------- 13 files changed, 1491 insertions(+), 1491 deletions(-) create mode 100644 test/_bsd.py create mode 100644 test/_linux.py create mode 100644 test/_osx.py create mode 100644 test/_posix.py create mode 100644 test/_sunos.py create mode 100644 test/_windows.py delete mode 100644 test/test_bsd.py delete mode 100644 test/test_linux.py delete mode 100644 test/test_osx.py delete mode 100644 test/test_posix.py delete mode 100644 test/test_sunos.py delete mode 100644 test/test_windows.py diff --git a/test/_bsd.py b/test/_bsd.py new file mode 100644 index 00000000..76f12442 --- /dev/null +++ b/test/_bsd.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python + +# 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. + +# TODO: add test for comparing connections with 'sockstat' cmd + +"""BSD specific tests. These are implicitly run by test_psutil.py.""" + +import os +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3 +from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, + retry_before_failing, reap_children, unittest) + + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") +if os.getuid() == 0: # muse requires root privileges + MUSE_AVAILABLE = which('muse') +else: + MUSE_AVAILABLE = False + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + result = sh("sysctl " + cmdline) + result = result[result.find(": ") + 2:] + try: + return int(result) + except ValueError: + return result + + +def muse(field): + """Thin wrapper around 'muse' cmdline utility.""" + out = sh('muse') + for line in out.split('\n'): + if line.startswith(field): + break + else: + raise ValueError("line not found") + return int(line.split()[1]) + + +@unittest.skipUnless(BSD, "not a BSD system") +class BSDSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_boot_time(self): + s = sysctl('sysctl kern.boottime') + s = s[s.find(" sec = ") + 7:] + s = s[:s.find(',')] + btime = int(s) + self.assertEqual(btime, psutil.boot_time()) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" % self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if PY3: + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time() + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -k "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total = int(total) * 1024 + used = int(used) * 1024 + free = int(free) * 1024 + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.free, free)) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.used, used)) + + @retry_before_failing() + def test_memory_maps(self): + out = sh('procstat -v %s' % self.pid) + maps = psutil.Process(self.pid).memory_maps(grouped=False) + lines = out.split('\n')[1:] + while lines: + line = lines.pop() + fields = line.split() + _, start, stop, perms, res = fields[:5] + map = maps.pop() + self.assertEqual("%s-%s" % (start, stop), map.addr) + self.assertEqual(int(res), map.rss) + if not map.path.startswith('['): + self.assertEqual(fields[10], map.path) + + def test_exe(self): + out = sh('procstat -b %s' % self.pid) + self.assertEqual(psutil.Process(self.pid).exe(), + out.split('\n')[1].split()[-1]) + + def test_cmdline(self): + out = sh('procstat -c %s' % self.pid) + self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), + ' '.join(out.split('\n')[1].split()[2:])) + + def test_uids_gids(self): + out = sh('procstat -s %s' % self.pid) + euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] + p = psutil.Process(self.pid) + uids = p.uids() + gids = p.gids() + self.assertEqual(uids.real, int(ruid)) + self.assertEqual(uids.effective, int(euid)) + self.assertEqual(uids.saved, int(suid)) + self.assertEqual(gids.real, int(rgid)) + self.assertEqual(gids.effective, int(egid)) + self.assertEqual(gids.saved, int(sgid)) + + # --- virtual_memory(); tests against sysctl + + def test_vmem_total(self): + syst = sysctl("sysctl vm.stats.vm.v_page_count") * PAGESIZE + self.assertEqual(psutil.virtual_memory().total, syst) + + @retry_before_failing() + def test_vmem_active(self): + syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().active, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_inactive(self): + syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_wired(self): + syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().wired, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_cached(self): + syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().cached, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_free(self): + syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE + self.assertAlmostEqual(psutil.virtual_memory().free, syst, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_buffers(self): + syst = sysctl("vfs.bufspace") + self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, + delta=TOLERANCE) + + # --- virtual_memory(); tests against muse + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + def test_total(self): + num = muse('Total') + self.assertEqual(psutil.virtual_memory().total, num) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_active(self): + num = muse('Active') + self.assertAlmostEqual(psutil.virtual_memory().active, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_inactive(self): + num = muse('Inactive') + self.assertAlmostEqual(psutil.virtual_memory().inactive, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_wired(self): + num = muse('Wired') + self.assertAlmostEqual(psutil.virtual_memory().wired, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_cached(self): + num = muse('Cache') + self.assertAlmostEqual(psutil.virtual_memory().cached, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_free(self): + num = muse('Free') + self.assertAlmostEqual(psutil.virtual_memory().free, num, + delta=TOLERANCE) + + @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") + @retry_before_failing() + def test_buffers(self): + num = muse('Buffer') + self.assertAlmostEqual(psutil.virtual_memory().buffers, num, + delta=TOLERANCE) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/_linux.py b/test/_linux.py new file mode 100644 index 00000000..5d7f0521 --- /dev/null +++ b/test/_linux.py @@ -0,0 +1,368 @@ +#!/usr/bin/env python + +# 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. + +"""Linux specific tests. These are implicitly run by test_psutil.py.""" + +from __future__ import division +import contextlib +import fcntl +import os +import pprint +import re +import socket +import struct +import sys +import time +import warnings + +try: + from unittest import mock # py3 +except ImportError: + import mock # requires "pip install mock" + +from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX +from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, + retry_before_failing, get_kernel_version, unittest, + which) + +import psutil +import psutil._pslinux +from psutil._compat import PY3 + + +SIOCGIFADDR = 0x8915 +SIOCGIFCONF = 0x8912 +SIOCGIFHWADDR = 0x8927 + + +def get_ipv4_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + return socket.inet_ntoa( + fcntl.ioctl(s.fileno(), + SIOCGIFADDR, + struct.pack('256s', ifname))[20:24]) + + +def get_mac_address(ifname): + ifname = ifname[:15] + if PY3: + ifname = bytes(ifname, 'ascii') + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + with contextlib.closing(s): + info = fcntl.ioctl( + s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) + if PY3: + def ord(x): + return x + else: + import __builtin__ + ord = __builtin__.ord + return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] + + +@unittest.skipUnless(LINUX, "not a Linux system") +class LinuxSpecificTestCase(unittest.TestCase): + + @unittest.skipIf( + POSIX and not hasattr(os, 'statvfs'), + reason="os.statvfs() function not available on this platform") + @skip_on_not_implemented() + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -P -B 1 "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total, used, free = int(total), int(used), int(free) + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.free, free)) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % (usage.used, used)) + + def test_memory_maps(self): + sproc = get_test_subprocess() + time.sleep(1) + p = psutil.Process(sproc.pid) + maps = p.memory_maps(grouped=False) + pmap = sh('pmap -x %s' % p.pid).split('\n') + # get rid of header + del pmap[0] + del pmap[0] + while maps and pmap: + this = maps.pop(0) + other = pmap.pop(0) + addr, _, rss, dirty, mode, path = other.split(None, 5) + if not path.startswith('[') and not path.endswith(']'): + self.assertEqual(path, os.path.basename(this.path)) + self.assertEqual(int(rss) * 1024, this.rss) + # test only rwx chars, ignore 's' and 'p' + self.assertEqual(mode[:3], this.perms[:3]) + + def test_vmem_total(self): + lines = sh('free').split('\n')[1:] + total = int(lines[0].split()[1]) * 1024 + self.assertEqual(total, psutil.virtual_memory().total) + + @retry_before_failing() + def test_vmem_used(self): + lines = sh('free').split('\n')[1:] + used = int(lines[0].split()[2]) * 1024 + self.assertAlmostEqual(used, psutil.virtual_memory().used, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_free(self): + lines = sh('free').split('\n')[1:] + free = int(lines[0].split()[3]) * 1024 + self.assertAlmostEqual(free, psutil.virtual_memory().free, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_buffers(self): + lines = sh('free').split('\n')[1:] + buffers = int(lines[0].split()[5]) * 1024 + self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_cached(self): + lines = sh('free').split('\n')[1:] + cached = int(lines[0].split()[6]) * 1024 + self.assertAlmostEqual(cached, psutil.virtual_memory().cached, + delta=TOLERANCE) + + def test_swapmem_total(self): + lines = sh('free').split('\n')[1:] + total = int(lines[2].split()[1]) * 1024 + self.assertEqual(total, psutil.swap_memory().total) + + @retry_before_failing() + def test_swapmem_used(self): + lines = sh('free').split('\n')[1:] + used = int(lines[2].split()[2]) * 1024 + self.assertAlmostEqual(used, psutil.swap_memory().used, + delta=TOLERANCE) + + @retry_before_failing() + def test_swapmem_free(self): + lines = sh('free').split('\n')[1:] + free = int(lines[2].split()[3]) * 1024 + self.assertAlmostEqual(free, psutil.swap_memory().free, + delta=TOLERANCE) + + @unittest.skipIf(TRAVIS, "unknown failure on travis") + def test_cpu_times(self): + fields = psutil.cpu_times()._fields + kernel_ver = re.findall('\d+\.\d+\.\d+', os.uname()[2])[0] + kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) + if kernel_ver_info >= (2, 6, 11): + self.assertIn('steal', fields) + else: + self.assertNotIn('steal', fields) + if kernel_ver_info >= (2, 6, 24): + self.assertIn('guest', fields) + else: + self.assertNotIn('guest', fields) + if kernel_ver_info >= (3, 2, 0): + self.assertIn('guest_nice', fields) + else: + self.assertNotIn('guest_nice', fields) + + def test_net_if_addrs_ips(self): + for name, addrs in psutil.net_if_addrs().items(): + for addr in addrs: + if addr.family == psutil.AF_LINK: + self.assertEqual(addr.address, get_mac_address(name)) + elif addr.family == socket.AF_INET: + self.assertEqual(addr.address, get_ipv4_address(name)) + # TODO: test for AF_INET6 family + + @unittest.skipUnless(which('ip'), "'ip' utility not available") + @unittest.skipIf(TRAVIS, "skipped on Travis") + def test_net_if_names(self): + out = sh("ip addr").strip() + nics = psutil.net_if_addrs() + found = 0 + for line in out.split('\n'): + line = line.strip() + if re.search("^\d+:", line): + found += 1 + name = line.split(':')[1].strip() + self.assertIn(name, nics.keys()) + self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( + pprint.pformat(nics), out)) + + @unittest.skipUnless(which("nproc"), "nproc utility not available") + def test_cpu_count_logical_w_nproc(self): + num = int(sh("nproc --all")) + self.assertEqual(psutil.cpu_count(logical=True), num) + + @unittest.skipUnless(which("lscpu"), "lscpu utility not available") + def test_cpu_count_logical_w_lscpu(self): + out = sh("lscpu -p") + num = len([x for x in out.split('\n') if not x.startswith('#')]) + self.assertEqual(psutil.cpu_count(logical=True), num) + + # --- mocked tests + + def test_virtual_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.virtual_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'cached', 'active' and 'inactive' memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.cached, 0) + self.assertEqual(ret.active, 0) + self.assertEqual(ret.inactive, 0) + + def test_swap_memory_mocked_warnings(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always") + ret = psutil._pslinux.swap_memory() + assert m.called + self.assertEqual(len(ws), 1) + w = ws[0] + self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) + self.assertIn( + "'sin' and 'sout' swap memory stats couldn't " + "be determined", str(w.message)) + self.assertEqual(ret.sin, 0) + self.assertEqual(ret.sout, 0) + + def test_cpu_count_logical_mocked(self): + import psutil._pslinux + original = psutil._pslinux.cpu_count_logical() + with mock.patch( + 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: + # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in + # order to test /proc/cpuinfo parsing. + # We might also test /proc/stat parsing but mocking open() + # like that is too difficult. + self.assertEqual(psutil._pslinux.cpu_count_logical(), original) + assert m.called + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_logical()) + assert m.called + + def test_cpu_count_physical_mocked(self): + # Have open() return emtpy data and make sure None is returned + # ('cause we want to mimick os.cpu_count()) + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertIsNone(psutil._pslinux.cpu_count_physical()) + assert m.called + + def test_proc_terminal_mocked(self): + with mock.patch('psutil._pslinux._psposix._get_terminal_map', + return_value={}) as m: + self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) + assert m.called + + def test_proc_num_ctx_switches_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_ctx_switches) + assert m.called + + def test_proc_num_threads_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).num_threads) + assert m.called + + def test_proc_ppid_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).ppid) + assert m.called + + def test_proc_uids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).uids) + assert m.called + + def test_proc_gids_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).gids) + assert m.called + + # --- tests for specific kernel versions + + @unittest.skipUnless( + get_kernel_version() >= (2, 6, 36), + "prlimit() not available on this Linux kernel version") + def test_prlimit_availability(self): + # prlimit() should be available starting from kernel 2.6.36 + p = psutil.Process(os.getpid()) + p.rlimit(psutil.RLIMIT_NOFILE) + # if prlimit() is supported *at least* these constants should + # be available + self.assertTrue(hasattr(psutil, "RLIM_INFINITY")) + self.assertTrue(hasattr(psutil, "RLIMIT_AS")) + self.assertTrue(hasattr(psutil, "RLIMIT_CORE")) + self.assertTrue(hasattr(psutil, "RLIMIT_CPU")) + self.assertTrue(hasattr(psutil, "RLIMIT_DATA")) + self.assertTrue(hasattr(psutil, "RLIMIT_FSIZE")) + self.assertTrue(hasattr(psutil, "RLIMIT_LOCKS")) + self.assertTrue(hasattr(psutil, "RLIMIT_MEMLOCK")) + self.assertTrue(hasattr(psutil, "RLIMIT_NOFILE")) + self.assertTrue(hasattr(psutil, "RLIMIT_NPROC")) + self.assertTrue(hasattr(psutil, "RLIMIT_RSS")) + self.assertTrue(hasattr(psutil, "RLIMIT_STACK")) + + @unittest.skipUnless( + get_kernel_version() >= (3, 0), + "prlimit constants not available on this Linux kernel version") + def test_resource_consts_kernel_v(self): + # more recent constants + self.assertTrue(hasattr(psutil, "RLIMIT_MSGQUEUE")) + self.assertTrue(hasattr(psutil, "RLIMIT_NICE")) + self.assertTrue(hasattr(psutil, "RLIMIT_RTPRIO")) + self.assertTrue(hasattr(psutil, "RLIMIT_RTTIME")) + self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/_osx.py b/test/_osx.py new file mode 100644 index 00000000..6e6e4380 --- /dev/null +++ b/test/_osx.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python + +# 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. + +"""OSX specific tests. These are implicitly run by test_psutil.py.""" + +import os +import re +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3 +from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, + reap_children, retry_before_failing, unittest) + + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip().split()[1] + if PY3: + result = str(result, sys.stdout.encoding) + try: + return int(result) + except ValueError: + return result + + +def vm_stat(field): + """Wrapper around 'vm_stat' cmdline utility.""" + out = sh('vm_stat') + for line in out.split('\n'): + if field in line: + break + else: + raise ValueError("line not found") + return int(re.search('\d+', line).group(0)) * PAGESIZE + + +@unittest.skipUnless(OSX, "not an OSX system") +class OSXSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" % self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if PY3: + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time() + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + def test_disks(self): + # test psutil.disk_usage() and psutil.disk_partitions() + # against "df -a" + def df(path): + out = sh('df -k "%s"' % path).strip() + lines = out.split('\n') + lines.pop(0) + line = lines.pop(0) + dev, total, used, free = line.split()[:4] + if dev == 'none': + dev = '' + total = int(total) * 1024 + used = int(used) * 1024 + free = int(free) * 1024 + return dev, total, used, free + + for part in psutil.disk_partitions(all=False): + usage = psutil.disk_usage(part.mountpoint) + dev, total, used, free = df(part.mountpoint) + self.assertEqual(part.device, dev) + self.assertEqual(usage.total, total) + # 10 MB tollerance + if abs(usage.free - free) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % usage.free, free) + if abs(usage.used - used) > 10 * 1024 * 1024: + self.fail("psutil=%s, df=%s" % usage.used, used) + + # --- virtual mem + + def test_vmem_total(self): + sysctl_hwphymem = sysctl('sysctl hw.memsize') + self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) + + @retry_before_failing() + def test_vmem_free(self): + num = vm_stat("free") + self.assertAlmostEqual(psutil.virtual_memory().free, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_active(self): + num = vm_stat("active") + self.assertAlmostEqual(psutil.virtual_memory().active, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_inactive(self): + num = vm_stat("inactive") + self.assertAlmostEqual(psutil.virtual_memory().inactive, num, + delta=TOLERANCE) + + @retry_before_failing() + def test_vmem_wired(self): + num = vm_stat("wired") + self.assertAlmostEqual(psutil.virtual_memory().wired, num, + delta=TOLERANCE) + + # --- swap mem + + def test_swapmem_sin(self): + num = vm_stat("Pageins") + self.assertEqual(psutil.swap_memory().sin, num) + + def test_swapmem_sout(self): + num = vm_stat("Pageouts") + self.assertEqual(psutil.swap_memory().sout, num) + + def test_swapmem_total(self): + tot1 = psutil.swap_memory().total + tot2 = 0 + # OSX uses multiple cache files: + # http://en.wikipedia.org/wiki/Paging#OS_X + for name in os.listdir("/var/vm/"): + file = os.path.join("/var/vm", name) + if os.path.isfile(file): + tot2 += os.path.getsize(file) + self.assertEqual(tot1, tot2) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/_posix.py b/test/_posix.py new file mode 100644 index 00000000..2a263a3f --- /dev/null +++ b/test/_posix.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python + +# 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. + +"""POSIX specific tests. These are implicitly run by test_psutil.py.""" + +import datetime +import os +import subprocess +import sys +import time + +import psutil + +from psutil._compat import PY3, callable +from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX +from test_psutil import (get_test_subprocess, skip_on_access_denied, + retry_before_failing, reap_children, sh, unittest, + get_kernel_version, wait_for_pid) + + +def ps(cmd): + """Expects a ps command with a -o argument and parse the result + returning only the value of interest. + """ + if not LINUX: + cmd = cmd.replace(" --no-headers ", " ") + if SUNOS: + cmd = cmd.replace("-o command", "-o comm") + cmd = cmd.replace("-o start", "-o stime") + p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + if not LINUX: + output = output.split('\n')[1].strip() + try: + return int(output) + except ValueError: + return output + + +@unittest.skipUnless(POSIX, "not a POSIX system") +class PosixSpecificTestCase(unittest.TestCase): + """Compare psutil results against 'ps' command line utility.""" + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess([PYTHON, "-E", "-O"], + stdin=subprocess.PIPE).pid + wait_for_pid(cls.pid) + + @classmethod + def tearDownClass(cls): + reap_children() + + # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps + + def test_process_parent_pid(self): + ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) + ppid_psutil = psutil.Process(self.pid).ppid() + self.assertEqual(ppid_ps, ppid_psutil) + + def test_process_uid(self): + uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) + uid_psutil = psutil.Process(self.pid).uids().real + self.assertEqual(uid_ps, uid_psutil) + + def test_process_gid(self): + gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) + gid_psutil = psutil.Process(self.pid).gids().real + self.assertEqual(gid_ps, gid_psutil) + + def test_process_username(self): + username_ps = ps("ps --no-headers -o user -p %s" % self.pid) + username_psutil = psutil.Process(self.pid).username() + self.assertEqual(username_ps, username_psutil) + + @skip_on_access_denied() + @retry_before_failing() + def test_process_rss_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) + rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 + self.assertEqual(rss_ps, rss_psutil) + + @skip_on_access_denied() + @retry_before_failing() + def test_process_vsz_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) + vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 + self.assertEqual(vsz_ps, vsz_psutil) + + def test_process_name(self): + # use command + arg since "comm" keyword not supported on all platforms + name_ps = ps("ps --no-headers -o command -p %s" % ( + self.pid)).split(' ')[0] + # remove path if there is any, from the command + name_ps = os.path.basename(name_ps).lower() + name_psutil = psutil.Process(self.pid).name().lower() + self.assertEqual(name_ps, name_psutil) + + @unittest.skipIf(OSX or BSD, + 'ps -o start not available') + def test_process_create_time(self): + time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] + time_psutil = psutil.Process(self.pid).create_time() + time_psutil_tstamp = datetime.datetime.fromtimestamp( + time_psutil).strftime("%H:%M:%S") + # sometimes ps shows the time rounded up instead of down, so we check + # for both possible values + round_time_psutil = round(time_psutil) + round_time_psutil_tstamp = datetime.datetime.fromtimestamp( + round_time_psutil).strftime("%H:%M:%S") + self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) + + def test_process_exe(self): + ps_pathname = ps("ps --no-headers -o command -p %s" % + self.pid).split(' ')[0] + psutil_pathname = psutil.Process(self.pid).exe() + try: + self.assertEqual(ps_pathname, psutil_pathname) + except AssertionError: + # certain platforms such as BSD are more accurate returning: + # "/usr/local/bin/python2.7" + # ...instead of: + # "/usr/local/bin/python" + # We do not want to consider this difference in accuracy + # an error. + adjusted_ps_pathname = ps_pathname[:len(ps_pathname)] + self.assertEqual(ps_pathname, adjusted_ps_pathname) + + def test_process_cmdline(self): + ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) + psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) + if SUNOS: + # ps on Solaris only shows the first part of the cmdline + psutil_cmdline = psutil_cmdline.split(" ")[0] + self.assertEqual(ps_cmdline, psutil_cmdline) + + @retry_before_failing() + def test_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + if SUNOS: + cmd = ["ps", "ax"] + else: + cmd = ["ps", "ax", "-o", "pid"] + p = get_test_subprocess(cmd, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + pids_ps = [] + for line in output.split('\n')[1:]: + if line: + pid = int(line.split()[0].strip()) + pids_ps.append(pid) + # remove ps subprocess pid which is supposed to be dead in meantime + pids_ps.remove(p.pid) + pids_psutil = psutil.pids() + pids_ps.sort() + pids_psutil.sort() + + # on OSX ps doesn't show pid 0 + if OSX and 0 not in pids_ps: + pids_ps.insert(0, 0) + + if pids_ps != pids_psutil: + difference = [x for x in pids_psutil if x not in pids_ps] + \ + [x for x in pids_ps if x not in pids_psutil] + self.fail("difference: " + str(difference)) + + # for some reason ifconfig -a does not report differente interfaces + # psutil does + @unittest.skipIf(SUNOS, "test not reliable on SUNOS") + def test_nic_names(self): + p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if PY3: + output = str(output, sys.stdout.encoding) + for nic in psutil.net_io_counters(pernic=True).keys(): + for line in output.split(): + if line.startswith(nic): + break + else: + self.fail("couldn't find %s nic in 'ifconfig -a' output" % nic) + + @retry_before_failing() + def test_users(self): + out = sh("who") + lines = out.split('\n') + users = [x.split()[0] for x in lines] + self.assertEqual(len(users), len(psutil.users())) + terminals = [x.split()[1] for x in lines] + for u in psutil.users(): + self.assertTrue(u.name in users, u.name) + self.assertTrue(u.terminal in terminals, u.terminal) + + def test_fds_open(self): + # Note: this fails from time to time; I'm keen on thinking + # it doesn't mean something is broken + def call(p, attr): + args = () + attr = getattr(p, name, None) + if attr is not None and callable(attr): + if name == 'rlimit': + args = (psutil.RLIMIT_NOFILE,) + attr(*args) + else: + attr + + p = psutil.Process(os.getpid()) + failures = [] + ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', + 'send_signal', 'wait', 'children', 'as_dict'] + if LINUX and get_kernel_version() < (2, 6, 36): + ignored_names.append('rlimit') + if LINUX and get_kernel_version() < (2, 6, 23): + ignored_names.append('num_ctx_switches') + for name in dir(psutil.Process): + if (name.startswith('_') or name in ignored_names): + continue + else: + try: + num1 = p.num_fds() + for x in range(2): + call(p, name) + num2 = p.num_fds() + except psutil.AccessDenied: + pass + else: + if abs(num2 - num1) > 1: + fail = "failure while processing Process.%s method " \ + "(before=%s, after=%s)" % (name, num1, num2) + failures.append(fail) + if failures: + self.fail('\n' + '\n'.join(failures)) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/_sunos.py b/test/_sunos.py new file mode 100644 index 00000000..3d54ccd8 --- /dev/null +++ b/test/_sunos.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# 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. + +"""Sun OS specific tests. These are implicitly run by test_psutil.py.""" + +import sys +import os + +from test_psutil import SUNOS, sh, unittest +import psutil + + +@unittest.skipUnless(SUNOS, "not a SunOS system") +class SunOSSpecificTestCase(unittest.TestCase): + + def test_swap_memory(self): + out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) + lines = out.strip().split('\n')[1:] + if not lines: + raise ValueError('no swap device(s) configured') + total = free = 0 + for line in lines: + line = line.split() + t, f = line[-2:] + t = t.replace('K', '') + f = f.replace('K', '') + total += int(int(t) * 1024) + free += int(int(f) * 1024) + used = total - free + + psutil_swap = psutil.swap_memory() + self.assertEqual(psutil_swap.total, total) + self.assertEqual(psutil_swap.used, used) + self.assertEqual(psutil_swap.free, free) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/_windows.py b/test/_windows.py new file mode 100644 index 00000000..a0a22052 --- /dev/null +++ b/test/_windows.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python + +# 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. + +"""Windows specific tests. These are implicitly run by test_psutil.py.""" + +import errno +import os +import platform +import signal +import subprocess +import sys +import time +import traceback + +from test_psutil import WINDOWS, get_test_subprocess, reap_children, unittest + +try: + import wmi +except ImportError: + wmi = None +try: + import win32api + import win32con +except ImportError: + win32api = win32con = None + +from psutil._compat import PY3, callable, long +import psutil + + +cext = psutil._psplatform.cext + + +def wrap_exceptions(fun): + def wrapper(self, *args, **kwargs): + try: + return fun(self, *args, **kwargs) + except OSError as err: + from psutil._pswindows import ACCESS_DENIED_SET + if err.errno in ACCESS_DENIED_SET: + raise psutil.AccessDenied(None, None) + if err.errno == errno.ESRCH: + raise psutil.NoSuchProcess(None, None) + raise + return wrapper + + +@unittest.skipUnless(WINDOWS, "not a Windows system") +class WindowsSpecificTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.pid = get_test_subprocess().pid + + @classmethod + def tearDownClass(cls): + reap_children() + + def test_issue_24(self): + p = psutil.Process(0) + self.assertRaises(psutil.AccessDenied, p.kill) + + def test_special_pid(self): + p = psutil.Process(4) + self.assertEqual(p.name(), 'System') + # use __str__ to access all common Process properties to check + # that nothing strange happens + str(p) + p.username() + self.assertTrue(p.create_time() >= 0.0) + try: + rss, vms = p.memory_info() + except psutil.AccessDenied: + # expected on Windows Vista and Windows 7 + if not platform.uname()[1] in ('vista', 'win-7', 'win7'): + raise + else: + self.assertTrue(rss > 0) + + def test_send_signal(self): + p = psutil.Process(self.pid) + self.assertRaises(ValueError, p.send_signal, signal.SIGINT) + + def test_nic_names(self): + p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) + out = p.communicate()[0] + if PY3: + out = str(out, sys.stdout.encoding) + nics = psutil.net_io_counters(pernic=True).keys() + for nic in nics: + if "pseudo-interface" in nic.replace(' ', '-').lower(): + continue + if nic not in out: + self.fail( + "%r nic wasn't found in 'ipconfig /all' output" % nic) + + def test_exe(self): + for p in psutil.process_iter(): + try: + self.assertEqual(os.path.basename(p.exe()), p.name()) + except psutil.Error: + pass + + # --- Process class tests + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_name(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.name(), w.Caption) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_exe(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + # Note: wmi reports the exe as a lower case string. + # Being Windows paths case-insensitive we ignore that. + self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_cmdline(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(' '.join(p.cmdline()), + w.CommandLine.replace('"', '')) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_username(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + domain, _, username = w.GetOwner() + username = "%s\\%s" % (domain, username) + self.assertEqual(p.username(), username) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_rss_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + rss = p.memory_info().rss + self.assertEqual(rss, int(w.WorkingSetSize)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_vms_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + vms = p.memory_info().vms + # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx + # ...claims that PageFileUsage is represented in Kilo + # bytes but funnily enough on certain platforms bytes are + # returned instead. + wmi_usage = int(w.PageFileUsage) + if (vms != wmi_usage) and (vms != wmi_usage * 1024): + self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_process_create_time(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time())) + self.assertEqual(wmic_create, psutil_create) + + # --- psutil namespace functions and constants tests + + @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, + 'NUMBER_OF_PROCESSORS env var is not available') + def test_cpu_count(self): + num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) + self.assertEqual(num_cpus, psutil.cpu_count()) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_total_phymem(self): + w = wmi.WMI().Win32_ComputerSystem()[0] + self.assertEqual(int(w.TotalPhysicalMemory), + psutil.virtual_memory().total) + + # @unittest.skipIf(wmi is None, "wmi module is not installed") + # def test__UPTIME(self): + # # _UPTIME constant is not public but it is used internally + # # as value to return for pid 0 creation time. + # # WMI behaves the same. + # w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + # p = psutil.Process(0) + # wmic_create = str(w.CreationDate.split('.')[0]) + # psutil_create = time.strftime("%Y%m%d%H%M%S", + # time.localtime(p.create_time())) + # + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + w = wmi.WMI().Win32_Process() + wmi_pids = [x.ProcessId for x in w] + wmi_pids.sort() + psutil_pids = psutil.pids() + psutil_pids.sort() + if wmi_pids != psutil_pids: + difference = \ + filter(lambda x: x not in wmi_pids, psutil_pids) + \ + filter(lambda x: x not in psutil_pids, wmi_pids) + self.fail("difference: " + str(difference)) + + @unittest.skipIf(wmi is None, "wmi module is not installed") + def test_disks(self): + ps_parts = psutil.disk_partitions(all=True) + wmi_parts = wmi.WMI().Win32_LogicalDisk() + for ps_part in ps_parts: + for wmi_part in wmi_parts: + if ps_part.device.replace('\\', '') == wmi_part.DeviceID: + if not ps_part.mountpoint: + # this is usually a CD-ROM with no disk inserted + break + try: + usage = psutil.disk_usage(ps_part.mountpoint) + except OSError as err: + if err.errno == errno.ENOENT: + # usually this is the floppy + break + else: + raise + self.assertEqual(usage.total, int(wmi_part.Size)) + wmi_free = int(wmi_part.FreeSpace) + self.assertEqual(usage.free, wmi_free) + # 10 MB tollerance + if abs(usage.free - wmi_free) > 10 * 1024 * 1024: + self.fail("psutil=%s, wmi=%s" % ( + usage.free, wmi_free)) + break + else: + self.fail("can't find partition %s" % repr(ps_part)) + + @unittest.skipIf(win32api is None, "pywin32 module is not installed") + def test_num_handles(self): + p = psutil.Process(os.getpid()) + before = p.num_handles() + handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, + win32con.FALSE, os.getpid()) + after = p.num_handles() + self.assertEqual(after, before + 1) + win32api.CloseHandle(handle) + self.assertEqual(p.num_handles(), before) + + @unittest.skipIf(win32api is None, "pywin32 module is not installed") + def test_num_handles_2(self): + # Note: this fails from time to time; I'm keen on thinking + # it doesn't mean something is broken + def call(p, attr): + attr = getattr(p, name, None) + if attr is not None and callable(attr): + attr() + else: + attr + + p = psutil.Process(self.pid) + failures = [] + for name in dir(psutil.Process): + if name.startswith('_') \ + or name in ('terminate', 'kill', 'suspend', 'resume', + 'nice', 'send_signal', 'wait', 'children', + 'as_dict'): + continue + else: + try: + call(p, name) + num1 = p.num_handles() + call(p, name) + num2 = p.num_handles() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + if num2 > num1: + fail = \ + "failure while processing Process.%s method " \ + "(before=%s, after=%s)" % (name, num1, num2) + failures.append(fail) + if failures: + self.fail('\n' + '\n'.join(failures)) + + +@unittest.skipUnless(WINDOWS, "not a Windows system") +class TestDualProcessImplementation(unittest.TestCase): + fun_names = [ + # function name, tolerance + ('proc_cpu_times', 0.2), + ('proc_create_time', 0.5), + ('proc_num_handles', 1), # 1 because impl #1 opens a handle + ('proc_memory_info', 1024), # KB + ('proc_io_counters', 0), + ] + + def test_compare_values(self): + # Certain APIs on Windows have 2 internal implementations, one + # based on documented Windows APIs, another one based + # NtQuerySystemInformation() which gets called as fallback in + # case the first fails because of limited permission error. + # Here we test that the two methods return the exact same value, + # see: + # https://github.com/giampaolo/psutil/issues/304 + def assert_ge_0(obj): + if isinstance(obj, tuple): + for value in obj: + self.assertGreaterEqual(value, 0, msg=obj) + elif isinstance(obj, (int, long, float)): + self.assertGreaterEqual(obj, 0) + else: + assert 0 # case not handled which needs to be fixed + + def compare_with_tolerance(ret1, ret2, tolerance): + if ret1 == ret2: + return + else: + if isinstance(ret2, (int, long, float)): + diff = abs(ret1 - ret2) + self.assertLessEqual(diff, tolerance) + elif isinstance(ret2, tuple): + for a, b in zip(ret1, ret2): + diff = abs(a - b) + self.assertLessEqual(diff, tolerance) + + from psutil._pswindows import ntpinfo + failures = [] + for p in psutil.process_iter(): + try: + nt = ntpinfo(*cext.proc_info(p.pid)) + except psutil.NoSuchProcess: + continue + assert_ge_0(nt) + + for name, tolerance in self.fun_names: + if name == 'proc_memory_info' and p.pid == os.getpid(): + continue + if name == 'proc_create_time' and p.pid in (0, 4): + continue + meth = wrap_exceptions(getattr(cext, name)) + try: + ret = meth(p.pid) + except (psutil.NoSuchProcess, psutil.AccessDenied): + continue + # compare values + try: + if name == 'proc_cpu_times': + compare_with_tolerance(ret[0], nt.user_time, tolerance) + compare_with_tolerance(ret[1], + nt.kernel_time, tolerance) + elif name == 'proc_create_time': + compare_with_tolerance(ret, nt.create_time, tolerance) + elif name == 'proc_num_handles': + compare_with_tolerance(ret, nt.num_handles, tolerance) + elif name == 'proc_io_counters': + compare_with_tolerance(ret[0], nt.io_rcount, tolerance) + compare_with_tolerance(ret[1], nt.io_wcount, tolerance) + compare_with_tolerance(ret[2], nt.io_rbytes, tolerance) + compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) + elif name == 'proc_memory_info': + try: + rawtupl = cext.proc_memory_info_2(p.pid) + except psutil.NoSuchProcess: + continue + compare_with_tolerance(ret, rawtupl, tolerance) + except AssertionError: + trace = traceback.format_exc() + msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % ( + trace, p.pid, name, ret, nt) + failures.append(msg) + break + + if failures: + self.fail('\n\n'.join(failures)) + + def test_compare_name_exe(self): + for p in psutil.process_iter(): + try: + a = os.path.basename(p.exe()) + b = p.name() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + self.assertEqual(a, b) + + def test_zombies(self): + # test that NPS is raised by the 2nd implementation in case a + # process no longer exists + ZOMBIE_PID = max(psutil.pids()) + 5000 + for name, _ in self.fun_names: + meth = wrap_exceptions(getattr(cext, name)) + self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) + + +def main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) + test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + return result.wasSuccessful() + +if __name__ == '__main__': + if not main(): + sys.exit(1) diff --git a/test/test_bsd.py b/test/test_bsd.py deleted file mode 100644 index 76f12442..00000000 --- a/test/test_bsd.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/env python - -# 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. - -# TODO: add test for comparing connections with 'sockstat' cmd - -"""BSD specific tests. These are implicitly run by test_psutil.py.""" - -import os -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3 -from test_psutil import (TOLERANCE, BSD, sh, get_test_subprocess, which, - retry_before_failing, reap_children, unittest) - - -PAGESIZE = os.sysconf("SC_PAGE_SIZE") -if os.getuid() == 0: # muse requires root privileges - MUSE_AVAILABLE = which('muse') -else: - MUSE_AVAILABLE = False - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - result = sh("sysctl " + cmdline) - result = result[result.find(": ") + 2:] - try: - return int(result) - except ValueError: - return result - - -def muse(field): - """Thin wrapper around 'muse' cmdline utility.""" - out = sh('muse') - for line in out.split('\n'): - if line.startswith(field): - break - else: - raise ValueError("line not found") - return int(line.split()[1]) - - -@unittest.skipUnless(BSD, "not a BSD system") -class BSDSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_boot_time(self): - s = sysctl('sysctl kern.boottime') - s = s[s.find(" sec = ") + 7:] - s = s[:s.find(',')] - btime = int(s) - self.assertEqual(btime, psutil.boot_time()) - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" % self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if PY3: - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -k "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.free, free)) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.used, used)) - - @retry_before_failing() - def test_memory_maps(self): - out = sh('procstat -v %s' % self.pid) - maps = psutil.Process(self.pid).memory_maps(grouped=False) - lines = out.split('\n')[1:] - while lines: - line = lines.pop() - fields = line.split() - _, start, stop, perms, res = fields[:5] - map = maps.pop() - self.assertEqual("%s-%s" % (start, stop), map.addr) - self.assertEqual(int(res), map.rss) - if not map.path.startswith('['): - self.assertEqual(fields[10], map.path) - - def test_exe(self): - out = sh('procstat -b %s' % self.pid) - self.assertEqual(psutil.Process(self.pid).exe(), - out.split('\n')[1].split()[-1]) - - def test_cmdline(self): - out = sh('procstat -c %s' % self.pid) - self.assertEqual(' '.join(psutil.Process(self.pid).cmdline()), - ' '.join(out.split('\n')[1].split()[2:])) - - def test_uids_gids(self): - out = sh('procstat -s %s' % self.pid) - euid, ruid, suid, egid, rgid, sgid = out.split('\n')[1].split()[2:8] - p = psutil.Process(self.pid) - uids = p.uids() - gids = p.gids() - self.assertEqual(uids.real, int(ruid)) - self.assertEqual(uids.effective, int(euid)) - self.assertEqual(uids.saved, int(suid)) - self.assertEqual(gids.real, int(rgid)) - self.assertEqual(gids.effective, int(egid)) - self.assertEqual(gids.saved, int(sgid)) - - # --- virtual_memory(); tests against sysctl - - def test_vmem_total(self): - syst = sysctl("sysctl vm.stats.vm.v_page_count") * PAGESIZE - self.assertEqual(psutil.virtual_memory().total, syst) - - @retry_before_failing() - def test_vmem_active(self): - syst = sysctl("vm.stats.vm.v_active_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().active, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_inactive(self): - syst = sysctl("vm.stats.vm.v_inactive_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().inactive, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_wired(self): - syst = sysctl("vm.stats.vm.v_wire_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().wired, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_cached(self): - syst = sysctl("vm.stats.vm.v_cache_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().cached, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_free(self): - syst = sysctl("vm.stats.vm.v_free_count") * PAGESIZE - self.assertAlmostEqual(psutil.virtual_memory().free, syst, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_buffers(self): - syst = sysctl("vfs.bufspace") - self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, - delta=TOLERANCE) - - # --- virtual_memory(); tests against muse - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - def test_total(self): - num = muse('Total') - self.assertEqual(psutil.virtual_memory().total, num) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_active(self): - num = muse('Active') - self.assertAlmostEqual(psutil.virtual_memory().active, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_inactive(self): - num = muse('Inactive') - self.assertAlmostEqual(psutil.virtual_memory().inactive, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_wired(self): - num = muse('Wired') - self.assertAlmostEqual(psutil.virtual_memory().wired, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_cached(self): - num = muse('Cache') - self.assertAlmostEqual(psutil.virtual_memory().cached, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_free(self): - num = muse('Free') - self.assertAlmostEqual(psutil.virtual_memory().free, num, - delta=TOLERANCE) - - @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") - @retry_before_failing() - def test_buffers(self): - num = muse('Buffer') - self.assertAlmostEqual(psutil.virtual_memory().buffers, num, - delta=TOLERANCE) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_linux.py b/test/test_linux.py deleted file mode 100644 index 5d7f0521..00000000 --- a/test/test_linux.py +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Linux specific tests. These are implicitly run by test_psutil.py.""" - -from __future__ import division -import contextlib -import fcntl -import os -import pprint -import re -import socket -import struct -import sys -import time -import warnings - -try: - from unittest import mock # py3 -except ImportError: - import mock # requires "pip install mock" - -from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX -from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, - retry_before_failing, get_kernel_version, unittest, - which) - -import psutil -import psutil._pslinux -from psutil._compat import PY3 - - -SIOCGIFADDR = 0x8915 -SIOCGIFCONF = 0x8912 -SIOCGIFHWADDR = 0x8927 - - -def get_ipv4_address(ifname): - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): - return socket.inet_ntoa( - fcntl.ioctl(s.fileno(), - SIOCGIFADDR, - struct.pack('256s', ifname))[20:24]) - - -def get_mac_address(ifname): - ifname = ifname[:15] - if PY3: - ifname = bytes(ifname, 'ascii') - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - with contextlib.closing(s): - info = fcntl.ioctl( - s.fileno(), SIOCGIFHWADDR, struct.pack('256s', ifname)) - if PY3: - def ord(x): - return x - else: - import __builtin__ - ord = __builtin__.ord - return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1] - - -@unittest.skipUnless(LINUX, "not a Linux system") -class LinuxSpecificTestCase(unittest.TestCase): - - @unittest.skipIf( - POSIX and not hasattr(os, 'statvfs'), - reason="os.statvfs() function not available on this platform") - @skip_on_not_implemented() - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -P -B 1 "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total, used, free = int(total), int(used), int(free) - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.free, free)) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % (usage.used, used)) - - def test_memory_maps(self): - sproc = get_test_subprocess() - time.sleep(1) - p = psutil.Process(sproc.pid) - maps = p.memory_maps(grouped=False) - pmap = sh('pmap -x %s' % p.pid).split('\n') - # get rid of header - del pmap[0] - del pmap[0] - while maps and pmap: - this = maps.pop(0) - other = pmap.pop(0) - addr, _, rss, dirty, mode, path = other.split(None, 5) - if not path.startswith('[') and not path.endswith(']'): - self.assertEqual(path, os.path.basename(this.path)) - self.assertEqual(int(rss) * 1024, this.rss) - # test only rwx chars, ignore 's' and 'p' - self.assertEqual(mode[:3], this.perms[:3]) - - def test_vmem_total(self): - lines = sh('free').split('\n')[1:] - total = int(lines[0].split()[1]) * 1024 - self.assertEqual(total, psutil.virtual_memory().total) - - @retry_before_failing() - def test_vmem_used(self): - lines = sh('free').split('\n')[1:] - used = int(lines[0].split()[2]) * 1024 - self.assertAlmostEqual(used, psutil.virtual_memory().used, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_free(self): - lines = sh('free').split('\n')[1:] - free = int(lines[0].split()[3]) * 1024 - self.assertAlmostEqual(free, psutil.virtual_memory().free, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_buffers(self): - lines = sh('free').split('\n')[1:] - buffers = int(lines[0].split()[5]) * 1024 - self.assertAlmostEqual(buffers, psutil.virtual_memory().buffers, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_cached(self): - lines = sh('free').split('\n')[1:] - cached = int(lines[0].split()[6]) * 1024 - self.assertAlmostEqual(cached, psutil.virtual_memory().cached, - delta=TOLERANCE) - - def test_swapmem_total(self): - lines = sh('free').split('\n')[1:] - total = int(lines[2].split()[1]) * 1024 - self.assertEqual(total, psutil.swap_memory().total) - - @retry_before_failing() - def test_swapmem_used(self): - lines = sh('free').split('\n')[1:] - used = int(lines[2].split()[2]) * 1024 - self.assertAlmostEqual(used, psutil.swap_memory().used, - delta=TOLERANCE) - - @retry_before_failing() - def test_swapmem_free(self): - lines = sh('free').split('\n')[1:] - free = int(lines[2].split()[3]) * 1024 - self.assertAlmostEqual(free, psutil.swap_memory().free, - delta=TOLERANCE) - - @unittest.skipIf(TRAVIS, "unknown failure on travis") - def test_cpu_times(self): - fields = psutil.cpu_times()._fields - kernel_ver = re.findall('\d+\.\d+\.\d+', os.uname()[2])[0] - kernel_ver_info = tuple(map(int, kernel_ver.split('.'))) - if kernel_ver_info >= (2, 6, 11): - self.assertIn('steal', fields) - else: - self.assertNotIn('steal', fields) - if kernel_ver_info >= (2, 6, 24): - self.assertIn('guest', fields) - else: - self.assertNotIn('guest', fields) - if kernel_ver_info >= (3, 2, 0): - self.assertIn('guest_nice', fields) - else: - self.assertNotIn('guest_nice', fields) - - def test_net_if_addrs_ips(self): - for name, addrs in psutil.net_if_addrs().items(): - for addr in addrs: - if addr.family == psutil.AF_LINK: - self.assertEqual(addr.address, get_mac_address(name)) - elif addr.family == socket.AF_INET: - self.assertEqual(addr.address, get_ipv4_address(name)) - # TODO: test for AF_INET6 family - - @unittest.skipUnless(which('ip'), "'ip' utility not available") - @unittest.skipIf(TRAVIS, "skipped on Travis") - def test_net_if_names(self): - out = sh("ip addr").strip() - nics = psutil.net_if_addrs() - found = 0 - for line in out.split('\n'): - line = line.strip() - if re.search("^\d+:", line): - found += 1 - name = line.split(':')[1].strip() - self.assertIn(name, nics.keys()) - self.assertEqual(len(nics), found, msg="%s\n---\n%s" % ( - pprint.pformat(nics), out)) - - @unittest.skipUnless(which("nproc"), "nproc utility not available") - def test_cpu_count_logical_w_nproc(self): - num = int(sh("nproc --all")) - self.assertEqual(psutil.cpu_count(logical=True), num) - - @unittest.skipUnless(which("lscpu"), "lscpu utility not available") - def test_cpu_count_logical_w_lscpu(self): - out = sh("lscpu -p") - num = len([x for x in out.split('\n') if not x.startswith('#')]) - self.assertEqual(psutil.cpu_count(logical=True), num) - - # --- mocked tests - - def test_virtual_memory_mocked_warnings(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil._pslinux.virtual_memory() - assert m.called - self.assertEqual(len(ws), 1) - w = ws[0] - self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) - self.assertIn( - "'cached', 'active' and 'inactive' memory stats couldn't " - "be determined", str(w.message)) - self.assertEqual(ret.cached, 0) - self.assertEqual(ret.active, 0) - self.assertEqual(ret.inactive, 0) - - def test_swap_memory_mocked_warnings(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - with warnings.catch_warnings(record=True) as ws: - warnings.simplefilter("always") - ret = psutil._pslinux.swap_memory() - assert m.called - self.assertEqual(len(ws), 1) - w = ws[0] - self.assertTrue(w.filename.endswith('psutil/_pslinux.py')) - self.assertIn( - "'sin' and 'sout' swap memory stats couldn't " - "be determined", str(w.message)) - self.assertEqual(ret.sin, 0) - self.assertEqual(ret.sout, 0) - - def test_cpu_count_logical_mocked(self): - import psutil._pslinux - original = psutil._pslinux.cpu_count_logical() - with mock.patch( - 'psutil._pslinux.os.sysconf', side_effect=ValueError) as m: - # Here we want to mock os.sysconf("SC_NPROCESSORS_ONLN") in - # order to test /proc/cpuinfo parsing. - # We might also test /proc/stat parsing but mocking open() - # like that is too difficult. - self.assertEqual(psutil._pslinux.cpu_count_logical(), original) - assert m.called - # Have open() return emtpy data and make sure None is returned - # ('cause we want to mimick os.cpu_count()) - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_logical()) - assert m.called - - def test_cpu_count_physical_mocked(self): - # Have open() return emtpy data and make sure None is returned - # ('cause we want to mimick os.cpu_count()) - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertIsNone(psutil._pslinux.cpu_count_physical()) - assert m.called - - def test_proc_terminal_mocked(self): - with mock.patch('psutil._pslinux._psposix._get_terminal_map', - return_value={}) as m: - self.assertIsNone(psutil._pslinux.Process(os.getpid()).terminal()) - assert m.called - - def test_proc_num_ctx_switches_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).num_ctx_switches) - assert m.called - - def test_proc_num_threads_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).num_threads) - assert m.called - - def test_proc_ppid_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).ppid) - assert m.called - - def test_proc_uids_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).uids) - assert m.called - - def test_proc_gids_mocked(self): - with mock.patch('psutil._pslinux.open', create=True) as m: - self.assertRaises( - NotImplementedError, - psutil._pslinux.Process(os.getpid()).gids) - assert m.called - - # --- tests for specific kernel versions - - @unittest.skipUnless( - get_kernel_version() >= (2, 6, 36), - "prlimit() not available on this Linux kernel version") - def test_prlimit_availability(self): - # prlimit() should be available starting from kernel 2.6.36 - p = psutil.Process(os.getpid()) - p.rlimit(psutil.RLIMIT_NOFILE) - # if prlimit() is supported *at least* these constants should - # be available - self.assertTrue(hasattr(psutil, "RLIM_INFINITY")) - self.assertTrue(hasattr(psutil, "RLIMIT_AS")) - self.assertTrue(hasattr(psutil, "RLIMIT_CORE")) - self.assertTrue(hasattr(psutil, "RLIMIT_CPU")) - self.assertTrue(hasattr(psutil, "RLIMIT_DATA")) - self.assertTrue(hasattr(psutil, "RLIMIT_FSIZE")) - self.assertTrue(hasattr(psutil, "RLIMIT_LOCKS")) - self.assertTrue(hasattr(psutil, "RLIMIT_MEMLOCK")) - self.assertTrue(hasattr(psutil, "RLIMIT_NOFILE")) - self.assertTrue(hasattr(psutil, "RLIMIT_NPROC")) - self.assertTrue(hasattr(psutil, "RLIMIT_RSS")) - self.assertTrue(hasattr(psutil, "RLIMIT_STACK")) - - @unittest.skipUnless( - get_kernel_version() >= (3, 0), - "prlimit constants not available on this Linux kernel version") - def test_resource_consts_kernel_v(self): - # more recent constants - self.assertTrue(hasattr(psutil, "RLIMIT_MSGQUEUE")) - self.assertTrue(hasattr(psutil, "RLIMIT_NICE")) - self.assertTrue(hasattr(psutil, "RLIMIT_RTPRIO")) - self.assertTrue(hasattr(psutil, "RLIMIT_RTTIME")) - self.assertTrue(hasattr(psutil, "RLIMIT_SIGPENDING")) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_osx.py b/test/test_osx.py deleted file mode 100644 index 6e6e4380..00000000 --- a/test/test_osx.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""OSX specific tests. These are implicitly run by test_psutil.py.""" - -import os -import re -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3 -from test_psutil import (TOLERANCE, OSX, sh, get_test_subprocess, - reap_children, retry_before_failing, unittest) - - -PAGESIZE = os.sysconf("SC_PAGE_SIZE") - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip().split()[1] - if PY3: - result = str(result, sys.stdout.encoding) - try: - return int(result) - except ValueError: - return result - - -def vm_stat(field): - """Wrapper around 'vm_stat' cmdline utility.""" - out = sh('vm_stat') - for line in out.split('\n'): - if field in line: - break - else: - raise ValueError("line not found") - return int(re.search('\d+', line).group(0)) * PAGESIZE - - -@unittest.skipUnless(OSX, "not an OSX system") -class OSXSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" % self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if PY3: - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time() - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - def test_disks(self): - # test psutil.disk_usage() and psutil.disk_partitions() - # against "df -a" - def df(path): - out = sh('df -k "%s"' % path).strip() - lines = out.split('\n') - lines.pop(0) - line = lines.pop(0) - dev, total, used, free = line.split()[:4] - if dev == 'none': - dev = '' - total = int(total) * 1024 - used = int(used) * 1024 - free = int(free) * 1024 - return dev, total, used, free - - for part in psutil.disk_partitions(all=False): - usage = psutil.disk_usage(part.mountpoint) - dev, total, used, free = df(part.mountpoint) - self.assertEqual(part.device, dev) - self.assertEqual(usage.total, total) - # 10 MB tollerance - if abs(usage.free - free) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % usage.free, free) - if abs(usage.used - used) > 10 * 1024 * 1024: - self.fail("psutil=%s, df=%s" % usage.used, used) - - # --- virtual mem - - def test_vmem_total(self): - sysctl_hwphymem = sysctl('sysctl hw.memsize') - self.assertEqual(sysctl_hwphymem, psutil.virtual_memory().total) - - @retry_before_failing() - def test_vmem_free(self): - num = vm_stat("free") - self.assertAlmostEqual(psutil.virtual_memory().free, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_active(self): - num = vm_stat("active") - self.assertAlmostEqual(psutil.virtual_memory().active, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_inactive(self): - num = vm_stat("inactive") - self.assertAlmostEqual(psutil.virtual_memory().inactive, num, - delta=TOLERANCE) - - @retry_before_failing() - def test_vmem_wired(self): - num = vm_stat("wired") - self.assertAlmostEqual(psutil.virtual_memory().wired, num, - delta=TOLERANCE) - - # --- swap mem - - def test_swapmem_sin(self): - num = vm_stat("Pageins") - self.assertEqual(psutil.swap_memory().sin, num) - - def test_swapmem_sout(self): - num = vm_stat("Pageouts") - self.assertEqual(psutil.swap_memory().sout, num) - - def test_swapmem_total(self): - tot1 = psutil.swap_memory().total - tot2 = 0 - # OSX uses multiple cache files: - # http://en.wikipedia.org/wiki/Paging#OS_X - for name in os.listdir("/var/vm/"): - file = os.path.join("/var/vm", name) - if os.path.isfile(file): - tot2 += os.path.getsize(file) - self.assertEqual(tot1, tot2) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_posix.py b/test/test_posix.py deleted file mode 100644 index 2a263a3f..00000000 --- a/test/test_posix.py +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""POSIX specific tests. These are implicitly run by test_psutil.py.""" - -import datetime -import os -import subprocess -import sys -import time - -import psutil - -from psutil._compat import PY3, callable -from test_psutil import LINUX, SUNOS, OSX, BSD, PYTHON, POSIX -from test_psutil import (get_test_subprocess, skip_on_access_denied, - retry_before_failing, reap_children, sh, unittest, - get_kernel_version, wait_for_pid) - - -def ps(cmd): - """Expects a ps command with a -o argument and parse the result - returning only the value of interest. - """ - if not LINUX: - cmd = cmd.replace(" --no-headers ", " ") - if SUNOS: - cmd = cmd.replace("-o command", "-o comm") - cmd = cmd.replace("-o start", "-o stime") - p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - if not LINUX: - output = output.split('\n')[1].strip() - try: - return int(output) - except ValueError: - return output - - -@unittest.skipUnless(POSIX, "not a POSIX system") -class PosixSpecificTestCase(unittest.TestCase): - """Compare psutil results against 'ps' command line utility.""" - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess([PYTHON, "-E", "-O"], - stdin=subprocess.PIPE).pid - wait_for_pid(cls.pid) - - @classmethod - def tearDownClass(cls): - reap_children() - - # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps - - def test_process_parent_pid(self): - ppid_ps = ps("ps --no-headers -o ppid -p %s" % self.pid) - ppid_psutil = psutil.Process(self.pid).ppid() - self.assertEqual(ppid_ps, ppid_psutil) - - def test_process_uid(self): - uid_ps = ps("ps --no-headers -o uid -p %s" % self.pid) - uid_psutil = psutil.Process(self.pid).uids().real - self.assertEqual(uid_ps, uid_psutil) - - def test_process_gid(self): - gid_ps = ps("ps --no-headers -o rgid -p %s" % self.pid) - gid_psutil = psutil.Process(self.pid).gids().real - self.assertEqual(gid_ps, gid_psutil) - - def test_process_username(self): - username_ps = ps("ps --no-headers -o user -p %s" % self.pid) - username_psutil = psutil.Process(self.pid).username() - self.assertEqual(username_ps, username_psutil) - - @skip_on_access_denied() - @retry_before_failing() - def test_process_rss_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - rss_ps = ps("ps --no-headers -o rss -p %s" % self.pid) - rss_psutil = psutil.Process(self.pid).memory_info()[0] / 1024 - self.assertEqual(rss_ps, rss_psutil) - - @skip_on_access_denied() - @retry_before_failing() - def test_process_vsz_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - vsz_ps = ps("ps --no-headers -o vsz -p %s" % self.pid) - vsz_psutil = psutil.Process(self.pid).memory_info()[1] / 1024 - self.assertEqual(vsz_ps, vsz_psutil) - - def test_process_name(self): - # use command + arg since "comm" keyword not supported on all platforms - name_ps = ps("ps --no-headers -o command -p %s" % ( - self.pid)).split(' ')[0] - # remove path if there is any, from the command - name_ps = os.path.basename(name_ps).lower() - name_psutil = psutil.Process(self.pid).name().lower() - self.assertEqual(name_ps, name_psutil) - - @unittest.skipIf(OSX or BSD, - 'ps -o start not available') - def test_process_create_time(self): - time_ps = ps("ps --no-headers -o start -p %s" % self.pid).split(' ')[0] - time_psutil = psutil.Process(self.pid).create_time() - time_psutil_tstamp = datetime.datetime.fromtimestamp( - time_psutil).strftime("%H:%M:%S") - # sometimes ps shows the time rounded up instead of down, so we check - # for both possible values - round_time_psutil = round(time_psutil) - round_time_psutil_tstamp = datetime.datetime.fromtimestamp( - round_time_psutil).strftime("%H:%M:%S") - self.assertIn(time_ps, [time_psutil_tstamp, round_time_psutil_tstamp]) - - def test_process_exe(self): - ps_pathname = ps("ps --no-headers -o command -p %s" % - self.pid).split(' ')[0] - psutil_pathname = psutil.Process(self.pid).exe() - try: - self.assertEqual(ps_pathname, psutil_pathname) - except AssertionError: - # certain platforms such as BSD are more accurate returning: - # "/usr/local/bin/python2.7" - # ...instead of: - # "/usr/local/bin/python" - # We do not want to consider this difference in accuracy - # an error. - adjusted_ps_pathname = ps_pathname[:len(ps_pathname)] - self.assertEqual(ps_pathname, adjusted_ps_pathname) - - def test_process_cmdline(self): - ps_cmdline = ps("ps --no-headers -o command -p %s" % self.pid) - psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline()) - if SUNOS: - # ps on Solaris only shows the first part of the cmdline - psutil_cmdline = psutil_cmdline.split(" ")[0] - self.assertEqual(ps_cmdline, psutil_cmdline) - - @retry_before_failing() - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - if SUNOS: - cmd = ["ps", "ax"] - else: - cmd = ["ps", "ax", "-o", "pid"] - p = get_test_subprocess(cmd, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - pids_ps = [] - for line in output.split('\n')[1:]: - if line: - pid = int(line.split()[0].strip()) - pids_ps.append(pid) - # remove ps subprocess pid which is supposed to be dead in meantime - pids_ps.remove(p.pid) - pids_psutil = psutil.pids() - pids_ps.sort() - pids_psutil.sort() - - # on OSX ps doesn't show pid 0 - if OSX and 0 not in pids_ps: - pids_ps.insert(0, 0) - - if pids_ps != pids_psutil: - difference = [x for x in pids_psutil if x not in pids_ps] + \ - [x for x in pids_ps if x not in pids_psutil] - self.fail("difference: " + str(difference)) - - # for some reason ifconfig -a does not report differente interfaces - # psutil does - @unittest.skipIf(SUNOS, "test not reliable on SUNOS") - def test_nic_names(self): - p = subprocess.Popen("ifconfig -a", shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if PY3: - output = str(output, sys.stdout.encoding) - for nic in psutil.net_io_counters(pernic=True).keys(): - for line in output.split(): - if line.startswith(nic): - break - else: - self.fail("couldn't find %s nic in 'ifconfig -a' output" % nic) - - @retry_before_failing() - def test_users(self): - out = sh("who") - lines = out.split('\n') - users = [x.split()[0] for x in lines] - self.assertEqual(len(users), len(psutil.users())) - terminals = [x.split()[1] for x in lines] - for u in psutil.users(): - self.assertTrue(u.name in users, u.name) - self.assertTrue(u.terminal in terminals, u.terminal) - - def test_fds_open(self): - # Note: this fails from time to time; I'm keen on thinking - # it doesn't mean something is broken - def call(p, attr): - args = () - attr = getattr(p, name, None) - if attr is not None and callable(attr): - if name == 'rlimit': - args = (psutil.RLIMIT_NOFILE,) - attr(*args) - else: - attr - - p = psutil.Process(os.getpid()) - failures = [] - ignored_names = ['terminate', 'kill', 'suspend', 'resume', 'nice', - 'send_signal', 'wait', 'children', 'as_dict'] - if LINUX and get_kernel_version() < (2, 6, 36): - ignored_names.append('rlimit') - if LINUX and get_kernel_version() < (2, 6, 23): - ignored_names.append('num_ctx_switches') - for name in dir(psutil.Process): - if (name.startswith('_') or name in ignored_names): - continue - else: - try: - num1 = p.num_fds() - for x in range(2): - call(p, name) - num2 = p.num_fds() - except psutil.AccessDenied: - pass - else: - if abs(num2 - num1) > 1: - fail = "failure while processing Process.%s method " \ - "(before=%s, after=%s)" % (name, num1, num2) - failures.append(fail) - if failures: - self.fail('\n' + '\n'.join(failures)) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_psutil.py b/test/test_psutil.py index ed85708c..470dafc4 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2767,23 +2767,23 @@ def main(): tests.append(LimitedUserTestCase) if POSIX: - from test_posix import PosixSpecificTestCase + from _posix import PosixSpecificTestCase tests.append(PosixSpecificTestCase) # import the specific platform test suite stc = None if LINUX: - from test_linux import LinuxSpecificTestCase as stc + from _linux import LinuxSpecificTestCase as stc elif WINDOWS: - from test_windows import WindowsSpecificTestCase as stc - from test_windows import TestDualProcessImplementation + from _windows import WindowsSpecificTestCase as stc + from _windows import TestDualProcessImplementation tests.append(TestDualProcessImplementation) elif OSX: - from test_osx import OSXSpecificTestCase as stc + from _osx import OSXSpecificTestCase as stc elif BSD: - from test_bsd import BSDSpecificTestCase as stc + from _bsd import BSDSpecificTestCase as stc elif SUNOS: - from test_sunos import SunOSSpecificTestCase as stc + from _sunos import SunOSSpecificTestCase as stc if stc is not None: tests.append(stc) diff --git a/test/test_sunos.py b/test/test_sunos.py deleted file mode 100644 index 3d54ccd8..00000000 --- a/test/test_sunos.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Sun OS specific tests. These are implicitly run by test_psutil.py.""" - -import sys -import os - -from test_psutil import SUNOS, sh, unittest -import psutil - - -@unittest.skipUnless(SUNOS, "not a SunOS system") -class SunOSSpecificTestCase(unittest.TestCase): - - def test_swap_memory(self): - out = sh('env PATH=/usr/sbin:/sbin:%s swap -l -k' % os.environ['PATH']) - lines = out.strip().split('\n')[1:] - if not lines: - raise ValueError('no swap device(s) configured') - total = free = 0 - for line in lines: - line = line.split() - t, f = line[-2:] - t = t.replace('K', '') - f = f.replace('K', '') - total += int(int(t) * 1024) - free += int(int(f) * 1024) - used = total - free - - psutil_swap = psutil.swap_memory() - self.assertEqual(psutil_swap.total, total) - self.assertEqual(psutil_swap.used, used) - self.assertEqual(psutil_swap.free, free) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(SunOSSpecificTestCase)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) diff --git a/test/test_windows.py b/test/test_windows.py deleted file mode 100644 index a0a22052..00000000 --- a/test/test_windows.py +++ /dev/null @@ -1,405 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Windows specific tests. These are implicitly run by test_psutil.py.""" - -import errno -import os -import platform -import signal -import subprocess -import sys -import time -import traceback - -from test_psutil import WINDOWS, get_test_subprocess, reap_children, unittest - -try: - import wmi -except ImportError: - wmi = None -try: - import win32api - import win32con -except ImportError: - win32api = win32con = None - -from psutil._compat import PY3, callable, long -import psutil - - -cext = psutil._psplatform.cext - - -def wrap_exceptions(fun): - def wrapper(self, *args, **kwargs): - try: - return fun(self, *args, **kwargs) - except OSError as err: - from psutil._pswindows import ACCESS_DENIED_SET - if err.errno in ACCESS_DENIED_SET: - raise psutil.AccessDenied(None, None) - if err.errno == errno.ESRCH: - raise psutil.NoSuchProcess(None, None) - raise - return wrapper - - -@unittest.skipUnless(WINDOWS, "not a Windows system") -class WindowsSpecificTestCase(unittest.TestCase): - - @classmethod - def setUpClass(cls): - cls.pid = get_test_subprocess().pid - - @classmethod - def tearDownClass(cls): - reap_children() - - def test_issue_24(self): - p = psutil.Process(0) - self.assertRaises(psutil.AccessDenied, p.kill) - - def test_special_pid(self): - p = psutil.Process(4) - self.assertEqual(p.name(), 'System') - # use __str__ to access all common Process properties to check - # that nothing strange happens - str(p) - p.username() - self.assertTrue(p.create_time() >= 0.0) - try: - rss, vms = p.memory_info() - except psutil.AccessDenied: - # expected on Windows Vista and Windows 7 - if not platform.uname()[1] in ('vista', 'win-7', 'win7'): - raise - else: - self.assertTrue(rss > 0) - - def test_send_signal(self): - p = psutil.Process(self.pid) - self.assertRaises(ValueError, p.send_signal, signal.SIGINT) - - def test_nic_names(self): - p = subprocess.Popen(['ipconfig', '/all'], stdout=subprocess.PIPE) - out = p.communicate()[0] - if PY3: - out = str(out, sys.stdout.encoding) - nics = psutil.net_io_counters(pernic=True).keys() - for nic in nics: - if "pseudo-interface" in nic.replace(' ', '-').lower(): - continue - if nic not in out: - self.fail( - "%r nic wasn't found in 'ipconfig /all' output" % nic) - - def test_exe(self): - for p in psutil.process_iter(): - try: - self.assertEqual(os.path.basename(p.exe()), p.name()) - except psutil.Error: - pass - - # --- Process class tests - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_name(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(p.name(), w.Caption) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_exe(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - # Note: wmi reports the exe as a lower case string. - # Being Windows paths case-insensitive we ignore that. - self.assertEqual(p.exe().lower(), w.ExecutablePath.lower()) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_cmdline(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(' '.join(p.cmdline()), - w.CommandLine.replace('"', '')) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_username(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - domain, _, username = w.GetOwner() - username = "%s\\%s" % (domain, username) - self.assertEqual(p.username(), username) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_rss_memory(self): - time.sleep(0.1) - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - rss = p.memory_info().rss - self.assertEqual(rss, int(w.WorkingSetSize)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_vms_memory(self): - time.sleep(0.1) - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - vms = p.memory_info().vms - # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx - # ...claims that PageFileUsage is represented in Kilo - # bytes but funnily enough on certain platforms bytes are - # returned instead. - wmi_usage = int(w.PageFileUsage) - if (vms != wmi_usage) and (vms != wmi_usage * 1024): - self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_process_create_time(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime("%Y%m%d%H%M%S", - time.localtime(p.create_time())) - self.assertEqual(wmic_create, psutil_create) - - # --- psutil namespace functions and constants tests - - @unittest.skipUnless('NUMBER_OF_PROCESSORS' in os.environ, - 'NUMBER_OF_PROCESSORS env var is not available') - def test_cpu_count(self): - num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - self.assertEqual(num_cpus, psutil.cpu_count()) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_total_phymem(self): - w = wmi.WMI().Win32_ComputerSystem()[0] - self.assertEqual(int(w.TotalPhysicalMemory), - psutil.virtual_memory().total) - - # @unittest.skipIf(wmi is None, "wmi module is not installed") - # def test__UPTIME(self): - # # _UPTIME constant is not public but it is used internally - # # as value to return for pid 0 creation time. - # # WMI behaves the same. - # w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - # p = psutil.Process(0) - # wmic_create = str(w.CreationDate.split('.')[0]) - # psutil_create = time.strftime("%Y%m%d%H%M%S", - # time.localtime(p.create_time())) - # - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - w = wmi.WMI().Win32_Process() - wmi_pids = [x.ProcessId for x in w] - wmi_pids.sort() - psutil_pids = psutil.pids() - psutil_pids.sort() - if wmi_pids != psutil_pids: - difference = \ - filter(lambda x: x not in wmi_pids, psutil_pids) + \ - filter(lambda x: x not in psutil_pids, wmi_pids) - self.fail("difference: " + str(difference)) - - @unittest.skipIf(wmi is None, "wmi module is not installed") - def test_disks(self): - ps_parts = psutil.disk_partitions(all=True) - wmi_parts = wmi.WMI().Win32_LogicalDisk() - for ps_part in ps_parts: - for wmi_part in wmi_parts: - if ps_part.device.replace('\\', '') == wmi_part.DeviceID: - if not ps_part.mountpoint: - # this is usually a CD-ROM with no disk inserted - break - try: - usage = psutil.disk_usage(ps_part.mountpoint) - except OSError as err: - if err.errno == errno.ENOENT: - # usually this is the floppy - break - else: - raise - self.assertEqual(usage.total, int(wmi_part.Size)) - wmi_free = int(wmi_part.FreeSpace) - self.assertEqual(usage.free, wmi_free) - # 10 MB tollerance - if abs(usage.free - wmi_free) > 10 * 1024 * 1024: - self.fail("psutil=%s, wmi=%s" % ( - usage.free, wmi_free)) - break - else: - self.fail("can't find partition %s" % repr(ps_part)) - - @unittest.skipIf(win32api is None, "pywin32 module is not installed") - def test_num_handles(self): - p = psutil.Process(os.getpid()) - before = p.num_handles() - handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, - win32con.FALSE, os.getpid()) - after = p.num_handles() - self.assertEqual(after, before + 1) - win32api.CloseHandle(handle) - self.assertEqual(p.num_handles(), before) - - @unittest.skipIf(win32api is None, "pywin32 module is not installed") - def test_num_handles_2(self): - # Note: this fails from time to time; I'm keen on thinking - # it doesn't mean something is broken - def call(p, attr): - attr = getattr(p, name, None) - if attr is not None and callable(attr): - attr() - else: - attr - - p = psutil.Process(self.pid) - failures = [] - for name in dir(psutil.Process): - if name.startswith('_') \ - or name in ('terminate', 'kill', 'suspend', 'resume', - 'nice', 'send_signal', 'wait', 'children', - 'as_dict'): - continue - else: - try: - call(p, name) - num1 = p.num_handles() - call(p, name) - num2 = p.num_handles() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - else: - if num2 > num1: - fail = \ - "failure while processing Process.%s method " \ - "(before=%s, after=%s)" % (name, num1, num2) - failures.append(fail) - if failures: - self.fail('\n' + '\n'.join(failures)) - - -@unittest.skipUnless(WINDOWS, "not a Windows system") -class TestDualProcessImplementation(unittest.TestCase): - fun_names = [ - # function name, tolerance - ('proc_cpu_times', 0.2), - ('proc_create_time', 0.5), - ('proc_num_handles', 1), # 1 because impl #1 opens a handle - ('proc_memory_info', 1024), # KB - ('proc_io_counters', 0), - ] - - def test_compare_values(self): - # Certain APIs on Windows have 2 internal implementations, one - # based on documented Windows APIs, another one based - # NtQuerySystemInformation() which gets called as fallback in - # case the first fails because of limited permission error. - # Here we test that the two methods return the exact same value, - # see: - # https://github.com/giampaolo/psutil/issues/304 - def assert_ge_0(obj): - if isinstance(obj, tuple): - for value in obj: - self.assertGreaterEqual(value, 0, msg=obj) - elif isinstance(obj, (int, long, float)): - self.assertGreaterEqual(obj, 0) - else: - assert 0 # case not handled which needs to be fixed - - def compare_with_tolerance(ret1, ret2, tolerance): - if ret1 == ret2: - return - else: - if isinstance(ret2, (int, long, float)): - diff = abs(ret1 - ret2) - self.assertLessEqual(diff, tolerance) - elif isinstance(ret2, tuple): - for a, b in zip(ret1, ret2): - diff = abs(a - b) - self.assertLessEqual(diff, tolerance) - - from psutil._pswindows import ntpinfo - failures = [] - for p in psutil.process_iter(): - try: - nt = ntpinfo(*cext.proc_info(p.pid)) - except psutil.NoSuchProcess: - continue - assert_ge_0(nt) - - for name, tolerance in self.fun_names: - if name == 'proc_memory_info' and p.pid == os.getpid(): - continue - if name == 'proc_create_time' and p.pid in (0, 4): - continue - meth = wrap_exceptions(getattr(cext, name)) - try: - ret = meth(p.pid) - except (psutil.NoSuchProcess, psutil.AccessDenied): - continue - # compare values - try: - if name == 'proc_cpu_times': - compare_with_tolerance(ret[0], nt.user_time, tolerance) - compare_with_tolerance(ret[1], - nt.kernel_time, tolerance) - elif name == 'proc_create_time': - compare_with_tolerance(ret, nt.create_time, tolerance) - elif name == 'proc_num_handles': - compare_with_tolerance(ret, nt.num_handles, tolerance) - elif name == 'proc_io_counters': - compare_with_tolerance(ret[0], nt.io_rcount, tolerance) - compare_with_tolerance(ret[1], nt.io_wcount, tolerance) - compare_with_tolerance(ret[2], nt.io_rbytes, tolerance) - compare_with_tolerance(ret[3], nt.io_wbytes, tolerance) - elif name == 'proc_memory_info': - try: - rawtupl = cext.proc_memory_info_2(p.pid) - except psutil.NoSuchProcess: - continue - compare_with_tolerance(ret, rawtupl, tolerance) - except AssertionError: - trace = traceback.format_exc() - msg = '%s\npid=%s, method=%r, ret_1=%r, ret_2=%r' % ( - trace, p.pid, name, ret, nt) - failures.append(msg) - break - - if failures: - self.fail('\n\n'.join(failures)) - - def test_compare_name_exe(self): - for p in psutil.process_iter(): - try: - a = os.path.basename(p.exe()) - b = p.name() - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - else: - self.assertEqual(a, b) - - def test_zombies(self): - # test that NPS is raised by the 2nd implementation in case a - # process no longer exists - ZOMBIE_PID = max(psutil.pids()) + 5000 - for name, _ in self.fun_names: - meth = wrap_exceptions(getattr(cext, name)) - self.assertRaises(psutil.NoSuchProcess, meth, ZOMBIE_PID) - - -def main(): - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) - test_suite.addTest(unittest.makeSuite(TestDualProcessImplementation)) - result = unittest.TextTestRunner(verbosity=2).run(test_suite) - return result.wasSuccessful() - -if __name__ == '__main__': - if not main(): - sys.exit(1) -- cgit v1.2.1 From 7f062361ec6b545ac11bff1ef405c42dcffe36f0 Mon Sep 17 00:00:00 2001 From: Jeff Tang Date: Mon, 13 Apr 2015 16:18:38 -0400 Subject: Implement inet_ntop to support Windows XP --- CREDITS | 2 +- HISTORY.rst | 1 + psutil/_psutil_windows.c | 1 + psutil/arch/windows/inet_ntop.c | 24 ++++++++++++++++++++++++ psutil/arch/windows/inet_ntop.h | 10 ++++++++++ setup.py | 1 + 6 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 psutil/arch/windows/inet_ntop.c create mode 100644 psutil/arch/windows/inet_ntop.h diff --git a/CREDITS b/CREDITS index 22f604c4..432e744d 100644 --- a/CREDITS +++ b/CREDITS @@ -255,7 +255,7 @@ I: 492 N: Jeff Tang W: https://github.com/mrjefftang -I: 340, 529 +I: 340, 529, 616 N: Yaolong Huang E: airekans@gmail.com diff --git a/HISTORY.rst b/HISTORY.rst index faeb0c9e..8c026524 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -22,6 +22,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues when running as a limited user. - #602: pre-commit GIT hook. - #629: enhanced support for py.test and nose test discovery and tests run. +- #616: [Windows] Add inet_ntop function for Windows XP. **Bug fixes** diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 670be24c..f789e03d 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -31,6 +31,7 @@ #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" #include "arch/windows/ntextapi.h" +#include "arch/windows/inet_ntop.h" #ifdef __MINGW32__ #include "arch/windows/glpi.h" diff --git a/psutil/arch/windows/inet_ntop.c b/psutil/arch/windows/inet_ntop.c new file mode 100644 index 00000000..39b05dfb --- /dev/null +++ b/psutil/arch/windows/inet_ntop.c @@ -0,0 +1,24 @@ +#include "inet_ntop.h" + +// From: https://memset.wordpress.com/2010/10/09/inet_ntop-for-win32/ +PCSTR +WSAAPI +inet_ntop( + __in INT Family, + __in PVOID pAddr, + __out_ecount(StringBufSize) PSTR pStringBuf, + __in size_t StringBufSize + ) +{ + struct sockaddr_in srcaddr; + + memset(&srcaddr, 0, sizeof(struct sockaddr_in)); + memcpy(&(srcaddr.sin_addr), pAddr, sizeof(srcaddr.sin_addr)); + + srcaddr.sin_family = Family; + if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, pStringBuf, (LPDWORD) &StringBufSize) != 0) { + DWORD rv = WSAGetLastError(); + return NULL; + } + return pStringBuf; +} \ No newline at end of file diff --git a/psutil/arch/windows/inet_ntop.h b/psutil/arch/windows/inet_ntop.h new file mode 100644 index 00000000..e9b83585 --- /dev/null +++ b/psutil/arch/windows/inet_ntop.h @@ -0,0 +1,10 @@ +#include + +PCSTR +WSAAPI +inet_ntop( + __in INT Family, + __in PVOID pAddr, + __out_ecount(StringBufSize) PSTR pStringBuf, + __in size_t StringBufSize + ); \ No newline at end of file diff --git a/setup.py b/setup.py index 54a70c49..4c42548e 100644 --- a/setup.py +++ b/setup.py @@ -70,6 +70,7 @@ if sys.platform.startswith("win32"): 'psutil/arch/windows/process_info.c', 'psutil/arch/windows/process_handles.c', 'psutil/arch/windows/security.c', + 'psutil/arch/windows/inet_ntop.c', ], define_macros=[ VERSION_MACRO, -- cgit v1.2.1 From b5d675ab1f2ab3d849564173539891ab56cd1703 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 12 Jun 2015 01:50:40 -0700 Subject: fix test on Windows --- test/test_psutil.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/test_psutil.py b/test/test_psutil.py index 470dafc4..66209ead 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -713,10 +713,11 @@ class TestSystemAPIs(unittest.TestCase): self.assertEqual(logical, len(psutil.cpu_times(percpu=True))) self.assertGreaterEqual(logical, 1) # - with open("/proc/cpuinfo") as fd: - cpuinfo_data = fd.read() - if "physical id" not in cpuinfo_data: - raise unittest.SkipTest("cpuinfo doesn't include physical id") + if LINUX: + with open("/proc/cpuinfo") as fd: + cpuinfo_data = fd.read() + if "physical id" not in cpuinfo_data: + raise unittest.SkipTest("cpuinfo doesn't include physical id") physical = psutil.cpu_count(logical=False) self.assertGreaterEqual(physical, 1) self.assertGreaterEqual(logical, physical) -- cgit v1.2.1 From 6a9ca74c6fa5d88481648bb7be4355d756d23222 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 12 Jun 2015 23:22:21 +0200 Subject: (freebsd) fix memleak + add test --- psutil/_psbsd.py | 6 +++++- test/_bsd.py | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index 3ce5fd1c..db54a02e 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -143,7 +143,11 @@ def cpu_count_physical(): if index != -1: s = s[:index + 9] root = ET.fromstring(s) - ret = len(root.findall('group/children/group/cpu')) or None + try: + ret = len(root.findall('group/children/group/cpu')) or None + finally: + # needed otherwise it will memleak + root.clear() if not ret: # If logical CPUs are 1 it's obvious we'll have only 1 # physical CPU. diff --git a/test/_bsd.py b/test/_bsd.py index 76f12442..e4a3225d 100644 --- a/test/_bsd.py +++ b/test/_bsd.py @@ -187,6 +187,10 @@ class BSDSpecificTestCase(unittest.TestCase): self.assertAlmostEqual(psutil.virtual_memory().buffers, syst, delta=TOLERANCE) + def test_cpu_count_logical(self): + syst = sysctl("hw.ncpu") + self.assertEqual(psutil.cpu_count(logical=True), syst) + # --- virtual_memory(); tests against muse @unittest.skipUnless(MUSE_AVAILABLE, "muse cmdline tool is not available") -- cgit v1.2.1 From e703a4aa3f82f1c963166cbeb371b537256c27ec Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 13 Jun 2015 08:07:08 -0700 Subject: (windows) remove inet_ntop --- psutil/_psutil_windows.c | 1 - psutil/arch/windows/inet_ntop.c | 24 ------------------------ psutil/arch/windows/inet_ntop.h | 10 ---------- setup.py | 1 - 4 files changed, 36 deletions(-) delete mode 100644 psutil/arch/windows/inet_ntop.c delete mode 100644 psutil/arch/windows/inet_ntop.h diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index f789e03d..670be24c 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -31,7 +31,6 @@ #include "arch/windows/process_info.h" #include "arch/windows/process_handles.h" #include "arch/windows/ntextapi.h" -#include "arch/windows/inet_ntop.h" #ifdef __MINGW32__ #include "arch/windows/glpi.h" diff --git a/psutil/arch/windows/inet_ntop.c b/psutil/arch/windows/inet_ntop.c deleted file mode 100644 index 39b05dfb..00000000 --- a/psutil/arch/windows/inet_ntop.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "inet_ntop.h" - -// From: https://memset.wordpress.com/2010/10/09/inet_ntop-for-win32/ -PCSTR -WSAAPI -inet_ntop( - __in INT Family, - __in PVOID pAddr, - __out_ecount(StringBufSize) PSTR pStringBuf, - __in size_t StringBufSize - ) -{ - struct sockaddr_in srcaddr; - - memset(&srcaddr, 0, sizeof(struct sockaddr_in)); - memcpy(&(srcaddr.sin_addr), pAddr, sizeof(srcaddr.sin_addr)); - - srcaddr.sin_family = Family; - if (WSAAddressToString((struct sockaddr*) &srcaddr, sizeof(struct sockaddr_in), 0, pStringBuf, (LPDWORD) &StringBufSize) != 0) { - DWORD rv = WSAGetLastError(); - return NULL; - } - return pStringBuf; -} \ No newline at end of file diff --git a/psutil/arch/windows/inet_ntop.h b/psutil/arch/windows/inet_ntop.h deleted file mode 100644 index e9b83585..00000000 --- a/psutil/arch/windows/inet_ntop.h +++ /dev/null @@ -1,10 +0,0 @@ -#include - -PCSTR -WSAAPI -inet_ntop( - __in INT Family, - __in PVOID pAddr, - __out_ecount(StringBufSize) PSTR pStringBuf, - __in size_t StringBufSize - ); \ No newline at end of file diff --git a/setup.py b/setup.py index 4c42548e..54a70c49 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,6 @@ if sys.platform.startswith("win32"): 'psutil/arch/windows/process_info.c', 'psutil/arch/windows/process_handles.c', 'psutil/arch/windows/security.c', - 'psutil/arch/windows/inet_ntop.c', ], define_macros=[ VERSION_MACRO, -- cgit v1.2.1 From 296759638981d5eba54ee0ab1b831d018e97d903 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 13 Jun 2015 21:14:24 +0200 Subject: update README --- README.rst | 1 + test/README.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index c34a63d6..f3ffeca4 100644 --- a/README.rst +++ b/README.rst @@ -333,6 +333,7 @@ http://groups.google.com/group/psutil/ Timeline ======== +- 2015-06-13: `psutil-3.0.0.tar.gz `_ - 2015-02-02: `psutil-2.2.1.tar.gz `_ - 2015-01-06: `psutil-2.2.0.tar.gz `_ - 2014-09-26: `psutil-2.1.3.tar.gz `_ diff --git a/test/README.rst b/test/README.rst index c79c69a6..3f2a468e 100644 --- a/test/README.rst +++ b/test/README.rst @@ -12,7 +12,7 @@ ``_*.py`` scripts (which should be ignored). - ``test_memory_leaks.py`` looks for memory leaks into C extension modules and must - be run separately with ``make memtest``. + be run separately with ``make test-memleaks``. - To run tests on all supported Python version install tox (pip install tox) then run ``tox``. -- cgit v1.2.1 From 661e22dfce9ee66cba41dd46159d5ac8742bf6a5 Mon Sep 17 00:00:00 2001 From: Arni Mar Jonsson Date: Tue, 16 Jun 2015 16:41:15 +0000 Subject: fixing cmdline() for empty-string argumemts on posix systems --- psutil/_pslinux.py | 2 +- test/test_psutil.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 668154bb..1e124dd3 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -765,7 +765,7 @@ class Process(object): fname = "/proc/%s/cmdline" % self.pid kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() with open(fname, "rt", **kw) as f: - return [x for x in f.read().split('\x00') if x] + return [x for x in f.read()[:-1].split('\x00')] @wrap_exceptions def terminal(self): diff --git a/test/test_psutil.py b/test/test_psutil.py index 66209ead..050197d5 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1558,11 +1558,11 @@ class TestProcess(unittest.TestCase): with open(c_file, "w") as f: f.write("void main() { pause(); }") subprocess.check_call(["gcc", c_file, "-o", funky_name]) - sproc = get_test_subprocess([funky_name, "arg1", "arg2"]) + sproc = get_test_subprocess([funky_name, "arg1", "arg2", "", "arg3", ""]) p = psutil.Process(sproc.pid) self.assertEqual(p.name(), "foo bar )") self.assertEqual(p.exe(), "/tmp/foo bar )") - self.assertEqual(p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2"]) + self.assertEqual(p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2", "", "arg3", ""]) @unittest.skipUnless(POSIX, 'posix only') def test_uids(self): -- cgit v1.2.1 From 7ae00aaca9e432147071a076b4bc4ea6e40e69a3 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 11:40:11 +0200 Subject: update dev-deps --- Makefile | 2 +- README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index b13547c8..73875b31 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ build: clean install-dev-deps: $(PYTHON) -m pip install --user --upgrade pip $(PYTHON) -m pip install --user --upgrade \ - ipaddress unittest2 mock ipdb coverage nose + ipaddress unittest2 mock ipdb coverage nose sphinx sphinx-pypi-upload install: build $(PYTHON) setup.py install --user; \ diff --git a/README.rst b/README.rst index f3ffeca4..fc65cf07 100644 --- a/README.rst +++ b/README.rst @@ -38,7 +38,7 @@ running processes**. It implements many functionalities offered by command line tools such as: ps, top, lsof, netstat, ifconfig, who, df, kill, free, nice, ionice, iostat, iotop, uptime, pidof, tty, taskset, pmap. It currently supports **Linux, Windows, OSX, FreeBSD** and **Sun Solaris**, both **32-bit** and -**64-bit** architectures, with Python versions from **2.6 to 3.4** (users of +**64-bit** architectures, with Python versions from **2.6 to 3.5** (users of Python 2.4 and 2.5 may use `2.1.3 `__ version). `PyPy `__ is also known to work. -- cgit v1.2.1 From 17325efdd1b500870f58e794b7c2e02bf3516c78 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 11:50:46 +0200 Subject: fix #632: [Linux] better error message if cannot parse process UNIX connections. --- HISTORY.rst | 10 +++++++++- psutil/_pslinux.py | 16 +++++++++++++--- test/test_psutil.py | 2 ++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 8c026524..4793d3d3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,14 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -3.0.0 - XXXX-XX-XX +3.0.1 - XXXX-XX-XX +================== + +**Bug fixes** + +- #632: [Linux] better error message if cannot parse process UNIX connections. + + +3.0.0 - 2015-06-13 ================== **Enhancements** diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 668154bb..db96776a 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -466,8 +466,13 @@ class Connections: with open(file, 'rt') as f: f.readline() # skip the first line for line in f: - _, laddr, raddr, status, _, _, _, _, _, inode = \ - line.split()[:10] + try: + _, laddr, raddr, status, _, _, _, _, _, inode = \ + line.split()[:10] + except ValueError: + raise RuntimeError( + "error while parsing %s; malformed line %r" % ( + file, line)) if inode in inodes: # # We assume inet sockets are unique, so we error # # out if there are multiple references to the @@ -495,7 +500,12 @@ class Connections: f.readline() # skip the first line for line in f: tokens = line.split() - _, _, _, _, type_, _, inode = tokens[0:7] + try: + _, _, _, _, type_, _, inode = tokens[0:7] + except ValueError: + raise RuntimeError( + "error while parsing %s; malformed line %r" % ( + file, line)) if inode in inodes: # With UNIX sockets we can have a single inode # referencing many file descriptors. diff --git a/test/test_psutil.py b/test/test_psutil.py index 66209ead..50749c46 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1560,6 +1560,8 @@ class TestProcess(unittest.TestCase): subprocess.check_call(["gcc", c_file, "-o", funky_name]) sproc = get_test_subprocess([funky_name, "arg1", "arg2"]) p = psutil.Process(sproc.pid) + # ...in order to try to prevent occasional failures on travis + wait_for_pid(p.pid) self.assertEqual(p.name(), "foo bar )") self.assertEqual(p.exe(), "/tmp/foo bar )") self.assertEqual(p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2"]) -- cgit v1.2.1 From 8a8a8fd5e0a8901bde0fedf331df8587bda59ae0 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 05:31:46 -0700 Subject: windows: add some C comments --- psutil/_psutil_windows.c | 15 ++++++++++++++- psutil/arch/windows/process_info.c | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 670be24c..7e6792db 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -2691,6 +2691,19 @@ psutil_proc_num_handles(PyObject *self, PyObject *args) } +/* + * Get various process information by using NtQuerySystemInformation. + * We use this as a fallback when faster functions fail with access + * denied. This is slower because it iterates over all processes. + * Returned tuple includes the following process info: + * + * - num_threads + * - ctx_switches + * - num_handles (fallback) + * - user/kernel times (fallback) + * - create time (fallback) + * - io counters (fallback) + */ static PyObject * psutil_proc_info(PyObject *self, PyObject *args) { @@ -3395,4 +3408,4 @@ void init_psutil_windows(void) #if PY_MAJOR_VERSION >= 3 return module; #endif -} \ No newline at end of file +} diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c index ad272bff..a59cce47 100644 --- a/psutil/arch/windows/process_info.c +++ b/psutil/arch/windows/process_info.c @@ -357,7 +357,10 @@ const int STATUS_BUFFER_TOO_SMALL = 0xC0000023L; /* * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure - * fills the structure with process information. + * fills the structure with various process information by using + * NtQuerySystemInformation. + * We use this as a fallback when faster functions fail with access + * denied. This is slower because it iterates over all processes. * On success return 1, else 0 with Python exception already set. */ int -- cgit v1.2.1 From 2c98f3bb208e1cc96d69040363b56b1a1f1f729e Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 18 Jun 2015 02:32:39 +0200 Subject: have 'make install-dev-deps' install pip --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 73875b31..bd46c498 100644 --- a/Makefile +++ b/Makefile @@ -37,9 +37,14 @@ build: clean $(PYTHON) setup.py build_ext -i install-dev-deps: + python -c "import urllib2; \ + r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \ + open('/tmp/get-pip.py', 'w').write(r.read());" + $(PYTHON) /tmp/get-pip.py --user $(PYTHON) -m pip install --user --upgrade pip $(PYTHON) -m pip install --user --upgrade \ ipaddress unittest2 mock ipdb coverage nose sphinx sphinx-pypi-upload + rm /tmp/get-pip.py install: build $(PYTHON) setup.py install --user; \ -- cgit v1.2.1 From 2006f27b6fa19a82bf2538ad90813d93708f7da2 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 18 Jun 2015 03:23:23 +0200 Subject: more dev-deps --- Makefile | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index bd46c498..f309e84f 100644 --- a/Makefile +++ b/Makefile @@ -36,15 +36,25 @@ build: clean @# this directory. $(PYTHON) setup.py build_ext -i +# useful deps which are nice to have while developing / testing install-dev-deps: python -c "import urllib2; \ r = urllib2.urlopen('https://bootstrap.pypa.io/get-pip.py'); \ open('/tmp/get-pip.py', 'w').write(r.read());" $(PYTHON) /tmp/get-pip.py --user + rm /tmp/get-pip.py $(PYTHON) -m pip install --user --upgrade pip $(PYTHON) -m pip install --user --upgrade \ - ipaddress unittest2 mock ipdb coverage nose sphinx sphinx-pypi-upload - rm /tmp/get-pip.py + coverage \ + flake8 \ + ipaddress \ + ipdb \ + mock \ + nose \ + pep8 \ + sphinx \ + sphinx-pypi-upload \ + unittest2 \ install: build $(PYTHON) setup.py install --user; \ -- cgit v1.2.1 From b2a447dbb1f886343c20263829659f29354ec123 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 18 Jun 2015 03:38:14 +0200 Subject: fix #635: [UNIX] crash on module import if 'enum' package is installed on python < 3.4 --- HISTORY.rst | 2 ++ Makefile | 6 ++---- psutil/_common.py | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 4793d3d3..bf694ca4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Bug fixes** - #632: [Linux] better error message if cannot parse process UNIX connections. +- #635: [UNIX] crash on module import if 'enum' package is installed on python + < 3.4. 3.0.0 - 2015-06-13 diff --git a/Makefile b/Makefile index f309e84f..18bc45e1 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,7 @@ install-dev-deps: mock \ nose \ pep8 \ + pyflakes \ sphinx \ sphinx-pypi-upload \ unittest2 \ @@ -80,7 +81,7 @@ test-memleaks: install test-by-name: install @$(PYTHON) -m nose test/test_psutil.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) -# same as above but for test_memory_leaks.py script +# Same as above but for test_memory_leaks.py script. test-memleaks-by-name: install @$(PYTHON) -m nose test/test_memory_leaks.py --nocapture -v -m $(filter-out $@,$(MAKECMDGOALS)) @@ -92,16 +93,13 @@ coverage: install $(PYTHON) -m coverage html $(COVERAGE_OPTS) $(PYTHON) -m webbrowser -t htmlcov/index.html -# requires "pip install pep8" pep8: @git ls-files | grep \\.py$ | xargs $(PYTHON) -m pep8 -# requires "pip install pyflakes" pyflakes: @export PYFLAKES_NODOCTEST=1 && \ git ls-files | grep \\.py$ | xargs $(PYTHON) -m pyflakes -# requires "pip install flake8" flake8: @git ls-files | grep \\.py$ | xargs $(PYTHON) -m flake8 diff --git a/psutil/_common.py b/psutil/_common.py index 132d9d59..e9acf595 100644 --- a/psutil/_common.py +++ b/psutil/_common.py @@ -12,17 +12,19 @@ import functools import os import socket import stat +import sys +from collections import namedtuple +from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM try: import threading except ImportError: import dummy_threading as threading -try: - import enum # py >= 3.4 -except ImportError: + +if sys.version_info >= (3, 4): + import enum +else: enum = None -from collections import namedtuple -from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM # --- constants -- cgit v1.2.1 From 6a7d1db1e6dcbef4d172f94d16c6e4903e1fb3d7 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 18 Jun 2015 03:48:03 +0200 Subject: PEP8 fixes + update HISTORY/CREDITS --- CREDITS | 5 +++++ HISTORY.rst | 1 + Makefile | 8 +++++--- test/test_psutil.py | 6 ++++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CREDITS b/CREDITS index 432e744d..37c653f2 100644 --- a/CREDITS +++ b/CREDITS @@ -303,3 +303,8 @@ N: John Burnett W: http://www.johnburnett.com/ C: Irvine, CA, US I: 614 + +N: Árni Már Jónsson +E: Reykjavik, Iceland +E: https://github.com/arnimarj +I: 634 diff --git a/HISTORY.rst b/HISTORY.rst index bf694ca4..59036f72 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Bug fixes** - #632: [Linux] better error message if cannot parse process UNIX connections. +- #634: [Linux] Proces.cmdline() does not include empty string arguments. - #635: [UNIX] crash on module import if 'enum' package is installed on python < 3.4. diff --git a/Makefile b/Makefile index 18bc45e1..2c9817a9 100644 --- a/Makefile +++ b/Makefile @@ -45,17 +45,19 @@ install-dev-deps: rm /tmp/get-pip.py $(PYTHON) -m pip install --user --upgrade pip $(PYTHON) -m pip install --user --upgrade \ + # mandatory for unittests + ipaddress \ + mock \ + unittest2 \ + # nice to have coverage \ flake8 \ - ipaddress \ ipdb \ - mock \ nose \ pep8 \ pyflakes \ sphinx \ sphinx-pypi-upload \ - unittest2 \ install: build $(PYTHON) setup.py install --user; \ diff --git a/test/test_psutil.py b/test/test_psutil.py index 14b5187e..b93cd863 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1558,13 +1558,15 @@ class TestProcess(unittest.TestCase): with open(c_file, "w") as f: f.write("void main() { pause(); }") subprocess.check_call(["gcc", c_file, "-o", funky_name]) - sproc = get_test_subprocess([funky_name, "arg1", "arg2", "", "arg3", ""]) + sproc = get_test_subprocess( + [funky_name, "arg1", "arg2", "", "arg3", ""]) p = psutil.Process(sproc.pid) # ...in order to try to prevent occasional failures on travis wait_for_pid(p.pid) self.assertEqual(p.name(), "foo bar )") self.assertEqual(p.exe(), "/tmp/foo bar )") - self.assertEqual(p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2", "", "arg3", ""]) + self.assertEqual( + p.cmdline(), ["/tmp/foo bar )", "arg1", "arg2", "", "arg3", ""]) @unittest.skipUnless(POSIX, 'posix only') def test_uids(self): -- cgit v1.2.1 From b7cc5955fa6c8a1d9a408a4e517dfb321bfb5e2f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 18 Jun 2015 04:33:33 +0200 Subject: bump up release --- HISTORY.rst | 2 +- README.rst | 1 + psutil/__init__.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 59036f72..4a277c08 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,6 @@ Bug tracker at https://github.com/giampaolo/psutil/issues -3.0.1 - XXXX-XX-XX +3.0.1 - 2015-06-18 ================== **Bug fixes** diff --git a/README.rst b/README.rst index fc65cf07..f2f68c7c 100644 --- a/README.rst +++ b/README.rst @@ -333,6 +333,7 @@ http://groups.google.com/group/psutil/ Timeline ======== +- 2015-06-18: `psutil-3.0.1.tar.gz `_ - 2015-06-13: `psutil-3.0.0.tar.gz `_ - 2015-02-02: `psutil-2.2.1.tar.gz `_ - 2015-01-06: `psutil-2.2.0.tar.gz `_ diff --git a/psutil/__init__.py b/psutil/__init__.py index 9e8cb740..700edd93 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -158,7 +158,7 @@ __all__ = [ ] __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "3.0.0" +__version__ = "3.0.1" version_info = tuple([int(num) for num in __version__.split('.')]) AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None -- cgit v1.2.1 From c301387ceed192574cb7f968bbcf5c43a03cd702 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 19:40:24 -0700 Subject: make.bat improvements --- make.bat | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/make.bat b/make.bat index d7c1091b..2daffe39 100644 --- a/make.bat +++ b/make.bat @@ -26,8 +26,16 @@ if "%TSCRIPT%" == "" ( set TSCRIPT=test\test_psutil.py ) -rem Needed to compile using Mingw. -set PATH=C:\MinGW\bin;%PATH% +set PYTHON26=C:\Python26\python.exe +set PYTHON27=C:\Python27\python.exe +set PYTHON33=C:\Python33\python.exe +set PYTHON34=C:\Python34\python.exe +set PYTHON26-64=C:\Python26-64\python.exe +set PYTHON27-64=C:\Python27-64\python.exe +set PYTHON33-64=C:\Python33-64\python.exe +set PYTHON34-64=C:\Python34-64\python.exe + +set ALL_PYTHONS=%PYTHON26% %PYTHON27% %PYTHON33% %PYTHON34% %PYTHON26-64% %PYTHON27-64% %PYTHON33-64% %PYTHON34-64% rem Needed to locate the .pypirc file and upload exes on PYPI. set HOME=%USERPROFILE% @@ -73,7 +81,7 @@ if "%1" == "build" ( %PYTHON% setup.py build if %errorlevel% neq 0 goto :error rem copies *.pyd files in ./psutil directory in order to allow - rem "import psutil" when using the interactive interpreter from + rem "import psutil" when using the interactive interpreter from rem within this directory. %PYTHON% setup.py build_ext -i if %errorlevel% neq 0 goto :error @@ -123,42 +131,34 @@ if "%1" == "test-memleaks" ( if "%1" == "build-exes" ( :build-exes - rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+) - C:\Python26\python.exe setup.py build bdist_wininst || goto :error - C:\Python27\python.exe setup.py build bdist_wininst || goto :error - C:\Python33\python.exe setup.py build bdist_wininst || goto :error - C:\Python34\python.exe setup.py build bdist_wininst || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: + rem 64 bit versions. + rem Python 2.7 64 bit + VS 2008 requires vcvars64.bat to be run first: rem http://stackoverflow.com/questions/11072521/ rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wininst || goto :error - C:\Python33-64\python.exe setup.py build bdist_wininst || goto :error - C:\Python34-64\python.exe setup.py build bdist_wininst || goto :error + for %%P in (%ALL_PYTHONS%) do ( + %%P setup.py build bdist_wininst || goto :error + ) echo OK goto :eof ) if "%1" == "build-wheels" ( :build-wheels - C:\Python26\python.exe setup.py build bdist_wheel || goto :error - C:\Python27\python.exe setup.py build bdist_wheel || goto :error - C:\Python33\python.exe setup.py build bdist_wheel || goto :error - C:\Python34\python.exe setup.py build bdist_wheel || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: + rem 64 bit versions. + rem Python 2.7 64 bit + VS 2008 requires vcvars64.bat to be run first: rem http://stackoverflow.com/questions/11072521/ rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wheel || goto :error - C:\Python33-64\python.exe setup.py build bdist_wheel || goto :error - C:\Python34-64\python.exe setup.py build bdist_wheel || goto :error + for %%P in (%ALL_PYTHONS%) do ( + %%P setup.py build bdist_wheel || goto :error + ) echo OK goto :eof ) if "%1" == "build-all" ( + :build-all rem for some reason this needs to be called twice (f**king windows...) call :build-exes call :build-exes -- cgit v1.2.1 From fcfd37231030f33811996fa5ea44c8b7acfc86b8 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 20:17:53 -0700 Subject: make.bat refactoring --- make.bat | 73 ++++++------------------------------------------------ psutil/__init__.py | 2 +- 2 files changed, 9 insertions(+), 66 deletions(-) diff --git a/make.bat b/make.bat index 2daffe39..35587fe4 100644 --- a/make.bat +++ b/make.bat @@ -46,8 +46,6 @@ if "%1" == "help" ( :help echo Run `make ^` where ^ is one of: echo build compile without installing - echo build-exes create exe installers in dist directory - echo build-wheels create wheel installers in dist directory echo build-all build exes + wheels echo clean clean build files echo install compile and install @@ -57,8 +55,6 @@ if "%1" == "help" ( echo test-process run process related tests echo test-system run system APIs related tests echo uninstall uninstall - echo upload-exes upload exe installers on pypi - echo upload-wheels upload wheel installers on pypi echo upload-all upload exes + wheels goto :eof ) @@ -78,6 +74,7 @@ if "%1" == "clean" ( if "%1" == "build" ( :build + "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" %PYTHON% setup.py build if %errorlevel% neq 0 goto :error rem copies *.pyd files in ./psutil directory in order to allow @@ -129,82 +126,28 @@ if "%1" == "test-memleaks" ( goto :eof ) -if "%1" == "build-exes" ( - :build-exes - rem 64 bit versions. - rem Python 2.7 64 bit + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) +if "%1" == "build-all" ( + :build-all "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" for %%P in (%ALL_PYTHONS%) do ( %%P setup.py build bdist_wininst || goto :error - ) - echo OK - goto :eof -) - -if "%1" == "build-wheels" ( - :build-wheels - rem 64 bit versions. - rem Python 2.7 64 bit + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) - "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - for %%P in (%ALL_PYTHONS%) do ( %%P setup.py build bdist_wheel || goto :error ) echo OK goto :eof ) -if "%1" == "build-all" ( - :build-all - rem for some reason this needs to be called twice (f**king windows...) - call :build-exes - call :build-exes - echo OK - goto :eof -) - -if "%1" == "upload-exes" ( +if "%1" == "upload-all" ( :upload-exes - rem "standard" 32 bit versions, using VS 2008 (2.6, 2.7) or VS 2010 (3.3+) - C:\Python26\python.exe setup.py bdist_wininst upload || goto :error - C:\Python27\python.exe setup.py bdist_wininst upload || goto :error - C:\Python33\python.exe setup.py bdist_wininst upload || goto :error - C:\Python34\python.exe setup.py bdist_wininst upload || goto :error - rem 64 bit versions - C:\Python27-64\python.exe setup.py build bdist_wininst upload || goto :error - C:\Python33-64\python.exe setup.py build bdist_wininst upload || goto :error - C:\Python34-64\python.exe setup.py build bdist_wininst upload || goto :error - echo OK - goto :eof -) - -if "%1" == "upload-wheels" ( - :build-wheels - C:\Python26\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python27\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python33\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python34\python.exe setup.py build bdist_wheel upload || goto :error - rem 64 bit versions - rem Python 2.7 + VS 2008 requires vcvars64.bat to be run first: - rem http://stackoverflow.com/questions/11072521/ - rem Windows SDK and .NET Framework 3.5 SP1 also need to be installed (sigh) "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" - C:\Python27-64\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python33-64\python.exe setup.py build bdist_wheel upload || goto :error - C:\Python34-64\python.exe setup.py build bdist_wheel upload || goto :error + for %%P in (%ALL_PYTHONS%) do ( + %%P setup.py build bdist_wininst upload || goto :error + %%P setup.py build bdist_wheel upload || goto :error + ) echo OK goto :eof ) -if "%1" == "upload-all" ( - call :upload-exes - call :upload-wheels - echo OK - goto :eof -) if "%1" == "setup-env" ( echo downloading pip installer diff --git a/psutil/__init__.py b/psutil/__init__.py index 700edd93..64a8d691 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -158,7 +158,7 @@ __all__ = [ ] __all__.extend(_psplatform.__extra__all__) __author__ = "Giampaolo Rodola'" -__version__ = "3.0.1" +__version__ = "3.0.2" version_info = tuple([int(num) for num in __version__.split('.')]) AF_LINK = _psplatform.AF_LINK _TOTAL_PHYMEM = None -- cgit v1.2.1 From 34b41805a9060a16824fa2eb9beb91105e117aee Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 20:41:23 -0700 Subject: make.bat refactoring --- make.bat | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/make.bat b/make.bat index 35587fe4..d2f953ea 100644 --- a/make.bat +++ b/make.bat @@ -130,7 +130,13 @@ if "%1" == "build-all" ( :build-all "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo building exe for %%P + @echo ------------------------------------------------ %%P setup.py build bdist_wininst || goto :error + @echo ------------------------------------------------ + @echo building wheel for %%P + @echo ------------------------------------------------ %%P setup.py build bdist_wheel || goto :error ) echo OK @@ -141,7 +147,13 @@ if "%1" == "upload-all" ( :upload-exes "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\vcvars64.bat" for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo uploading exe for %%P + @echo ------------------------------------------------ %%P setup.py build bdist_wininst upload || goto :error + @echo ------------------------------------------------ + @echo uploading wheel for %%P + @echo ------------------------------------------------ %%P setup.py build bdist_wheel upload || goto :error ) echo OK @@ -150,27 +162,30 @@ if "%1" == "upload-all" ( if "%1" == "setup-env" ( - echo downloading pip installer + @echo ------------------------------------------------ + @echo downloading pip installer + @echo ------------------------------------------------ C:\python27\python.exe -c "import urllib2; url = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); data = url.read(); f = open('get-pip.py', 'w'); f.write(data)" - C:\python26\python.exe get-pip.py & C:\python26\scripts\pip install unittest2 wheel ipaddress --upgrade - C:\python27\python.exe get-pip.py & C:\python27\scripts\pip install wheel ipaddress --upgrade - C:\python33\python.exe get-pip.py & C:\python33\scripts\pip install wheel ipaddress --upgrade - C:\python34\scripts\easy_install.exe wheel - rem 64-bit versions - C:\python27-64\python.exe get-pip.py & C:\python27-64\scripts\pip install wheel ipaddress --upgrade - C:\python33-64\python.exe get-pip.py & C:\python33-64\scripts\pip install wheel ipaddress --upgrade - C:\python34-64\scripts\easy_install.exe wheel - rem install ipdb only for py 2.7 and 3.4 - C:\python27\scripts\pip install ipdb --upgrade - C:\python34\scripts\easy_install.exe ipdb + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo installing pip for %%P + @echo ------------------------------------------------ + %%P get-pip.py + ) + for %%P in (%ALL_PYTHONS%) do ( + @echo ------------------------------------------------ + @echo building deps for %%P + @echo ------------------------------------------------ + %%P -m pip install unittest2 wheel ipaddress wmi --upgrade + ) goto :eof ) goto :help :error - echo ------------------------------------------------ - echo last command exited with error code %errorlevel% - echo ------------------------------------------------ - exit /b %errorlevel% + @echo ------------------------------------------------ + @echo last command exited with error code %errorlevel% + @echo ------------------------------------------------ + @exit /b %errorlevel% goto :eof -- cgit v1.2.1 From 069b8f2ee39708f7dc43e70efa33617b106a5f2d Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Wed, 17 Jun 2015 21:24:22 -0700 Subject: make.bat flake8 --- make.bat | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/make.bat b/make.bat index d2f953ea..f860cdfb 100644 --- a/make.bat +++ b/make.bat @@ -48,6 +48,7 @@ if "%1" == "help" ( echo build compile without installing echo build-all build exes + wheels echo clean clean build files + echo flake8 run flake8 echo install compile and install echo setup-env install pip, unittest2, wheels for all python versions echo test run tests @@ -160,12 +161,12 @@ if "%1" == "upload-all" ( goto :eof ) - if "%1" == "setup-env" ( + :setup-env @echo ------------------------------------------------ @echo downloading pip installer @echo ------------------------------------------------ - C:\python27\python.exe -c "import urllib2; url = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); data = url.read(); f = open('get-pip.py', 'w'); f.write(data)" + C:\python27\python.exe -c "import urllib2; r = urllib2.urlopen('https://raw.github.com/pypa/pip/master/contrib/get-pip.py'); open('get-pip.py', 'wb').write(r.read())" for %%P in (%ALL_PYTHONS%) do ( @echo ------------------------------------------------ @echo installing pip for %%P @@ -174,13 +175,22 @@ if "%1" == "setup-env" ( ) for %%P in (%ALL_PYTHONS%) do ( @echo ------------------------------------------------ - @echo building deps for %%P + @echo installing deps for %%P @echo ------------------------------------------------ - %%P -m pip install unittest2 wheel ipaddress wmi --upgrade + rem mandatory / for unittests + %%P -m pip install unittest2 ipaddress mock wmi wheel --upgrade + rem nice to have + %%P -m pip install ipdb pep8 pyflakes flake8 --upgrade ) goto :eof ) +if "%1" == "flake8" ( + :flake8 + %PYTHON% -c "from flake8.main import main; main()" + goto :eof +) + goto :help :error -- cgit v1.2.1 From ed660ec802f92c33a9a950ddf6945c162a803e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Jelen?= Date: Fri, 19 Jun 2015 23:18:54 +0200 Subject: small typo --- INSTALL.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/INSTALL.rst b/INSTALL.rst index f402b924..e518c430 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -56,7 +56,7 @@ Installing on Linux =================== gcc is required and so the python headers. They can easily be installed by -using the distro package manager. For example, on Debian amd Ubuntu:: +using the distro package manager. For example, on Debian and Ubuntu:: $ sudo apt-get install gcc python-dev -- cgit v1.2.1 From 48f898afa584ac9eed209b63e46a93f1876278f4 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 02:25:11 +0200 Subject: improve linux test coverage --- psutil/_pslinux.py | 17 ++++++++++------- test/_linux.py | 14 ++++++++++++++ test/test_psutil.py | 2 ++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index a0a4d350..3f1ff721 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -329,7 +329,7 @@ def boot_time(): ret = float(line.strip().split()[1]) BOOT_TIME = ret return ret - raise RuntimeError("line 'btime' not found") + raise RuntimeError("line 'btime' not found in /proc/stat") # --- processes @@ -1148,27 +1148,30 @@ class Process(object): @wrap_exceptions def ppid(self): - with open("/proc/%s/status" % self.pid, 'rb') as f: + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: if line.startswith(b"PPid:"): # PPid: nnnn return int(line.split()[1]) - raise NotImplementedError("line not found") + raise NotImplementedError("line 'PPid' not found in %s" % fpath) @wrap_exceptions def uids(self): - with open("/proc/%s/status" % self.pid, 'rb') as f: + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: if line.startswith(b'Uid:'): _, real, effective, saved, fs = line.split() return _common.puids(int(real), int(effective), int(saved)) - raise NotImplementedError("line not found") + raise NotImplementedError("line 'Uid' not found in %s" % fpath) @wrap_exceptions def gids(self): - with open("/proc/%s/status" % self.pid, 'rb') as f: + fpath = "/proc/%s/status" % self.pid + with open(fpath, 'rb') as f: for line in f: if line.startswith(b'Gid:'): _, real, effective, saved, fs = line.split() return _common.pgids(int(real), int(effective), int(saved)) - raise NotImplementedError("line not found") + raise NotImplementedError("line 'Gid' not found in %s" % fpath) diff --git a/test/_linux.py b/test/_linux.py index 5d7f0521..57e37bd2 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -321,6 +321,20 @@ class LinuxSpecificTestCase(unittest.TestCase): psutil._pslinux.Process(os.getpid()).gids) assert m.called + def test_proc_io_counters_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + NotImplementedError, + psutil._pslinux.Process(os.getpid()).io_counters) + assert m.called + + def test_boot_time_mocked(self): + with mock.patch('psutil._pslinux.open', create=True) as m: + self.assertRaises( + RuntimeError, + psutil._pslinux.boot_time) + assert m.called + # --- tests for specific kernel versions @unittest.skipUnless( diff --git a/test/test_psutil.py b/test/test_psutil.py index b93cd863..842fff78 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -675,6 +675,8 @@ class TestSystemAPIs(unittest.TestCase): self.assertFalse(psutil.pid_exists(sproc.pid)) self.assertFalse(psutil.pid_exists(-1)) self.assertEqual(psutil.pid_exists(0), 0 in psutil.pids()) + # pid 0 + psutil.pid_exists(0) == 0 in psutil.pids() def test_pid_exists_2(self): reap_children() -- cgit v1.2.1 From 3163e7ec7df88b6fdfd9b1fcdec29aa0fef695e8 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 03:03:38 +0200 Subject: (linux) proc io nice: provide better error messages --- psutil/_pslinux.py | 28 +++++++++++++++++++--------- test/test_psutil.py | 10 ++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 3f1ff721..f3b5b080 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1044,25 +1044,35 @@ class Process(object): @wrap_exceptions def ionice_set(self, ioclass, value): + if value is not None: + if not PY3 and not isinstance(value, (int, long)): + msg = "value argument is not an integer (gor %r)" % value + raise TypeError(msg) + if not 0 <= value <= 8: + raise ValueError( + "value argument range expected is between 0 and 8") + if ioclass in (IOPRIO_CLASS_NONE, None): if value: - msg = "can't specify value with IOPRIO_CLASS_NONE" + msg = "can't specify value with IOPRIO_CLASS_NONE " \ + "(got %r)" % value raise ValueError(msg) ioclass = IOPRIO_CLASS_NONE value = 0 - if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): - if value is None: - value = 4 elif ioclass == IOPRIO_CLASS_IDLE: if value: - msg = "can't specify value with IOPRIO_CLASS_IDLE" + msg = "can't specify value with IOPRIO_CLASS_IDLE " \ + "(got %r)" % value raise ValueError(msg) value = 0 + elif ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): + if value is None: + # TODO: add comment explaining why this is 4 (?) + value = 4 else: - value = 0 - if not 0 <= value <= 8: - raise ValueError( - "value argument range expected is between 0 and 8") + # otherwise we would get OSError(EVINAL) + raise ValueError("invalid ioclass argument %r" % ioclass) + return cext.proc_ioprio_set(self.pid, ioclass, value) if HAS_PRLIMIT: diff --git a/test/test_psutil.py b/test/test_psutil.py index 842fff78..bc750fdc 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1356,7 +1356,17 @@ class TestProcess(unittest.TestCase): ioclass, value = p.ionice() self.assertEqual(ioclass, 2) self.assertEqual(value, 7) + # self.assertRaises(ValueError, p.ionice, 2, 10) + self.assertRaises(ValueError, p.ionice, 2, -1) + self.assertRaises(ValueError, p.ionice, 4) + self.assertRaises(TypeError, p.ionice, 2, "foo") + self.assertRaisesRegexp( + ValueError, "can't specify value with IOPRIO_CLASS_NONE", + p.ionice, psutil.IOPRIO_CLASS_NONE, 1) + self.assertRaisesRegexp( + ValueError, "can't specify value with IOPRIO_CLASS_IDLE", + p.ionice, psutil.IOPRIO_CLASS_IDLE, 1) finally: p.ionice(IOPRIO_CLASS_NONE) else: -- cgit v1.2.1 From 4bd1596035b2d0b1beb795b98dce662054639d48 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 03:15:10 +0200 Subject: improve test coverage --- psutil/_pslinux.py | 8 +++++--- test/test_psutil.py | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index f3b5b080..6c84faf2 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1078,8 +1078,9 @@ class Process(object): if HAS_PRLIMIT: @wrap_exceptions def rlimit(self, resource, limits=None): - # if pid is 0 prlimit() applies to the calling process and - # we don't want that + # If pid is 0 prlimit() applies to the calling process and + # we don't want that. We should never get here though as + # PID 0 is not supported on Linux. if self.pid == 0: raise ValueError("can't use prlimit() against PID 0 process") try: @@ -1090,7 +1091,8 @@ class Process(object): # set if len(limits) != 2: raise ValueError( - "second argument must be a (soft, hard) tuple") + "second argument must be a (soft, hard) tuple, " + "got %s" % repr(limits)) soft, hard = limits cext.linux_prlimit(self.pid, resource, soft, hard) except OSError as err: diff --git a/test/test_psutil.py b/test/test_psutil.py index bc750fdc..0db9beb2 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1411,6 +1411,12 @@ class TestProcess(unittest.TestCase): p = psutil.Process(sproc.pid) p.rlimit(psutil.RLIMIT_NOFILE, (5, 5)) self.assertEqual(p.rlimit(psutil.RLIMIT_NOFILE), (5, 5)) + # If pid is 0 prlimit() applies to the calling process and + # we don't want that. + with self.assertRaises(ValueError): + psutil._psplatform.Process(0).rlimit(0) + with self.assertRaises(ValueError): + p.rlimit(psutil.RLIMIT_NOFILE, (5, 5, 5)) def test_num_threads(self): # on certain platforms such as Linux we might test for exact -- cgit v1.2.1 From 2214f431217ffbd6af1d76eadd100df64ccdaffb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 03:29:58 +0200 Subject: test we are able to instantiate Process() in case of AD and zombie process --- test/test_psutil.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/test_psutil.py b/test/test_psutil.py index 0db9beb2..8cda3691 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -46,6 +46,10 @@ try: import ipaddress # python >= 3.3 except ImportError: ipaddress = None +try: + from unittest import mock # py3 +except ImportError: + import mock # requires "pip install mock" import psutil from psutil._compat import PY3, callable, long, unicode @@ -2685,6 +2689,22 @@ class TestMisc(unittest.TestCase): module = imp.load_source('setup', setup_py) self.assertRaises(SystemExit, module.setup) + def test_ad_on_process_creation(self): + # We are supposed to be able to instantiate Process also in case + # of zombie processes or access denied. + with mock.patch.object(psutil.Process, 'create_time', + side_effect=psutil.AccessDenied) as meth: + psutil.Process() + assert meth.called + with mock.patch.object(psutil.Process, 'create_time', + side_effect=psutil.ZombieProcess(1)) as meth: + psutil.Process() + assert meth.called + with mock.patch.object(psutil.Process, 'create_time', + side_effect=ValueError) as meth: + with self.assertRaises(ValueError): + psutil.Process() + # =================================================================== # --- Example script tests -- cgit v1.2.1 From 84b2e3b4fc762ab77eebb381ed311b885373d912 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 03:34:35 +0200 Subject: improve tests for Process.__str__ --- test/test_psutil.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/test_psutil.py b/test/test_psutil.py index 8cda3691..a7e1f958 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2593,6 +2593,16 @@ class TestMisc(unittest.TestCase): p.wait() self.assertIn(str(sproc.pid), str(p)) self.assertIn("terminated", str(p)) + # test error conditions + with mock.patch.object(psutil.Process, 'name', + side_effect=psutil.ZombieProcess(1)) as meth: + self.assertIn("zombie", str(p)) + self.assertIn("pid", str(p)) + assert meth.called + with mock.patch.object(psutil.Process, 'name', + side_effect=psutil.AccessDenied) as meth: + self.assertIn("pid", str(p)) + assert meth.called def test__eq__(self): p1 = psutil.Process() @@ -2695,15 +2705,16 @@ class TestMisc(unittest.TestCase): with mock.patch.object(psutil.Process, 'create_time', side_effect=psutil.AccessDenied) as meth: psutil.Process() - assert meth.called + assert meth.called with mock.patch.object(psutil.Process, 'create_time', side_effect=psutil.ZombieProcess(1)) as meth: psutil.Process() - assert meth.called + assert meth.called with mock.patch.object(psutil.Process, 'create_time', side_effect=ValueError) as meth: with self.assertRaises(ValueError): psutil.Process() + assert meth.called # =================================================================== -- cgit v1.2.1 From b6e452b51f661babf4889b9d4b9e3537d273799f Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 14:06:32 +0200 Subject: improve test coverage for open_files on linux --- docs/index.rst | 5 ++--- test/_linux.py | 24 +++++++++++++++++++++++- test/test_psutil.py | 4 ++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9d527ebb..51cc6c7b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -598,9 +598,8 @@ Exceptions method called the OS may be able to succeed in retrieving the process information or not. Note: this is a subclass of :class:`NoSuchProcess` so if you're not - interested in retrieving zombies while iterating over all processes (e.g. - via :func:`process_iter()`) you can ignore this exception and just catch - :class:`NoSuchProcess`. + interested in retrieving zombies (e.g. when using :func:`process_iter()`) + you can ignore this exception and just catch :class:`NoSuchProcess`. *New in 3.0.0* diff --git a/test/_linux.py b/test/_linux.py index 57e37bd2..493c1491 100644 --- a/test/_linux.py +++ b/test/_linux.py @@ -8,6 +8,7 @@ from __future__ import division import contextlib +import errno import fcntl import os import pprint @@ -15,6 +16,7 @@ import re import socket import struct import sys +import tempfile import time import warnings @@ -26,7 +28,7 @@ except ImportError: from test_psutil import POSIX, TOLERANCE, TRAVIS, LINUX from test_psutil import (skip_on_not_implemented, sh, get_test_subprocess, retry_before_failing, get_kernel_version, unittest, - which) + which, call_until) import psutil import psutil._pslinux @@ -280,6 +282,26 @@ class LinuxSpecificTestCase(unittest.TestCase): self.assertIsNone(psutil._pslinux.cpu_count_physical()) assert m.called + def test_proc_open_files_file_gone(self): + # simulates a file which gets deleted during open_files() + # execution + p = psutil.Process() + files = p.open_files() + with tempfile.NamedTemporaryFile(): + # give the kernel some time to see the new file + call_until(p.open_files, "len(ret) != %i" % len(files)) + with mock.patch('psutil._pslinux.os.readlink', + side_effect=OSError(errno.ENOENT, "")) as m: + files = p.open_files() + assert not files + assert m.called + # also simulate the case where os.readlink() returns EINVAL + # in which case psutil is supposed to 'continue' + with mock.patch('psutil._pslinux.os.readlink', + side_effect=OSError(errno.EINVAL, "")) as m: + self.assertEqual(p.open_files(), []) + assert m.called + def test_proc_terminal_mocked(self): with mock.patch('psutil._pslinux._psposix._get_terminal_map', return_value={}) as m: diff --git a/test/test_psutil.py b/test/test_psutil.py index a7e1f958..7609d35d 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1725,6 +1725,7 @@ class TestProcess(unittest.TestCase): files = p.open_files() self.assertFalse(TESTFN in files) with open(TESTFN, 'w'): + # give the kernel some time to see the new file call_until(p.open_files, "len(ret) != %i" % len(files)) filenames = [x.path for x in p.open_files()] self.assertIn(TESTFN, filenames) @@ -2604,6 +2605,9 @@ class TestMisc(unittest.TestCase): self.assertIn("pid", str(p)) assert meth.called + def test__repr__(self): + repr(psutil.Process()) + def test__eq__(self): p1 = psutil.Process() p2 = psutil.Process() -- cgit v1.2.1 From 9ad8e1416dfc579de0fec640dd3ea015ebb3e7cb Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 14:39:42 +0200 Subject: (linux / net connections) do not swallow all OSError exceptions --- HISTORY.rst | 3 ++- psutil/_pslinux.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 4a277c08..9b86c6e8 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -9,7 +9,8 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #634: [Linux] Proces.cmdline() does not include empty string arguments. - #635: [UNIX] crash on module import if 'enum' package is installed on python < 3.4. - +- #636: [Linux] *connections functions may swallow errors and return an + incomplete list of connnections. 3.0.0 - 2015-06-13 ================== diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 6c84faf2..3f2d802c 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -383,9 +383,17 @@ class Connections: for fd in os.listdir("/proc/%s/fd" % pid): try: inode = os.readlink("/proc/%s/fd/%s" % (pid, fd)) - except OSError: - # TODO: need comment here - continue + except OSError as err: + # ENOENT == file which is gone in the meantime; + # os.stat('/proc/%s' % self.pid) will be done later + # to force NSP (if it's the case) + if err.errno in (errno.ENOENT, errno.ESRCH): + continue + elif err.errno == errno.EINVAL: + # not a link + continue + else: + raise else: if inode.startswith('socket:['): # the process is using a socket -- cgit v1.2.1 From 5cd6fcac01e442b5022d223e3e366dfae456ad64 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 14:41:48 +0200 Subject: exceptions unification --- psutil/_pslinux.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 3f2d802c..4568216d 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -752,7 +752,7 @@ class Process(object): def exe(self): try: exe = os.readlink("/proc/%s/exe" % self.pid) - except (OSError, IOError) as err: + except OSError as err: if err.errno in (errno.ENOENT, errno.ESRCH): # no such file error; might be raised also if the # path actually exists for system processes with @@ -991,7 +991,7 @@ class Process(object): try: with open(fname, 'rb') as f: st = f.read().strip() - except EnvironmentError as err: + except IOError as err: if err.errno == errno.ENOENT: # no such file or directory; it means thread # disappeared on us -- cgit v1.2.1 From fcf5ca3e938045c72d26495353446eabea70d4e3 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 15:07:46 +0200 Subject: enhance test coverage --- psutil/__init__.py | 10 +--------- test/test_psutil.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/psutil/__init__.py b/psutil/__init__.py index 64a8d691..16314476 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -681,7 +681,7 @@ class Process(object): """ if ioclass is None: if value is not None: - raise ValueError("'ioclass' must be specified") + raise ValueError("'ioclass' argument must be specified") return self._proc.ionice_get() else: return self._proc.ionice_set(ioclass, value) @@ -1853,14 +1853,6 @@ def test(): time.localtime(sum(pinfo['cpu_times']))) try: user = p.username() - except KeyError: - if _POSIX: - if pinfo['uids']: - user = str(pinfo['uids'].real) - else: - user = '' - else: - raise except Error: user = '' if _WINDOWS and '\\' in user: diff --git a/test/test_psutil.py b/test/test_psutil.py index 7609d35d..b6dc1348 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -584,6 +584,7 @@ class TestSystemAPIs(unittest.TestCase): sproc3 = get_test_subprocess() procs = [psutil.Process(x.pid) for x in (sproc1, sproc2, sproc3)] self.assertRaises(ValueError, psutil.wait_procs, procs, timeout=-1) + self.assertRaises(TypeError, psutil.wait_procs, procs, callback=1) t = time.time() gone, alive = psutil.wait_procs(procs, timeout=0.01, callback=callback) @@ -1371,6 +1372,9 @@ class TestProcess(unittest.TestCase): self.assertRaisesRegexp( ValueError, "can't specify value with IOPRIO_CLASS_IDLE", p.ionice, psutil.IOPRIO_CLASS_IDLE, 1) + self.assertRaisesRegexp( + ValueError, "'ioclass' argument must be specified", + p.ionice, value=1) finally: p.ionice(IOPRIO_CLASS_NONE) else: @@ -1661,6 +1665,11 @@ class TestProcess(unittest.TestCase): if POSIX: import pwd self.assertEqual(p.username(), pwd.getpwuid(os.getuid()).pw_name) + with mock.patch("psutil.pwd.getpwuid", + side_effect=KeyError) as fun: + p.username() == str(p.uids().real) + assert fun.called + elif WINDOWS and 'USERNAME' in os.environ: expected_username = os.environ['USERNAME'] expected_domain = os.environ['USERDOMAIN'] @@ -2247,6 +2256,7 @@ class TestProcess(unittest.TestCase): proc.stdin self.assertTrue(hasattr(proc, 'name')) self.assertTrue(hasattr(proc, 'stdin')) + self.assertTrue(dir(proc)) self.assertRaises(AttributeError, getattr, proc, 'foo') finally: proc.kill() -- cgit v1.2.1 From 0d6368d61c24402b567134aff1e9b067aed8bb07 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 15:16:49 +0200 Subject: enhance test coverage --- test/test_psutil.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/test/test_psutil.py b/test/test_psutil.py index b6dc1348..a7213f4f 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -1136,13 +1136,30 @@ class TestProcess(unittest.TestCase): def test_send_signal(self): sig = signal.SIGKILL if POSIX else signal.SIGTERM sproc = get_test_subprocess() - test_pid = sproc.pid - p = psutil.Process(test_pid) + p = psutil.Process(sproc.pid) p.send_signal(sig) exit_sig = p.wait() - self.assertFalse(psutil.pid_exists(test_pid)) + self.assertFalse(psutil.pid_exists(p.pid)) if POSIX: self.assertEqual(exit_sig, sig) + # + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.send_signal(sig) + with mock.patch('psutil.os.kill', + side_effect=OSError(errno.ESRCH, "")) as fun: + with self.assertRaises(psutil.NoSuchProcess): + p.send_signal(sig) + assert fun.called + # + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.send_signal(sig) + with mock.patch('psutil.os.kill', + side_effect=OSError(errno.EPERM, "")) as fun: + with self.assertRaises(psutil.AccessDenied): + p.send_signal(sig) + assert fun.called def test_wait(self): # check exit code signal -- cgit v1.2.1 From 5ae30c7989e30702b457c2cae69e6b7e4a8b96ed Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sat, 20 Jun 2015 15:26:09 +0200 Subject: UNIX: prevent sending signals to PID 0 --- HISTORY.rst | 3 +++ psutil/__init__.py | 10 ++++++---- test/test_psutil.py | 4 ++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 9b86c6e8..18aa047c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -11,6 +11,9 @@ Bug tracker at https://github.com/giampaolo/psutil/issues < 3.4. - #636: [Linux] *connections functions may swallow errors and return an incomplete list of connnections. +- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it + will affect os.getpid()'s process group instead of PID 0. + 3.0.0 - 2015-06-13 ================== diff --git a/psutil/__init__.py b/psutil/__init__.py index 16314476..7d47a14f 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1007,10 +1007,12 @@ class Process(object): if _POSIX: def _send_signal(self, sig): - # XXX: according to "man 2 kill" PID 0 has a special - # meaning as it refers to <>, so should we prevent - # it here? + if self.pid == 0: + # see "man 2 kill" + raise ValueError( + "preventing sending signal to process with PID 0 as it " + "will affect every process in the process group of the " + "calling process (os.getpid()) instead of PID 0") try: os.kill(self.pid, sig) except OSError as err: diff --git a/test/test_psutil.py b/test/test_psutil.py index a7213f4f..31655b72 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2233,6 +2233,10 @@ class TestProcess(unittest.TestCase): except psutil.AccessDenied: pass + self.assertRaisesRegexp( + ValueError, "preventing sending signal to process with PID 0", + p.send_signal(signal.SIGTERM)) + self.assertIn(p.ppid(), (0, 1)) # self.assertEqual(p.exe(), "") p.cmdline() -- cgit v1.2.1 From d2a37e772906194b89c639e82ef9372fa3e222ce Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Jul 2015 10:18:46 +0200 Subject: update HISTORY --- HISTORY.rst | 15 +++++++++++---- psutil/__init__.py | 2 +- test/test_psutil.py | 1 - 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 18aa047c..fc812bcc 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,16 @@ Bug tracker at https://github.com/giampaolo/psutil/issues +3.0.2 - XXXX-XX-XX +================== + +**Bug fixes** + +- #636: [Linux] *connections functions may swallow errors and return an + incomplete list of connnections. +- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it + will affect os.getpid()'s process group instead of PID 0. + + 3.0.1 - 2015-06-18 ================== @@ -9,10 +20,6 @@ Bug tracker at https://github.com/giampaolo/psutil/issues - #634: [Linux] Proces.cmdline() does not include empty string arguments. - #635: [UNIX] crash on module import if 'enum' package is installed on python < 3.4. -- #636: [Linux] *connections functions may swallow errors and return an - incomplete list of connnections. -- #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it - will affect os.getpid()'s process group instead of PID 0. 3.0.0 - 2015-06-13 diff --git a/psutil/__init__.py b/psutil/__init__.py index 7d47a14f..7a3fb066 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -1011,7 +1011,7 @@ class Process(object): # see "man 2 kill" raise ValueError( "preventing sending signal to process with PID 0 as it " - "will affect every process in the process group of the " + "would affect every process in the process group of the " "calling process (os.getpid()) instead of PID 0") try: os.kill(self.pid, sig) diff --git a/test/test_psutil.py b/test/test_psutil.py index 31655b72..91423841 100644 --- a/test/test_psutil.py +++ b/test/test_psutil.py @@ -2250,7 +2250,6 @@ class TestProcess(unittest.TestCase): except psutil.AccessDenied: pass - # username property try: if POSIX: self.assertEqual(p.username(), 'root') -- cgit v1.2.1 From c875574d088ff38d49a5bf6ec10f0c05b733045c Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Jul 2015 10:36:18 +0200 Subject: fix memleaks failure --- test/test_memory_leaks.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/test/test_memory_leaks.py b/test/test_memory_leaks.py index 802e20ab..6f02dc0a 100644 --- a/test/test_memory_leaks.py +++ b/test/test_memory_leaks.py @@ -10,6 +10,7 @@ functions many times and compare process memory usage before and after the calls. It might produce false positives. """ +import functools import gc import os import socket @@ -20,7 +21,7 @@ import time import psutil import psutil._common -from psutil._compat import xrange +from psutil._compat import xrange, callable from test_psutil import (WINDOWS, POSIX, OSX, LINUX, SUNOS, BSD, TESTFN, RLIMIT_SUPPORT, TRAVIS) from test_psutil import (reap_children, supports_ipv6, safe_remove, @@ -92,7 +93,7 @@ class Base(unittest.TestCase): def get_mem(self): return psutil.Process().memory_info()[0] - def call(self, *args, **kwargs): + def call(self, function, *args, **kwargs): raise NotImplementedError("must be implemented in subclass") @@ -106,15 +107,25 @@ class TestProcessObjectLeaks(Base): reap_children() def call(self, function, *args, **kwargs): - meth = getattr(self.proc, function) - if '_exc' in kwargs: - exc = kwargs.pop('_exc') - self.assertRaises(exc, meth, *args, **kwargs) + if callable(function): + if '_exc' in kwargs: + exc = kwargs.pop('_exc') + self.assertRaises(exc, function, *args, **kwargs) + else: + try: + function(*args, **kwargs) + except psutil.Error: + pass else: - try: - meth(*args, **kwargs) - except psutil.Error: - pass + meth = getattr(self.proc, function) + if '_exc' in kwargs: + exc = kwargs.pop('_exc') + self.assertRaises(exc, meth, *args, **kwargs) + else: + try: + meth(*args, **kwargs) + except psutil.Error: + pass @skip_if_linux() def test_name(self): @@ -165,8 +176,10 @@ class TestProcessObjectLeaks(Base): value = psutil.Process().ionice() self.execute('ionice', value) else: + from psutil._pslinux import cext self.execute('ionice', psutil.IOPRIO_CLASS_NONE) - self.execute_w_exc(OSError, 'ionice', -1) + fun = functools.partial(cext.proc_ioprio_set, os.getpid(), -1, 0) + self.execute_w_exc(OSError, fun) @unittest.skipIf(OSX or SUNOS, "feature not supported on this platform") @skip_if_linux() -- cgit v1.2.1 From 9c49cd1d9d9eecad0c81ece9435db0625f32ba8a Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Jul 2015 10:41:04 +0200 Subject: fix travis failure --- psutil/_pslinux.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 4568216d..5f3f55fa 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -25,7 +25,7 @@ from . import _psutil_linux as cext from . import _psutil_posix as cext_posix from ._common import isfile_strict, usage_percent from ._common import NIC_DUPLEX_FULL, NIC_DUPLEX_HALF, NIC_DUPLEX_UNKNOWN -from ._compat import PY3 +from ._compat import PY3, long if sys.version_info >= (3, 4): import enum -- cgit v1.2.1 From 3a620d94b347a7044e1bd6c6aabcf5528c3089c3 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Jul 2015 10:46:23 +0200 Subject: #639: (Linux) proc cmdline can be truncated --- HISTORY.rst | 1 + psutil/_pslinux.py | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index fc812bcc..a97a802a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -9,6 +9,7 @@ Bug tracker at https://github.com/giampaolo/psutil/issues incomplete list of connnections. - #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it will affect os.getpid()'s process group instead of PID 0. +- #639: [Linux] Process.cmdline() can be truncated. 3.0.1 - 2015-06-18 diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 5f3f55fa..be443eff 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -783,7 +783,10 @@ class Process(object): fname = "/proc/%s/cmdline" % self.pid kw = dict(encoding=DEFAULT_ENCODING) if PY3 else dict() with open(fname, "rt", **kw) as f: - return [x for x in f.read()[:-1].split('\x00')] + data = f.read() + if data.endswith('\x00'): + data = data[:-1] + return [x for x in data.split('\x00')] @wrap_exceptions def terminal(self): -- cgit v1.2.1 From 73f54f47a7d4eac329c27af71c7fc5ad432d2e84 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Jul 2015 02:23:52 -0700 Subject: fix #636: (Windows): Process.memory_info() raises AccessDenied --- HISTORY.rst | 5 +++-- psutil/_psutil_windows.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index a97a802a..a8e8772a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,11 +5,12 @@ Bug tracker at https://github.com/giampaolo/psutil/issues **Bug fixes** -- #636: [Linux] *connections functions may swallow errors and return an - incomplete list of connnections. +- #636: [Windows] Process.memory_info() raise AccessDenied. - #637: [UNIX] raise exception if trying to send signal to Process PID 0 as it will affect os.getpid()'s process group instead of PID 0. - #639: [Linux] Process.cmdline() can be truncated. +- #640: [Linux] *connections functions may swallow errors and return an + incomplete list of connnections. 3.0.1 - 2015-06-18 diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 7e6792db..a45124e7 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -3201,7 +3201,7 @@ PsutilMethods[] = "seconds since the epoch"}, {"proc_memory_info", psutil_proc_memory_info, METH_VARARGS, "Return a tuple of process memory information"}, - {"proc_memory_info_2", psutil_proc_memory_info, METH_VARARGS, + {"proc_memory_info_2", psutil_proc_memory_info_2, METH_VARARGS, "Alternate implementation"}, {"proc_cwd", psutil_proc_cwd, METH_VARARGS, "Return process current working directory"}, -- cgit v1.2.1