Skip to content

Commit

Permalink
pytest-monitor #3 - Compute user time and kernel time on a per test b…
Browse files Browse the repository at this point in the history
…asis. (#6)

* PYMON #3 - Take user time and kernel time using a per test approach. The previous implementation was using an aggregative approach which is harder to exploit.

* PYMON #3 - Removed unused function scoper.

* PYMON #3 - Updated docs
  • Loading branch information
js-dieu committed Mar 26, 2020
1 parent 64c858c commit d93a746
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 32 deletions.
6 changes: 6 additions & 0 deletions docs/sources/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@
Changelog
=========

* :release:`next`
* :feature:`PM #3` Compute user time and kernel time on a per test basis for clarity and ease of exploitation.

* :release:`1.0.1 <2020-03-18>`
* :bug:`PM #2` pytest-monitor hangs infinitely when a pytest outcome (skip, fail...) is issued.

* :release:`1.0.0 <2020-02-20>`
* :feature:`PM` Initial release
4 changes: 4 additions & 0 deletions examples/pkg3/test_mod_cl.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import time

class TestClass:
def setup_method(self, test_method):
self.__value = test_method.__name__
time.sleep(1)

def test_method1(self):
time.sleep(0.5)
assert self.__value == "test_method1"
50 changes: 22 additions & 28 deletions pytest_monitor/pytest_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,43 +169,37 @@ def pytest_sessionstart(session):
remote = None if session.config.option.mtr_none else session.config.option.remote
PYTEST_MONITOR_SESSION = PyTestMonitorSession(db=db, remote=remote, component=component)
PYTEST_MONITOR_SESSION.set_environment_info(ExecutionContext())
session.pytest_monitor = PYTEST_MONITOR_SESSION
yield


def scoper(scope, monitor_skip_flag, set_scope):
should_skip = monitor_skip_flag
if scope in set_scope and not should_skip:
global PYTEST_MONITOR_SESSION
return PYTEST_MONITOR_SESSION
return None


@pytest.fixture(autouse=True, scope='module')
def prf_module_tracer(request):
t_a = time.time()
ptimes_a = request.session.pytest_monitor.process.cpu_times()
yield
wrt = scoper('module', False, request.config.option.mtr_scope)
if wrt is not None:
t_z = time.time()
process = psutil.Process(os.getpid())
rss = process.memory_info().rss / 1024 ** 2
ptimes = process.cpu_times()
component = getattr(request.module, 'pytest_monitor_component', '')
wrt.add_test_info(request.module.__name__, 'module', component,
t_a, t_z - t_a, ptimes.user, ptimes.system,
rss)
ptimes_b = request.session.pytest_monitor.process.cpu_times()
t_z = time.time()
rss = request.session.pytest_monitor.process.memory_info().rss / 1024 ** 2
component = getattr(request.module, 'pytest_monitor_component', '')
request.session.pytest_monitor.add_test_info(request.module.__name__, 'module', request.config.option.mtr_scope,
component, t_a, t_z - t_a,
ptimes_b.user - ptimes_a.user,
ptimes_b.system - ptimes_a.system,
rss)


@pytest.fixture(autouse=True)
def prf_tracer(request):
ptimes_a = request.session.pytest_monitor.process.cpu_times()
yield
wrt = scoper('function', request.node.monitor_skip_test, request.config.option.mtr_scope)
if wrt is not None:
process = psutil.Process(os.getpid())
ptimes = process.cpu_times()
if request.node.monitor_results:
full_item_name = f'{request.module.__name__}/{request.node.name}'
wrt.add_test_info(full_item_name, 'function', request.node.monitor_component,
request.node.test_effective_start_time,
request.node.test_run_duration,
ptimes.user, ptimes.system, request.node.mem_usage)
ptimes_b = request.session.pytest_monitor.process.cpu_times()
if not request.node.monitor_skip_test and request.node.monitor_results:
full_item_name = f'{request.module.__name__}/{request.node.name}'
request.session.pytest_monitor.add_test_info(full_item_name, 'function', request.config.option.mtr_scope,
request.node.monitor_component,
request.node.test_effective_start_time,
request.node.test_run_duration,
ptimes_b.user - ptimes_a.user,
ptimes_b.system - ptimes_a.system,
request.node.mem_usage)
13 changes: 11 additions & 2 deletions pytest_monitor/session.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import datetime
import json
import memory_profiler
import os
import psutil
import requests
import subprocess
import warnings
Expand All @@ -17,7 +19,7 @@ def __init__(self, db=None, remote=None, component=''):
self.__scm = ''
self.__eid = (None, None)
self.__mem_usage_base = None

self.__process = psutil.Process(os.getpid())
self.prepare()

@property
Expand All @@ -28,6 +30,10 @@ def remote_env_id(self):
def db_env_id(self):
return self.__eid[0]

@property
def process(self):
return self.__process

def get_env_id(self, env):
db, remote = None, None
if self.__db:
Expand Down Expand Up @@ -77,7 +83,10 @@ def dummy():
if self.__db:
self.__db.prepare()

def add_test_info(self, item, kind, component, item_start_time, total_time, user_time, kernel_time, mem_usage):
def add_test_info(self, item, kind, allowed_scope, component, item_start_time, total_time,
user_time, kernel_time, mem_usage):
if kind not in allowed_scope:
return
mem_usage = float(mem_usage) - self.__mem_usage_base
cpu_usage = (user_time + kernel_time) / total_time
item_start_time = datetime.datetime.fromtimestamp(item_start_time).isoformat()
Expand Down
2 changes: 0 additions & 2 deletions tests/test_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,5 +214,3 @@ def test_monitored():
cursor = db.cursor()
cursor.execute('SELECT ITEM FROM TEST_METRICS;')
assert 2 == len(cursor.fetchall()) # current test + test_monitored


0 comments on commit d93a746

Please sign in to comment.