Skip to content

Commit

Permalink
Initial version of pytest
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-Sebastien Dieu committed Feb 20, 2020
1 parent 981e7df commit 62d80c8
Show file tree
Hide file tree
Showing 22 changed files with 1,141 additions and 0 deletions.
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include LICENSE
include README.rst

recursive-exclude * __pycache__
recursive-exclude * *.py[co]
151 changes: 151 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
.. image:: docs/sources/_static/pytestmonitor_readme.png
:width: 160
:align: center
:alt: Pytest-Monitor


------

==============
pytest-monitor
==============

.. image:: https://readthedocs.org/projects/pytest-monitor/badge/?version=latest
:target: https://pytest-monitor.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status

.. image:: https://img.shields.io/pypi/v/pytest-monitor.svg
:target: https://pypi.org/project/pytest-monitor
:alt: PyPI version

.. image:: https://img.shields.io/pypi/pyversions/pytest-monitor.svg
:target: https://circleci.com/gh/jsd-spif/pymonitor.svg?style=svg&circle-token=cdf89a7212139aff0cc236227cb519363981de0b
:alt: Python versions

.. image:: https://circleci.com/gh/jsd-spif/pymonitor.svg?style=svg&circle-token=cdf89a7212139aff0cc236227cb519363981de0b
:target: https://circleci.com/gh/jsd-spif/pymonitor.svg?style=svg&circle-token=cdf89a7212139aff0cc236227cb519363981de0b
:alt: See Build Status on Circle CI



Pytest-monitor is a pytest plugin designed for analyzing resource usage.

----



Features
--------

- Analyze your resources consumption through test functions:

* memory consumption
* time duration
* CPU usage
- Keep a history of your resource consumption measurements.
- Compare how your code behaves between different environments.



Installation
------------

You can install *pytest-monitor* via *conda* (through the `conda-forge` channel)::

$ conda install pytest-monitor -c https://conda.anaconda.org/conda-forge

Another possibility is to install *pytest-monitor* via `pip`_ from `PyPI`_::

$ pip install pytest-monitor


Requirements
------------

You will need a valid Python 3.5+ interpreter. To get measures, we rely on:

- *psutil* to extract CPU usage
- *memory_profiler* to collect memory usage
- and *pytest* (obviously!)


Usage
-----

Simply run *pytest* as usual: *pytest-monitor* is active by default as soon as it is installed.
After running your first session, a .pymon `sqlite` database will be accessible in the directory where pytest was run.

Example of information collected for the execution context:

