Commit dff34472 authored by Jérome Perrin's avatar Jérome Perrin

Python coverage

Depends on nexedi/slapos!1290

See merge request nexedi/erp5!1695
parents 455c5322 1c1618a3
...@@ -104,6 +104,9 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -104,6 +104,9 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(test_web.getPortalType(), 'Business Template') self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web') self.assertEqual(test_web.getTitle(), 'test_web')
self.assertEqual(len(test_web.getRevision()), 28) self.assertEqual(len(test_web.getRevision()), 28)
self.assertEqual(
test_web.getPublicationUrl(),
'http://www.erp5.org/dists/snapshot/test_bt5/test_web.bt5')
def _svn_setup_ssl(self): def _svn_setup_ssl(self):
""" """
...@@ -139,6 +142,8 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -139,6 +142,8 @@ class TestTemplateTool(ERP5TypeTestCase):
self.assertEqual(test_web.getPortalType(), 'Business Template') self.assertEqual(test_web.getPortalType(), 'Business Template')
self.assertEqual(test_web.getTitle(), 'test_web') self.assertEqual(test_web.getTitle(), 'test_web')
self.assertEqual(len(test_web.getRevision()), 28) self.assertEqual(len(test_web.getRevision()), 28)
self.assertEqual(
test_web.getPublicationUrl(), bt5_url)
def test_00_updateBusinessTemplateFromUrl_simple(self): def test_00_updateBusinessTemplateFromUrl_simple(self):
""" """
...@@ -549,6 +554,7 @@ class TestTemplateTool(ERP5TypeTestCase): ...@@ -549,6 +554,7 @@ class TestTemplateTool(ERP5TypeTestCase):
bt = self.templates_tool.getInstalledBusinessTemplate(bt5_name, strict=True) bt = self.templates_tool.getInstalledBusinessTemplate(bt5_name, strict=True)
self.assertNotEquals(bt, None) self.assertNotEquals(bt, None)
self.assertEqual(bt.getTitle(), bt5_name) self.assertEqual(bt.getTitle(), bt5_name)
self.assertEqual(bt.getPublicationUrl(), self._getBTPathAndIdList([bt5_name])[0][0])
# Repeat operation, the bt5 should be ignored # Repeat operation, the bt5 should be ignored
self.templates_tool.installBusinessTemplateListFromRepository([bt5_name]) self.templates_tool.installBusinessTemplateListFromRepository([bt5_name])
......
...@@ -77,17 +77,18 @@ from Products.ERP5Type.TransactionalVariable import getTransactionalVariable ...@@ -77,17 +77,18 @@ from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from OFS.Traversable import NotFound from OFS.Traversable import NotFound
from OFS import SimpleItem from OFS import SimpleItem
from OFS.Image import Pdata from OFS.Image import Pdata
import coverage
from io import BytesIO from io import BytesIO
from copy import deepcopy from copy import deepcopy
from zExceptions import BadRequest from zExceptions import BadRequest
from Products.ERP5Type.XMLExportImport import exportXML, customImporters from Products.ERP5Type.XMLExportImport import exportXML, customImporters
from Products.ERP5Type.Workflow import WorkflowHistoryList from Products.ERP5Type.Workflow import WorkflowHistoryList
from zLOG import LOG, WARNING, INFO from zLOG import LOG, WARNING, INFO, PROBLEM
from warnings import warn from warnings import warn
from lxml.etree import parse from lxml.etree import parse
from xml.sax.saxutils import escape from xml.sax.saxutils import escape
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
from six.moves.urllib.parse import quote, unquote from six.moves.urllib.parse import quote, unquote, urlparse
from difflib import unified_diff from difflib import unified_diff
import posixpath import posixpath
import transaction import transaction
...@@ -97,6 +98,7 @@ import threading ...@@ -97,6 +98,7 @@ import threading
from ZODB.broken import Broken, BrokenModified from ZODB.broken import Broken, BrokenModified
from Products.ERP5.genbt5list import BusinessTemplateRevision, \ from Products.ERP5.genbt5list import BusinessTemplateRevision, \
item_name_list, item_set item_name_list, item_set
from Products.ERP5Type.mixin.component import ComponentMixin
CACHE_DATABASE_PATH = None CACHE_DATABASE_PATH = None
try: try:
...@@ -1156,22 +1158,33 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1156,22 +1158,33 @@ class ObjectTemplateItem(BaseTemplateItem):
""" """
pass pass
def onNewObject(self, obj): def onNewObject(self, obj, context):
""" """
Installation hook. Installation hook.
Called when installation process determined that object to install is Called when installation process determined that object to install is
new on current site (it's not replacing an existing object). new on current site (it's not replacing an existing object).
`obj` parameter is the newly created object in its acquisition context. `obj` parameter is the newly created object in its acquisition context.
`context` is the business template instance, in its acquisition context.
Can be overridden by subclasses. Can be overridden by subclasses.
""" """
pass if isinstance(obj, (PythonScript, ComponentMixin)) and coverage.Coverage.current():
relative_path = '/'.join(obj.getPhysicalPath()[len(context.getPortalObject().getPhysicalPath()):])
filename = os.path.join(
context.getPublicationUrl(),
self.__class__.__name__,
relative_path + '.py')
if os.path.exists(filename):
obj._erp5_coverage_filename = filename
else:
LOG('BusinessTemplate', PROBLEM, 'Could not find file for %s' % filename)
def onReplaceObject(self, obj): def onReplaceObject(self, obj, context):
""" """
Installation hook. Installation hook.
Called when installation process determined that object to install is Called when installation process determined that object to install is
to replace an existing object on current site (it's not new). to replace an existing object on current site (it's not new).
`obj` parameter is the replaced object in its acquisition context. `obj` parameter is the replaced object in its acquisition context.
`context` is the business template instance, in its acquisition context.
Can be overridden by subclasses. Can be overridden by subclasses.
""" """
pass pass
...@@ -1416,9 +1429,9 @@ class ObjectTemplateItem(BaseTemplateItem): ...@@ -1416,9 +1429,9 @@ class ObjectTemplateItem(BaseTemplateItem):
if not object_existed: if not object_existed:
# A new object was added, call the hook # A new object was added, call the hook
self.onNewObject(obj) self.onNewObject(obj, context)
else: else:
self.onReplaceObject(obj) self.onReplaceObject(obj, context)
# mark a business template installation so in 'PortalType_afterClone' scripts # mark a business template installation so in 'PortalType_afterClone' scripts
# we can implement logical for reseting or not attributes (i.e reference). # we can implement logical for reseting or not attributes (i.e reference).
...@@ -1972,9 +1985,11 @@ class CategoryTemplateItem(ObjectTemplateItem): ...@@ -1972,9 +1985,11 @@ class CategoryTemplateItem(ObjectTemplateItem):
def beforeInstall(self): def beforeInstall(self):
self._installed_new_category = False self._installed_new_category = False
return super(CategoryTemplateItem, self).beforeInstall()
def onNewObject(self, obj): def onNewObject(self, obj, context):
self._installed_new_category = True self._installed_new_category = True
return super(CategoryTemplateItem, self).onNewObject(obj, context)
def afterInstall(self): def afterInstall(self):
if self._installed_new_category: if self._installed_new_category:
...@@ -2392,7 +2407,8 @@ class WorkflowTemplateItem(ObjectTemplateItem): ...@@ -2392,7 +2407,8 @@ class WorkflowTemplateItem(ObjectTemplateItem):
continue continue
raise raise
container_ids = container.objectIds() container_ids = container.objectIds()
if object_id in container_ids: # Object already exists object_existed = object_id in container_ids
if object_existed:
self._backupObject(action, trashbin, container_path, object_id, keep_subobjects=1) self._backupObject(action, trashbin, container_path, object_id, keep_subobjects=1)
container.manage_delObjects([object_id]) container.manage_delObjects([object_id])
obj = self._objects[path] obj = self._objects[path]
...@@ -2402,6 +2418,11 @@ class WorkflowTemplateItem(ObjectTemplateItem): ...@@ -2402,6 +2418,11 @@ class WorkflowTemplateItem(ObjectTemplateItem):
obj = container._getOb(object_id) obj = container._getOb(object_id)
obj.manage_afterClone(obj) obj.manage_afterClone(obj)
obj.wl_clearLocks() obj.wl_clearLocks()
if not object_existed:
# A new object was added, call the hook
self.onNewObject(obj, context)
else:
self.onReplaceObject(obj, context)
def uninstall(self, context, **kw): def uninstall(self, context, **kw):
object_path = kw.get('object_path', None) object_path = kw.get('object_path', None)
...@@ -4218,7 +4239,7 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem): ...@@ -4218,7 +4239,7 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem):
raise NotImplementedError raise NotImplementedError
def __init__(self, id_list, tool_id='portal_components', **kw): def __init__(self, id_list, tool_id='portal_components', **kw):
ObjectTemplateItem.__init__(self, id_list, tool_id=tool_id, **kw) super(_ZodbComponentTemplateItem, self).__init__(id_list, tool_id=tool_id, **kw)
def isKeepWorkflowObjectLastHistoryOnly(self, path): def isKeepWorkflowObjectLastHistoryOnly(self, path):
""" """
...@@ -4248,9 +4269,13 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem): ...@@ -4248,9 +4269,13 @@ class _ZodbComponentTemplateItem(ObjectTemplateItem):
obj.workflow_history[wf_id] = WorkflowHistoryList([wf_history]) obj.workflow_history[wf_id] = WorkflowHistoryList([wf_history])
def onNewObject(self, _): def onNewObject(self, obj, context):
self._do_reset = True self._do_reset = True
onReplaceObject = onNewObject return super(_ZodbComponentTemplateItem, self).onNewObject(obj, context)
def onReplaceObject(self, obj, context):
self._do_reset = True
return super(_ZodbComponentTemplateItem, self).onReplaceObject(obj, context)
def afterInstall(self): def afterInstall(self):
""" """
...@@ -5718,6 +5743,9 @@ Business Template is a set of definitions, such as skins, portal types and categ ...@@ -5718,6 +5743,9 @@ Business Template is a set of definitions, such as skins, portal types and categ
value = self.getProperty(id) value = self.getProperty(id)
if not value: if not value:
continue continue
if id == 'publication_url':
if urlparse(value).scheme in ('file', ''):
continue
if prop_type in ('text', 'string', 'int', 'boolean'): if prop_type in ('text', 'string', 'int', 'boolean'):
bta.addObject(str(value), name=id, path='bt', ext='') bta.addObject(str(value), name=id, path='bt', ext='')
elif prop_type in ('lines', 'tokens'): elif prop_type in ('lines', 'tokens'):
......
...@@ -401,6 +401,7 @@ class TemplateTool (BaseTool): ...@@ -401,6 +401,7 @@ class TemplateTool (BaseTool):
bt = self._download_local(path, id) bt = self._download_local(path, id)
bt.build(no_action=True) bt.build(no_action=True)
bt.setPublicationUrl(url)
return bt return bt
security.declareProtected('Import/Export objects', 'importBase64EncodedText') security.declareProtected('Import/Export objects', 'importBase64EncodedText')
......
...@@ -36,6 +36,7 @@ import imp ...@@ -36,6 +36,7 @@ import imp
import collections import collections
from six import reraise from six import reraise
import coverage
from Products.ERP5Type.Utils import ensure_list from Products.ERP5Type.Utils import ensure_list
from Products.ERP5.ERP5Site import getSite from Products.ERP5.ERP5Site import getSite
from Products.ERP5Type import product_path as ERP5Type_product_path from Products.ERP5Type import product_path as ERP5Type_product_path
...@@ -333,6 +334,14 @@ class ComponentDynamicPackage(ModuleType): ...@@ -333,6 +334,14 @@ class ComponentDynamicPackage(ModuleType):
# This must be set for imports at least (see PEP 302) # This must be set for imports at least (see PEP 302)
module.__file__ = '<' + relative_url + '>' module.__file__ = '<' + relative_url + '>'
if coverage.Coverage.current():
if hasattr(component, '_erp5_coverage_filename'):
module.__file__ = component._erp5_coverage_filename
else:
LOG(
"ERP5Type.Tool.ComponentTool",
WARNING,
"No coverage filesystem mapping for %s" % (module_fullname_alias or module_fullname))
# Only useful for get_source(), do it before exec'ing the source code # Only useful for get_source(), do it before exec'ing the source code
# so that the source code is properly display in case of error # so that the source code is properly display in case of error
......
...@@ -140,6 +140,9 @@ def main(): ...@@ -140,6 +140,9 @@ def main():
args.test_node_title, suite.allow_restart, test_suite_title, args.test_node_title, suite.allow_restart, test_suite_title,
args.project_title) args.project_title)
if test_result is not None: if test_result is not None:
os.environ['ERP5_TEST_RESULT_REVISION'] = test_result.revision
os.environ['ERP5_TEST_RESULT_ID'] = test_result.test_result_path.split('/')[-1]
assert revision == test_result.revision, (revision, test_result.revision) assert revision == test_result.revision, (revision, test_result.revision)
while suite.acquire(): while suite.acquire():
test = test_result.start(suite.running.keys()) test = test_result.start(suite.running.keys())
......
...@@ -12,10 +12,7 @@ import errno ...@@ -12,10 +12,7 @@ import errno
import random import random
import transaction import transaction
from glob import glob from glob import glob
try:
from coverage import coverage
except ImportError:
coverage = None
WIN = os.name == 'nt' WIN = os.name == 'nt'
...@@ -27,8 +24,6 @@ Options: ...@@ -27,8 +24,6 @@ Options:
-v, --verbose produce verbose output -v, --verbose produce verbose output
-h, --help this help screen -h, --help this help screen
-p, --profile print profiling results at the end -p, --profile print profiling results at the end
--coverage=STRING Use the given path as a coverage config file and
thus enable code coverateg report
--portal_id=STRING force id of the portal. Useful when using --portal_id=STRING force id of the portal. Useful when using
--data_fs_path to run tests on an existing --data_fs_path to run tests on an existing
Data.fs Data.fs
...@@ -638,11 +633,6 @@ def runUnitTestList(test_list, verbosity=1, debug=0, run_only=None): ...@@ -638,11 +633,6 @@ def runUnitTestList(test_list, verbosity=1, debug=0, run_only=None):
signal.signal(signal.SIGINT, shutdown) signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGHUP, shutdown) signal.signal(signal.SIGHUP, shutdown)
coverage_config = os.environ.get('coverage', None)
if coverage_config:
coverage_process = coverage(config_file=coverage_config)
coverage_process.start()
try: try:
save = int(os.environ.get('erp5_save_data_fs', 0)) save = int(os.environ.get('erp5_save_data_fs', 0))
load = int(os.environ.get('erp5_load_data_fs', 0)) load = int(os.environ.get('erp5_load_data_fs', 0))
...@@ -717,11 +707,6 @@ def runUnitTestList(test_list, verbosity=1, debug=0, run_only=None): ...@@ -717,11 +707,6 @@ def runUnitTestList(test_list, verbosity=1, debug=0, run_only=None):
# disconnected from it. # disconnected from it.
wcfs_server.stop() wcfs_server.stop()
if coverage_config:
coverage_process.stop()
coverage_process.save()
coverage_process.html_report()
if save and save_mysql: if save and save_mysql:
save_mysql(verbosity) save_mysql(verbosity)
...@@ -747,7 +732,7 @@ def main(argument_list=None): ...@@ -747,7 +732,7 @@ def main(argument_list=None):
sys.argv.extend(old_argv[1:]) sys.argv.extend(old_argv[1:])
try: try:
opts, args = getopt.getopt(sys.argv[1:], opts, args = getopt.getopt(sys.argv[1:],
"hpvD", ["help", "verbose", "profile", "coverage=", "portal_id=", "hpvD", ["help", "verbose", "profile", "portal_id=",
"data_fs_path=", "data_fs_path=",
"bt5_path=", "bt5_path=",
"firefox_bin=", "firefox_bin=",
...@@ -810,11 +795,6 @@ def main(argument_list=None): ...@@ -810,11 +795,6 @@ def main(argument_list=None):
elif opt == '-D': elif opt == '-D':
debug = 1 debug = 1
os.environ["erp5_debug_mode"] = str(debug) os.environ["erp5_debug_mode"] = str(debug)
elif opt == "--coverage":
if coverage:
os.environ['coverage'] = arg
else:
_print("WARNING Coverage module not found")
elif opt in ("-p", "--profile"): elif opt in ("-p", "--profile"):
os.environ['PROFILE_TESTS'] = "1" os.environ['PROFILE_TESTS'] = "1"
# profiling of setup and teardown is disabled by default, just set # profiling of setup and teardown is disabled by default, just set
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment