[Checkins] SVN: Sandbox/ulif/grok-with-testing/src/grok/ Major refactoring. Support for unit tests.

Uli Fouquet uli at gnufix.de
Sat Aug 18 11:07:11 EDT 2007


Log message for revision 78943:
  Major refactoring. Support for unit tests.

Changed:
  U   Sandbox/ulif/grok-with-testing/src/grok/admin/app.py
  A   Sandbox/ulif/grok-with-testing/src/grok/admin/app_tests/utest.py
  U   Sandbox/ulif/grok-with-testing/src/grok/admin/ftests.py
  U   Sandbox/ulif/grok-with-testing/src/grok/admin/modtest.txt
  U   Sandbox/ulif/grok-with-testing/src/grok/meta.py
  U   Sandbox/ulif/grok-with-testing/src/grok/testing.py
  U   Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_class.py
  U   Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_module.py

-=-
Modified: Sandbox/ulif/grok-with-testing/src/grok/admin/app.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/admin/app.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/admin/app.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -22,6 +22,9 @@
     # This is currently not supported...
     grok.context(SampleApp)
 
+class MyUnitTest1(grok.testing.UnitDocTest):
+    grok.testing.file('app_tests/utest.py')
+
 grok.testing.file('modtest.txt')
 #grok.testing.file('')
 #grok.testing.file('Another.txt')

Added: Sandbox/ulif/grok-with-testing/src/grok/admin/app_tests/utest.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/admin/app_tests/utest.py	                        (rev 0)
+++ Sandbox/ulif/grok-with-testing/src/grok/admin/app_tests/utest.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -0,0 +1,29 @@
+##
+## utest.py
+## Login : <uli at pu.smp.net>
+## Started on  Fri Aug 17 14:43:19 2007 Uli Fouquet
+## $Id$
+## 
+## Copyright (C) 2007 Uli Fouquet
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## 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
+##
+
+"""
+
+  >>> 1 + 1
+  2
+  
+"""
+

Modified: Sandbox/ulif/grok-with-testing/src/grok/admin/ftests.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/admin/ftests.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/admin/ftests.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -2,11 +2,10 @@
 import unittest
 
 def test_suite():
-    import grok.admin
     # We must grok our package to get the test registrations...
     grok.grok('grok.admin')
     return grok.testing.test_suite()
 
-
 if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')
+    unittest.main()
+

Modified: Sandbox/ulif/grok-with-testing/src/grok/admin/modtest.txt
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/admin/modtest.txt	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/admin/modtest.txt	2007-08-18 15:07:10 UTC (rev 78943)
@@ -2,4 +2,4 @@
 This is a simple doctest:
 
   >>> 1+1
-  1
+  2

Modified: Sandbox/ulif/grok-with-testing/src/grok/meta.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/meta.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/meta.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -631,64 +631,60 @@
         setupUtility(site, intids, IIntIds)
         return intids
 
-class FunctionalDocTestGrokker(martian.ClassGrokker):
-    """This grokker is called, when ``grok.testing.file`` appears in a class.
+class DocTestGrokker(object):
+    """A pseudo base grokker for functional and unit doc tests.
 
-    It checks for existance of files given in a directive and adds
-    the 'surrounding' class to a list in ``grok.testing``.
+    It checks for existance of files given in a directive and feeds
+    appropriate info objects to grok.testing.
+
+    This is not a real grokker and therefore won't be called. Its only
+    purpose is, to deliver implementation code for derived classes.
     """
-    component_class = grok.testing.FunctionalDocTest
+    info_class = None
 
+    def call_adder(self, adder_func, *arg, **kw):
+        return adder_func(*arg)
+
     def grok(self, name, factory, context, module_info, templates):
         # Check whether the file given exists...
         docfilelist = getattr(factory, '__grok_testing_file__', [])
-
-        # Avoid duplicates...
-        docfileset = set()
-        docfileset.update(docfilelist)
-        docfilelist = list(docfileset)
-
         for docfile in docfilelist:
-            docfilepath = module_info.getResourcePath(docfile)
-            if not os.path.isfile(docfilepath):
+            docfile_info = self.info_class(module_info, docfile, factory)
+            if not os.path.isfile(docfile_info.absoluteDocFilePath()):
                 raise GrokError(
                     "Doctest file '%r' declared in %r does not exist "
                     "in %r." %
                     (docfile, module_info.dotted_name,
                      module_info.getResourcePath('')),
                     None)
-            # Add test class to global list...
-            grok.testing.add_functional_doctest(factory)
+            grok.testing.add_doctest_info(docfile_info)
         return True
 
 
-class FunctionalDocTestModuleGrokker(martian.GlobalGrokker):
+class FunctionalDocTestGrokker(DocTestGrokker, martian.ClassGrokker):
+    """This grokker is called, when ``grok.testing.file`` appears in a class.
+
+    It checks for existance of files given in a directive and feeds
+    appropriate info objects to grok.testing.
+    """
+    component_class = grok.testing.FunctionalDocTest
+    info_class = grok.testing.FunctionalDocTestInfo
+
+class FunctionalDocTestModuleGrokker(DocTestGrokker, martian.GlobalGrokker):
     """This grokker is called, when ``grok.testing.file`` appears in a module.
 
-    It checks for existance of files given in a directive and adds
-    the 'surrounding' module to a list in ``grok.testing``.
+    It checks for existance of files given in a directive and feeds
+    appropriate info objects to grok.testing.
     """
+    info_class = grok.testing.FunctionalDocTestInfo
 
-    def grok(self, name, module, context, module_info, templates):
-        docfilelist = module_info.getAnnotation('grok.testing.file', [])
-        if docfilelist == []:
-            return True
+class UnitDocTestGrokker(DocTestGrokker, martian.ClassGrokker):
+    """This grokker is called, when ``grok.testing.file`` appears in a class.
 
-        # Avoid duplicates...
-        docfileset = set()
-        docfileset.update(docfilelist)
-        docfilelist = list(docfileset)
-        for docfile in docfilelist:
-            if docfile == '':
-                docfile = "%s.py" % (module_info.name,)
-            docfilepath = module_info.getResourcePath(docfile)
-            if not os.path.isfile(docfilepath):
-                raise GrokError(
-                    "Doctest file '%r' declared in %r does not exist "
-                    "in %r." %
-                    (docfile, module_info.dotted_name,
-                     module_info.getResourcePath('')),
-                    None)
-        grok.testing.add_functional_doctest_location(docfilelist,
-                                                     module_info.dotted_name)
-        return True
+    It checks for existance of files given in a directive and feeds
+    appropriate info objects to grok.testing.
+    """
+    component_class = grok.testing.FunctionalDocTest
+    info_class = grok.testing.UnitDocTestInfo
+
+

Modified: Sandbox/ulif/grok-with-testing/src/grok/testing.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/testing.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/testing.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -12,14 +12,16 @@
                                ModuleDirectiveContext,
                                ClassDirectiveContext,
                                ClassOrModuleDirectiveContext)
-from martian import util
+from martian import util, scan
 
 from pkg_resources import resource_listdir
-from zope.testing import doctest
+import zope.component.eventtesting
+from zope.testing import doctest, cleanup
 from zope.app.testing.functional import (HTTPCaller, getRootFolder,
                                          FunctionalTestSetup, sync, ZCMLLayer,
                                          FunctionalDocFileSuite)
 
+
 class FunctionalDocTest(object):
     """A functional doc test, that will be automatically executed.
     """
@@ -33,16 +35,14 @@
         FunctionalTestSetup().setUp()
 
     def tearDown(self, test):
-        return True
         FunctionalTestSetup().tearDown()
 
-    def suiteFromFile(self, filepath):
+    def suiteFromFile(self, module_info, filepath):
         suite = unittest.TestSuite()
-        # We must get the pkg of the target test...
-        pkg = self.__module__.rsplit('.', 1)[0].replace('.', '/')
         test = FunctionalDocFileSuite(
             filepath, setUp=self.setUp, tearDown=self.tearDown,
-            module_relative = True, package = pkg,
+            module_relative = True,
+            package = module_info.package_dotted_name,
             globs = dict(http=HTTPCaller(),
                    getRootFolder=getRootFolder,
                    sync=sync
@@ -55,62 +55,113 @@
         suite.addTest(test)
         return suite
         
-    def __grok_test_suite__(self):
+    def grok_test_suite(self, fdoctest_info):
         suite = unittest.TestSuite()
-        for name in getattr(self, '__grok_testing_file__', []):
-            suite.addTest(self.suiteFromFile(name))
+        suite.addTest(self.suiteFromFile(
+            fdoctest_info.module_info,
+            fdoctest_info.docfile_path))
         return suite
-        
 
-class FunctionalDocTestForModule(FunctionalDocTest):
-    """A doctest with a given pkg and docfile path.
+
+class UnitDocTest(object):
+    """A unit doc test, that will be automatically executed.
     """
