launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24104
[Merge] ~cjwatson/launchpad:remove-zope.app.testing into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:remove-zope.app.testing into launchpad:master with ~cjwatson/launchpad:zope.testbrowser.wsgi as a prerequisite.
Commit message:
Remove all remaining uses of zope.app.testing
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/375206
The main work here is in refactoring functional test setup to use the new style based on zope.component.testlayer.ZCMLFileLayer and zope.testbrowser.wsgi.Layer. What used to be in wsgi_application is now pulled apart into a few pieces of middleware around zope.app.wsgi.WSGIPublisherApplication.
The extent to which we have to pretend that ZODB exists during test setup is now even smaller: we just need a couple of adjustments in lp.testing.layers._FunctionalBrowserLayer.
The zcml directory now doubles as a (trivial) package, since ZCMLFileLayer expects that.
We have to be a bit more careful in a couple of places:
* PageTestLayer now removes its log handler on tearDown.
* LaunchpadWebServiceCaller.addHeadersTo makes sure that the headers it adds are encoded in WSGI-friendly ways.
* A couple of tests in TestBuilderHistoryView need to clear the permission cache between creating test objects and doing privacy-related tests on them; I haven't found exactly what was doing this before, but I think it may have been an accidental side-effect of something in the guts of ZODB.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:remove-zope.app.testing into launchpad:master.
diff --git a/lib/lp/buildmaster/browser/tests/test_builder_views.py b/lib/lp/buildmaster/browser/tests/test_builder_views.py
index 0da8654..24a735b 100644
--- a/lib/lp/buildmaster/browser/tests/test_builder_views.py
+++ b/lib/lp/buildmaster/browser/tests/test_builder_views.py
@@ -23,6 +23,7 @@ from lp.buildmaster.interfaces.buildfarmjob import (
)
from lp.registry.interfaces.person import IPersonSet
from lp.services.database.sqlbase import flush_database_updates
+from lp.services.webapp.authorization import clear_cache
from lp.soyuz.browser.build import getSpecificJobs
from lp.testing import (
celebrity_logged_in,
@@ -224,6 +225,7 @@ class TestBuilderHistoryView(TestCaseWithFactory, BuildCreationMixin):
self.createRecipeBuildWithBuilder(builder=self.builder)
self.createRecipeBuildWithBuilder(
private_branch=True, builder=self.builder)
+ clear_cache()
view = create_initialized_view(self.builder, '+history')
view.setupBuildList()
@@ -233,6 +235,7 @@ class TestBuilderHistoryView(TestCaseWithFactory, BuildCreationMixin):
self.createRecipeBuildWithBuilder(builder=self.builder)
self.createRecipeBuildWithBuilder(
private_branch=True, builder=self.builder)
+ clear_cache()
view = create_initialized_view(self.builder, '+history')
private_build_icon_matcher = soupmatchers.HTMLContains(
soupmatchers.Tag(
diff --git a/lib/lp/services/profile/profiling.txt b/lib/lp/services/profile/profiling.txt
index 852a222..9a16054 100644
--- a/lib/lp/services/profile/profiling.txt
+++ b/lib/lp/services/profile/profiling.txt
@@ -18,10 +18,7 @@ variable.
The pagetests profiler is created by the layer during its setUp.
- >>> from lp.testing.layers import (
- ... PageTestLayer,
- ... wsgi_application,
- ... )
+ >>> from lp.testing.layers import PageTestLayer
(Save the existing configuration.)
@@ -58,7 +55,7 @@ other things).
Requests made with a testbrowser will also be profiled.
>>> from zope.testbrowser.wsgi import Browser
- >>> browser = Browser(wsgi_app=wsgi_application)
+ >>> browser = Browser()
>>> browser.open('http://launchpad.test/')
>>> len(PageTestLayer.profiler.getstats()) > profile_count
True
diff --git a/lib/lp/services/webapp/tests/test_authutility.py b/lib/lp/services/webapp/tests/test_authutility.py
index d2dfcbc..279dd3d 100644
--- a/lib/lp/services/webapp/tests/test_authutility.py
+++ b/lib/lp/services/webapp/tests/test_authutility.py
@@ -6,14 +6,16 @@ __metaclass__ = type
import base64
import testtools
-from zope.app.testing.placelesssetup import PlacelessSetup
from zope.authentication.interfaces import ILoginPassword
from zope.component import getUtility
+from zope.container.testing import ContainerPlacelessSetup
from zope.interface import implementer
from zope.principalregistry.principalregistry import UnauthenticatedPrincipal
from zope.publisher.browser import TestRequest
from zope.publisher.http import BasicAuthAdapter
from zope.publisher.interfaces.http import IHTTPCredentials
+from zope.security.management import newInteraction
+from zope.security.testing import addCheckerPublic
from lp.registry.interfaces.person import IPerson
from lp.services.config import config
@@ -58,11 +60,13 @@ class DummyPlacelessLoginSource(object):
return [Bruce]
-class TestPlacelessAuth(PlacelessSetup, testtools.TestCase):
+class TestPlacelessAuth(ContainerPlacelessSetup, testtools.TestCase):
def setUp(self):
testtools.TestCase.setUp(self)
- PlacelessSetup.setUp(self)
+ ContainerPlacelessSetup.setUp(self)
+ addCheckerPublic()
+ newInteraction()
self.useFixture(ZopeUtilityFixture(
DummyPlacelessLoginSource(), IPlacelessLoginSource))
self.useFixture(ZopeUtilityFixture(
@@ -71,7 +75,7 @@ class TestPlacelessAuth(PlacelessSetup, testtools.TestCase):
BasicAuthAdapter, (IHTTPCredentials,), ILoginPassword))
def tearDown(self):
- PlacelessSetup.tearDown(self)
+ ContainerPlacelessSetup.tearDown(self)
testtools.TestCase.tearDown(self)
def _make(self, login, pwd):
diff --git a/lib/lp/services/webapp/tests/test_error.py b/lib/lp/services/webapp/tests/test_error.py
index 3170ae3..7f52168 100644
--- a/lib/lp/services/webapp/tests/test_error.py
+++ b/lib/lp/services/webapp/tests/test_error.py
@@ -8,7 +8,6 @@ import httplib
import logging
import socket
import time
-import urllib2
from fixtures import FakeLogger
import psycopg2
@@ -44,7 +43,6 @@ from lp.testing.fixture import (
from lp.testing.layers import (
DatabaseLayer,
LaunchpadFunctionalLayer,
- wsgi_application,
)
from lp.testing.matchers import Contains
@@ -80,7 +78,7 @@ class TestDatabaseErrorViews(TestCase):
def getHTTPError(self, url):
try:
- Browser(wsgi_app=wsgi_application).open(url)
+ Browser().open(url)
except HTTPError as error:
return error
else:
@@ -107,7 +105,7 @@ class TestDatabaseErrorViews(TestCase):
Raise a TimeoutException if the connection cannot be established.
"""
- browser = Browser(wsgi_app=wsgi_application)
+ browser = Browser()
for i in range(retries):
try:
browser.open(url)
diff --git a/lib/lp/services/webapp/tests/test_login.py b/lib/lp/services/webapp/tests/test_login.py
index 3d93b8c..9aaae8f 100644
--- a/lib/lp/services/webapp/tests/test_login.py
+++ b/lib/lp/services/webapp/tests/test_login.py
@@ -81,7 +81,6 @@ from lp.testing.layers import (
AppServerLayer,
DatabaseFunctionalLayer,
FunctionalLayer,
- wsgi_application,
)
from lp.testing.pages import (
extract_text,
@@ -732,7 +731,7 @@ class TestMissingServerShowsNiceErrorPage(TestCase):
fixture.replacement = OpenIDLoginThatFailsDiscovery
self.useFixture(fixture)
- browser = TestBrowser(wsgi_app=wsgi_application)
+ browser = TestBrowser()
self.assertRaises(HTTPError,
browser.open, 'http://launchpad.test/+login')
self.assertEqual('503 Service Unavailable',
diff --git a/lib/lp/services/webapp/tests/test_notifications.py b/lib/lp/services/webapp/tests/test_notifications.py
index c516ac6..7473838 100644
--- a/lib/lp/services/webapp/tests/test_notifications.py
+++ b/lib/lp/services/webapp/tests/test_notifications.py
@@ -8,8 +8,11 @@ __metaclass__ = type
from doctest import DocTestSuite
import unittest
-from zope.app.testing import placelesssetup
from zope.component import provideAdapter
+from zope.container.testing import (
+ setUp as containerSetUp,
+ tearDown as containerTearDown,
+ )
from zope.interface import implementer
from zope.publisher.browser import TestRequest
from zope.publisher.interfaces.browser import IBrowserRequest
@@ -68,7 +71,7 @@ def adaptNotificationRequestToResponse(request):
def setUp(test):
- placelesssetup.setUp()
+ containerSetUp()
mock_session = MockSession()
provideAdapter(lambda x: mock_session, (INotificationRequest,), ISession)
provideAdapter(lambda x: mock_session, (INotificationResponse,), ISession)
@@ -86,7 +89,7 @@ def setUp(test):
def tearDown(test):
- placelesssetup.tearDown()
+ containerTearDown()
def test_suite():
diff --git a/lib/lp/services/worlddata/tests/test_helpers.py b/lib/lp/services/worlddata/tests/test_helpers.py
index 54a8a2f..a129604 100644
--- a/lib/lp/services/worlddata/tests/test_helpers.py
+++ b/lib/lp/services/worlddata/tests/test_helpers.py
@@ -95,8 +95,8 @@ class DummyLaunchBag:
def test_preferred_or_request_languages():
'''
- >>> from zope.app.testing.placelesssetup import setUp, tearDown
>>> from zope.component import provideAdapter, provideUtility
+ >>> from zope.container.testing import setUp, tearDown
>>> from zope.i18n.interfaces import IUserPreferredLanguages
>>> from lp.services.geoip.interfaces import IRequestPreferredLanguages
>>> from lp.services.geoip.interfaces import IRequestLocalLanguages
diff --git a/lib/lp/testing/browser.py b/lib/lp/testing/browser.py
index 7f4284b..7bacfbe 100644
--- a/lib/lp/testing/browser.py
+++ b/lib/lp/testing/browser.py
@@ -17,7 +17,6 @@ __all__ = [
import ssl
from lazr.uri import URI
-import transaction
from urllib3 import PoolManager
from wsgiproxy.proxies import TransparentProxy
from wsgiproxy.urllib3_client import HttpClient
@@ -26,6 +25,7 @@ from zope.testbrowser.wsgi import (
Browser as _Browser,
)
+from lp.testing.layers import TransactionMiddleware
from lp.testing.pages import (
extract_text,
find_main_content,
@@ -34,21 +34,6 @@ from lp.testing.pages import (
)
-class TransactionMiddleware:
- """Middleware to commit the current transaction before the test.
-
- This is like `zope.app.wsgi.TransactionMiddleware`, but avoids ZODB.
- """
-
- def __init__(self, app):
- self.app = app
-
- def __call__(self, environ, start_response):
- transaction.commit()
- for entry in self.app(environ, start_response):
- yield entry
-
-
class Browser(_Browser):
def __init__(self, url=None, wsgi_app=None):
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index c38af8e..b90c853 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -50,7 +50,6 @@ __all__ = [
'ZopelessLayer',
'disconnect_stores',
'reconnect_stores',
- 'wsgi_application',
]
from cProfile import Profile
@@ -86,26 +85,24 @@ import transaction
from webob.request import environ_from_url as orig_environ_from_url
import wsgi_intercept
from wsgi_intercept import httplib2_intercept
-from zope.app.publication.httpfactory import (
- chooseClasses,
- HTTPPublicationRequestFactory,
- )
-from zope.app.testing.functional import (
- FunctionalTestSetup,
- ZopePublication,
- )
+from zope.app.publication.httpfactory import HTTPPublicationRequestFactory
+from zope.app.wsgi import WSGIPublisherApplication
from zope.component import (
getUtility,
globalregistry,
provideUtility,
)
from zope.component.interfaces import ComponentLookupError
-import zope.publisher.publish
+from zope.component.testlayer import ZCMLFileLayer
+from zope.event import notify
+from zope.processlifetime import DatabaseOpened
from zope.security.management import (
endInteraction,
getSecurityPolicy,
)
from zope.server.logger.pythonlogger import PythonLogger
+import zope.testbrowser.wsgi
+from zope.testbrowser.wsgi import AuthorizationMiddleware
from lp.services import pidfile
from lp.services.auditor.server import AuditorServer
@@ -157,16 +154,13 @@ from lp.testing import (
)
from lp.testing.pgsql import PgTestSetup
from lp.testing.smtpd import SMTPController
+import zcml
COMMA = ','
WAIT_INTERVAL = datetime.timedelta(seconds=180)
-def set_up_functional_test():
- return FunctionalTestSetup('zcml/ftesting.zcml')
-
-
class LayerError(Exception):
pass
@@ -258,20 +252,6 @@ def wait_children(seconds=120):
break
-class MockRootFolder:
- """Implement the minimum functionality required by Z3 ZODB dependencies
-
- Installed as part of FunctionalLayer.testSetUp() to allow the http()
- method (zope.app.testing.functional.HTTPCaller) to work.
- """
- @property
- def _p_jar(self):
- return self
-
- def sync(self):
- pass
-
-
class BaseLayer:
"""Base layer.
@@ -993,59 +973,110 @@ class LaunchpadLayer(LibrarianLayer, MemcachedLayer, RabbitMQLayer):
"DELETE FROM SessionData")
-def raw_wsgi_application(environ, start_response):
- """This is a wsgi application for Zope functional testing.
+class TransactionMiddleware:
+ """Middleware to commit the current transaction before the test.
+
+ This is like `zope.app.wsgi.testlayer.TransactionMiddleware`, but avoids
+ ZODB.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ transaction.commit()
+ for entry in self.app(environ, start_response):
+ yield entry
+
+
+class RemoteAddrMiddleware:
+ """Middleware to set a default for `REMOTE_ADDR`.
+
+ zope.app.testing.functional.HTTPCaller used to set this, but WebTest
+ doesn't. However, some tests rely on it.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ environ.setdefault('REMOTE_ADDR', wsgi_native_string('127.0.0.1'))
+ return self.app(environ, start_response)
+
+
+class SortHeadersMiddleware:
+ """Middleware to sort response headers.
+
+ This makes it easier to write reliable tests.
+ """
+
+ def __init__(self, app):
+ self.app = app
+
+ def __call__(self, environ, start_response):
+ def wrap_start_response(status, response_headers, exc_info=None):
+ return start_response(status, sorted(response_headers), exc_info)
+
+ return self.app(environ, wrap_start_response)
+
- We use it with wsgi_intercept, which is itself mostly interesting
- for our webservice (lazr.restful) tests.
+class _FunctionalBrowserLayer(zope.testbrowser.wsgi.Layer, ZCMLFileLayer):
+ """A variant of zope.app.wsgi.testlayer.BrowserLayer for FunctionalLayer.
+
+ This is not a layer for use in Launchpad tests (hence the leading
+ underscore), as zope.component's layer composition strategy is different
+ from the one zope.testrunner expects.
"""
- # Committing work done up to now is a convenience that the Zope
- # zope.app.testing.functional.HTTPCaller does. We're replacing that bit,
- # so it is easiest to follow that lead, even if it feels a little loose.
- transaction.commit()
- # Let's support post-mortem debugging.
- if environ.pop('HTTP_X_ZOPE_HANDLE_ERRORS', 'True') == 'False':
- environ['wsgi.handleErrors'] = False
- handle_errors = environ.get('wsgi.handleErrors', True)
-
- # Make sure the request method is something Launchpad will
- # recognize. httplib2 usually takes care of this, but we've
- # bypassed that code in our test environment.
- environ['REQUEST_METHOD'] = environ['REQUEST_METHOD'].upper()
- # zope.app.testing.functional.HTTPCaller used to set this, but WebTest
- # doesn't. However, some tests rely on it.
- environ.setdefault('REMOTE_ADDR', wsgi_native_string('127.0.0.1'))
- # Now we do the proper dance to get the desired request. This is an
- # almalgam of code from zope.app.testing.functional.HTTPCaller and
- # zope.publisher.paste.Application.
- request_cls, publication_cls = chooseClasses(
- environ['REQUEST_METHOD'], environ)
- publication = publication_cls(set_up_functional_test().db)
- request = request_cls(environ['wsgi.input'], environ)
- request.setPublication(publication)
- # The rest of this function is an amalgam of
- # zope.publisher.paste.Application.__call__ and van.testing.layers.
- request = zope.publisher.publish.publish(
- request, handle_errors=handle_errors)
- response = request.response
- # We sort these because it makes it easier to write reliable tests.
- headers = sorted(response.getHeaders())
- status = response.getStatusString()
- # Start the WSGI server response.
- start_response(status, headers)
- # Return the result body iterable.
- return response.consumeBodyIter()
-
-
-_wsgi_application_middlewares = []
-
-
-def wsgi_application(environ, start_response):
- """As `raw_wsgi_application`, but possibly wrapped in middleware."""
- app = raw_wsgi_application
- for middleware in reversed(_wsgi_application_middlewares):
- app = middleware(app)
- return app(environ, start_response)
+
+ # A meaningless object passed to publication classes that just require
+ # something other than None. In Zope this would be a ZODB connection,
+ # but we don't use ZODB in Launchpad.
+ fake_db = object()
+
+ def __init__(self, *args, **kwargs):
+ super(_FunctionalBrowserLayer, self).__init__(*args, **kwargs)
+ self.middlewares = [
+ AuthorizationMiddleware,
+ RemoteAddrMiddleware,
+ SortHeadersMiddleware,
+ TransactionMiddleware,
+ ]
+
+ def setUp(self):
+ super(_FunctionalBrowserLayer, self).setUp()
+ # We don't use ZODB, but the webapp subscribes to IDatabaseOpened to
+ # perform some post-configuration tasks, so emit that event
+ # manually.
+ notify(DatabaseOpened(None))
+
+ def _resetWSGIApp(self):
+ """Reset `zope.testbrowser.wsgi.Layer.get_app`'s cache.
+
+ `zope.testbrowser.wsgi.Layer` sets up a cached WSGI application in
+ `setUp` and assumes that it won't change for the lifetime of the
+ layer. This assumption doesn't hold in Launchpad, but
+ `zope.testbrowser.wsgi.Layer` doesn't provide a straightforward way
+ to avoid making it. We do our best.
+ """
+ zope.testbrowser.wsgi._APP_UNDER_TEST = self.make_wsgi_app()
+
+ def addMiddlewares(self, *middlewares):
+ self.middlewares.extend(middlewares)
+ self._resetWSGIApp()
+
+ def removeMiddlewares(self, *middlewares):
+ for middleware in middlewares:
+ self.middlewares.remove(middleware)
+ self._resetWSGIApp()
+
+ def setupMiddleware(self, app):
+ for middleware in self.middlewares:
+ app = middleware(app)
+ return app
+
+ def make_wsgi_app(self):
+ """See `zope.testbrowser.wsgi.Layer`."""
+ return self.setupMiddleware(WSGIPublisherApplication(self.fake_db))
class FunctionalLayer(BaseLayer):
@@ -1058,9 +1089,17 @@ class FunctionalLayer(BaseLayer):
@profiled
def setUp(cls):
FunctionalLayer.isSetUp = True
- set_up_functional_test().setUp()
- # Assert that set_up_functional_test did what it says it does
+ # zope.component.testlayer.LayerBase has a different strategy for
+ # layer composition that doesn't play well with zope.testrunner's
+ # approach to setting up and tearing down individual layers. Work
+ # around this by creating a BrowserLayer instance here rather than
+ # having this layer subclass it.
+ FunctionalLayer.browser_layer = _FunctionalBrowserLayer(
+ zcml, zcml_file='ftesting.zcml')
+ FunctionalLayer.browser_layer.setUp()
+
+ # Assert that ZCMLFileLayer did what it says it does
if not is_ca_available():
raise LayerInvariantError("Component architecture failed to load")
@@ -1068,14 +1107,18 @@ class FunctionalLayer(BaseLayer):
# If we don't, it may issue extra queries depending on test order.
lp.services.webapp.session.idmanager.secret
# If our request publication factories were defined using ZCML,
- # they'd be set up by set_up_functional_test().setUp(). Since
- # they're defined by Python code, we need to call that code
- # here.
+ # they'd be set up by ZCMLFileLayer. Since they're defined by Python
+ # code, we need to call that code here.
register_launchpad_request_publication_factories()
+
+ # Most tests use the WSGI application directly via
+ # zope.testbrowser.wsgi.Layer.get_app, but some (especially those
+ # that use lazr.restfulclient or launchpadlib) still talk to the app
+ # server over HTTP and need to be intercepted.
wsgi_intercept.add_wsgi_intercept(
- 'localhost', 80, lambda: wsgi_application)
+ 'localhost', 80, _FunctionalBrowserLayer.get_app)
wsgi_intercept.add_wsgi_intercept(
- 'api.launchpad.test', 80, lambda: wsgi_application)
+ 'api.launchpad.test', 80, _FunctionalBrowserLayer.get_app)
httplib2_intercept.install()
# webob.request.environ_from_url defaults to HTTP/1.0, which is
@@ -1099,6 +1142,7 @@ class FunctionalLayer(BaseLayer):
wsgi_intercept.remove_wsgi_intercept('localhost', 80)
wsgi_intercept.remove_wsgi_intercept('api.launchpad.test', 80)
httplib2_intercept.uninstall()
+ FunctionalLayer.browser_layer.tearDown()
# Signal Layer cannot be torn down fully
raise NotImplementedError
@@ -1108,13 +1152,6 @@ class FunctionalLayer(BaseLayer):
transaction.abort()
transaction.begin()
- # Fake a root folder to keep Z3 ZODB dependencies happy.
- fs = set_up_functional_test()
- if not fs.connection:
- fs.connection = fs.db.open()
- root = fs.connection.root()
- root[ZopePublication.root_name] = MockRootFolder()
-
# Allow the WSGI test browser to talk to our various test hosts.
def assert_allowed_host(self):
host = self.host
@@ -1128,6 +1165,7 @@ class FunctionalLayer(BaseLayer):
'zope.testbrowser.wsgi.WSGIConnection.assert_allowed_host',
assert_allowed_host)
FunctionalLayer._testbrowser_allowed.setUp()
+ FunctionalLayer.browser_layer.testSetUp()
# Should be impossible, as the CA cannot be unloaded. Something
# mighty nasty has happened if this is triggered.
@@ -1138,6 +1176,7 @@ class FunctionalLayer(BaseLayer):
@classmethod
@profiled
def testTearDown(cls):
+ FunctionalLayer.browser_layer.testTearDown()
FunctionalLayer._testbrowser_allowed.cleanUp()
# Should be impossible, as the CA cannot be unloaded. Something
@@ -1676,18 +1715,19 @@ class PageTestLayer(LaunchpadFunctionalLayer, BingServiceLayer):
ProfilingMiddleware, profiler=PageTestLayer.profiler)
PageTestLayer._access_logging_middleware = partial(
AccessLoggingMiddleware, access_logger=access_logger)
- _wsgi_application_middlewares.extend([
+ FunctionalLayer.browser_layer.addMiddlewares(
PageTestLayer._profiling_middleware,
- PageTestLayer._access_logging_middleware,
- ])
+ PageTestLayer._access_logging_middleware)
@classmethod
@profiled
def tearDown(cls):
- _wsgi_application_middlewares.remove(
- PageTestLayer._access_logging_middleware)
- _wsgi_application_middlewares.remove(
+ FunctionalLayer.browser_layer.removeMiddlewares(
+ PageTestLayer._access_logging_middleware,
PageTestLayer._profiling_middleware)
+ logger = PythonLogger('pagetests-access')
+ for handler in list(logger.logger.handlers):
+ logger.logger.removeHandler(handler)
if PageTestLayer.profiler:
PageTestLayer.profiler.dump_stats(
os.environ.get('PROFILE_PAGETESTS_REQUESTS'))
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index 311feac..cff8368 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -46,14 +46,17 @@ from lazr.restful.testing.webservice import WebServiceCaller
import six
import transaction
from webtest import TestRequest
-from zope.app.wsgi.testlayer import FakeResponse as _FakeResponse
+from zope.app.wsgi.testlayer import (
+ FakeResponse as _FakeResponse,
+ NotInBrowserLayer,
+ )
from zope.component import getUtility
from zope.security.management import setSecurityPolicy
from zope.security.proxy import removeSecurityProxy
from zope.session.interfaces import ISession
from zope.testbrowser.wsgi import (
- AuthorizationMiddleware,
Browser,
+ Layer as TestBrowserWSGILayer,
)
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
@@ -83,10 +86,7 @@ from lp.testing import (
)
from lp.testing.dbuser import dbuser
from lp.testing.factory import LaunchpadObjectFactory
-from lp.testing.layers import (
- PageTestLayer,
- wsgi_application,
- )
+from lp.testing.layers import PageTestLayer
from lp.testing.systemdocs import (
LayeredDocFileSuite,
stop,
@@ -103,8 +103,8 @@ SAMPLEDATA_ACCESS_SECRETS = {
class FakeResponse(_FakeResponse):
"""A fake response for use in tests.
- This is like `zope.app.wsgi.FakeResponse`, but does a better job of
- emulating `zope.app.testing.functional` by using the request's
+ This is like `zope.app.wsgi.testlayer.FakeResponse`, but does a better
+ job of emulating `zope.app.testing.functional` by using the request's
`SERVER_PROTOCOL` in the response.
"""
@@ -133,6 +133,10 @@ def http(string, handle_errors=True):
`SERVER_PORT` to 80, which confuses
`VirtualHostRequestPublicationFactory.canHandle`.
"""
+ app = TestBrowserWSGILayer.get_app()
+ if app is None:
+ raise NotInBrowserLayer(NotInBrowserLayer.__doc__)
+
if not isinstance(string, bytes):
string = string.encode('UTF-8')
request = TestRequest.from_file(BytesIO(string.lstrip()))
@@ -145,7 +149,7 @@ def http(string, handle_errors=True):
port = 80
request.environ['SERVER_NAME'] = host
request.environ['SERVER_PORT'] = int(port)
- response = request.get_response(AuthorizationMiddleware(wsgi_application))
+ response = request.get_response(app)
return FakeResponse(response, request)
@@ -191,7 +195,11 @@ class LaunchpadWebServiceCaller(WebServiceCaller):
request.sign_request(
OAuthSignatureMethod_PLAINTEXT(), self.consumer,
self.access_token)
- full_headers.update(request.to_header(OAUTH_REALM))
+ oauth_headers = request.to_header(OAUTH_REALM)
+ full_headers.update({
+ six.ensure_str(key, encoding='ISO-8859-1'):
+ six.ensure_str(value, encoding='ISO-8859-1')
+ for key, value in oauth_headers.items()})
if not self.handle_errors:
full_headers['X_Zope_handle_errors'] = 'False'
@@ -692,7 +700,7 @@ def setupBrowser(auth=None):
string of the form 'Basic email:password' for an authenticated user.
:return: A `Browser` object.
"""
- browser = Browser(wsgi_app=AuthorizationMiddleware(wsgi_application))
+ browser = Browser()
# Set up our Browser objects with handleErrors set to False, since
# that gives a tracebacks instead of unhelpful error messages.
browser.handleErrors = False
diff --git a/lib/lp/testopenid/testing/helpers.py b/lib/lp/testopenid/testing/helpers.py
index c35c8f1..27f9855 100644
--- a/lib/lp/testopenid/testing/helpers.py
+++ b/lib/lp/testopenid/testing/helpers.py
@@ -22,7 +22,6 @@ from six.moves.urllib.error import HTTPError
from zope.testbrowser.wsgi import Browser
from lp.services.webapp import LaunchpadView
-from lp.testing.layers import wsgi_application
from lp.testopenid.interfaces.server import get_server_url
@@ -42,7 +41,7 @@ class ZopeFetcher(fetchers.HTTPFetcher):
"""An `HTTPFetcher` based on zope.testbrowser."""
def fetch(self, url, body=None, headers=None):
- browser = Browser(wsgi_app=wsgi_application)
+ browser = Browser()
if headers is not None:
for key, value in headers.items():
browser.addHeader(key, value)
diff --git a/lib/zcml b/lib/zcml
new file mode 120000
index 0000000..e0e5cba
--- /dev/null
+++ b/lib/zcml
@@ -0,0 +1 @@
+../zcml
\ No newline at end of file
diff --git a/setup.py b/setup.py
index ed2da29..a0518bf 100644
--- a/setup.py
+++ b/setup.py
@@ -243,7 +243,6 @@ setup(
'zope.app.publication',
'zope.app.publisher',
'zope.app.server',
- 'zope.app.testing',
'zope.app.wsgi',
'zope.authentication',
'zope.component[zcml]',
diff --git a/utilities/list-pages b/utilities/list-pages
index 823128d..7de2538 100755
--- a/utilities/list-pages
+++ b/utilities/list-pages
@@ -47,7 +47,7 @@ import _pythonpath
from inspect import getmro
import os
-from zope.app.testing.functional import FunctionalTestSetup
+from zope.app.wsgi.testlayer import BrowserLayer
from zope.browserpage.simpleviewclass import simple
from zope.component import adapter, getGlobalSiteManager
from zope.interface import directlyProvides, implementer
@@ -57,6 +57,7 @@ from lp.services.config import config
from lp.services.scripts import execute_zcml_for_scripts
from lp.services.webapp.interfaces import ICanonicalUrlData
from lp.services.webapp.publisher import canonical_url
+import zcml
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@@ -69,7 +70,7 @@ def load_zcml(zopeless=False):
if zopeless:
execute_zcml_for_scripts()
else:
- FunctionalTestSetup(os.path.join(ROOT, 'zcml', 'webapp.zcml')).setUp()
+ BrowserLayer(zcml, zcml_file='webapp.zcml').setUp()
def is_page_adapter(a):
diff --git a/zcml/__init__.py b/zcml/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/zcml/__init__.py