Friday, June 05, 2009

Internationalization & Theming with Deliverance: Toronto Plone Users June 2009 Meeting

This coming Tuesday June 9th the Toronto Plone Users will meet. We have a information packed session ahead of us.

Amir Toole will be presenting on creating multi-lingual plone sites using Plone's excellent Internationalization support.

And following up on our progress on documenting Deliverance for Plone at the Plone Symposium East 2009 Sprint, Adam Kozlowski and myself will be presenting a quick look at Deliverance and how this tool can be used to quickly and easily theme a Plone site.

Toronto Plone Users is welcome to all. Hope you can join us!

Tuesday, June 02, 2009

Testing ZODB changes

Came up with a handy decorator for asserting that a given method does not modify the ZODB.

It can be used in tests to ensure that a given test does not change the ZODB in any way (for example views).

I came up with it because a regression was discovered where a read-only view was modifying some things in ZODB causing ConflictErrors.To ensure it would not come up again I had to come up with a failing test.

def readonly(fun):
  """
    requires that self is at least a PortalTestCase
    
    Usage hint:  Use a savepoint to reset the changed object listing.
    """

  def decorated(self, *args, **kwargs):
      # this resets registered objects
      transaction.savepoint()
      before = list(self.portal._p_jar._registered_objects)
      result = fun(self, *args, **kwargs)
      after = self.portal._p_jar._registered_objects
      if before != after:
          s = SequenceMatcher(a=before, b=after)
          for tag, i1, i2, j1, j2 in s.get_opcodes():
              print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % (tag, i1, i2, before[i1:i2], j1, j2, after[j1:j2]))
          raise RuntimeError("ZODB was changed")
      return result
  return decorated

An example of usage:

class ReadonlyTests(YourTestCase):
 @readonly
 # This one will fail
 def test_isnt_readonly(self):
     self.portal.invokeFactory('Document', 'test')
     self.portal.test.setTitle('foo')

 @readonly
 def test_is_readonly(self):
     pass