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

Tuesday, November 11, 2008

World Plone Day Toronto 2008 Reflections

On Friday November 7th, the Toronto Plone Users put on a World Plone Day event at the Center for Social Innovation in Toronto, Canada.

Over 40 people showed up to listen to 5 talks that ranged from an introduction to Plone, several case studies and a technical talk covering rapid application development with Plone. We raffled away two books that were generously donated by O'Reilly.

We streamed live via ustream.tv and the recordings and slides from the presenters are now available.

Based on our pre-event survey we had a wide range of attendees: new to Plone, developers, managers and end-users from corporate, non-profit and government sectors. We consider the event to be a huge success!

The event was sponsored by my company Scryent, and co-sponsors Digital Opportunity Trust (DOT) and Softwerke.

Hugh Ranalli of DOT also blogged about the event.

The November Toronto Plone Users is meeting today, Tuesday November 11.

Sunday, November 02, 2008

World Plone Day Toronto

Only 5 days before World Plone Day on November 7!

In Toronto we're organizing a half-day of talks and case studies on Plone at the Center for Social Innovation (near Queen & Spadina).

I'll be giving two talks at the event: one mainly geared to decision makers who want to know why Plone is a competitive CMS solution and what it can do for them. The second talk is "Intranet in 25 minutes" where I'll show building an intranet in real time that truly showcases the power of Plone out of the box and also showcase a couple key add-ons.

Along with myself will be 3 other speakers covering case studies of Plone in government and non-profit organizations, along with using ArchGenXML to generate Plone applications from UML diagrams.

There's more info available at the Toronto Plone Users website.

There's a free registration and those who do register will get a chance to win one of two Python books donated by O'Reilly!

Space in our room is limited so register now!

Thursday, July 17, 2008

Zope Crashing, MaximumRecursion & OSX Thread Stack Size

Just making a note here about a problem I had earlier today with Zope crashing under OSX and how I fixed it. This is apparently a well-known issue with Zope on some platforms including OSX and FreeBSD.

I had just made a small change to an Archetypes schema in my content class and reloaded the relevant code. When I refreshed the page Zope chugged away for a bit and then Zope just exited silently.

Using gdb I tried the request again and found that it was dying from a memory access violation.

When I exercised the same code under the testrunner I found that due to a small omission on my part I had triggered a MaximumRecursion exception.

So as it turns out there are different stack sizes for child threads. And under OSX this size is smaller than Zope requires.

Now Python 2.5 has a runtime option in the thread module to set the stack size, but Python 2.4, required for Zope 2.X, does not.

The solution I found for Python 2.4 was build Python myself, adding -DTHREAD_STACK_SIZE=0x100000 to CFLAGS in the Python Makefile. This sets to the child stack size to 1 meg which seems to be sufficient.

Now when I run the offending code in the Zope server it no longer crashes and` correctly displays the MaximumRecursion exception.

Friday, May 30, 2008

Testing OSX applications with Python

Recently I started a new wxPython based project that would be primarily be running on OSX.

I figured it would be rather useful and neat to have some tests managed by Python that would exercise the whole system so I took a stab at setting some up using some of my standard toolkit. You don't have to test exclusively Python-based application. You can use Python to write the test scripts for the application under test which could be written in any language that OSX supports.

How

I found out that Apple has already added all the hooks for scripting the UI via an AppleScript service called GUI Scripting or System Events. You can script it easily via AppleScript. Or you can access it all from Python using the appscript module.

It's Easy

Here's some code to get you started testing with GUI Scripting.
from appscript import *
import time

# some boilerplate setup
sysevents = app('System Events')
app('Finder').activate()
sysevents.processes['Finder'].menu_bars[1].menus[u'Apple'].menu_items[u'About This Mac'].click()

# some operations may need a pause
time.sleep(1)

loginwindow = sysevents.processes['loginwindow']

# Check that an About this Mac window exists
assert [ win for win in loginwindow.windows() if win.title() == 'About This Mac']

# Check that there is an All Rights Reserved present.
assert loginwindow.windows['About This Mac'].static_texts['All Rights Reserved.'].get()
The example is a little convoluted because Finder owns the "About This Mac" menu entry, but loginwindow owns the window that pops up.
The hardest part was figuring out the API to work with Apple's UI scripting support. Especially since all of Apple's examples were in AppleScript. Thankfully theres some tools that come with appscript that help. One called ASDictionary that autogenerates documentation for the various AppleScript plugins on your system. Also ASTranslate helps with the job of translating AppleScript code into equivalent appscript usage under Python.

Is that all?

There don't seem to be many Open Source GUI Functional Testing tools for OSX. Anyone know any ? Lots for Web functional testing but not for UI.

There is pywinauto for Windows.

Might be nice if OSX had a similar test layer. pymacauto anyone?

Thursday, May 22, 2008

quick way to debug testbrowser in doctests

Writing doctests I am sometimes wondering what is going on under the hood. Keeping this function around in my test environment makes it a bit easier to find out. After rendering the output to the browser it will pause waiting for a keypress to continue the test.  Note, this code works only on OSX though it should be trivial to modify for any OS.
>>> def render_browser(browser):
...   open('/tmp/browser.html', 'w').write(browser.contents)
...   import os
...   os.system("open /tmp/browser.html")
...   _ = raw_input()