Files
deb-python-pecan/pecan/testing.py
2011-04-26 15:31:11 -04:00

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()