-    pkg = None
-    filepath = None
-    
-    def __init__(self, pkg, filepath):
-        self.pkg = pkg
-        self.filepath = filepath
 
-    def __grok_test_suite__(self):
+
+    optionflags = (doctest.ELLIPSIS+
+                   doctest.NORMALIZE_WHITESPACE)
+
+    def setUp(self, test):
+        zope.component.eventtesting.setUp(test)
+
+    def tearDown(self, test):
+        cleanup.cleanUp()
+
+    def suiteFromFile(self, module_info, filepath):
         suite = unittest.TestSuite()
-        for path in self.filepath:
-            test = FunctionalDocFileSuite(
-                path, setUp=self.setUp, tearDown=self.tearDown,
-                module_relative = True, package = self.pkg,
-                globs = dict(http=HTTPCaller(),
-                             getRootFolder=getRootFolder,
-                             sync=sync
-                             ),
-                optionflags = (doctest.ELLIPSIS+
-                               doctest.NORMALIZE_WHITESPACE+
-                               doctest.REPORT_NDIFF)
-                )
-            test.layer = self.FunctionalLayer
-            suite.addTest(test)
+        test = doctest.DocFileSuite(
+            filepath,
+            package=module_info.package_dotted_name,
+            setUp=self.setUp,
+            tearDown=self.tearDown,
+            optionflags = self.optionflags
+            )
+        suite.addTest(test)
         return suite
+        
+    def grok_test_suite(self, unitdoctest_info):
+        suite = unittest.TestSuite()
+        suite.addTest(
+            self.suiteFromFile(unitdoctest_info.module_info,
+                               unitdoctest_info.docfile_path)
+            )
+        return suite
 
 
+class DocTestInfo(object):
+    """Base for information about doctests of some kind.
+
+    DocTestInfos store some more information about doctests, than only
+    the doctest filepath. The ``klass`` is one of the more specialized
+    DocTest classes implemented above. It is responsible for setup of
+    tests for a certain test. The module_info is of use for computing
+    package paths and similar.
+    """
+    module_info = None
+    klass = None
+    docfile_path = None
+
+    doctest_class = None
+
+    def __init__(self, module_info, docfile_path, klass):
+        if not isinstance(klass, self.doctest_class):
+            klass = self.doctest_class
+        self.module_info = module_info
+        self.klass = klass
+        self.docfile_path = docfile_path
+
+    def absoluteDocFilePath(self):
+        return self.module_info.getResourcePath(self.docfile_path)
+
+
+class UnitDocTestInfo(DocTestInfo):
+    """Information about a unit doctest.
+
+    If klass is not a UnitDocTest (or derived), it will be replaced by
+    a UnitDocTest. We need this when ``ModuleGrokkers`` provide their
+    factory as klass, which means: a module.
+    """
+    doctest_class = UnitDocTest
+
+
+class FunctionalDocTestInfo(DocTestInfo):
+    """Information about a functional doctest.
+
+    If klass is not a FunctionalDocTest (or derived), it will be
+    replaced by a FunctionalDocTest. We need this when
+    ``ModuleGrokkers`` provide their factory as klass, which means: a
+    module.
+    """
+    doctest_class=FunctionalDocTest
+
+
 # Setup for the grok.testing.file directive.
 #
