220 lines
6.5 KiB
Python
220 lines
6.5 KiB
Python
"""
|
|
Plugin for py.test that sets up the app.
|
|
|
|
App configuration inspired by the Pylons nose equivalent:
|
|
https://github.com/Pylons/pylons/blob/master/pylons/test.py
|
|
|
|
Handling of multiprocessing inspired by pytest-cov.
|
|
"""
|
|
from configuration import set_config, _runtime_conf as conf
|
|
from tempfile import mkdtemp
|
|
|
|
import py
|
|
import py.test
|
|
import os
|
|
import shutil
|
|
import socket
|
|
import sys
|
|
|
|
|
|
def pytest_addoption(parser):
|
|
"""
|
|
Adds the custom "with-config" option to take in the config file.
|
|
"""
|
|
group = parser.getgroup('pecan')
|
|
group._addoption('--with-config',
|
|
dest='config_file',
|
|
metavar='path',
|
|
default='./test.py',
|
|
action='store',
|
|
type='string',
|
|
help='configuration file for pecan tests')
|
|
|
|
|
|
def pytest_configure(config):
|
|
"""
|
|
Loads the Pecan plugin if using a configuration file.
|
|
"""
|
|
if config.getvalue('config_file'):
|
|
config.pluginmanager.register(PecanPlugin(config), '_pecan')
|
|
|
|
|
|
class PecanPlugin(object):
|
|
"""
|
|
Plugin for a Pecan application. Sets up and tears down the
|
|
WSGI application based on the configuration and session type.
|
|
"""
|
|
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.impl = None
|
|
|
|
def pytest_namespace(self):
|
|
"""
|
|
Add the session variables to the namespace.
|
|
"""
|
|
return {
|
|
'temp_dir': None,
|
|
'wsgi_app': None
|
|
}
|
|
|
|
def pytest_sessionstart(self, session):
|
|
"""
|
|
Set up the testing session.
|
|
"""
|
|
self.impl = PecanPluginImpl.create_from_session(session)
|
|
self.impl.sessionstart(session)
|
|
|
|
def pytest_configure_node(self, node):
|
|
"""
|
|
Configures a new slave node.
|
|
"""
|
|
if self.impl:
|
|
self.impl.configure_node(node)
|
|
|
|
def pytest_testnodedown(self, node, error):
|
|
"""
|
|
Tears down an exiting node.
|
|
"""
|
|
if self.impl:
|
|
self.impl.testnodedown(node, error)
|
|
|
|
def pytest_sessionfinish(self, session, exitstatus):
|
|
"""
|
|
Cleans up the testing session.
|
|
"""
|
|
if self.impl:
|
|
self.impl.sessionfinish(session, exitstatus)
|
|
|
|
|
|
class PecanPluginImpl(object):
|
|
"""
|
|
Actual implementation of the Pecan plugin. This ensures the proper
|
|
environment is configured for each session type.
|
|
"""
|
|
|
|
def __init__(self, config):
|
|
self.config = config
|
|
self.log = py.log.Producer('pecan-%s' % self.name)
|
|
if not config.option.debug:
|
|
py.log.setconsumer(self.log._keywords, None)
|
|
self.log('Created %s instance' % self.__class__.__name__)
|
|
|
|
@property
|
|
def name(self):
|
|
return 'main'
|
|
|
|
def _setup_app(self):
|
|
self.log('Invoking setup_app')
|
|
path = os.getcwd()
|
|
if path not in sys.path:
|
|
sys.path.insert(0, path)
|
|
set_config(self.config.getvalue('config_file'))
|
|
py.test.wsgi_app = self._load_app(conf)
|
|
|
|
def _get_package_names(self, config):
|
|
if not hasattr(config.app, 'modules'):
|
|
return []
|
|
return [module.__name__ for module in config.app.modules if hasattr(module, '__name__')]
|
|
|
|
def _can_import(self, name):
|
|
try:
|
|
__import__(name)
|
|
return True
|
|
except ImportError:
|
|
return False
|
|
|
|
def _load_app(self, config):
|
|
for package_name in self._get_package_names(config):
|
|
module_name = '%s.app' % package_name
|
|
if self._can_import(module_name):
|
|
module = sys.modules[module_name]
|
|
if hasattr(module, 'setup_app'):
|
|
return module.setup_app(config)
|
|
raise RuntimeError('No app.setup_app found in any of the configured app.modules')
|
|
|
|
def _create_temp_directory(self):
|
|
temp_dir = mkdtemp()
|
|
self.log('Created temporary directory %s' % temp_dir)
|
|
py.test.temp_dir = temp_dir
|
|
|
|
def _delete_temp_directory(self):
|
|
if py.test.temp_dir and os.path.exists(py.test.temp_dir):
|
|
self.log('Removing temporary directory %s' % py.test.temp_dir)
|
|
shutil.rmtree(py.test.temp_dir)
|
|
|
|
def sessionstart(self, session):
|
|
self.log('Starting session')
|
|
self._setup_app()
|
|
self._create_temp_directory()
|
|
|
|
def configure_node(self, node):
|
|
pass
|
|
|
|
def testnodedown(self, node, error):
|
|
pass
|
|
|
|
def sessionfinish(self, session, exitstatus):
|
|
self.log('Stopping session')
|
|
self._delete_temp_directory()
|
|
|
|
@staticmethod
|
|
def create_from_session(session):
|
|
if session.config.option.dist != 'no':
|
|
impl_cls = MasterPecanPluginImpl
|
|
elif getattr(session.config, 'slaveinput', {}).get('slaveid'):
|
|
impl_cls = SlavePecanPluginImpl
|
|
else:
|
|
impl_cls = PecanPluginImpl
|
|
return impl_cls(session.config)
|
|
|
|
|
|
class MasterPecanPluginImpl(PecanPluginImpl):
|
|
"""
|
|
Plugin implementation for distributed master.
|
|
"""
|
|
|
|
def sessionstart(self, session):
|
|
self.log('Starting master session')
|
|
self._setup_app()
|
|
self._create_temp_directory()
|
|
|
|
def configure_node(self, node):
|
|
self.log('Configuring slave node %s' % node.gateway.id)
|
|
node.slaveinput['pecan_master_host'] = socket.gethostname()
|
|
node.slaveinput['pecan_temp_dir'] = py.test.temp_dir
|
|
|
|
def sessionfinish(self, session, exitstatus):
|
|
self.log('Stopping master session')
|
|
self._delete_temp_directory()
|
|
|
|
|
|
class SlavePecanPluginImpl(PecanPluginImpl):
|
|
"""
|
|
Plugin implementation for distributed slaves.
|
|
"""
|
|
|
|
@property
|
|
def name(self):
|
|
return self.config.slaveinput['slaveid']
|
|
|
|
def _is_collocated(self, session):
|
|
return (socket.gethostname() == session.config.slaveinput['pecan_master_host'])
|
|
|
|
def _set_temp_directory(self, session):
|
|
self.log('Setting temporary directory to %s' % session.config.slaveinput['pecan_temp_dir'])
|
|
py.test.temp_dir = session.config.slaveinput['pecan_temp_dir']
|
|
|
|
def sessionstart(self, session):
|
|
self.log('Starting slave session')
|
|
self._setup_app()
|
|
if self._is_collocated(session):
|
|
self._set_temp_directory(session)
|
|
else:
|
|
self._create_temp_directory()
|
|
|
|
def sessionfinish(self, session, exitstatus):
|
|
self.log('Stopping slave session')
|
|
if not self._is_collocated(session):
|
|
self._delete_temp_directory()
|