[KSS-checkins] r53362 - in kukit/kss.demo/branch/1.4-kss-test/kss/demo: . browser selenium_utils tests

reebalazs at codespeak.net reebalazs at codespeak.net
Fri Apr 4 22:43:39 CEST 2008


Author: reebalazs
Date: Fri Apr  4 22:43:37 2008
New Revision: 53362

Added:
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layer.py   (contents, props changed)
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layers.txt   (contents, props changed)
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_suite.py   (contents, props changed)
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/base.py   (contents, props changed)
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_doc.py   (contents, props changed)
Modified:
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/__init__.py
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/registry.py
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setDevMode.html
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setProdMode.html
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/registry.py
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/resource.py
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_utils/builder.py
   kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_demoview.py
Log:
Implement selenium layers properly.

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/__init__.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/__init__.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/__init__.py	Fri Apr  4 22:43:37 2008
@@ -1,4 +1,15 @@
 
+from resource import (
+    KSSSeleniumTestCase,
+    KSSSeleniumTestDirectory,
+    KSSSeleniumTestCaseList,
+    KSSSeleniumEmptyTestCase,
+    KSSSeleniumSandboxCreationTestCase,
+    KSSDemo,
+    )
+from selenium_layer import KSSSeleniumTestLayerBase
+from selenium_suite import KSSSeleniumTestSuite
+
 try:
     import Products.Five
 except ImportError:

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/registry.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/registry.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/registry.py	Fri Apr  4 22:43:37 2008
@@ -5,6 +5,7 @@
 from zope.component import getUtility
 from zope.interface import implements
 from kss.demo.selenium_utils.builder import cookSeleniumTests
+from kss.demo.selenium_layer import LayerTree
 import os.path
 import re
 import urllib
@@ -71,16 +72,15 @@
         """Get selenium tests annotated with title and href."""
         registry = getUtility(IKSSDemoRegistry)
         results = []
-        for suite in registry.test_suites:
-            if not suite.application.lower() == application.strip().lower():
-                continue
-            relative_urls = suite.get_relative_urls(self)
-            for relative_url in relative_urls:
-                results.append(dict(
-                    href = relative_url,
-                    title = self._url_to_title(relative_url),
-                    ))
-        return results
+        # Filter suites that we need.
+        suites = [suite for suite in registry.test_suites
+                 if suite.application.lower() == application.strip().lower()]
+        # Build a tree with the suite, to find the optimal setup / teardown sequence. 
+        tree = LayerTree(suites)
+        return [dict(
+                href = relative_url,
+                title = self._url_to_title(relative_url),
+                ) for relative_url in tree.get_relative_urls(self)]
 
     @staticmethod
     def _url_to_title(url):

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setDevMode.html
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setDevMode.html	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setDevMode.html	Fri Apr  4 22:43:37 2008
@@ -1,7 +1,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<title>basic_commands</title>
+<title>Set development mode</title>
 </head>
 <body>
 <table cellpadding="1" cellspacing="1" border="1">

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setProdMode.html
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setProdMode.html	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/browser/setProdMode.html	Fri Apr  4 22:43:37 2008
@@ -1,7 +1,7 @@
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<title>basic_commands</title>
+<title>Set production mode</title>
 </head>
 <body>
 <table cellpadding="1" cellspacing="1" border="1">

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/registry.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/registry.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/registry.py	Fri Apr  4 22:43:37 2008
@@ -12,7 +12,7 @@
     IKSSDemoRegistryEvent,
     IKSSSeleniumTestSuite
     )
-from resource import KSSSeleniumTestSuite
+from selenium_suite import KSSSeleniumTestSuite
 from zope.component.interfaces import (
     IUtilityRegistration,
     IRegistered,

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/resource.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/resource.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/resource.py	Fri Apr  4 22:43:37 2008
@@ -2,7 +2,6 @@
 from interfaces import (
     IKSSDemo,
     IKSSSeleniumTestCaseCollection,
-    IKSSSeleniumTestSuite,
     )
 import itertools
 import sys, os.path
@@ -36,18 +35,15 @@
     "Abstract base class to help collection implementation"
 
     def find_test_root_dir(self):
-        # XXX This must be called directly from the __init__ of the class
-        # and it is able to find out the directory where the class
-        # was defined from.
         #
         # Zope CA magic used to find where the class is defined from
         frame = sys._getframe(2)
-        locals = frame.f_locals
+        f_locals = frame.f_locals
         # Try to make sure we were called from a class def
-        if (locals is frame.f_globals) or ('__module__' not in locals):
-            raise TypeError("classProvides can be used only from a class definition.")
+        if (f_locals is frame.f_globals) or ('__module__' not in f_locals):
+            raise TypeError("The %r suite class can be used only from inside a class definition." % (self.__class__, ))
         # store it
-        self.test_root_dir = getRootDirOfModule(locals['__module__'])
+        self.test_root_dir = getRootDirOfModule(f_locals['__module__'])
 
     def find_absolute_path(self, filename):
         "Returns the absolute path of the filename based on the test root directory."
@@ -70,7 +66,7 @@
         test_filename = self.find_absolute_path(self.test_filename)
         return [test_filename]
 
-    def get_relative_urls(self, view):
+    def get_relative_urls(self, view, tree=None):
         return self.make_relative_urls_from_paths(self.get_testcase_paths())
 
 class KSSSeleniumTestDirectory(KSSSeleniumTestCaseCollectionBase):
@@ -96,7 +92,7 @@
         test_filenames = (filename[1] for filename in test_filenames)
         return test_filenames
 
-    def get_relative_urls(self, view):
+    def get_relative_urls(self, view, tree=None):
         return self.make_relative_urls_from_paths(self.get_testcase_paths())
 
 class KSSSeleniumTestCaseList(KSSSeleniumTestCaseCollectionBase):
@@ -111,8 +107,8 @@
     def get_testcase_paths(self):
         return itertools.chain(*[case.get_testcase_paths() for case in self.cases])
 
-    def get_relative_urls(self, view):
-        return itertools.chain(*[case.get_relative_urls(view) for case in self.cases])
+    def get_relative_urls(self, view, tree=None):
+        return itertools.chain(*(case.get_relative_urls(view, tree) for case in self.cases))
 
 class KSSSeleniumEmptyTestCase(KSSSeleniumTestCaseCollectionBase):
     """An empty test case."""
@@ -121,11 +117,11 @@
     def get_testcase_paths(self):
         return ()
 
-    def get_relative_urls(self, view):
+    def get_relative_urls(self, view, tree=None):
         return ()
 
 
-class KSSSandboxCreationTestCase(KSSSeleniumTestCaseCollectionBase):
+class KSSSeleniumSandboxCreationTestCase(KSSSeleniumTestCaseCollectionBase):
     """A method that can be used for sandbox creation.
     
     It is a factory-generated test. It visits a page with a button
@@ -148,93 +144,10 @@
     def get_testcase_paths(self):
         return ()
 
-    def get_relative_urls(self, view):
+    def get_relative_urls(self, view, tree=None):
         relative_url = "@@kss-selenium-create-sandbox-test.html?method_name=%s&admin_username=%s&admin_password=%s" % (
                         self.method_name,
                         view.request.get('admin_username', ''),
                         view.request.get('admin_password', ''))
         return (relative_url, )
 
-# --
-# The Suite itself is a collection, but provides a way to use layers with setup and
-# tesrdown from the registry.
-# --
-
-class KSSSeleniumTestLayerBase(object):
-    setup = KSSSeleniumEmptyTestCase()
-    teardown = KSSSeleniumEmptyTestCase()
-
-
-# XXX TODO Currently the tuest suite handles the setup and teardown
-# of the layer. The suites with the same layer should have a common
-# setup. Very easy to implement.
-class KSSSeleniumTestSuite(KSSSeleniumTestCaseCollectionBase):
-    """Represents a selenium suite,
-    that makes part of an applicaion and a component,
-    as well as an optional (test) layer.
-    """
-    implements(IKSSSeleniumTestSuite)
- 
-    # convenience access for page templates
-    __allow_access_to_unprotected_subobjects__ = 1
-
-    tests = KSSSeleniumEmptyTestCase()
-    component = u''
-    application = u''
-    # default is an empty layer.
-    layer = KSSSeleniumTestLayerBase
-    dual_mode = True
-
-    # resources we use for dual mode
-    set_devel_mode = KSSSeleniumTestCase('browser/setDevMode.html')
-    set_prod_mode = KSSSeleniumTestCase('browser/setProdMode.html')
-
-    def __init__(self, tests=None, component=u'', application=u'', layer=None, dual_mode=None):
-        if tests is not None:
-            self.tests = tests
-        self.test_root_dir = self.tests.test_root_dir
-        if component:
-            self.component = component
-        if application:
-            self.application = application
-        if layer is not None:
-            self.layer = layer
-        if dual_mode is not None:
-            self.dual_mode = dual_mode
-
-    def get_testcase_paths(self):
-        return itertools.chain(
-            * (case.get_testcase_paths() for case in self.iterables)
-            )
-
-    def get_relative_urls(self, view):
-        return itertools.chain(
-            * (case.get_relative_urls(view) for case in self.iterables)
-            )
-
-    @property
-    def iterables(self):
-        # Check if we have tests at all. If no, we don't need the
-        # setup/teardown.
-        #
-        # The next expression decided if the iterator yields any resuls
-        # TODO make an empty() on the interface ?
-        if tuple(itertools.islice(self.tests.get_relative_urls(self), 0, 1)):
-            if self.dual_mode:
-                return (
-                    self.layer.setup,
-                    self.set_devel_mode,
-                    self.tests,
-                    self.set_prod_mode,
-                    self.tests,
-                    self.layer.teardown,
-                )
-            else:
-                return (
-                    self.layer.setup,
-                    self.tests,
-                    self.layer.teardown,
-                )
-        else:
-            # No tests, nothing to do.
-            return ()

Added: kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layer.py
==============================================================================
--- (empty file)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layer.py	Fri Apr  4 22:43:37 2008
@@ -0,0 +1,202 @@
+"""\
+Layers for Selenium tests
+
+The goal of this structures to allow to generate the setup / teardown
+sequence in an optimal way. Since we have to group tests to be
+executed in the same layer, and trim away layers with no tests,
+this must be done for each list of selected testcases.
+"""
+
+import itertools
+from resource import KSSSeleniumEmptyTestCase, KSSSeleniumTestCase
+
+class metalayer(type):
+    def __init__(cls, name, bases, attrs):
+        # If setup or teardown are not declared, we consider
+        # them as empty.
+        if 'setup' not in attrs:
+            attrs['setup'] = KSSSeleniumEmptyTestCase()
+        if 'teardown' not in attrs:
+            attrs['teardown'] = KSSSeleniumEmptyTestCase()
+        # Set up the class
+        super(metalayer, cls).__init__(name, bases, attrs)
+
+class KSSSeleniumTestLayerBase(object):
+    __metaclass__ = metalayer
+    setup = KSSSeleniumEmptyTestCase()
+    teardown = KSSSeleniumEmptyTestCase()
+
+class LayerTreeNode(object):
+    """An active tree node
+    
+    It participates in a Tree, it contains a list of suites.
+    If the list of suites is empty, that means this layer
+    is "inactive" in the tree: it is needed for the setup / teardown
+    sequence, but contains no tests itself.
+    """
+
+    # resources we use for dual mode
+    set_devel_mode = KSSSeleniumTestCase('browser/setDevMode.html')
+    set_prod_mode = KSSSeleniumTestCase('browser/setProdMode.html')
+
+    def __init__(self, tree, layer):
+        self.tree = tree
+        self.suites = []
+        self.layer = layer
+        self.base_layer = None
+        self.child_nodes = []
+
+    def add_suite(self, suite):
+        assert suite.layer == self.layer, "Suite must have the same layer."
+        self.suites.append(suite)
+
+    def add_parents(self):
+        layer = self.layer
+        # A layer must be a subclass of the base class.
+        assert issubclass(layer, KSSSeleniumTestLayerBase), \
+                "Layers must be classes inheriting from KSSSeleniumTestLayerBase."
+        # Find out the bases. We only consider bases
+        # that inherit from the base class.
+        try:
+            bases = layer.__bases__
+        except AttributeError:
+            raise Exception, "Layers must be classes."
+        bases = (base for base in bases if issubclass(base, KSSSeleniumTestLayerBase))
+        # We are only interested in the first base.
+        # (In case of multi inheritence we could have more bases, 
+        # and although this is hardly imaginable as use case, it is supported anyway.)
+        bases = tuple(itertools.islice(bases, 0, 1))
+        # There must be one base,
+        # unless we are at the root node.
+        if not bases:
+            # We found the root.
+            assert layer == KSSSeleniumTestLayerBase, "This should not happen."
+            self.tree.root = self
+        else:
+            if len(bases) > 1:
+                raise Exception, "Double inheritence of layers is not supported."
+            base_layer = bases[0]
+            # Add this layer to the tree as well, and remember it.
+            base_node = self.tree.add_layer(base_layer)
+            # Mark ourselves as a child node.
+            self.base_node = base_node
+            base_node.child_nodes.append(self)
+
+    def get_testcase_paths_for_layer(self):
+        """Give the absolute paths "owned" for the layer itself."""
+        # order does not matter here.
+        # Just needed to enable these resources.
+        # Also: enable the set devel / prod resources always.
+        if self.base_layer is not None:
+            base_layer_tests = self.tree.lookup_layer(self.base_layer).get_testcase_paths_for_layer(),
+        else:
+            # We are at the root.
+            base_layer_tests = ()
+        # Compose all paths
+        return itertools.chain(
+            base_layer_tests,
+            self.layer.setup.get_testcase_paths(),
+            self.layer.teardown.get_testcase_paths(),
+            self.set_devel_mode.get_testcase_paths(),
+            self.set_prod_mode.get_testcase_paths(),
+            )
+
+    def get_relative_urls_for_own_tests(self, view):
+        """Generate the tests in all suites in this layer subtree. No setup or teardown.
+        
+        This is needed to decide if we can prune a part of the tree (if there are no tests,
+        setup and teardown are not needed either.)
+        """
+        return itertools.chain(
+            # Our own tests
+            itertools.chain(*(suite.tests.get_relative_urls(view, self.tree) for suite in self.suites)),
+            # Tests from the child layers
+            itertools.chain(*(node.get_relative_urls_for_own_tests(view) for node in self.child_nodes)),
+            )
+
+    def get_relative_urls(self, view):
+        """Generate the walking order for the node and its subtree."""
+        # Look ahead check if we have any tests at all.
+        suite_tests_lookahead = self.get_relative_urls_for_own_tests(view)
+        # Do we have tests at all?
+        # If not, we just don't want to emit setup and teardown at all.
+        if tuple(itertools.islice(suite_tests_lookahead, 0, 1)):
+            # Does any of our suites have dual mode?
+            suites_by_duality = {True: [], False: []}
+            for suite in self.suites:
+                suites_by_duality[suite.dual_mode].append(suite)
+            # Create the tests for suites.
+            # Look ahead check if we have any dual tests at all.
+            dual_tests_lookahead = itertools.chain(*(suite.tests.get_relative_urls(view, self.tree) for suite in suites_by_duality[True]))
+            if tuple(itertools.islice(dual_tests_lookahead, 0, 1)):
+                # We do have some dual tests. So we need to run them twice and set 
+                # development and production modes for this.
+                dual_tests = itertools.chain(
+                    # Dual mode tests from the suites in this layer, in Development mode
+                    self.set_devel_mode.get_relative_urls(view, self.tree),                
+                    itertools.chain(*(suite.tests.get_relative_urls(view, self.tree) for suite in suites_by_duality[True])),
+                    # Dual mode tests from the suites in this layer, in Production mode
+                    self.set_prod_mode.get_relative_urls(view, self.tree),              
+                    itertools.chain(*(suite.tests.get_relative_urls(view, self.tree) for suite in suites_by_duality[True])),
+                    )
+            else:
+                # No dual mode tests
+                dual_tests = ()
+            # Now put them together with setup and teardown
+            return itertools.chain(
+                # Setup from parent layer
+                self.layer.setup.get_relative_urls(view, self.tree),
+                # Dual mode tests from the suites in this layer
+                dual_tests,
+                # Single mode tests from the suites in this layer
+                itertools.chain(*(suite.tests.get_relative_urls(view, self.tree) for suite in suites_by_duality[False])),
+                # Add tests recursively from contained layers
+                itertools.chain(*(node.get_relative_urls(view) for node in self.child_nodes)),
+                # Teardown to parent layer
+                self.layer.teardown.get_relative_urls(view, self.tree),
+                )
+        else:
+            # No tests at all.
+            return ()
+
+class LayerTree(object):
+    """Builds an inheritence tree from the suites passed in.
+    """
+    def __init__(self, suites=()):
+        self.suites = []
+        self.nodes = {}
+        self.root = None
+        self.add_suites(suites)
+
+    def add_suites(self, suites):
+        for suite in suites:
+            self.add_suite(suite)
+
+    def add_suite(self, suite):
+        # make sure we have the layer
+        node = self.add_layer(suite.layer)
+        # Add the suite to the node.
+        node.add_suite(suite)
+
+    def add_layer(self, layer):
+        # We must fetch the layer node from the tree,
+        # or create it and its inheritence path to the root layer.
+        try:
+            node = self.nodes[layer]
+        except KeyError:
+            # create and remember the tree node
+            node = self.nodes[layer] = LayerTreeNode(self, layer)
+            # Recursively process parents of the node
+            node.add_parents()
+        # caller may access the node
+        return node
+
+    def lookup_layer(self, layer):
+        return self.nodes[layer]
+
+    def get_relative_urls(self, view):
+        """Generate all tests in the tree."""
+        if not self.root:
+            # no root, no tests.
+            return ()
+        return self.root.get_relative_urls(view)

Added: kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layers.txt
==============================================================================
--- (empty file)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_layers.txt	Fri Apr  4 22:43:37 2008
@@ -0,0 +1,282 @@
+====================
+Selenium test layers
+====================
+
+We can make our own layer by inheritance from the base class.
+
+   >>> from kss.demo import KSSSeleniumTestLayerBase
+   
+Create the class:
+
+    >>> class aLayer(KSSSeleniumTestLayerBase):
+    ...     pass
+
+Since we did not overwrite setup and teardown, they must be emtpy.
+
+    >>> aLayer.setup
+    <kss.demo.resource.KSSSeleniumEmptyTestCase object at ...>
+    >>> aLayer.teardown
+    <kss.demo.resource.KSSSeleniumEmptyTestCase object at ...>
+
+
+Specifying setup and teardown for a layer
+-----------------------------------------
+
+A more sensible use case is to create some setup and teardown for
+ourselves. We will create test cases that refer to non-existent
+files, now.
+
+   >>> from kss.demo import KSSSeleniumTestCase
+
+    >>> class aLayer(KSSSeleniumTestLayerBase):
+    ...     setup = KSSSeleniumTestCase('1') 
+    ...     teardown = KSSSeleniumTestCase('2') 
+
+    >>> aLayer.setup
+    <kss.demo.resource.KSSSeleniumTestCase object at ...>
+    >>> aLayer.teardown
+    <kss.demo.resource.KSSSeleniumTestCase object at ...>
+
+We also check the actual urls that will result the testing
+sequence. Note that get_relative_urls would take the view
+in normal use, but it is not important for this test.
+
+    >>> tuple(aLayer.setup.get_relative_urls(None))
+    ('@@resource?path=1',)
+    >>> tuple(aLayer.teardown.get_relative_urls(None))
+    ('@@resource?path=2',)
+
+If I inherit from this layer, and I do not overwrite setup
+or teardown, it still gets an empty value.
+
+    >>> class zLayer(KSSSeleniumTestLayerBase):
+    ...     setup = KSSSeleniumTestCase('3') 
+
+    >>> zLayer.setup
+    <kss.demo.resource.KSSSeleniumTestCase object at ...>
+    >>> tuple(zLayer.setup.get_relative_urls(None))
+    ('@@resource?path=3',)
+
+    >>> zLayer.teardown
+    <kss.demo.resource.KSSSeleniumEmptyTestCase object at ...>
+
+
+Inheriting layers
+-----------------
+
+Now, let us inherit a layer from another layer.
+
+    >>> class bLayer(aLayer):
+    ...     setup = KSSSeleniumTestCase('5') 
+    ...     teardown = KSSSeleniumTestCase('6')
+
+And another one.
+
+    >>> class cLayer(aLayer):
+    ...     setup = KSSSeleniumTestCase('7') 
+    ...     teardown = KSSSeleniumTestCase('8')
+
+Even we inherit more.
+
+    >>> class dLayer(cLayer):
+    ...     setup = KSSSeleniumTestCase('9') 
+    ...     teardown = KSSSeleniumTestCase('10')
+
+    >>> class eLayer(cLayer):
+    ...     setup = KSSSeleniumTestCase('11')
+    ...     teardown = KSSSeleniumTestCase('12')
+
+
+This results in the following inheritence structure::
+
+        a
+        |
+      ------  
+      |    |
+      b    c
+           |
+           ------
+           |    |
+           d    e
+
+
+Using layers from suites
+========================
+
+We define suites that use the above layers.
+
+    >>> from kss.demo import KSSSeleniumTestSuite
+
+The following definition to define a suite would fail:
+
+    >>> aSuite = KSSSeleniumTestSuite(
+    ...     tests = KSSSeleniumTestCase('ta'),
+    ...     layer = aLayer,
+    ...     )
+    Traceback (most recent call last):
+    ...
+    TypeError: The <class 'kss.demo.resource.KSSSeleniumTestCase'> suite class can be used only from inside a class definition.
+
+As the error message says, we must define the class from inside another
+class. This is used to locate files relatively from the module where the
+definition is. So we have to define them from a dummy plugin (however the plugin
+itself play no role in testing).
+
+    >>> class DummyPlugin(object):
+    ...
+    ...     aSuite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('ta'),
+    ...         layer = aLayer,
+    ...         )
+    >>> aSuite = DummyPlugin.aSuite
+
+The layers whose tests are active, will build a tree to compute the
+optimal setup-teardown order.
+
+    >>> from kss.demo.selenium_layer import LayerTree
+
+    >>> tree = LayerTree((aSuite, ))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=2')
+
+As we can see in the above results, the tests are generated properly with setup and teardown.
+
+If more cases use the same layer, they are grouped. They also just need to change
+the production mode twice:
+
+    >>> class DummyPlugin2(object):
+    ...
+    ...     a2Suite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('ta2'),
+    ...         layer = aLayer,
+    ...         )
+    >>> a2Suite = DummyPlugin2.a2Suite
+
+    >>> tree = LayerTree((aSuite, a2Suite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=ta2', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=ta2', '@@resource?path=2')
+
+Now, let's see the tests for the other suites:
+
+    >>> class DummyPlugin3(object):
+    ...
+    ...     bSuite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('tb'),
+    ...         layer = bLayer,
+    ...         )
+    ...
+    ...     cSuite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('tc'),
+    ...         layer = cLayer,
+    ...         )
+    ...
+    ...     dSuite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('td'),
+    ...         layer = dLayer,
+    ...         )
+    ...
+    ...     d2Suite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('td2'),
+    ...         layer = dLayer,
+    ...         )
+    ...     eSuite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('te'),
+    ...         layer = eLayer,
+    ...         )
+    ...
+    >>> bSuite = DummyPlugin3.bSuite
+    >>> cSuite = DummyPlugin3.cSuite
+    >>> dSuite = DummyPlugin3.dSuite
+    >>> d2Suite = DummyPlugin3.d2Suite
+    >>> eSuite = DummyPlugin3.eSuite
+
+And see what happens if only suite "d" is selected. We can see that the setup and teardown sequence is correct.
+
+    >>> tree = LayerTree((dSuite, ))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=7', '@@resource?path=9', '@@resource?path=...setDevMode.html', '@@resource?path=td', '@@resource?path=...setProdMode.html', '@@resource?path=td', '@@resource?path=10', '@@resource?path=8', '@@resource?path=2')
+
+
+Replay this with a bunch of other tests added. Setup and teardown is executed ideally.
+In this case we have more dev / prod mode changes, since these are the fastest
+they are wrapped inside the layer.
+
+    >>> tree = LayerTree((cSuite, dSuite, d2Suite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=7', '@@resource?path=...setDevMode.html', '@@resource?path=tc', '@@resource?path=...setProdMode.html', '@@resource?path=tc', '@@resource?path=9', '@@resource?path=...setDevMode.html', '@@resource?path=td', '@@resource?path=td2', '@@resource?path=...setProdMode.html', '@@resource?path=td', '@@resource?path=td2', '@@resource?path=10', '@@resource?path=8', '@@resource?path=2')
+
+So far we have only tried single path setups. Let's now try a split route.
+The setup and teardown work correctly in one level.
+
+    >>> tree = LayerTree((aSuite, bSuite, cSuite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=5', '@@resource?path=...setDevMode.html', '@@resource?path=tb', '@@resource?path=...setProdMode.html', '@@resource?path=tb', '@@resource?path=6', '@@resource?path=7', '@@resource?path=...setDevMode.html', '@@resource?path=tc', '@@resource?path=...setProdMode.html', '@@resource?path=tc', '@@resource?path=8', '@@resource?path=2')
+
+
+Another functionality is that certain tests may mark that they don't want
+to be in dual mode:
+
+    >>> class DummyPlugin4(object):
+    ...
+    ...     b2Suite = KSSSeleniumTestSuite(
+    ...         tests = KSSSeleniumTestCase('tb2'),
+    ...         layer = bLayer,
+    ...         dual_mode = False,
+    ...         )
+    >>> b2Suite = DummyPlugin4.b2Suite
+
+    >>> tree = LayerTree((b2Suite, ))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=5', '@@resource?path=tb2', '@@resource?path=6', '@@resource?path=2')
+
+This is also true if both dual and single mode tests are in the same layer. In this case
+the tests will wun in the end, which in practice means Production mode.
+
+    >>> tree = LayerTree((bSuite, b2Suite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=5', '@@resource?path=...setDevMode.html', '@@resource?path=tb', '@@resource?path=...setProdMode.html', '@@resource?path=tb', '@@resource?path=tb2', '@@resource?path=6', '@@resource?path=2')
+
+Now let's add a bunch of suites:
+
+    >>> tree = LayerTree((aSuite, a2Suite, bSuite, b2Suite, cSuite, dSuite, d2Suite, eSuite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=ta2', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=ta2', '@@resource?path=5', '@@resource?path=...setDevMode.html', '@@resource?path=tb', '@@resource?path=...setProdMode.html', '@@resource?path=tb', '@@resource?path=tb2', '@@resource?path=6', '@@resource?path=7', '@@resource?path=...setDevMode.html', '@@resource?path=tc', '@@resource?path=...setProdMode.html', '@@resource?path=tc', '@@resource?path=9', '@@resource?path=...setDevMode.html', '@@resource?path=td', '@@resource?path=td2', '@@resource?path=...setProdMode.html', '@@resource?path=td', '@@resource?path=td2', '@@resource?path=10', '@@resource?path=11', '@@resource?path=...setDevMode.html', '@@resource?path=te', '@@resource?path=...setProdMode.html', '@@resource?path=te', '@@resource?path=12', '@@resource?path=8', '@@resource?path=2')
+
+
+Finally, we still need to check the following:
+Suites that are empty are skipped, and no setup teardown or devel / prod mode is generated for them.
+
+    >>> class DummyPlugin5(object):
+    ...
+    ...     a3Suite = KSSSeleniumTestSuite(
+    ...
+    ...         )
+    ...
+    ...     b3Suite = KSSSeleniumTestSuite(
+    ...         )
+    >>> a3Suite = DummyPlugin5.a3Suite
+    >>> b3Suite = DummyPlugin5.b3Suite
+
+    >>> tree = LayerTree((a3Suite, ))
+    >>> tuple(tree.get_relative_urls(None))
+    ()
+
+    >>> tree = LayerTree((a3Suite, aSuite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=2')
+
+    >>> tree = LayerTree((a3Suite, b3Suite))
+    >>> tuple(tree.get_relative_urls(None))
+    ()
+
+    >>> tree = LayerTree((a3Suite, b3Suite, bSuite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=5', '@@resource?path=...setDevMode.html', '@@resource?path=tb', '@@resource?path=...setProdMode.html', '@@resource?path=tb', '@@resource?path=6', '@@resource?path=2')
+
+    >>> tree = LayerTree((aSuite, a3Suite, b3Suite, bSuite))
+    >>> tuple(tree.get_relative_urls(None))
+    ('@@resource?path=1', '@@resource?path=...setDevMode.html', '@@resource?path=ta', '@@resource?path=...setProdMode.html', '@@resource?path=ta', '@@resource?path=5', '@@resource?path=...setDevMode.html', '@@resource?path=tb', '@@resource?path=...setProdMode.html', '@@resource?path=tb', '@@resource?path=6', '@@resource?path=2')
+
+
+And this concludes the layer tests.
+

Added: kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_suite.py
==============================================================================
--- (empty file)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_suite.py	Fri Apr  4 22:43:37 2008
@@ -0,0 +1,65 @@
+
+import itertools
+from resource import (
+    KSSSeleniumTestCaseCollectionBase,
+    KSSSeleniumEmptyTestCase,
+    )
+from interfaces import IKSSSeleniumTestSuite
+from selenium_layer import LayerTree, KSSSeleniumTestLayerBase
+from zope.interface import implements
+
+# --
+# The Suite itself is a collection, but provides a way to use layers with setup and
+# tesrdown from the registry.
+# --
+
+class KSSSeleniumTestSuite(KSSSeleniumTestCaseCollectionBase):
+    """Represents a selenium suite,
+    that makes part of an applicaion and a component,
+    as well as an optional (test) layer.
+    """
+    implements(IKSSSeleniumTestSuite)
+ 
+    # convenience access for page templates
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    tests = KSSSeleniumEmptyTestCase()
+    component = u''
+    application = u''
+    # default is an empty layer.
+    layer = KSSSeleniumTestLayerBase
+    dual_mode = True
+
+    def __init__(self, tests=None, component=u'', application=u'', layer=None, dual_mode=None):
+        if tests is not None:
+            self.tests = tests
+        if component:
+            self.component = component
+        if application:
+            self.application = application
+        if layer is not None:
+            self.layer = layer
+        if dual_mode is not None:
+            self.dual_mode = dual_mode
+
+    def get_testcase_paths(self, tree=None):
+        # No need to care for order. We only need to give
+        # access to resources that we "own":         
+        # from our setup, own tests, and
+        # teardown.
+        # 
+        if not tree:
+            # Just handle this suite individually.
+            tree = LayerTree()
+            tree.add_suites((self, ))
+        return itertools.chain(
+            self.tests.get_testcase_paths(),
+            tree.lookup_layer(self.layer).get_testcase_paths_for_layer()
+            )
+
+    def get_relative_urls(self, view, tree=None):
+        # Just handle this suite individually,
+        # even if we had a tree passed in.
+        tree = LayerTree()
+        tree.add_suites((self, ))
+        return tree.get_relative_urls(view)

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_utils/builder.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_utils/builder.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/selenium_utils/builder.py	Fri Apr  4 22:43:37 2008
@@ -8,7 +8,12 @@
 
 # root directory finding code
 def getRootDirOfModule(module_name):
-    return os.path.dirname(sys.modules[module_name].__file__)
+    if module_name == '__builtin__':
+        # This may happen in tests and interactively,
+        # and it's sensible to use an absolute path in this case.
+        return ''
+    else:
+        return os.path.dirname(sys.modules[module_name].__file__)
 
 def getRootDirOfClass(cls):
     return getRootDirOfModule(cls.__module__)

Added: kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/base.py
==============================================================================
--- (empty file)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/base.py	Fri Apr  4 22:43:37 2008
@@ -0,0 +1,13 @@
+
+from Products.Five.zcml import load_config
+from kss.core.tests.base import KSSViewTestCase
+import kss.demo
+
+class KSSDemoTestLayer(KSSViewTestCase.layer):
+    @classmethod
+    def setUp(cls):
+        load_config('meta.zcml', package=kss.demo)
+        load_config('configure.zcml', package=kss.demo)
+
+class KSSDemoTestCaseBase(KSSViewTestCase):
+    layer = KSSDemoTestLayer

Modified: kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_demoview.py
==============================================================================
--- kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_demoview.py	(original)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_demoview.py	Fri Apr  4 22:43:37 2008
@@ -17,30 +17,22 @@
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 # 02111-1307, USA.
 #
-import unittest, os
-from zope.testing import doctest
-from Testing.ZopeTestCase import ZopeTestCase
-from kss.core.tests.base import KSSViewTestCase
-from Products.Five.zcml import load_string, load_config
-import kss.demo
+import unittest
+from base import KSSDemoTestCaseBase
 
 try:
     import Products.Five
+    Products = Products         # to satisfy pyflakes
 except AttributeError:
     from kss.demo.simplecontent_z3 import SimpleContent
+    SimpleContent = SimpleContent # to satisfy pyflakes
 else:
     from kss.demo.simplecontent import SimpleContent
 
-class KSSDemoTestCase(KSSViewTestCase):
-
-    class layer(KSSViewTestCase.layer):
-        @classmethod
-        def setUp(cls):
-            load_config('meta.zcml', package=kss.demo)
-            load_config('configure.zcml', package=kss.demo)
+class KSSDemoTestCase(KSSDemoTestCaseBase):
 
     def afterSetUp(self):
-        KSSViewTestCase.afterSetUp(self)
+        KSSDemoTestCaseBase.afterSetUp(self)
         self.setDebugRequest()
         self.folder._setObject('demo', SimpleContent('Demo', 'Demo'))
         self.view = self.folder.demo.restrictedTraverse('getDivContent')

Added: kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_doc.py
==============================================================================
--- (empty file)
+++ kukit/kss.demo/branch/1.4-kss-test/kss/demo/tests/test_doc.py	Fri Apr  4 22:43:37 2008
@@ -0,0 +1,28 @@
+# Copyright (c) 2008
+# Authors:
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+#
+import unittest
+import doctest
+from zope.testing.doctestunit import DocFileSuite
+
+def test_suite():
+    return unittest.TestSuite((
+            DocFileSuite('selenium_layers.txt',
+                package = 'kss.demo',
+                optionflags = doctest.ELLIPSIS,
+            ),
+        ))


More information about the Kukit-checkins mailing list