Commit 69e1d229 authored by Jérome Perrin's avatar Jérome Perrin

CodingStyleTest: test we don't have duplicate actions

Duplicate actions are actions from the same category with the same name.
parent fb31fb52
......@@ -36,7 +36,7 @@ from requests.packages.urllib3.util.retry import Retry
from subprocess import Popen, PIPE
from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import addUserToDeveloperRole
from Products.ERP5Type.tests.utils import addUserToDeveloperRole, findContentChain
from Products.CMFCore.utils import getToolByName
from zLOG import LOG
# You can invoke same tests in your favourite collection of business templates
......@@ -669,58 +669,6 @@ def makeTestMethod(validator, portal_type, view_name, bt_name):
content.newContent(portal_type=portal_type_list[0]),
portal_type_list[1:])
def findContentChain(portal, target_portal_type):
# type: (erp5.portal_type.ERP5Site,str) -> Tuple[erp5.portal_type.Folder, Tuple[str, ...]]
"""Returns the module and the chain of portal types to create a document of target_portal_type.
This tries all allowed content types up to three levels and if not found, use portal_trash,
which allows anything.
"""
# These types have a special `newContent` which does not really follow the interface, we
# cannot not use them as container.
invalid_container_type_set = {
'Session Tool',
'Contribution Tool',
}
# first look modules and their content to find a real container chain.
for module in portal.contentValues():
module_type = module.getTypeInfo()
if module_type is not None:
if module_type.getId() == target_portal_type:
return module, ()
if module_type.isTypeFilterContentType() \
and module_type.getId() not in invalid_container_type_set:
for allowed_type in module.allowedContentTypes():
# Actions on portal_actions are global actions which can be rendered on any context.
# We don't test them on all portal types, only on the first type "top level document"
if target_portal_type in ('portal_actions', allowed_type.getId()):
return module, (allowed_type.getId(),)
for sub_allowed_type in allowed_type.getTypeAllowedContentTypeList():
if target_portal_type == sub_allowed_type:
return module, (allowed_type.getId(), target_portal_type)
if sub_allowed_type in portal.portal_types:
for sub_sub_allowed_type in portal.portal_types[
sub_allowed_type].getTypeAllowedContentTypeList():
if target_portal_type == sub_sub_allowed_type:
return module, (
allowed_type.getId(),
sub_allowed_type,
target_portal_type,
)
# we did not find a valid chain of containers, so we'll fallback to creating
# in portal_trash, which allow anything.
# We still make one attempt at finding a valid container.
for ti in portal.portal_types.contentValues():
if ti.getId() not in invalid_container_type_set\
and target_portal_type in ti.getTypeAllowedContentTypeList():
return portal.portal_trash, (ti.getId(), target_portal_type,)
# no suitable container found, use directly portal_trash.
ZopeTestCase._print(
'Could not find container for %s. Using portal_trash as a container\n'
% target_portal_type)
return portal.portal_trash, (target_portal_type,)
def testMethod(self):
module, portal_type_list = findContentChain(
self.portal,
......
......@@ -27,13 +27,17 @@
#
##############################################################################
import tarfile
import os
import collections
import glob
import os
import tarfile
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Testing import ZopeTestCase
from Acquisition import aq_base
from Testing import ZopeTestCase
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from Products.ERP5Type.tests.utils import findContentChain
class CodingStyleTestCase(ERP5TypeTestCase):
"""Test case to test coding style in business templates.
......@@ -61,17 +65,20 @@ class CodingStyleTestCase(ERP5TypeTestCase):
def afterSetUp(self):
self.login()
def _getTestedBusinessTemplateValueList(self):
for business_template in self.portal.portal_templates.contentValues():
if business_template.getTitle() in self.getTestedBusinessTemplateList():
yield business_template
def test_SkinCodingStyle(self):
"""
Find all skin items of business templates to be checked
and gather all consistency messages.
"""
# Find the list if skins to test - we only test the last business template
portal_templates = self.portal.portal_templates
skin_id_set = set()
for business_template in portal_templates.contentValues():
if business_template.getTitle() in self.getTestedBusinessTemplateList():
skin_id_set.update(business_template.getTemplateSkinIdList())
for business_template in self._getTestedBusinessTemplateValueList():
skin_id_set.update(business_template.getTemplateSkinIdList())
# Init message list
message_list = []
......@@ -87,7 +94,7 @@ class CodingStyleTestCase(ERP5TypeTestCase):
if getattr(aq_base(document), 'checkConsistency', None) is not None:
message_list.extend(document.checkConsistency())
self.maxDiff = None
self.assertEqual([], message_list)
self.assertEqual(message_list, [])
def test_PythonSourceCode(self):
"""test python script from the tested business templates.
......@@ -95,9 +102,8 @@ class CodingStyleTestCase(ERP5TypeTestCase):
reuses BusinessTemplate_getPythonSourceCodeMessageList
"""
self.maxDiff = None
for business_template in self.portal.portal_templates.contentValues():
if business_template.getTitle() in self.getTestedBusinessTemplateList():
self.assertEqual([], business_template.BusinessTemplate_getPythonSourceCodeMessageList())
for business_template in self._getTestedBusinessTemplateValueList():
self.assertEqual(business_template.BusinessTemplate_getPythonSourceCodeMessageList(), [])
def test_rebuild_business_template(self):
"""Try to rebuild business template to catch packaging errors.
......@@ -151,3 +157,38 @@ class CodingStyleTestCase(ERP5TypeTestCase):
.upgrader_check_post_upgrade.Alarm_getReportResultList()
],
)
def test_DuplicateActions(self):
"""test actions from the tested business templates are not duplicated.
"""
self.maxDiff = None
duplicate_action_list = []
for business_template in self._getTestedBusinessTemplateValueList():
# consider all portal types for which this business template defines actions
portal_type_list = set([
action_definition.split(' | ')[0] for action_definition in
business_template.getTemplateActionPathList()
])
for portal_type in portal_type_list:
# ignore other actions providers, such as the ones for global actions.
if self.portal.portal_types.get(portal_type) is None:
continue
document, content_portal_type_list = findContentChain(
self.portal, portal_type)
for content_portal_type in content_portal_type_list:
document = document.newContent(
portal_type=content_portal_type,
temp_object=True,
)
for action_category, action_list in self.portal.portal_actions.listFilteredActionsFor(
document).iteritems():
for action_name, action_count in collections.Counter(
[action['name'] for action in action_list]).iteritems():
if action_count > 1:
duplicate_action_list.append({
'portal_type': portal_type,
'category': action_category,
'action_name': action_name,
})
self.assertEqual(duplicate_action_list, [])
......@@ -42,6 +42,7 @@ from Products.ERP5Type.Globals import InitializeClass
from App.config import getConfiguration
from ZConfig.matcher import SectionValue
from Zope2.Startup.datatypes import ZopeDatabase
from Testing import ZopeTestCase
import Products.ERP5Type
from Products.MailHost.MailHost import MailHost
from email import message_from_string
......@@ -210,6 +211,59 @@ def addUserToDeveloperRole(user_name):
elif user_name not in product_config['erp5'].developer_list:
product_config['erp5'].developer_list.append(user_name)
def findContentChain(portal, target_portal_type):
# type: (erp5.portal_type.ERP5Site,str) -> Tuple[erp5.portal_type.Folder, Tuple[str, ...]]
"""Returns the module and the chain of portal types to create a document of target_portal_type.
This tries all allowed content types up to three levels and if not found, use portal_trash,
which allows anything.
"""
# These types have a special `newContent` which does not really follow the interface, we
# cannot not use them as container.
invalid_container_type_set = {
'Session Tool',
'Contribution Tool',
}
# first look modules and their content to find a real container chain.
for module in portal.contentValues():
module_type = module.getTypeInfo()
if module_type is not None:
if module_type.getId() == target_portal_type:
return module, ()
if module_type.isTypeFilterContentType() \
and module_type.getId() not in invalid_container_type_set:
for allowed_type in module.allowedContentTypes():
# Actions on portal_actions are global actions which can be rendered on any context.
# We don't test them on all portal types, only on the first type "top level document"
if target_portal_type in ('portal_actions', allowed_type.getId()):
return module, (allowed_type.getId(),)
for sub_allowed_type in allowed_type.getTypeAllowedContentTypeList():
if target_portal_type == sub_allowed_type:
return module, (allowed_type.getId(), target_portal_type)
if sub_allowed_type in portal.portal_types:
for sub_sub_allowed_type in portal.portal_types[
sub_allowed_type].getTypeAllowedContentTypeList():
if target_portal_type == sub_sub_allowed_type:
return module, (
allowed_type.getId(),
sub_allowed_type,
target_portal_type,
)
# we did not find a valid chain of containers, so we'll fallback to creating
# in portal_trash, which allow anything.
# We still make one attempt at finding a valid container.
for ti in portal.portal_types.contentValues():
if ti.getId() not in invalid_container_type_set\
and target_portal_type in ti.getTypeAllowedContentTypeList():
return portal.portal_trash, (ti.getId(), target_portal_type,)
# no suitable container found, use directly portal_trash.
ZopeTestCase._print(
'Could not find container for %s. Using portal_trash as a container\n'
% target_portal_type)
return portal.portal_trash, (target_portal_type,)
# python scripts
def createZODBPythonScript(container, script_id, script_params,
script_content):
......
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