Finalized integration of coverage.py within the Appy framework.
This commit is contained in:
parent
01487db688
commit
c3f5cfc9cd
|
@ -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
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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()
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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('...')
|
||||||
|
|
Loading…
Reference in a new issue