[Checkins] SVN: gocept.selenium/trunk/ Merge branch wosc-zodb-isolation

Wolfgang Schnerring wosc at wosc.de
Sun Dec 26 09:31:26 EST 2010


Log message for revision 119133:
  Merge branch wosc-zodb-isolation
  

Changed:
  U   gocept.selenium/trunk/plone.cfg
  U   gocept.selenium/trunk/plone4.cfg
  U   gocept.selenium/trunk/src/gocept/selenium/grok/fixtures.py
  U   gocept.selenium/trunk/src/gocept/selenium/grok/tests.py
  U   gocept.selenium/trunk/src/gocept/selenium/plone/__init__.py
  U   gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone3/test_plone3.py
  U   gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone4/test_plone4.py
  U   gocept.selenium/trunk/src/gocept/selenium/tests/fixture/configure.zcml
  U   gocept.selenium/trunk/src/gocept/selenium/tests/fixture/dummy.py
  U   gocept.selenium/trunk/src/gocept/selenium/tests/isolation.py
  U   gocept.selenium/trunk/src/gocept/selenium/zope2/__init__.py
  U   gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope210/test_zope210.py
  U   gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope212/test_zope212.py
  U   gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_ztk.py

-=-
Modified: gocept.selenium/trunk/plone.cfg
===================================================================
--- gocept.selenium/trunk/plone.cfg	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/plone.cfg	2010-12-26 14:31:25 UTC (rev 119133)
@@ -15,7 +15,7 @@
 [test]
 recipe = collective.recipe.z2testrunner
 zope2part = instance
-defaults = --ignore_dir=ztk --ignore_dir=zope2 --ignore_dir=static --tests-pattern=plone3
+defaults = --ignore_dir=ztk --ignore_dir=zope2 --ignore_dir=static --ignore_dir=plonetesting --tests-pattern=plone3
 packages = ${buildout:package}
 
 [instance]

Modified: gocept.selenium/trunk/plone4.cfg
===================================================================
--- gocept.selenium/trunk/plone4.cfg	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/plone4.cfg	2010-12-26 14:31:25 UTC (rev 119133)
@@ -11,7 +11,7 @@
 
 [test]
 recipe = zc.recipe.testrunner
-defaults = ["--ignore_dir=ztk", "--ignore_dir=zope",  "--ignore_dir=static", "--tests-pattern=plone4"]
+defaults = ["--ignore_dir=ztk", "--ignore_dir=zope",  "--ignore_dir=static", "--ignore_dir=plonetesting", "--tests-pattern=plone4"]
 eggs = ${instance:eggs}
 
 [instance]

Modified: gocept.selenium/trunk/src/gocept/selenium/grok/fixtures.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/grok/fixtures.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/grok/fixtures.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -12,6 +12,7 @@
 #
 ##############################################################################
 
+import gocept.selenium.tests.fixture.dummy
 import grok
 
 
@@ -25,18 +26,29 @@
         return '''<html><body>Hello from grok</body></html>'''
 
 
-class Set(grok.View):
-    grok.name('set.html')
+class DelegatingView(grok.View):
+    # delegates actual functionality to the common isolation fixture, but lets
+    # us register the views grok-style
+
     grok.context(object)
 
     def render(self):
-        self.context.foo = 1
-        return u'setting done'
+        view = getattr(
+            gocept.selenium.tests.fixture.dummy, self.__class__.__name__)()
+        view.context = self.context
+        return view()
 
 
-class Get(grok.View):
+class Set(DelegatingView):
+
+    grok.name('set.html')
+
+
+class Get(DelegatingView):
+
     grok.name('get.html')
-    grok.context(object)
 
-    def render(self):
-        return str(getattr(self.context, 'foo', 0))
+
+class IncrementVolatile(DelegatingView):
+
+    grok.name('inc-volatile.html')

Modified: gocept.selenium/trunk/src/gocept/selenium/grok/tests.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/grok/tests.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/grok/tests.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -44,3 +44,6 @@
     gocept.selenium.grok.TestCase):
 
     layer = test_layer
+
+    def getDatabase(self):
+        return self.layer.db

Modified: gocept.selenium/trunk/src/gocept/selenium/plone/__init__.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/plone/__init__.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/plone/__init__.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -19,6 +19,11 @@
 
 
 class TestCase(gocept.selenium.base.TestCase,
+               gocept.selenium.zope2.SandboxPatch,
                Products.PloneTestCase.PloneTestCase.FunctionalTestCase):
 
     layer = gocept.selenium.zope2.Layer(PloneSiteLayer)
+
+    def getRootFolder(self):
+        """forward API-compatibility with zope.app.testing"""
+        return self.app

