mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-18 15:33:02 +00:00
report db query counts in test output
This commit is contained in:
@@ -34,6 +34,7 @@ from subprocess import TimeoutExpired
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from pytest import fixture, raises
|
from pytest import fixture, raises
|
||||||
|
from sqlalchemy import event
|
||||||
from tornado.httpclient import HTTPError
|
from tornado.httpclient import HTTPError
|
||||||
from tornado.platform.asyncio import AsyncIOMainLoop
|
from tornado.platform.asyncio import AsyncIOMainLoop
|
||||||
|
|
||||||
@@ -484,3 +485,52 @@ def preserve_scopes():
|
|||||||
scope_definitions = copy.deepcopy(scopes.scope_definitions)
|
scope_definitions = copy.deepcopy(scopes.scope_definitions)
|
||||||
yield scope_definitions
|
yield scope_definitions
|
||||||
scopes.scope_definitions = scope_definitions
|
scopes.scope_definitions = scope_definitions
|
||||||
|
|
||||||
|
|
||||||
|
# collect db query counts and report the top N tests by db query count
|
||||||
|
@fixture(autouse=True)
|
||||||
|
def count_db_executions(request, record_property):
|
||||||
|
if 'app' in request.fixturenames:
|
||||||
|
app = request.getfixturevalue("app")
|
||||||
|
initial_count = app.db_query_count
|
||||||
|
yield
|
||||||
|
# populate property, collected later in pytest_terminal_summary
|
||||||
|
record_property("db_executions", app.db_query_count - initial_count)
|
||||||
|
elif 'db' in request.fixturenames:
|
||||||
|
# some use the 'db' fixture directly for one-off database tests
|
||||||
|
count = 0
|
||||||
|
engine = request.getfixturevalue("db").get_bind()
|
||||||
|
|
||||||
|
@event.listens_for(engine, "before_execute")
|
||||||
|
def before_execute(conn, clauseelement, multiparams, params, execution_options):
|
||||||
|
nonlocal count
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
yield
|
||||||
|
record_property("db_executions", count)
|
||||||
|
else:
|
||||||
|
# nothing to do, still have to yield
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_terminal_summary(terminalreporter, exitstatus, config):
|
||||||
|
# collect db_executions property
|
||||||
|
# populated by the count_db_executions fixture
|
||||||
|
db_counts = {}
|
||||||
|
for report in terminalreporter.getreports(""):
|
||||||
|
properties = dict(report.user_properties)
|
||||||
|
db_executions = properties.get("db_executions", 0)
|
||||||
|
if db_executions:
|
||||||
|
db_counts[report.nodeid] = db_executions
|
||||||
|
|
||||||
|
total_queries = sum(db_counts.values())
|
||||||
|
if total_queries == 0:
|
||||||
|
# nothing to report (e.g. test subset)
|
||||||
|
return
|
||||||
|
n = min(10, len(db_counts))
|
||||||
|
terminalreporter.section(f"top {n} database queries")
|
||||||
|
terminalreporter.line(f"{total_queries:<6} (total)")
|
||||||
|
for nodeid in sorted(db_counts, key=db_counts.get, reverse=True)[:n]:
|
||||||
|
queries = db_counts[nodeid]
|
||||||
|
if queries:
|
||||||
|
terminalreporter.line(f"{queries:<6} {nodeid}")
|
||||||
|
@@ -35,6 +35,7 @@ from unittest import mock
|
|||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from pamela import PAMError
|
from pamela import PAMError
|
||||||
|
from sqlalchemy import event
|
||||||
from tornado.httputil import url_concat
|
from tornado.httputil import url_concat
|
||||||
from traitlets import Bool, Dict, default
|
from traitlets import Bool, Dict, default
|
||||||
|
|
||||||
@@ -308,6 +309,14 @@ class MockHub(JupyterHub):
|
|||||||
self.db.delete(role)
|
self.db.delete(role)
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
|
|
||||||
|
# count db requests
|
||||||
|
self.db_query_count = 0
|
||||||
|
engine = self.db.get_bind()
|
||||||
|
|
||||||
|
@event.listens_for(engine, "before_execute")
|
||||||
|
def before_execute(conn, clauseelement, multiparams, params, execution_options):
|
||||||
|
self.db_query_count += 1
|
||||||
|
|
||||||
async def initialize(self, argv=None):
|
async def initialize(self, argv=None):
|
||||||
self.pid_file = NamedTemporaryFile(delete=False).name
|
self.pid_file = NamedTemporaryFile(delete=False).name
|
||||||
self.db_file = NamedTemporaryFile()
|
self.db_file = NamedTemporaryFile()
|
||||||
|
Reference in New Issue
Block a user