Skip to content

Commit

Permalink
Delay wsgi interception until after fixtures
Browse files Browse the repository at this point in the history
Some WSGI applications require a lot of prior setup before they can
be use. Since we want most of that setup to happen in fixtures,
wsgi-interception is delayed until after all extant fixtures have been
started. This is done with a built in default fixture that is run as
the most deeply nested of the context managers.

The intecept callable is passed on along to the tests so that suite
can be made aware of and use it when using the intercept context
manager. If the callable is a function, when it is passed into the
class builder, it can become bound to the TestCase. This binding is
unbound in the caller.
  • Loading branch information
cdent committed Jan 22, 2015
1 parent 3bb36fa commit c65dbba
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 23 deletions.
2 changes: 1 addition & 1 deletion gabbi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# under the License.
"""See gabbi.driver and gabbbi.case."""

__version__ = '0.4.0'
__version__ = '0.5.0'
26 changes: 6 additions & 20 deletions gabbi/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
import uuid

import httplib2
import wsgi_intercept
from wsgi_intercept import httplib2_intercept
import yaml

from .suite import GabbiSuite
Expand Down Expand Up @@ -83,46 +81,33 @@ def build_tests(path, loader, host=None, port=8001, intercept=None,
test_loader_name = os.path.splitext(os.path.basename(
test_loader_name[1]))[0]

if intercept:
host = install_intercept(intercept, port)

yaml_file_glob = '%s/*.yaml' % path

# Return an empty suite if we have no host to access, either via
# a real host or an intercept
if host or intercept:
for test_file in glob.iglob(yaml_file_glob):
if intercept:
host = str(uuid.uuid4())
test_yaml = load_yaml(test_file)
test_name = '%s_%s' % (test_loader_name,
os.path.splitext(
os.path.basename(test_file))[0])
file_suite = test_suite_from_yaml(loader, test_name, test_yaml,
path, host, port, fixture_module)
path, host, port, fixture_module,
intercept)
top_suite.addTest(file_suite)
return top_suite


def factory(wsgi_app):
"""Satisfy a bad API."""
return wsgi_app


def install_intercept(wsgi_callable, port):
"""Install a wsgi-intercept on a random hostname."""
hostname = str(uuid.uuid4())
httplib2_intercept.install()
wsgi_intercept.add_wsgi_intercept(hostname, port, factory(wsgi_callable))
return hostname


def load_yaml(yaml_file):
"""Read and parse any YAML file. Let exceptions flow where they may."""
with open(yaml_file) as source:
return yaml.safe_load(source.read())


def test_suite_from_yaml(loader, test_base_name, test_yaml, test_directory,
host, port, fixture_module):
host, port, fixture_module, intercept):
"""Generate a TestSuite from YAML data."""

file_suite = GabbiSuite()
Expand Down Expand Up @@ -160,6 +145,7 @@ def test_suite_from_yaml(loader, test_base_name, test_yaml, test_directory,
'fixtures': fixture_classes,
'http': httplib2.Http(),
'host': host,
'intercept': intercept,
'port': port,
'prior': prior_test})

Expand Down
20 changes: 20 additions & 0 deletions gabbi/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from contextlib import contextmanager

import six
import wsgi_intercept
from wsgi_intercept import httplib2_intercept


class GabbiFixtureError(Exception):
Expand Down Expand Up @@ -54,6 +56,24 @@ def stop_fixture(self):
pass


class InterceptFixture(GabbiFixture):
"""Start up the wsgi intercept. This should not be called directly."""

httplib2_intercept.install()

def __init__(self, host, port, app):
self.host = host
self.port = port
self.app = app

def start_fixture(self):
wsgi_intercept.add_wsgi_intercept(self.host, self.port,
lambda: self.app())

def stop_fixture(self):
wsgi_intercept.remove_wsgi_intercept(self.host, self.port)


@contextmanager
def nest(fixtures):
"""Nest a series of fixtures.
Expand Down
22 changes: 20 additions & 2 deletions gabbi/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,31 @@ def run(self, result, debug=False):

# If there are fixtures, nest in their context.
fixtures = [fixture.GabbiFixture]
intercept = None

try:
fixtures = self._tests[0].fixtures
first_test = self._tests[0]
fixtures = first_test.fixtures
host = first_test.host
port = first_test.port
intercept = first_test.intercept

# Unbind a passed in WSGI application. During the
# metaclass building process intercept becomes bound.
try:
intercept = intercept.__func__
except AttributeError:
pass
except AttributeError:
pass

try:
with fixture.nest([fix() for fix in fixtures]):
result = super(GabbiSuite, self).run(result, debug)
if intercept:
with fixture.InterceptFixture(host, port, intercept):
result = super(GabbiSuite, self).run(result, debug)
else:
result = super(GabbiSuite, self).run(result, debug)
except case.SkipTest as exc:
[result.addSkip(test, str(exc)) for test in self._tests]

Expand Down

0 comments on commit c65dbba

Please sign in to comment.