From 01840c37f04ca20fdc3859a177438dc717afc81c Mon Sep 17 00:00:00 2001 From: Sebastien Robin <seb@nexedi.com> Date: Tue, 3 Feb 2004 09:53:38 +0000 Subject: [PATCH] added new tests (change an object on two different places at the same time, working on subojbects) added the simulation of conflicts in the synchronization corrected some bugs git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@387 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5SyncML/Conduit/ERP5Conduit.py | 62 +++++---- product/ERP5SyncML/Subscription.py | 26 ++++ product/ERP5SyncML/XMLSyncUtils.py | 64 ++++++--- product/ERP5SyncML/tests/testERP5SyncML.py | 143 ++++++++++++++------- 4 files changed, 211 insertions(+), 84 deletions(-) diff --git a/product/ERP5SyncML/Conduit/ERP5Conduit.py b/product/ERP5SyncML/Conduit/ERP5Conduit.py index 0ba44d1ec9..6e2c811974 100755 --- a/product/ERP5SyncML/Conduit/ERP5Conduit.py +++ b/product/ERP5SyncML/Conduit/ERP5Conduit.py @@ -103,7 +103,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): security.declareProtected(Permissions.ModifyPortalContent, 'addNode') def addNode(self, xml=None, object=None, previous_xml=None, - object_id=None, force=0, **kw): + object_id=None, force=0, simulate=0, **kw): """ A node is added @@ -132,7 +132,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): for element in self.getXupdateElementList(xml): xml = self.getElementFromXupdate(element) conflict_list += self.addNode(xml=xml,object=object, - previous_xml=previous_xml, force=force, **kw) + previous_xml=previous_xml, force=force, + simulate=simulate, **kw) elif xml.nodeName == 'object': if object_id is None: object_id = self.getAttribute(xml,'id') @@ -170,7 +171,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): px_tool._modifyProxy(proxy,rpath) subobject = object._getOb(object_id) - self.newObject(object=subobject,xml=xml) + self.newObject(object=subobject,xml=xml,simulate=simulate) elif xml.nodeName in self.XUPDATE_INSERT_OR_ADD \ and self.getSubObjectDepth(xml)>=1: sub_object_id = self.getSubObjectId(xml) @@ -194,7 +195,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): LOG('addNode',0,'sub_xml: %s' % str(sub_xml)) # Then do the udpate conflict_list += self.addNode(xml=sub_xml,object=sub_object, - previous_xml=sub_previous_xml, force=force) + previous_xml=sub_previous_xml, force=force, + simulate=simulate, **kw) elif xml.nodeName == self.history_tag or self.isHistoryAdd(xml)>0: # We want to add a workflow action wf_tool = getToolByName(object,'portal_workflow') @@ -211,12 +213,12 @@ class ERP5Conduit(XMLSyncUtilsMixin): status=status,wf_tool=wf_tool, xml=xml) LOG('addNode, workflow_history wf_conflict_list:',0,wf_conflict_list) - if wf_conflict_list==[] or force: + if wf_conflict_list==[] or force and not simulate: LOG('addNode, setting status:',0,'ok') wf_tool.setStatusOf(wf_id,object,status) else: conflict_list += wf_conflict_list - elif xml.nodeName in self.local_role_list: + elif xml.nodeName in self.local_role_list and not simulate: # We want to add a local role roles = self.convertXmlValue(xml.childNodes[0].data,data_type='tokens') roles = list(roles) # Needed for CPS, or we have a CPS error @@ -224,11 +226,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): roles = roles[1:] object.manage_setLocalRoles(user,roles) else: - conflict_list += self.updateNode(xml=xml,object=object, force=force, **kw) + conflict_list += self.updateNode(xml=xml,object=object, force=force, + simulate=simulate, **kw) return conflict_list security.declareProtected(Permissions.ModifyPortalContent, 'deleteNode') - def deleteNode(self, xml=None, object=None, object_id=None, force=None, **kw): + def deleteNode(self, xml=None, object=None, object_id=None, force=None, + simulate=0, **kw): """ A node is deleted """ @@ -251,7 +255,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): sub_object = object._getOb(sub_object_id) sub_xml = self.getSubObjectXupdate(xml) conflict_list += self.deleteNode(xml=sub_xml,object=sub_object, - force=force) + force=force, simulate=simulate, **kw) except KeyError: pass else: # We do have an object_id @@ -263,7 +267,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): return conflict_list security.declareProtected(Permissions.ModifyPortalContent, 'updateNode') - def updateNode(self, xml=None, object=None, previous_xml=None, force=0, **kw): + def updateNode(self, xml=None, object=None, previous_xml=None, force=0, + simulate=0, **kw): """ A node is updated with some xupdate - xml : the xml corresponding to the update, it should be xupdate @@ -280,7 +285,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): #xupdate_utils = XupdateUtils() xupdate_utils = self conflict_list += xupdate_utils.applyXupdate(object=object,xupdate=xml,conduit=self, - previous_xml=previous_xml, force=force) + previous_xml=previous_xml, force=force, simulate=simulate, + **kw) # we may have only the part of an xupdate else: args = {} @@ -358,7 +364,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): #publisher_value=current_data, # not needed any more #subscriber_value=data)] # not needed any more # We will now apply the argument with the method edit - if args != {} and (isConflict==0 or force): + if args != {} and (isConflict==0 or force) and (not simulate): LOG('updateNode',0,'object._edit, args: %s' % str(args)) object._edit(**args) # It is sometimes required to do something after an edit @@ -368,12 +374,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): if keyword == 'object': # This is the case where we have to call addNode LOG('updateNode',0,'we will add sub-object') - conflict_list += self.addNode(xml=subnode,object=object,force=force) - elif keyword == self.history_tag: + conflict_list += self.addNode(xml=subnode,object=object,force=force, + simulate=simulate, **kw) + elif keyword == self.history_tag and not simulate: # This is the case where we have to call addNode LOG('updateNode',0,'we will add history') - conflict_list += self.addNode(xml=subnode,object=object,force=force) - elif keyword == self.local_role_tag: + conflict_list += self.addNode(xml=subnode,object=object,force=force, + simulate=simulate,**kw) + elif keyword == self.local_role_tag and not simulate: # This is the case where we have to call addNode LOG('updateNode',0,'we will add a local role') roles = self.convertXmlValue(data,data_type='tokens') @@ -403,7 +411,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): LOG('updateNode',0,'sub_xml: %s' % str(sub_xml)) # Then do the udpate conflict_list += self.updateNode(xml=sub_xml,object=sub_object, force=force, - previous_xml=sub_previous_xml) + previous_xml=sub_previous_xml,simulate=simulate, **kw) return conflict_list security.declareProtected(Permissions.AccessContentsInformation,'getFormatedArgs') @@ -635,7 +643,6 @@ class ERP5Conduit(XMLSyncUtilsMixin): except IndexError: # There is no data data = None data = self.convertXmlValue(data, data_type=data_type) - LOG('getObjectProperty',0,'prop; %s, data_type:%s, data: %s' % (property,data_type,data)) return data return None @@ -699,12 +706,14 @@ class ERP5Conduit(XMLSyncUtilsMixin): security.declareProtected(Permissions.ModifyPortalContent, 'newObject') - def newObject(self, object=None, xml=None): + def newObject(self, object=None, xml=None, simulate=0): """ modify the object with datas from the xml (action section) """ args = {} + if simulate: + return # Retrieve the list of users with a role and delete default roles user_role_list = map(lambda x:x[0],object.get_local_roles()) object.manage_delLocalRoles(user_role_list) @@ -766,7 +775,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): for subnode in self.getElementNodeList(xml): if subnode.nodeName in self.XUPDATE_EL: e_list += [subnode] - LOG('getXupdateElementList, e_list:%',0,e_list) + LOG('getXupdateElementList, e_list:',0,e_list) return e_list security.declareProtected(Permissions.AccessContentsInformation,'getElementFromXupdate') @@ -775,7 +784,7 @@ class ERP5Conduit(XMLSyncUtilsMixin): from a xupdate:element returns the element as xml """ if xml.nodeName in self.XUPDATE_EL: - result = '<' + result = u'<' result += xml.attributes[0].nodeValue for subnode in self.getElementNodeList(xml): #getElementNodeList if subnode.nodeName == 'xupdate:attribute': @@ -786,11 +795,11 @@ class ERP5Conduit(XMLSyncUtilsMixin): xml_string = StringIO() PrettyPrint(xml,xml_string) xml_string = xml_string.getvalue() + xml_string = unicode(xml_string,encoding='utf-8') maxi = max(xml_string.find('>')+1,\ xml_string.rfind('</xupdate:attribute>')+len('</xupdate:attribute>')) result += xml_string[maxi:xml_string.find('</xupdate:element>')] result += '</' + xml.attributes[0].nodeValue + '>' - LOG('getElementFromXupdate, result:',0,result) return self.convertToXml(result) return xml @@ -856,7 +865,8 @@ class ERP5Conduit(XMLSyncUtilsMixin): # XXX is it the right place ? It should be in XupdateUtils, but here we # have some specific things to do security.declareProtected(Permissions.ModifyPortalContent, 'applyXupdate') - def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0, **kw): + def applyXupdate(self, object=None, xupdate=None, conduit=None, force=0, + simulate=0, **kw): """ Parse the xupdate and then it will call the conduit """ @@ -869,13 +879,13 @@ class ERP5Conduit(XMLSyncUtilsMixin): selection_name = '' if subnode.nodeName in self.XUPDATE_INSERT_OR_ADD: conflict_list += conduit.addNode(xml=sub_xupdate,object=object, \ - force=force, **kw) + force=force, simulate=simulate, **kw) elif subnode.nodeName in self.XUPDATE_DEL: conflict_list += conduit.deleteNode(xml=sub_xupdate, object=object, \ - force=force, **kw) + force=force, simulate=simulate, **kw) elif subnode.nodeName in self.XUPDATE_UPDATE: conflict_list += conduit.updateNode(xml=sub_xupdate, object=object, \ - force=force, **kw) + force=force, simulate=simulate, **kw) #elif subnode.nodeName in self.XUPDATE_INSERT: # conflict_list += conduit.addNode(xml=subnode, object=object, force=force, **kw) diff --git a/product/ERP5SyncML/Subscription.py b/product/ERP5SyncML/Subscription.py index c5e04c9d35..04b2358eb8 100755 --- a/product/ERP5SyncML/Subscription.py +++ b/product/ERP5SyncML/Subscription.py @@ -198,6 +198,8 @@ class Signature(SyncCode): self.resetConflictList() self.md5_string = None self.force = 0 + self.setSubscriberXupdate(None) + self.setPublisherXupdate(None) #def __init__(self,object=None, status=None, xml_string=None): # self.uid = object.uid @@ -271,6 +273,30 @@ class Signature(SyncCode): """ return self.temp_xml + def setSubscriberXupdate(self, xupdate): + """ + set the full temp xupdate + """ + self.subscriber_xupdate = xupdate + + def getSubscriberXupdate(self): + """ + get the full temp xupdate + """ + return self.subscriber_xupdate + + def setPublisherXupdate(self, xupdate): + """ + set the full temp xupdate + """ + self.publisher_xupdate = xupdate + + def getPublisherXupdate(self): + """ + get the full temp xupdate + """ + return self.publisher_xupdate + def setMD5(self, xml): """ set the MD5 object of this signature diff --git a/product/ERP5SyncML/XMLSyncUtils.py b/product/ERP5SyncML/XMLSyncUtils.py index 301bb1ec25..f21202ce8c 100755 --- a/product/ERP5SyncML/XMLSyncUtils.py +++ b/product/ERP5SyncML/XMLSyncUtils.py @@ -552,13 +552,23 @@ class XMLSyncUtilsMixin(SyncCode): def getSyncMLData(self, domain=None,remote_xml=None,cmd_id=0, subscriber=None,destination_path=None, - xml_confirmation=None): + xml_confirmation=None,conduit=None): """ This generate the syncml data message. This returns a string with all modification made locally (ie replace, add ,delete...) + + if object is not None, this usually means we want to set the + actual xupdate on the signature. """ local_gid_list = [] syncml_data = '' +# store_xupdate = 0 +# if object is None: +# object_list = domain.getObjectList() +# else: +# store_xupdate = 1 +# object_list = [object] + for object in domain.getObjectList(): status = self.SENT gid_generator = getattr(object,domain.getGidGenerator(),None) @@ -575,6 +585,7 @@ class XMLSyncUtilsMixin(SyncCode): LOG('getSyncMLData',0,'hasSignature: %s' % str(subscriber.hasSignature(object_gid))) LOG('getSyncMLData',0,'alert_code == slowsync: %s' % str(self.getAlertCode(remote_xml)==self.SLOW_SYNC)) signature = subscriber.getSignature(object_gid) + LOG('getSyncMLData',0,'current object: %s' % str(object.getId())) if signature is not None: LOG('getSyncMLData',0,'signature.status: %s' % str(signature.getStatus())) LOG('getSyncMLData',0,'signature.action: %s' % str(signature.getAction())) @@ -615,7 +626,9 @@ class XMLSyncUtilsMixin(SyncCode): if signature.getStatus()==self.PUB_CONFLICT_MERGE: xml_confirmation += self.SyncMLConfirmation(cmd_id,object.id, self.CONFLICT_MERGE,'Replace') + set_synchronized = 1 if not signature.checkMD5(xml_object): + set_synchronized = 0 # This object has changed on this side, we have to generate some xmldiff xml_string = self.getXupdateObject(object=object, xml_mapping=domain.xml_mapping, @@ -638,7 +651,18 @@ class XMLSyncUtilsMixin(SyncCode): xml_string=xml_string, more_data=more_data) cmd_id += 1 signature.setTempXML(xml_object) - else: # We should not have this case when we are in CONFLICT_MERGE + # Now we can apply the xupdate from the subscriber + subscriber_xupdate = signature.getSubscriberXupdate() + LOG('getSyncMLData subscriber_xupdate',0,subscriber_xupdate) + if subscriber_xupdate is not None: + old_xml = signature.getXML() + conduit.updateNode(xml=subscriber_xupdate, object=object, + previous_xml=old_xml,force=(domain.getDomainType==self.SUB), + simulate=0) + xml_object = self.getXMLObject(object=object,xml_mapping=domain.xml_mapping) + signature.setTempXML(xml_object) + if set_synchronized: # We have to do that after this previous update + # We should not have this case when we are in CONFLICT_MERGE signature.setStatus(self.SYNCHRONIZED) elif signature.getStatus()==self.PUB_CONFLICT_CLIENT_WIN: # We have decided to apply the update @@ -691,7 +715,7 @@ class XMLSyncUtilsMixin(SyncCode): return (syncml_data,xml_confirmation,cmd_id) def applyActionList(self, domain=None, subscriber=None,destination_path=None, - cmd_id=0,remote_xml=None,conduit=None): + cmd_id=0,remote_xml=None,conduit=None,simulate=0): """ This just look to a list of action to do, then id applies each action one by one, thanks to a conduit @@ -706,8 +730,6 @@ class XMLSyncUtilsMixin(SyncCode): status_code = self.SUCCESS # Thirst we have to check the kind of action it is partial_data = self.getPartialData(next_action) - #LOG('XMLSyncUtils',0,'partial_data: %s' % str(partial_data)) - #LOG('XMLSyncUtils',0,'checkActionMoreData: %s' % str(self.checkActionMoreData(next_action))) object_gid = self.getActionId(next_action) signature = subscriber.getSignature(object_gid) if signature == None: @@ -749,12 +771,6 @@ class XMLSyncUtilsMixin(SyncCode): self.SyncMLConfirmation(cmd_id,object_gid,self.SUCCESS,'Add') cmd_id +=1 elif next_action.nodeName == 'Replace': - #object = domain.getObjectFromGid(object=destination_path,gid=object_gid) -# object =None -# try: -# object = destination_path._getOb(self.getActionId(next_action)) -# except (AttributeError, KeyError): -# pass LOG('SyncModif',0,'object: %s will be updated...' % str(object)) if object is not None: LOG('SyncModif',0,'object: %s will be updated...' % object.id) @@ -763,7 +779,8 @@ class XMLSyncUtilsMixin(SyncCode): previous_xml = signature.getXML() LOG('SyncModif',0,'previous signature: %i' % len(previous_xml)) conflict_list += conduit.updateNode(xml=data_subnode, object=object, - previous_xml=signature.getXML(),force=force) + previous_xml=signature.getXML(),force=force, + simulate=simulate) mapping = getattr(object,domain.getXMLMapping(),None) xml_object = '' if mapping is not None: @@ -778,11 +795,25 @@ class XMLSyncUtilsMixin(SyncCode): PrettyPrint(data_subnode,stream=string_io) data_subnode_string = string_io.getvalue() signature.setPartialXML(data_subnode_string) - else: + elif not simulate: signature.setStatus(self.SYNCHRONIZED) xml_confirmation += self.SyncMLConfirmation(cmd_id, object_gid,status_code,'Replace') cmd_id +=1 + if simulate: + # This means we are on the publiher side and we want to store + # the xupdate from the subscriber and we also want to generate + # the current xupdate from the last synchronization + string_io = StringIO() + PrettyPrint(data_subnode,stream=string_io) + data_subnode_string = string_io.getvalue() + LOG('applyActionList, subscriber_xupdate:',0,data_subnode_string) + signature.setSubscriberXupdate(data_subnode_string) +# xml_string = self.getXupdateObject(object=object, +# xml_mapping=domain.xml_mapping, +# old_xml=signature.getXML()) + # signature.setPublisherXupdate(xml_string) XXX is it needed ?? + elif next_action.nodeName == 'Delete': object_id = object.id conduit.deleteNode(xml=self.getDataSubNode(next_action), object=destination_path, @@ -881,7 +912,9 @@ class XMLSyncUtils(XMLSyncUtilsMixin): return subscriber = domain # If we are the client, this is fine + simulate = 0 # used by applyActionList, should be 0 for client if domain.domain_type == self.PUB: + simulate = 1 for subnode in xml_header.childNodes: if subnode.nodeType == subnode.ELEMENT_NODE and subnode.nodeName == "Source": subscription_url = str(subnode.childNodes[0].data) @@ -901,7 +934,7 @@ class XMLSyncUtils(XMLSyncUtilsMixin): destination_path=destination_path, subscriber=subscriber, remote_xml=remote_xml, - conduit=conduit) + conduit=conduit, simulate=simulate) LOG('SyncModif, has_next_action:',0,has_next_action) xml = "" @@ -925,7 +958,8 @@ class XMLSyncUtils(XMLSyncUtilsMixin): remote_xml=remote_xml, subscriber=subscriber, destination_path=destination_path, - cmd_id=cmd_id,xml_confirmation=xml_confirmation) + cmd_id=cmd_id,xml_confirmation=xml_confirmation, + conduit=conduit) # syncml body xml += ' <SyncBody>\n' diff --git a/product/ERP5SyncML/tests/testERP5SyncML.py b/product/ERP5SyncML/tests/testERP5SyncML.py index 6efebde022..9d0cd6b0da 100755 --- a/product/ERP5SyncML/tests/testERP5SyncML.py +++ b/product/ERP5SyncML/tests/testERP5SyncML.py @@ -58,10 +58,10 @@ class TestERP5SyncML(ERP5TypeTestCase): description1 = 'description1 $sdfrç_sdfsçdf_oisfsopf' first_name2 = 'Jean-Paul' last_name2 = 'Smets' - description2 = 'descrip ti on2éà@ $*<<<<>>>></title>&oekd' + description2 = 'description2éà@ $*< <<< >>>></title>&oekd' first_name3 = 'Yoshinori' last_name3 = 'Okuji' - description3 = 'description çsdf__sdfççç_df___&&é]]]°°°°°°' + description3 = 'description3 çsdf__sdfççç_df___&&é]]]°°°°°°' xml_mapping = 'asXML' id1 = '170' id2 = '171' @@ -157,11 +157,11 @@ class TestERP5SyncML(ERP5TypeTestCase): user = uf.getUserById('ERP5TypeTestCase').__of__(uf) newSecurityManager(None, user) - def testPopulatePersonServer(self, quiet=0, run=run_all_test): + def populatePersonServer(self, quiet=0, run=run_all_test): if not run: return if not quiet: ZopeTestCase._print('\nTest Populate Person Server ') - LOG('Testing... ',0,'testPopulatePersonServer') + LOG('Testing... ',0,'populatePersonServer') self.login() person_server = self.getPersonServer() person_id = '' @@ -205,7 +205,7 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'testGetObjectList') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) - nb_person = self.testPopulatePersonServer(quiet=1,run=1) + nb_person = self.populatePersonServer(quiet=1,run=1) portal_sync = self.getSynchronizationTool() publication_list = portal_sync.getPublicationList() publication = publication_list[0] @@ -231,7 +231,7 @@ class TestERP5SyncML(ERP5TypeTestCase): ZopeTestCase._print('\nTest Export and Import ') LOG('Testing... ',0,'testExportImport') self.login() - self.testPopulatePersonServer(quiet=1,run=1) + self.populatePersonServer(quiet=1,run=1) person_server = self.getPersonServer() person_client1 = self.getPersonClient1() person = person_server._getOb(self.id1) @@ -278,7 +278,7 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'testFirstSynchronization') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) - nb_person = self.testPopulatePersonServer(quiet=1,run=1) + nb_person = self.populatePersonServer(quiet=1,run=1) # Synchronize the first client nb_message1 = self.synchronize(self.sub_id1) self.failUnless(nb_message1==self.nb_message_first_synchronization) @@ -316,7 +316,7 @@ class TestERP5SyncML(ERP5TypeTestCase): LOG('Testing... ',0,'testGetObjectFromGid') self.login() self.setupPublicationAndSubscription(quiet=1,run=1) - self.testPopulatePersonServer(quiet=1,run=1) + self.populatePersonServer(quiet=1,run=1) # By default we can just give the id portal_sync = self.getSynchronizationTool() publication = portal_sync.getPublication(self.pub_id) @@ -400,13 +400,12 @@ class TestERP5SyncML(ERP5TypeTestCase): person1_s.edit(**kw) kw = {'description':self.description3} person1_c.edit(**kw) - # XXX Warning XXX This does not works actually, need to be CORRECTED !!!! -# self.synchronize(self.sub_id1) -# self.checkSynchronizationStateIsSynchronized() -# self.failUnless(person1_s.getFirstName()==self.first_name3) -# self.failUnless(person1_s.getDescription()==self.description3) -# self.failUnless(person1_c.getFirstName()==self.first_name3) -# self.failUnless(person1_c.getDescription()==self.description3) + self.synchronize(self.sub_id1) + self.checkSynchronizationStateIsSynchronized() + self.failUnless(person1_s.getFirstName()==self.first_name3) + self.failUnless(person1_s.getDescription()==self.description3) + self.failUnless(person1_c.getFirstName()==self.first_name3) + self.failUnless(person1_c.getDescription()==self.description3) def testGetConflictList(self, quiet=0, run=run_all_test): # We will try to generate a conflict and then to get it @@ -428,6 +427,8 @@ class TestERP5SyncML(ERP5TypeTestCase): conflict_list = portal_sync.getConflictList() self.failUnless(len(conflict_list)==1) conflict = conflict_list[0] + self.failUnless(person1_c.getDescription()==self.description3) + self.failUnless(person1_s.getDescription()==self.description2) self.failUnless(conflict.getPropertyId()=='description') self.failUnless(conflict.getPublisherValue()==self.description2) self.failUnless(conflict.getSubscriberValue()==self.description3) @@ -438,10 +439,10 @@ class TestERP5SyncML(ERP5TypeTestCase): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return + self.testGetConflictList(quiet=1,run=1) if not quiet: ZopeTestCase._print('\nTest Apply Publisher Value ') LOG('Testing... ',0,'testApplyPublisherValue') - self.testGetConflictList(quiet=1,run=1) portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() conflict = conflict_list[0] @@ -452,8 +453,8 @@ class TestERP5SyncML(ERP5TypeTestCase): conflict.applyPublisherValue() self.synchronize(self.sub_id1) self.checkSynchronizationStateIsSynchronized() - self.failUnless(person1_s.getDescription()==self.description2) self.failUnless(person1_c.getDescription()==self.description2) + self.failUnless(person1_s.getDescription()==self.description2) conflict_list = portal_sync.getConflictList() self.failUnless(len(conflict_list)==0) @@ -461,12 +462,12 @@ class TestERP5SyncML(ERP5TypeTestCase): # We will try to generate a conflict and then to get it # We will also make sure it contains what we want if not run: return - if not quiet: - ZopeTestCase._print('\nTest Apply Subscriber Value ') - LOG('Testing... ',0,'testApplySubscriberValue') self.testGetConflictList(quiet=1,run=1) portal_sync = self.getSynchronizationTool() conflict_list = portal_sync.getConflictList() + if not quiet: + ZopeTestCase._print('\nTest Apply Subscriber Value ') + LOG('Testing... ',0,'testApplySubscriberValue') conflict = conflict_list[0] person_server = self.getPersonServer() person1_s = person_server._getOb(self.id1) @@ -480,30 +481,86 @@ class TestERP5SyncML(ERP5TypeTestCase): conflict_list = portal_sync.getConflictList() self.failUnless(len(conflict_list)==0) -# def testPopulatePersonServerWithSubObject(self, quiet=0, run=1): -# if not run: return -# if not quiet: -# ZopeTestCase._print('\nTest Populate Person Server With Sub Object ') -# LOG('Testing... ',0,'testPopulatePersonServerWithSubObject') -# self.testPopulatePersonServer(quiet=1,run=1) -# person_server = self.getPersonServer() -# person1 = person_server._getOb(self.id1) -# sub_person1 = person1.newContent(id=self.id1,portal_type='Person') -# person2 = person_server.newContent(id=self.id2,portal_type='Person') -# kw = {'first_name':self.first_name1,'last_name':self.last_name1, -# 'description':self.description1} -# person1.edit(**kw) -# kw = {'first_name':self.first_name2,'last_name':self.last_name2, -# 'description':self.description2} -# person2.edit(**kw) -# nb_person = len(person_server.objectValues()) -# self.failUnless(nb_person==2) -# return nb_person - -# XXX TODO : need to add a test with all kind of strange sign, like &, à è...>< - - + def populatePersonServerWithSubObject(self, quiet=0, run=run_all_test): + """ + Before this method, we need to call populatePersonServer + Then it will give the following tree : + - person_server : + - id1 + - id1 + - id2 + - id2 + """ + if not run: return + if not quiet: + ZopeTestCase._print('\nTest Populate Person Server With Sub Object ') + LOG('Testing... ',0,'populatePersonServerWithSubObject') + person_server = self.getPersonServer() + person1 = person_server._getOb(self.id1) + sub_person1 = person1.newContent(id=self.id1,portal_type='Person') + kw = {'first_name':self.first_name1,'last_name':self.last_name1, + 'description':self.description1} + sub_person1.edit(**kw) + sub_sub_person = sub_person1.newContent(id=self.id2,portal_type='Person') + kw = {'first_name':self.first_name2,'last_name':self.last_name2, + 'description':self.description2} + sub_sub_person.edit(**kw) + # remove ('','portal...','person_server') + len_path = len(sub_sub_person.getPhysicalPath()) - 3 + self.failUnless(len_path==3) + def testAddSubObject(self, quiet=0, run=run_all_test): + """ + In this test, we synchronize, then add sub object on the + server and then see if the next synchronization will also + create sub-objects on the client + """ + if not run: return + self.testFirstSynchronization(quiet=1,run=1) + if not quiet: + ZopeTestCase._print('\nTest Add Sub Object ') + LOG('Testing... ',0,'testAddSubObject') + self.populatePersonServerWithSubObject(quiet=1,run=1) + self.synchronize(self.sub_id1) + self.checkSynchronizationStateIsSynchronized() + person_client1 = self.getPersonClient1() + person1_c = person_client1._getOb(self.id1) + sub_person1_c = person1_c._getOb(self.id1) + sub_sub_person = sub_person1_c._getOb(self.id2) + # remove ('','portal...','person_server') + len_path = len(sub_sub_person.getPhysicalPath()) - 3 + self.failUnless(len_path==3) + self.failUnless(sub_sub_person.getDescription()==self.description2) + self.failUnless(sub_sub_person.getFirstName()==self.first_name2) + self.failUnless(sub_sub_person.getLastName()==self.last_name2) + + def testUpdateSubObject(self, quiet=0, run=1): + """ + In this test, we start with a tree of object already + synchronized, then we update a subobject, and we will see + if it is updated correctly + """ + if not run: return + self.testAddSubObject(quiet=1,run=1) + if not quiet: + ZopeTestCase._print('\nTest Update Sub Object ') + LOG('Testing... ',0,'testUpdateSubObject') + person_client1 = self.getPersonClient1() + person1_c = person_client1._getOb(self.id1) + sub_person1_c = person1_c._getOb(self.id1) + sub_sub_person_c = sub_person1_c._getOb(self.id2) + person_server = self.getPersonServer() + sub_sub_person_s = person_server._getOb(self.id1)._getOb(self.id1)._getOb(self.id2) + kw = {'first_name':self.first_name3} + sub_sub_person_c.edit(**kw) + kw = {'description':self.description3} + sub_sub_person_s.edit(**kw) + self.synchronize(self.sub_id1) + self.checkSynchronizationStateIsSynchronized() + self.failUnless(sub_sub_person_c.getDescription()==self.description3) + self.failUnless(sub_sub_person_c.getFirstName()==self.first_name3) + self.failUnless(sub_sub_person_s.getDescription()==self.description3) + self.failUnless(sub_sub_person_s.getFirstName()==self.first_name3) if __name__ == '__main__': framework() -- 2.30.9