[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