-# This is a list of FunctionalDocTest classes and tuples containing a
-# dotted pkg name and a filepath. The first is injected by the testing
-# class grokker, while the latter is injected by module wide
-# directives.
-all_func_doc_tests = set()
-all_func_doc_test_locations = []
+# This is a list of DocTestInfo objects. We read it in our global
+# ``test_suite()`` function, when the testrunner asks for it. The
+# objects are injected by appropriate grokkers in meta.py.
+all_doctest_infos = []
 
-def add_functional_doctest(doctest):
-    # TODO: a set does not check hard enough for existing tests.
-    all_func_doc_tests.add(doctest)
 
-def add_functional_doctest_location(pkg, subpath):
-    # TODO: check harder for existing tests.
-    if subpath == []:
-        return
-    if (pkg, subpath) in all_func_doc_test_locations:
-        return
-    all_func_doc_test_locations.append((pkg, subpath))
+# This is the recommended way to add objects to the above list. Thus,
+# we can incorporate local checks (for duplicates or whatever) here,
+# without hassle elsewhere.
+#
+# A duplicate checker is easy to do, but intentionally left out: if a
+# developer wants to run a test twice, s/he will have reasons for it.
+# We will not try to be smarter.
+def add_doctest_info(doctest_info):
+    all_doctest_infos.append(doctest_info)
 
 
 class TestingFileDirective(MultipleTextDirective):
@@ -125,19 +176,15 @@
 
 
 def test_suite():
+    """Our hook into the testrunner.
+    """
     suite = unittest.TestSuite()
-    # First handle doctests registered on module level...
-    for elem in all_func_doc_test_locations:
-        pkg, path = elem
-        ftest = FunctionalDocTestForModule(pkg, path)
-    # Then handle doctests registered on class level...
-    for klass in all_func_doc_tests:
-        # This klass describes a class, which is derived from
-        # ``FunctionalDocTest`` and brings all neccessary methods
-        # to extract tests with it.
-        ftest = klass()
-        suite.addTest(ftest.__grok_test_suite__())
+
+    for info in all_doctest_infos:
+        doctest = info.klass()
+        suite.addTest(doctest.grok_test_suite(info))
     return suite
 
+
 if __name__ == '__main__':
     unittest.main(defaultTest='test_suite')

Modified: Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_class.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_class.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_class.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -50,10 +50,11 @@
    True
 
 But if we try to register the same file several time, it will only be
-registered once:
+registered once. To check this, we first 'delete' old doctests and
+then grok this module:
 
-   >>> len(grok.testing.all_func_doc_tests)
-   6
+   >>> len(grok.testing.all_doctest_infos)
+   28
 
 This is less than the amount of ``grok.testing.file`` directives seen
 below.

Modified: Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_module.py
===================================================================
--- Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_module.py	2007-08-18 14:34:52 UTC (rev 78942)
+++ Sandbox/ulif/grok-with-testing/src/grok/tests/testing/file_directive_in_module.py	2007-08-18 15:07:10 UTC (rev 78943)
@@ -19,9 +19,7 @@
 ``grok.testing.all_func_doc_test_locations``. This is where we can
 look for what really got registered.
 
-   >>> registered_tuple = [x for x in grok.testing.all_func_doc_test_locations if x[1] == 'grok.tests.testing.file_directive_in_module'][0]
-   
-   >>> pathlist, mod_path = registered_tuple
+   >>> pathlist = [x.docfile_path for x in grok.testing.all_doctest_infos]
    >>> 'AnotherSampleTest.txt' in pathlist
    True
 
@@ -45,7 +43,7 @@
 once. We declared ``SampleDocTest.txt`` several times below, but:
 
    >>> len([x for x in pathlist if x == 'SampleDocTest.txt'])
-   1
+   2
 
 
 """



More information about the Checkins mailing list