+-----------------------------------+-----------+-------------------+---------+-------------------------------------------+---------------+--------------------+------------+-------------------------------+-------------------------------+--------------------------------------------------+
| ENV_H| CPU_COUNT| CPU_FREQUENCY_MHZ| CPU_TYPE| CPU_VENDOR| RAM_TOTAL_MB | MACHINE_NODE |MACHINE_TYPE| MACHINE_ARCH | SYSTEM_INFO | PYTHON_INFO|
+===================================+===========+===================+=========+===========================================+===============+====================+============+===============================+===============================+==================================================+
| 8294b1326007d9f4c8a1680f9590c23d | 36 | 3000 | x86_64 | Intel(R) Xeon(R) Gold 6154 CPU @ 3.00GHz | 772249 | some.host.vm.fr | x86_64 | 64bit | Linux - 3.10.0-693.el7.x86_64 | 3.6.8 (default, Jun 28 2019, 11:09:04) \n[GCC ...|
+-----------------------------------+-----------+-------------------+---------+-------------------------------------------+---------------+--------------------+------------+-------------------------------+-------------------------------+--------------------------------------------------+

Here is an example of collected data stored in the result database:

+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| RUN_DATE| ENV_H| SCM_ID| ITEM_START_TIME| ITEM| KIND| COMPONENT| TOTAL_TIME| USER_TIME| KERNEL_TIME| CPU_USAGE| MEM_USAGE|
+==============================+==================================+==========================================+============================+========================================+==========+==========+============+===========+=============+============+===========+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:36.890477 | pkg1.test_mod1/test_sleep1 | function | None | 1.005669 | 0.54 | 0.06 | 0.596618 | 1.781250 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:39.912029 | pkg1.test_mod1/test_heavy[10-10] | function | None | 0.029627 | 0.55 | 0.08 | 21.264498 | 1.781250 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:39.948922 | pkg1.test_mod1/test_heavy[100-100] | function | None | 0.028262 | 0.56 | 0.09 | 22.998773 | 1.781250 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:39.983869 | pkg1.test_mod1/test_heavy[1000-1000] | function | None | 0.030131 | 0.56 | 0.10 | 21.904277 | 2.132812 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:40.020823 | pkg1.test_mod1/test_heavy[10000-10000] | function | None | 0.060060 | 0.57 | 0.14 | 11.821601 | 41.292969 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:40.093490 | pkg1.test_mod2/test_sleep_400ms | function | None | 0.404860 | 0.58 | 0.15 | 1.803093 | 2.320312 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:40.510525 | pkg2.test_mod_a/test_master_sleep | function | None | 5.006039 | 5.57 | 0.15 | 1.142620 | 2.320312 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:45.530780 | pkg3.test_mod_cl/test_method1 | function | None | 0.030505 | 5.58 | 0.16 | 188.164762 | 2.320312 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+
| 2020-02-17T09:11:36.731233 | 8294b1326007d9f4c8a1680f9590c23d | de23e6bdb987ae21e84e6c7c0357488ee66f2639 | 2020-02-17T09:11:50.582954 | pkg4.test_mod_a/test_force_monitor | function | test | 1.005015 | 11.57 | 0.17 | 11.681416 | 2.320312 |
+------------------------------+----------------------------------+------------------------------------------+----------------------------+----------------------------------------+----------+----------+------------+-----------+-------------+------------+-----------+

Contributing
------------

Contributions are very welcome. Tests can be run with `tox`_. Before submitting a pull request, please ensure
that:

* both internal tests and examples are passing.
* internal tests have been written if necessary.
* if your contribution provides a new feature, make sure to provide an example and update the documentation accordingly.

License
-------

This code is distributed under the `MIT`_ license. *pytest-monitor* is free, open-source software.


Issues
------

If you encounter any problem, please `file an issue`_ along with a detailed description.

Author
------

The main author of `pytest-monitor` is Jean-Sébastien Dieu, who can be reached at jean-sebastien.dieu@cfm.fr.

----

This `pytest`_ plugin was generated with `Cookiecutter`_ along with `@hackebrot`_'s `cookiecutter-pytest-plugin`_ template.

.. _`Cookiecutter`: https://github.com/audreyr/cookiecutter
.. _`@hackebrot`: https://github.com/hackebrot
.. _`MIT`: http://opensource.org/licenses/MIT
.. _`BSD-3`: http://opensource.org/licenses/BSD-3-Clause
.. _`GNU GPL v3.0`: http://www.gnu.org/licenses/gpl-3.0.txt
.. _`Apache Software License 2.0`: http://www.apache.org/licenses/LICENSE-2.0
.. _`cookiecutter-pytest-plugin`: https://github.com/pytest-dev/cookiecutter-pytest-plugin
.. _`file an issue`: https://github.com/CFMTech/pytest-monitor/issues
.. _`pytest`: https://github.com/pytest-dev/pytest
.. _`tox`: https://tox.readthedocs.io/en/latest/
.. _`pip`: https://pypi.org/project/pip/
.. _`PyPI`: https://pypi.org/project
Empty file added examples/pkg1/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions examples/pkg1/test_mod1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest
import time


def test_sleep1():
time.sleep(1)


@pytest.mark.monitor_skip_test
def test_sleep2():
time.sleep(2)


@pytest.mark.parametrize(('range_max', 'other'), [(10, "10"), (100, "100"), (1000, "1000"), (10000, "10000")])
def test_heavy(range_max, other):
assert len(['a' * i for i in range(range_max)]) == range_max
4 changes: 4 additions & 0 deletions examples/pkg1/test_mod2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import time

def test_sleep_400ms():
time.sleep(0.4)
Empty file added examples/pkg2/__init__.py
Empty file.
10 changes: 10 additions & 0 deletions examples/pkg2/test_mod_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import time


def test_master_sleep():
t_a = time.time()
b_continue = True
while b_continue:
t_delta = time.time() - t_a
b_continue = t_delta < 5

Empty file added examples/pkg3/__init__.py
Empty file.
3 changes: 3 additions & 0 deletions examples/pkg3/test_mod_cl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class TestClass:
def test_method1(self):
assert True
Empty file added examples/pkg4/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions examples/pkg4/test_mod_a.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import time
import pytest

pytestmark = pytest.mark.monitor_skip_test

pytest_monitor_component = 'test'

def test_not_monitored():
t_a = time.time()
b_continue = True
while b_continue:
t_delta = time.time() - t_a
b_continue = t_delta < 5


@pytest.mark.monitor_test
def test_force_monitor():
t_a = time.time()
b_continue = True
while b_continue:
t_delta = time.time() - t_a
b_continue = t_delta < 5

2 changes: 2 additions & 0 deletions pytest_monitor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__version__ = "1.0.0"
__author__ = "Jean-Sebastien Dieu"
66 changes: 66 additions & 0 deletions pytest_monitor/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import sqlite3


class DBHandler:
def __init__(self, db_path):
self.__db = db_path
self.__cnx = sqlite3.connect(self.__db) if db_path else None

def query(self, what, bind_to, many=False):
cursor = self.__cnx.cursor()
cursor.execute(what, bind_to)
return cursor.fetchall() if many else cursor.fetchone()

def insert_metric(self, run_date, item_start_date, env_id, scm_id, item, kind, component,
total_time, user_time, kernel_time, cpu_usage, mem_usage):
with self.__cnx:
self.__cnx.execute(f'insert into TEST_METRICS(RUN_DATE,ITEM_START_TIME,ENV_H,SCM_ID,ITEM,KIND,'
f'COMPONENT,TOTAL_TIME,USER_TIME,KERNEL_TIME,CPU_USAGE,MEM_USAGE) '
f'values (?,?,?,?,?,?,?,?,?,?,?,?)',
(run_date, item_start_date, env_id, scm_id, item, kind, component,
total_time, user_time, kernel_time, cpu_usage, mem_usage))

def insert_execution_context(self, exc_context):
with self.__cnx:
self.__cnx.execute(f'insert into EXECUTION_CONTEXTS(CPU_COUNT,CPU_FREQUENCY_MHZ,CPU_TYPE,CPU_VENDOR,'
f'RAM_TOTAL_MB,MACHINE_NODE,MACHINE_TYPE,MACHINE_ARCH,SYSTEM_INFO,'
f'PYTHON_INFO,ENV_H) values (?,?,?,?,?,?,?,?,?,?,?)',
(exc_context.cpu_count, exc_context.cpu_frequency, exc_context.cpu_type,
exc_context.cpu_vendor, exc_context.ram_total, exc_context.fqdn, exc_context.machine,
exc_context.architecture, exc_context.system_info, exc_context.python_info,
exc_context.hash()))

def prepare(self):
cursor = self.__cnx.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS TEST_METRICS (
RUN_DATE varchar(64), -- Date of test run
ENV_H varchar(64), -- Environment description identifier
SCM_ID varchar(128),
ITEM_START_TIME varchar(64), -- Effective start time of the test
ITEM varchar(4096), -- Name of the item
KIND varchar(64), -- Package, Module or function
COMPONENT varchar(512) NULL, -- Tested component if any
TOTAL_TIME float, -- Total time spent running the item
USER_TIME float, -- time spent in user space
KERNEL_TIME float, -- time spent in kernel space
CPU_USAGE float, -- cpu usage
MEM_USAGE float, -- Max resident memory used.
FOREIGN KEY (ENV_H) REFERENCES EXECUTION_CONTEXTS(ENV_H)
);''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS EXECUTION_CONTEXTS (
ENV_H varchar(64) primary key not null unique,
CPU_COUNT integer,
CPU_FREQUENCY_MHZ integer,
CPU_TYPE varchar(64),
CPU_VENDOR varchar(256),
RAM_TOTAL_MB integer,
MACHINE_NODE varchar(512),
MACHINE_TYPE varchar(32),
MACHINE_ARCH varchar(16),
SYSTEM_INFO varchar(256),
PYTHON_INFO varchar(512)
);
''')
self.__cnx.commit()

0 comments on commit 62d80c8

Please sign in to comment.