Commit f107e6dc authored by Alexandre Boeglin's avatar Alexandre Boeglin

When an activity fails, all activities that depend on it should not be

executed.

Note: earlier version of this test checked exactly the contrary, but it was
eventually agreed that this was a bug. If an activity fails, all the
activities that depend on it should be block until the first one is resolved.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@9994 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 516318b8
...@@ -17,7 +17,7 @@ SELECT ...@@ -17,7 +17,7 @@ SELECT
FROM FROM
message message
WHERE WHERE
processing_node >= -1 processing_node >= -2
<dtml-if method_id> <dtml-if method_id>
AND ( AND (
<dtml-in method_id> <dtml-in method_id>
......
...@@ -17,7 +17,7 @@ SELECT ...@@ -17,7 +17,7 @@ SELECT
FROM FROM
message_queue message_queue
WHERE WHERE
processing_node >= -1 processing_node >= -2
<dtml-if method_id> <dtml-if method_id>
AND ( AND (
<dtml-in method_id> <dtml-in method_id>
......
...@@ -82,7 +82,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -82,7 +82,7 @@ class TestCMFActivity(ERP5TypeTestCase):
base categories: base categories:
- region - region
- subordination - subordination
/organisation /organisation
""" """
return ('erp5_base',) return ('erp5_base',)
...@@ -107,7 +107,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -107,7 +107,7 @@ class TestCMFActivity(ERP5TypeTestCase):
message_list = portal.portal_activities.getMessageList() message_list = portal.portal_activities.getMessageList()
for message in message_list: for message in message_list:
portal.portal_activities.manageCancel(message.object_path,message.method_id) portal.portal_activities.manageCancel(message.object_path,message.method_id)
# Then add new components # Then add new components
if not(hasattr(portal,'organisation')): if not(hasattr(portal,'organisation')):
portal.portal_types.constructContent(type_name='Organisation Module', portal.portal_types.constructContent(type_name='Organisation Module',
...@@ -461,30 +461,30 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -461,30 +461,30 @@ class TestCMFActivity(ERP5TypeTestCase):
if not organisation_module.hasContent(self.company_id): if not organisation_module.hasContent(self.company_id):
organisation_module.newContent(id=self.company_id) organisation_module.newContent(id=self.company_id)
o = portal.organisation._getOb(self.company_id) o = portal.organisation._getOb(self.company_id)
o.setTitle('a') o.setTitle('a')
self.assertEquals(o.getTitle(), 'a') self.assertEquals(o.getTitle(), 'a')
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
def toto(self, value): def toto(self, value):
self.setTitle(self.getTitle() + value) self.setTitle(self.getTitle() + value)
o.__class__.toto = toto o.__class__.toto = toto
def titi(self, value): def titi(self, value):
self.setTitle(self.getTitle() + value) self.setTitle(self.getTitle() + value)
o.__class__.titi = titi o.__class__.titi = titi
o.activate(after_method_id = 'titi', activity = activity).toto('b') o.activate(after_method_id = 'titi', activity = activity).toto('b')
o.activate(activity = activity).titi('c') o.activate(activity = activity).titi('c')
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
self.assertEquals(o.getTitle(), 'acb') self.assertEquals(o.getTitle(), 'acb')
def ExpandedMethodWithDeletedSubObject(self, activity): def ExpandedMethodWithDeletedSubObject(self, activity):
""" """
Do recursiveReindexObject, then delete a Do recursiveReindexObject, then delete a
subobject an see if there is only one activity subobject an see if there is only one activity
in the queue in the queue
""" """
portal = self.getPortal() portal = self.getPortal()
...@@ -511,7 +511,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -511,7 +511,7 @@ class TestCMFActivity(ERP5TypeTestCase):
def ExpandedMethodWithDeletedObject(self, activity): def ExpandedMethodWithDeletedObject(self, activity):
""" """
Do recursiveReindexObject, then delete a Do recursiveReindexObject, then delete a
subobject an see if there is only one activity subobject an see if there is only one activity
in the queue in the queue
""" """
portal = self.getPortal() portal = self.getPortal()
...@@ -544,18 +544,18 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -544,18 +544,18 @@ class TestCMFActivity(ERP5TypeTestCase):
if not organisation_module.hasContent(self.company_id): if not organisation_module.hasContent(self.company_id):
organisation_module.newContent(id=self.company_id) organisation_module.newContent(id=self.company_id)
o = portal.organisation._getOb(self.company_id) o = portal.organisation._getOb(self.company_id)
o.setTitle('?') o.setTitle('?')
self.assertEquals(o.getTitle(), '?') self.assertEquals(o.getTitle(), '?')
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
o.activate(after_tag = 'toto', activity = activity).setTitle('b') o.activate(after_tag = 'toto', activity = activity).setTitle('b')
o.activate(tag = 'toto', activity = activity).setTitle('a') o.activate(tag = 'toto', activity = activity).setTitle('a')
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
self.assertEquals(o.getTitle(), 'b') self.assertEquals(o.getTitle(), 'b')
o._v_activate_kw = {'tag':'toto'} o._v_activate_kw = {'tag':'toto'}
def titi(self): def titi(self):
self.setCorporateName(self.getTitle() + 'd') self.setCorporateName(self.getTitle() + 'd')
...@@ -565,7 +565,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -565,7 +565,7 @@ class TestCMFActivity(ERP5TypeTestCase):
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
self.assertEquals(o.getCorporateName(), 'cd') self.assertEquals(o.getCorporateName(), 'cd')
def CheckScheduling(self, activity): def CheckScheduling(self, activity):
""" """
Check if active objects with different after parameters are executed in a correct order Check if active objects with different after parameters are executed in a correct order
...@@ -575,16 +575,16 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -575,16 +575,16 @@ class TestCMFActivity(ERP5TypeTestCase):
if not organisation_module.hasContent(self.company_id): if not organisation_module.hasContent(self.company_id):
organisation_module.newContent(id=self.company_id) organisation_module.newContent(id=self.company_id)
o = portal.organisation._getOb(self.company_id) o = portal.organisation._getOb(self.company_id)
o.setTitle('?') o.setTitle('?')
self.assertEquals(o.getTitle(), '?') self.assertEquals(o.getTitle(), '?')
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
def toto(self, s): def toto(self, s):
self.setTitle(self.getTitle() + s) self.setTitle(self.getTitle() + s)
o.__class__.toto = toto o.__class__.toto = toto
o.activate(tag = 'toto', activity = activity).toto('a') o.activate(tag = 'toto', activity = activity).toto('a')
get_transaction().commit() get_transaction().commit()
o.activate(after_tag = 'titi', activity = activity).toto('b') o.activate(after_tag = 'titi', activity = activity).toto('b')
...@@ -593,7 +593,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -593,7 +593,7 @@ class TestCMFActivity(ERP5TypeTestCase):
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
self.assertEquals(o.getTitle(), 'cb') self.assertEquals(o.getTitle(), 'cb')
def CheckClearActivities(self, activity): def CheckClearActivities(self, activity):
""" """
Check if active objects are held even after clearing the tables. Check if active objects are held even after clearing the tables.
...@@ -611,16 +611,16 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -611,16 +611,16 @@ class TestCMFActivity(ERP5TypeTestCase):
m = message_list[0] m = message_list[0]
self.assertEquals(m.object_path, o.getPhysicalPath()) self.assertEquals(m.object_path, o.getPhysicalPath())
self.assertEquals(m.method_id, '_setTitle') self.assertEquals(m.method_id, '_setTitle')
o = portal.organisation._getOb(self.company_id) o = portal.organisation._getOb(self.company_id)
o.activate(activity=activity)._setTitle('foo') o.activate(activity=activity)._setTitle('foo')
get_transaction().commit() get_transaction().commit()
check(o) check(o)
portal.portal_activities.manageClearActivities() portal.portal_activities.manageClearActivities()
get_transaction().commit() get_transaction().commit()
check(o) check(o)
get_transaction().commit() get_transaction().commit()
self.tic() self.tic()
...@@ -1135,7 +1135,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1135,7 +1135,7 @@ class TestCMFActivity(ERP5TypeTestCase):
ZopeTestCase._print(message) ZopeTestCase._print(message)
LOG('Testing... ',0,message) LOG('Testing... ',0,message)
self.TryMethodAfterMethod('SQLDict') self.TryMethodAfterMethod('SQLDict')
def test_55_TryAfterMethodIdWithSQLQueue(self, quiet=0, run=run_all_test): def test_55_TryAfterMethodIdWithSQLQueue(self, quiet=0, run=run_all_test):
# Test if after_method_id can be used # Test if after_method_id can be used
if not run: return if not run: return
...@@ -1144,7 +1144,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1144,7 +1144,7 @@ class TestCMFActivity(ERP5TypeTestCase):
ZopeTestCase._print(message) ZopeTestCase._print(message)
LOG('Testing... ',0,message) LOG('Testing... ',0,message)
self.TryMethodAfterMethod('SQLQueue') self.TryMethodAfterMethod('SQLQueue')
def test_56_TryCallActivityWithRightUser(self, quiet=0, run=run_all_test): def test_56_TryCallActivityWithRightUser(self, quiet=0, run=run_all_test):
# Test if me execute methods with the right user # Test if me execute methods with the right user
# This should be independant of the activity used # This should be independant of the activity used
...@@ -1181,7 +1181,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1181,7 +1181,7 @@ class TestCMFActivity(ERP5TypeTestCase):
ZopeTestCase._print(message) ZopeTestCase._print(message)
LOG('Testing... ',0,message) LOG('Testing... ',0,message)
self.ExpandedMethodWithDeletedSubObject('SQLDict') self.ExpandedMethodWithDeletedSubObject('SQLDict')
def test_58_ExpandedMethodWithDeletedObject(self, quiet=0, run=run_all_test): def test_58_ExpandedMethodWithDeletedObject(self, quiet=0, run=run_all_test):
# Test if after_method_id can be used # Test if after_method_id can be used
if not run: return if not run: return
...@@ -1200,7 +1200,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1200,7 +1200,7 @@ class TestCMFActivity(ERP5TypeTestCase):
LOG('Testing... ',0,message) LOG('Testing... ',0,message)
self.TryAfterTag('SQLDict') self.TryAfterTag('SQLDict')
def test_60_TryAfterTagWithSQLDict(self, quiet=0, run=run_all_test): def test_60_TryAfterTagWithSQLQueue(self, quiet=0, run=run_all_test):
# Test if after_tag can be used # Test if after_tag can be used
if not run: return if not run: return
if not quiet: if not quiet:
...@@ -1245,7 +1245,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1245,7 +1245,7 @@ class TestCMFActivity(ERP5TypeTestCase):
LOG('Testing... ',0,message) LOG('Testing... ',0,message)
self.CheckClearActivities('SQLQueue') self.CheckClearActivities('SQLQueue')
def flushAllActivities(self): def flushAllActivities(self, silent=0, loop_size=1000):
"""Executes all messages until the queue only contains failed """Executes all messages until the queue only contains failed
messages. messages.
""" """
...@@ -1254,12 +1254,14 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1254,12 +1254,14 @@ class TestCMFActivity(ERP5TypeTestCase):
# flush activities # flush activities
while 1: while 1:
loop_count += 1 loop_count += 1
if loop_count >= 1000: if loop_count >= loop_size:
if silent:
return
self.fail('flushAllActivities maximum loop count reached') self.fail('flushAllActivities maximum loop count reached')
activity_tool.distribute(node_count=1) activity_tool.distribute(node_count=1)
activity_tool.tic(processing_node=1) activity_tool.tic(processing_node=1)
finished = 1 finished = 1
for message in activity_tool.getMessageList(): for message in activity_tool.getMessageList():
if message.processing_node not in (INVOKE_ERROR_STATE, if message.processing_node not in (INVOKE_ERROR_STATE,
...@@ -1269,7 +1271,7 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1269,7 +1271,7 @@ class TestCMFActivity(ERP5TypeTestCase):
activity_tool.timeShift(3 * VALIDATION_ERROR_DELAY) activity_tool.timeShift(3 * VALIDATION_ERROR_DELAY)
get_transaction().commit() get_transaction().commit()
if finished: if finished:
return return
def test_65_TestMessageValidationAndFailedActivities(self, def test_65_TestMessageValidationAndFailedActivities(self,
quiet=0, run=run_all_test): quiet=0, run=run_all_test):
...@@ -1277,16 +1279,23 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1277,16 +1279,23 @@ class TestCMFActivity(ERP5TypeTestCase):
Tests that if we have an active method scheduled by Tests that if we have an active method scheduled by
after_method_id and a failed activity with this method id, the after_method_id and a failed activity with this method id, the
method is executed.""" method is NOT executed.
Note: earlier version of this test checked exactly the contrary, but it
was eventually agreed that this was a bug. If an activity fails, all the
activities that depend on it should be block until the first one is
resolved."""
if not run: return if not run: return
if not quiet: if not quiet:
message = '\nafter_method_id and failed activities' message = '\nafter_method_id and failed activities'
ZopeTestCase._print(message) ZopeTestCase._print(message)
LOG('Testing... ', 0, message) LOG('Testing... ', 0, message)
activity_tool = self.getPortal().portal_activities activity_tool = self.getPortal().portal_activities
original_title = 'something'
obj = self.getPortal().organisation_module.newContent( obj = self.getPortal().organisation_module.newContent(
portal_type='Organisation') portal_type='Organisation',
title=original_title)
# Monkey patch Organisation to add a failing method # Monkey patch Organisation to add a failing method
def failingMethod(self): def failingMethod(self):
raise ValueError, 'This method always fail' raise ValueError, 'This method always fail'
...@@ -1302,31 +1311,43 @@ class TestCMFActivity(ERP5TypeTestCase): ...@@ -1302,31 +1311,43 @@ class TestCMFActivity(ERP5TypeTestCase):
activity_list = ['SQLQueue', 'SQLDict', ] activity_list = ['SQLQueue', 'SQLDict', ]
for activity in activity_list: for activity in activity_list:
# reset # reset
activity_tool.manageClearActivities() activity_tool.manageClearActivities(keep=0)
obj.setTitle('something') obj.setTitle(original_title)
get_transaction().commit() get_transaction().commit()
# activate failing message and flush # activate failing message and flush
for fail_activity in activity_list: for fail_activity in activity_list:
obj.activate(activity = fail_activity).failingMethod() obj.activate(activity = fail_activity).failingMethod()
get_transaction().commit() get_transaction().commit()
self.flushAllActivities() self.flushAllActivities(silent=1, loop_size=100)
message_count = len(activity_tool.getMessageList()) full_message_list = activity_tool.getMessageList()
if message_count == 0: remaining_messages = [a for a in full_message_list if a.method_id !=
self.fail('Activity tool should have remaining messages') 'failingMethod']
if len(full_message_list) != 2:
self.fail('failingMethod should not have been flushed')
if len(remaining_messages) != 0:
self.fail('Activity tool should have no other remaining messages')
# activate our message # activate our message
new_title = 'nothing' new_title = 'nothing'
obj.activate(after_method_id = ['failingMethod'], obj.activate(after_method_id = ['failingMethod'],
activity = activity ).setTitle(new_title) activity = activity ).setTitle(new_title)
get_transaction().commit() get_transaction().commit()
self.flushAllActivities() self.flushAllActivities(silent=1, loop_size=100)
self.assertEquals(message_count, full_message_list = activity_tool.getMessageList()
len(activity_tool.getMessageList())) remaining_messages = [a for a in full_message_list if a.method_id !=
self.assertEquals(obj.getTitle(), new_title) 'failingMethod']
if len(full_message_list) != 3:
# restore notification self.fail('failingMethod should not have been flushed')
if len(remaining_messages) != 1:
self.fail('Activity tool should have one blocked setTitle activity')
self.assertEquals(remaining_messages[0].activity_kw['after_method_id'],
['failingMethod'])
self.assertEquals(obj.getTitle(), original_title)
# restore notification and flush failed and blocked activities
Message.notifyUser = originalNotifyUser Message.notifyUser = originalNotifyUser
activity_tool.manageClearActivities(keep=0)
def test_66_TestCountMessageWithTagWithSQLDict(self, quiet=0, run=run_all_test): def test_66_TestCountMessageWithTagWithSQLDict(self, quiet=0, run=run_all_test):
""" """
......
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