Finalized integration of coverage.py within the Appy framework.

This commit is contained in:
Gaetan Delannay 2009-12-03 16:45:05 +01:00
parent 01487db688
commit c3f5cfc9cd
5 changed files with 66 additions and 32 deletions

View file

@ -1,5 +1,5 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import os, os.path, sys, parser, symbol, token import os, os.path, sys, parser, symbol, token, types
from appy.gen import Type, State, Config, Tool, Flavour from appy.gen import Type, State, Config, Tool, Flavour
from appy.gen.descriptors import * from appy.gen.descriptors import *
from appy.gen.utils import produceNiceMessage from appy.gen.utils import produceNiceMessage
@ -150,6 +150,7 @@ class Generator:
self.initialize() self.initialize()
self.config = Config.getDefault() self.config = Config.getDefault()
self.modulesWithTests = set() self.modulesWithTests = set()
self.totalNumberOfTests = 0
def determineAppyType(self, klass): def determineAppyType(self, klass):
'''Is p_klass an Appy class ? An Appy workflow? None of this ? '''Is p_klass an Appy class ? An Appy workflow? None of this ?
@ -172,10 +173,23 @@ class Generator:
return res return res
def containsTests(self, moduleOrClass): def containsTests(self, moduleOrClass):
'''Does p_moduleOrClass contain doctests?''' '''Returns True if p_moduleOrClass contains doctests. This method also
if moduleOrClass.__doc__ and (moduleOrClass.__doc__.find('>>>') != -1): counts tests and updates self.totalNumberOfTests.'''
return True res = False
return False docString = moduleOrClass.__doc__
if docString and (docString.find('>>>') != -1):
self.totalNumberOfTests += 1
res = True
# Count also docstring in methods
if type(moduleOrClass) == types.ClassType:
for name, elem in moduleOrClass.__dict__.iteritems():
if type(elem) in (staticmethod, classmethod):
elem = elem.__get__(name)
if hasattr(elem, '__doc__') and elem.__doc__ and \
(elem.__doc__.find('>>>') != -1):
res = True
self.totalNumberOfTests += 1
return res
IMPORT_ERROR = 'Warning: error while importing module %s (%s)' IMPORT_ERROR = 'Warning: error while importing module %s (%s)'
SYNTAX_ERROR = 'Warning: error while parsing module %s (%s)' SYNTAX_ERROR = 'Warning: error while parsing module %s (%s)'
@ -320,5 +334,8 @@ class Generator:
for classDescr in self.classes: self.generateClass(classDescr) for classDescr in self.classes: self.generateClass(classDescr)
for wfDescr in self.workflows: self.generateWorkflow(wfDescr) for wfDescr in self.workflows: self.generateWorkflow(wfDescr)
self.finalize() self.finalize()
print 'Done.' msg = ''
if self.totalNumberOfTests:
msg = ' (number of tests found: %d)' % self.totalNumberOfTests
print 'Done%s.' % msg
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -325,6 +325,7 @@ class Generator(AbstractGenerator):
imports.append(importDef) imports.append(importDef)
repls = self.repls.copy() repls = self.repls.copy()
repls['imports'] = '\n'.join(imports) repls['imports'] = '\n'.join(imports)
repls['totalNumberOfTests'] = self.totalNumberOfTests
self.copyFile('__init__.py', repls) self.copyFile('__init__.py', repls)
def generateInstall(self): def generateInstall(self):

View file

