Add basic profiling capability for quteproc tests.

When --qute-profile-subprocs is given, we write a profile file for each
qutebrowser invocation and also create prof/combined.pstats afterwards.
This commit is contained in:
Florian Bruhin 2016-02-11 08:01:29 +01:00
parent 6d11e9ffd8
commit 02f367a308
4 changed files with 57 additions and 4 deletions

1
.gitignore vendored
View File

@ -29,4 +29,5 @@ __pycache__
/.cache
/.testmondata
/.hypothesis
/prof
TODO

View File

@ -151,6 +151,8 @@ def pytest_addoption(parser):
help='Disable xvfb in tests.')
parser.addoption('--qute-delay', action='store', default=0, type=int,
help="Delay between qutebrowser commands.")
parser.addoption('--qute-profile-subprocs', action='store_true',
default=False, help="Run cProfile for subprocesses.")
def pytest_configure(config):

View File

@ -21,6 +21,28 @@
"""Things needed for integration testing."""
import os
import shutil
import pstats
from webserver import httpbin, httpbin_after_test, ssl_server
from quteprocess import quteproc_process, quteproc, quteproc_new
from testprocess import pytest_runtest_makereport
def pytest_configure(config):
"""Remove old profile files."""
if config.getoption('--qute-profile-subprocs'):
try:
shutil.rmtree('prof')
except FileNotFoundError:
pass
def pytest_unconfigure(config):
"""Combine profiles."""
if config.getoption('--qute-profile-subprocs'):
stats = pstats.Stats()
for fn in os.listdir('prof'):
stats.add(os.path.join('prof', fn))
stats.dump_stats(os.path.join('prof', 'combined.pstats'))

View File

@ -28,6 +28,7 @@ import datetime
import logging
import tempfile
import contextlib
import itertools
import yaml
import pytest
@ -39,6 +40,9 @@ from qutebrowser.utils import log, utils
from helpers import utils as testutils
instance_counter = itertools.count()
def is_ignored_qt_message(message):
"""Check if the message is listed in qt_log_ignore."""
# pylint: disable=no-member
@ -124,6 +128,9 @@ class QuteProc(testprocess.Process):
basedir: The base directory for this instance.
_focus_ready: Whether the main window got focused.
_load_ready: Whether the about:blank page got loaded.
_profile: If True, do profiling of the subprocesses.
_instance_id: An unique ID for this QuteProc instance
_run_counter: A counter to get an unique ID for each run.
Signals:
got_error: Emitted when there was an error log line.
@ -134,14 +141,17 @@ class QuteProc(testprocess.Process):
KEYS = ['timestamp', 'loglevel', 'category', 'module', 'function', 'line',
'message']
def __init__(self, httpbin, delay, parent=None):
def __init__(self, httpbin, delay, *, profile=False, parent=None):
super().__init__(parent)
self._profile = profile
self._delay = delay
self._httpbin = httpbin
self._ipc_socket = None
self.basedir = None
self._focus_ready = False
self._load_ready = False
self._instance_id = next(instance_counter)
self._run_counter = itertools.count()
def _is_ready(self, what):
"""Called by _parse_line if loading/focusing is done.
@ -201,12 +211,28 @@ class QuteProc(testprocess.Process):
def _executable_args(self):
if hasattr(sys, 'frozen'):
if self._profile:
raise Exception("Can't profile with sys.frozen!")
executable = os.path.join(os.path.dirname(sys.executable),
'qutebrowser')
args = []
else:
executable = sys.executable
args = ['-m', 'qutebrowser']
if self._profile:
profile_dir = os.path.join(os.getcwd(), 'prof')
profile_id = '{}_{}'.format(self._instance_id,
next(self._run_counter))
profile_file = os.path.join(profile_dir,
'{}.pstats'.format(profile_id))
try:
os.mkdir(profile_dir)
except FileExistsError:
pass
args = [os.path.join('scripts', 'dev', 'run_profile.py'),
'--profile-tool', 'none',
'--profile-file', profile_file]
else:
args = ['-m', 'qutebrowser']
return executable, args
def _default_args(self):
@ -397,7 +423,8 @@ class QuteProc(testprocess.Process):
def quteproc_process(qapp, httpbin, request):
"""Fixture for qutebrowser process which is started once per file."""
delay = request.config.getoption('--qute-delay')
proc = QuteProc(httpbin, delay)
profile = request.config.getoption('--qute-profile-subprocs')
proc = QuteProc(httpbin, delay, profile=profile)
proc.start()
yield proc
proc.terminate()
@ -416,7 +443,8 @@ def quteproc(quteproc_process, httpbin, request):
def quteproc_new(qapp, httpbin, request):
"""Per-test qutebrowser process to test invocations."""
delay = request.config.getoption('--qute-delay')
proc = QuteProc(httpbin, delay)
profile = request.config.getoption('--qute-profile-subprocs')
proc = QuteProc(httpbin, delay, profile=profile)
request.node._quteproc_log = proc.captured_log
# Not calling before_test here as that would start the process
yield proc