Skip to content

Commit

Permalink
PSUTIL_TESTING env var (#1083)
Browse files Browse the repository at this point in the history
* Introduce PSUTIL_TESTING env var

...so that we can make stricter assertions in C and py code during tests
only.

* define a C function in _common.c which returns whether the var is set
* set PSUTIL_TESTING from the Makefile

* cache psutil_testing() result

* winmake: set PSUTIL_TESTING env var for tests
  • Loading branch information
giampaolo authored May 18, 2017
1 parent d58c433 commit cee414d
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 40 deletions.
24 changes: 12 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -118,65 +118,65 @@ setup-dev-env:
# Run all tests.
test:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) $(TSCRIPT)
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) $(TSCRIPT)

# Run process-related API tests.
test-process:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_process
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_process

# Run system-related API tests.
test-system:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_system
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v psutil.tests.test_system

# Run miscellaneous tests.
test-misc:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_misc.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_misc.py

# Test APIs dealing with strings.
test-unicode:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_unicode.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_unicode.py

# APIs sanity tests.
test-contracts:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_contracts.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_contracts.py

# Test net_connections() and Process.connections().
test-connections:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_connections.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_connections.py

# POSIX specific tests.
test-posix:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_posix.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_posix.py

# Run specific platform tests only.
test-platform:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS") if getattr(psutil, x)][0])'`.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_`$(PYTHON) -c 'import psutil; print([x.lower() for x in ("LINUX", "BSD", "OSX", "SUNOS", "WINDOWS") if getattr(psutil, x)][0])'`.py

# Memory leak tests.
test-memleaks:
${MAKE} install
PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_memory_leaks.py
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) psutil/tests/test_memory_leaks.py

# Run a specific test by name, e.g.
# make test-by-name psutil.tests.test_system.TestSystemAPIs.test_cpu_times
test-by-name:
${MAKE} install
@PYTHONWARNINGS=all $(PYTHON) -m unittest -v $(ARGS)
@PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m unittest -v $(ARGS)