@ -32,7 +32,6 @@ class TestMixin:
# absence of names beginning with other chars than "__". # absence of names beginning with other chars than "__".
for elem in moduleObj.__dict__.iterkeys(): for elem in moduleObj.__dict__.iterkeys():
if not elem.startswith('__'): if not elem.startswith('__'):
print 'Element found in this module!!!', moduleObj, elem
res.append(moduleObj) res.append(moduleObj)
break break
# Include sub-modules if any # Include sub-modules if any
@ -49,7 +48,8 @@ class TestMixin:
res += self.getNonEmptySubModules(subModuleName) res += self.getNonEmptySubModules(subModuleName)
return res return res
def getCovFolder(self): @staticmethod
def getCovFolder():
'''Returns the folder where to put the coverage folder if needed.''' '''Returns the folder where to put the coverage folder if needed.'''
for arg in sys.argv: for arg in sys.argv:
if arg.startswith('[coverage'): if arg.startswith('[coverage'):
@ -68,28 +68,18 @@ def beforeTest(test):
test.createUser('admin', ('Member','Manager')) test.createUser('admin', ('Member','Manager'))
test.login('admin') test.login('admin')
g['t'] = g['test'] g['t'] = g['test']
# Must we perform test coverage ?
covFolder = test.getCovFolder()
if covFolder:
try:
print 'COV!!!!', covFolder
import coverage
app = getattr(cfg, g['tool'].o.getAppName())
from coverage import coverage
cov = coverage()
g['cov'] = cov
g['covFolder'] = covFolder
cov.start()
except ImportError:
print 'You must install the "coverage" product.'
def afterTest(test): def afterTest(test):
'''Is executed after every test.''' '''Is executed after every test.'''
g = test.globs g = test.globs
if g.has_key('covFolder'): appName = g['tool'].o.getAppName()
cov = g['cov'] exec 'from Products.%s import cov, covFolder, totalNumberOfTests, ' \
'countTest' % appName
countTest()
exec 'from Products.%s import numberOfExecutedTests' % appName
if cov and (numberOfExecutedTests == totalNumberOfTests):
cov.stop() cov.stop()
# Dumps the coverage report # Dumps the coverage report
appModules = test.getNonEmptySubModules(g['tool'].o.getAppName()) appModules = test.getNonEmptySubModules(appName)
cov.html_report(directory=g['covFolder'], morfs=appModules) cov.html_report(directory=covFolder, morfs=appModules)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -1,4 +1,30 @@
<!codeHeader!> <!codeHeader!>
# Test coverage-related stuff --------------------------------------------------
import sys
from appy.gen.plone25.mixins.TestMixin import TestMixin
covFolder = TestMixin.getCovFolder()
# The previous method checks in sys.argv whether Zope was lauched for performing
# coverage tests or not.
cov = None # The main Coverage instance as created by the coverage program.
totalNumberOfTests = <!totalNumberOfTests!>
numberOfExecutedTests = 0
if covFolder:
try:
import coverage
from coverage import coverage
cov = coverage()
cov.start()
except ImportError:
print 'COVERAGE KO! The "coverage" program is not installed. You can ' \
'download it from http://nedbatchelder.com/code/coverage.' \
'\nHit <enter> to execute the test suite without coverage.'
sys.stdin.readline()
def countTest():
global numberOfExecutedTests
numberOfExecutedTests += 1
# ------------------------------------------------------------------------------
from config import * from config import *
import logging import logging
try: try:
@ -13,6 +39,7 @@ from Products.Archetypes import listTypes
from appy.gen.plone25.installer import ZopeInstaller from appy.gen.plone25.installer import ZopeInstaller
logger = logging.getLogger(PROJECTNAME) logger = logging.getLogger(PROJECTNAME)
# Zope-level installation of the generated product. ----------------------------
def initialize(context): def initialize(context):
<!imports!> <!imports!>
# I need to do those imports here; else, types and add permissions will not # I need to do those imports here; else, types and add permissions will not
@ -21,3 +48,4 @@ def initialize(context):
<!applicationName!>Tool.<!applicationName!>Tool, <!applicationName!>Tool.<!applicationName!>Tool,
DEFAULT_ADD_CONTENT_PERMISSION, ADD_CONTENT_PERMISSIONS, DEFAULT_ADD_CONTENT_PERMISSION, ADD_CONTENT_PERMISSIONS,
logger, globals()).install() logger, globals()).install()
# ------------------------------------------------------------------------------

View file

@ -613,12 +613,10 @@ class XmlComparator:
i += 1 i += 1
if line and (line[0] != ' '): if line and (line[0] != ' '):
if not atLeastOneDiff: if not atLeastOneDiff:
if report: msg = 'Difference(s) detected between files %s and %s:' % \
report.say('Difference(s) detected between files '\ (self.fileNameA, self.fileNameB)
'%s and %s:' % (self.fileNameA, self.fileNameB), if report: report.say(msg, encoding='utf-8')
encoding='utf-8') else: print msg
else:
print 'Differences:'
atLeastOneDiff = True atLeastOneDiff = True
if not lastLinePrinted: if not lastLinePrinted:
if report: report.say('...') if report: report.say('...')