Modified: gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone3/test_plone3.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone3/test_plone3.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone3/test_plone3.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -24,6 +24,9 @@
 class PloneTests(gocept.selenium.tests.isolation.IsolationTests,
                  gocept.selenium.plone.TestCase):
 
+    def getDatabase(self):
+        return gocept.selenium.zope2.get_current_db()
+
     def test_plone_login(self):
         sel = self.selenium
         sel.open('/plone')

Modified: gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone4/test_plone4.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone4/test_plone4.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/plone/tests/plone4/test_plone4.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -28,6 +28,9 @@
 
     layer = gocept.selenium.zope2.Layer(PloneSiteLayer, testing.isolationLayer)
 
+    def getDatabase(self):
+        return gocept.selenium.zope2.get_current_db()
+
     def test_plone_login(self):
         sel = self.selenium
         sel.open('/plone')

Modified: gocept.selenium/trunk/src/gocept/selenium/tests/fixture/configure.zcml
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/tests/fixture/configure.zcml	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/tests/fixture/configure.zcml	2010-12-26 14:31:25 UTC (rev 119133)
@@ -19,6 +19,13 @@
 
   <browser:page
     for="*"
+    name="inc-volatile.html"
+    class=".dummy.IncrementVolatile"
+    permission="zope.Public"
+    />
+
+  <browser:page
+    for="*"
     name="error.html"
     class=".dummy.Error"
     permission="zope.Public"

Modified: gocept.selenium/trunk/src/gocept/selenium/tests/fixture/dummy.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/tests/fixture/dummy.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/tests/fixture/dummy.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -34,3 +34,16 @@
 
     def __call__(Self):
         raise ValueError()
+
+
+class IncrementVolatile(object):
+
+    def __call__(self):
+        c = zope.security.proxy.removeSecurityProxy(self.context)
+        if hasattr(c, 'aq_base'):
+            c = c.aq_base
+
+        if not hasattr(c, '_v_counter'):
+            c._v_counter = 0
+        c._v_counter += 1
+        return str(c._v_counter)

Modified: gocept.selenium/trunk/src/gocept/selenium/tests/isolation.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/tests/isolation.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/tests/isolation.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -17,6 +17,10 @@
 
 class IsolationTests(object):
 
+    # test_0_set and test_1_get verify that different test methods are isolated
+    # from each other, i.e. that the underlying DemoStorage stacking is wired
+    # up correctly
+
     def test_0_set(self):
         global ENSURE_ORDER
         self.selenium.open('http://%s/set.html' % self.selenium.server)
@@ -31,3 +35,36 @@
         self.selenium.open('http://%s/get.html' % self.selenium.server)
         self.selenium.assertNotBodyText('1')
         ENSURE_ORDER = False
+
+    # subclasses need to implement getRootFolder() and getDatabase()
+    # for the tests below to work
+
+    def test_each_request_gets_a_separate_zodb_connection(self):
+        self.selenium.open(
+            'http://%s/inc-volatile.html' % self.selenium.server)
+        self.selenium.assertBodyText('1')
+        # We demonstrate isolation using volatile attributes (which are
+        # guaranteed not to be present on separate connections). But since
+        # there is no guarantee that volatile attributes disappear on
+        # transaction boundaries, we need to prevent re-use of the first
+        # connection -- to avoid trouble like "it's the same connection, so the
+        # volatile attribute is still there".
+        #
+        # The proper way to do this would be two requests that are processing
+        # concurrently, but a) gocept.selenium is not prepared for
+        # multi-threaded requests and b) simulating that would be a major pain,
+        # so we cheat and force the opening of another connection by claiming
+        # one here.
+        db = self.getDatabase()
+        conn = db.open()
+        self.selenium.open(
+            'http://%s/inc-volatile.html' % self.selenium.server)
+        conn.close()
+        self.selenium.assertBodyText('1')
+
+    def test_requests_get_different_zodb_connection_than_tests(self):
+        root = self.getRootFolder()
+        root._v_counter = 1
+        self.selenium.open(
+            'http://%s/inc-volatile.html' % self.selenium.server)
+        self.selenium.assertBodyText('1')

Modified: gocept.selenium/trunk/src/gocept/selenium/zope2/__init__.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/zope2/__init__.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/zope2/__init__.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -16,6 +16,7 @@
 import Testing.ZopeTestCase
 import Testing.ZopeTestCase.threadutils
 import Testing.ZopeTestCase.utils
+import Zope2
 import gocept.selenium.base
 
 
@@ -52,7 +53,49 @@
         super(Layer, self).tearDown()
 
 
+class SandboxPatch(object):
+    # Testing.ZopeTestCase.sandbox.Sandbox swapping of the DemoStorage is a
+    # little... crude:
+    #
+    # ZApplicationWrapper is instantiated with a DB from
+    # Testing/custom_zodb, which is never used later on, since Sandbox
+    # passes in the connection (to the current DB) to use instead. This
+    # connection is also stored globally in
+    # Testing.ZopeTestCase.sandbox.AppZapper (and passed to requests via
+    # the bobo_traverse monkey-patch there) -- which means that there only
+    # ever is one single ZODB connection, among the test code and the HTTP
+    # requests, and among concurrent requests. This clearly is not what we
+    # want.
+    #
+    # Thus, this rewrite of the upstream method, that properly changes the
+    # DB in ZApplicationWrapper and does *not* use AppZapper, yielding a
+    # new connection upon each traversal. (For reference and since it took
+    # me quite a while to figure out where everything is: this code is
+    # adapted from the original Sandbox._app and the normal Zope2 startup
+    # in Zope2.__init__).
+
+    def _app(self):
+        Zope2.startup()
+        stuff = Zope2.bobo_application._stuff
+        db = Testing.ZopeTestCase.ZopeLite.sandbox()
+        Zope2.bobo_application._stuff = (db,) + stuff[1:]
+        app = Zope2.bobo_application()
+        app = Testing.ZopeTestCase.utils.makerequest(app)
+        Testing.ZopeTestCase.connections.register(app)
+        return app
+
+
+def get_current_db():
+    """helper for gocept.selenium.tests.isolation"""
+    return Zope2.bobo_application._stuff[0]
+
+
 class TestCase(gocept.selenium.base.TestCase,
+               SandboxPatch,
                Testing.ZopeTestCase.FunctionalTestCase):
 
     layer = Layer(*BASE_LAYERS)
+
+    def getRootFolder(self):
+        """forward API-compatibility with zope.app.testing"""
+        return self.app

Modified: gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope210/test_zope210.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope210/test_zope210.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope210/test_zope210.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -12,18 +12,20 @@
 #
 ##############################################################################
 
+import Testing.ZopeTestCase
+import gocept.selenium.tests.isolation
+import gocept.selenium.zope2
 import unittest
-import gocept.selenium.zope2
-import gocept.selenium.tests.isolation
-import Testing.ZopeTestCase
 
 Testing.ZopeTestCase.installProduct('Five')
 
 
 class Zope2Tests(gocept.selenium.tests.isolation.IsolationTests,
                  gocept.selenium.zope2.TestCase):
-    pass
 
+    def getDatabase(self):
+        return gocept.selenium.zope2.get_current_db()
 
+
 def test_suite():
     return unittest.makeSuite(Zope2Tests)

Modified: gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope212/test_zope212.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope212/test_zope212.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/zope2/tests/zope212/test_zope212.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -12,12 +12,12 @@
 #
 ##############################################################################
 
-import unittest
-import gocept.selenium.zope2
+from gocept.selenium.zope2 import BASE_LAYERS
 from gocept.selenium.zope2 import testing
-from gocept.selenium.zope2 import BASE_LAYERS
+import Testing.ZopeTestCase
 import gocept.selenium.tests.isolation
-import Testing.ZopeTestCase
+import gocept.selenium.zope2
+import unittest
 
 Testing.ZopeTestCase.installProduct('Five')
 
@@ -27,6 +27,9 @@
 
     layer = gocept.selenium.zope2.Layer(testing.isolationLayer, *BASE_LAYERS)
 
+    def getDatabase(self):
+        return gocept.selenium.zope2.get_current_db()
 
+
 def test_suite():
     return unittest.makeSuite(Zope212Tests)

Modified: gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_ztk.py
===================================================================
--- gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_ztk.py	2010-12-26 14:30:03 UTC (rev 119132)
+++ gocept.selenium/trunk/src/gocept/selenium/ztk/tests/test_ztk.py	2010-12-26 14:31:25 UTC (rev 119133)
@@ -12,12 +12,16 @@
 #
 ##############################################################################
 
+import gocept.selenium.tests.isolation
 import gocept.selenium.ztk.testing
-import gocept.selenium.tests.isolation
+import zope.app.testing.functional
 
 
 class ZTKTests(gocept.selenium.tests.isolation.IsolationTests,
                gocept.selenium.ztk.testing.TestCase):
 
+    def getDatabase(self):
+        return zope.app.testing.functional.FunctionalTestSetup().db
+
     def test_selenium_http_500_handling(self):
         self.selenium.open('http://%s/error.html' % self.selenium.server)



More information about the checkins mailing list