# Run test coverage.
coverage:
${MAKE} install
# Note: coverage options are controlled by .coveragerc file
rm -rf .coverage htmlcov
PYTHONWARNINGS=all $(PYTHON) -m coverage run $(TSCRIPT)
PSUTIL_TESTING=1 PYTHONWARNINGS=all $(PYTHON) -m coverage run $(TSCRIPT)
$(PYTHON) -m coverage report
@echo "writing results to htmlcov/index.html"
$(PYTHON) -m coverage html
Expand Down
6 changes: 5 additions & 1 deletion psutil/_psutil_bsd.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,7 +908,6 @@ psutil_users(PyObject *self, PyObject *args) {
*/
static PyMethodDef
PsutilMethods[] = {

// --- per-process functions

{"proc_oneshot_info", psutil_proc_oneshot_info, METH_VARARGS,
Expand Down Expand Up @@ -983,6 +982,11 @@ PsutilMethods[] = {
{"sensors_battery", psutil_sensors_battery, METH_VARARGS,
"Return battery information."},
#endif

// --- others
{"py_psutil_testing", py_psutil_testing, METH_VARARGS,
"Return True if PSUTIL_TESTING env var is set"},

{NULL, NULL, 0, NULL}
};

Expand Down
31 changes: 31 additions & 0 deletions psutil/_psutil_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include <Python.h>
#include <stdio.h>

/*
* Set OSError(errno=ESRCH, strerror="No such process") Python exception.
Expand Down Expand Up @@ -36,6 +37,36 @@ AccessDenied(void) {
}


static int _psutil_testing = -1;


/*
* Return 1 if PSUTIL_TESTING env var is set else 0.
*/
int
psutil_testing(void) {
if (_psutil_testing == -1) {
if (getenv("PSUTIL_TESTING") != NULL)
_psutil_testing = 1;
else
_psutil_testing = 0;
}
return _psutil_testing;
}


/*
* Return True if PSUTIL_TESTING env var is set else False.
*/
PyObject *
py_psutil_testing(PyObject *self, PyObject *args) {
PyObject *res;
res = psutil_testing() ? Py_True : Py_False;
Py_INCREF(res);
return res;
}


/*
* Backport of unicode FS APIs from Python 3.
* On Python 2 we just return a plain byte string
Expand Down
4 changes: 3 additions & 1 deletion psutil/_psutil_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ static const int PSUTIL_CONN_NONE = 128;

PyObject* AccessDenied(void);
PyObject* NoSuchProcess(void);
int psutil_testing(void);
PyObject* py_psutil_testing(PyObject *self, PyObject *args);
#if PY_MAJOR_VERSION < 3
PyObject* PyUnicode_DecodeFSDefault(char *s);
PyObject* PyUnicode_DecodeFSDefaultAndSize(char *s, Py_ssize_t size);
#endif
#endif
6 changes: 5 additions & 1 deletion psutil/_psutil_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ psutil_disk_partitions(PyObject *self, PyObject *args) {
if (py_retlist == NULL)
return NULL;

psutil_testing();

// MOUNTED constant comes from mntent.h and it's == '/etc/mtab'
Py_BEGIN_ALLOW_THREADS
file = setmntent(MOUNTED, "r");
Expand Down Expand Up @@ -574,7 +576,6 @@ psutil_net_if_duplex_speed(PyObject* self, PyObject* args) {
*/
static PyMethodDef
PsutilMethods[] = {

// --- per-process functions

#if PSUTIL_HAVE_IOPRIO
Expand Down Expand Up @@ -607,6 +608,9 @@ PsutilMethods[] = {
"Get or set process resource limits."},
#endif

// --- others
{"py_psutil_testing", py_psutil_testing, METH_VARARGS,
"Return True if PSUTIL_TESTING env var is set"},

{NULL, NULL, 0, NULL}
};
Expand Down
5 changes: 4 additions & 1 deletion psutil/_psutil_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1779,7 +1779,6 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
*/
static PyMethodDef
PsutilMethods[] = {

// --- per-process functions

{"proc_kinfo_oneshot", psutil_proc_kinfo_oneshot, METH_VARARGS,
Expand Down Expand Up @@ -1841,6 +1840,10 @@ PsutilMethods[] = {
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return CPU statistics"},

// --- others
{"py_psutil_testing", py_psutil_testing, METH_VARARGS,
"Return True if PSUTIL_TESTING env var is set"},

{NULL, NULL, 0, NULL}
};

Expand Down
5 changes: 4 additions & 1 deletion psutil/_psutil_sunos.c
Original file line number Diff line number Diff line change
Expand Up @@ -1470,7 +1470,6 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
*/
static PyMethodDef
PsutilMethods[] = {

// --- process-related functions
{"proc_basic_info", psutil_proc_basic_info, METH_VARARGS,
"Return process ppid, rss, vms, ctime, nice, nthreads, status and tty"},
Expand Down Expand Up @@ -1513,6 +1512,10 @@ PsutilMethods[] = {
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return CPU statistics"},

// --- others
{"py_psutil_testing", py_psutil_testing, METH_VARARGS,
"Return True if PSUTIL_TESTING env var is set"},

{NULL, NULL, 0, NULL}
};

Expand Down
5 changes: 4 additions & 1 deletion psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -3479,7 +3479,6 @@ psutil_sensors_battery(PyObject *self, PyObject *args) {

static PyMethodDef
PsutilMethods[] = {

// --- per-process functions

{"proc_cmdline", psutil_proc_cmdline, METH_VARARGS,
Expand Down Expand Up @@ -3602,6 +3601,10 @@ PsutilMethods[] = {
{"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
"QueryDosDevice binding"},

// --- others
{"py_psutil_testing", py_psutil_testing, METH_VARARGS,
"Return True if PSUTIL_TESTING env var is set"},

{NULL, NULL, 0, NULL}
};

Expand Down
32 changes: 31 additions & 1 deletion psutil/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
'ThreadTask'
# test utils
'unittest', 'skip_on_access_denied', 'skip_on_not_implemented',
'retry_before_failing', 'run_test_module_by_name',
'retry_before_failing', 'run_test_module_by_name', 'get_suite',
'run_suite',
# install utils
'install_pip', 'install_test_deps',
# fs utils
Expand Down Expand Up @@ -141,6 +142,7 @@

ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
SCRIPTS_DIR = os.path.join(ROOT_DIR, 'scripts')
HERE = os.path.abspath(os.path.dirname(__file__))

# --- support

Expand Down Expand Up @@ -383,6 +385,9 @@ def reap_children(recursive=False):
# https://ci.appveyor.com/project/giampaolo/psutil/build/job/
# jiq2cgd6stsbtn60
def assert_gone(pid):
# XXX
if WINDOWS:
return
assert not psutil.pid_exists(pid), pid
assert pid not in psutil.pids(), pid
try:
Expand Down Expand Up @@ -699,9 +704,34 @@ def __str__(self):
unittest.TestCase = TestCase


def _setup_tests():
assert 'PSUTIL_TESTING' in os.environ
assert psutil._psplatform.cext.py_psutil_testing()


def get_suite():
testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
if x.endswith('.py') and x.startswith('test_') and not
x.startswith('test_memory_leaks')]
suite = unittest.TestSuite()
for tm in testmodules:
# ...so that the full test paths are printed on screen
tm = "psutil.tests.%s" % tm
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
return suite


def run_suite():
_setup_tests()
result = unittest.TextTestRunner(verbosity=VERBOSITY).run(get_suite())
success = result.wasSuccessful()
sys.exit(0 if success else 1)


def run_test_module_by_name(name):
# testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
# if x.endswith('.py') and x.startswith('test_')]
_setup_tests()
name = os.path.splitext(os.path.basename(name))[0]
suite = unittest.TestSuite()
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(name))
Expand Down
21 changes: 1 addition & 20 deletions psutil/tests/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
except ImportError:
from urllib2 import urlopen

from psutil.tests import unittest
from psutil.tests import VERBOSITY
from psutil.tests import run_suite


HERE = os.path.abspath(os.path.dirname(__file__))
Expand Down Expand Up @@ -73,24 +72,6 @@ def install_test_deps(deps=None):
return code


def get_suite():
testmodules = [os.path.splitext(x)[0] for x in os.listdir(HERE)
if x.endswith('.py') and x.startswith('test_') and not
x.startswith('test_memory_leaks')]
suite = unittest.TestSuite()
for tm in testmodules:
# ...so that the full test paths are printed on screen
tm = "psutil.tests.%s" % tm
suite.addTest(unittest.defaultTestLoader.loadTestsFromName(tm))
return suite


def run_suite():
result = unittest.TextTestRunner(verbosity=VERBOSITY).run(get_suite())
success = result.wasSuccessful()
sys.exit(0 if success else 1)


def main():
usage = "%s -m psutil.tests [opts]" % PYTHON
parser = optparse.OptionParser(usage=usage, description="run unit tests")
Expand Down
2 changes: 2 additions & 0 deletions psutil/tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,8 @@ def test_environ(self):
d2 = os.environ.copy()

removes = []
if 'PSUTIL_TESTING' in os.environ:
removes.append('PSUTIL_TESTING')
if OSX:
removes.extend([
"__CF_USER_TEXT_ENCODING",
Expand Down
Loading

0 comments on commit cee414d

Please sign in to comment.