[Commits] (donn) Updated Stamping for robustness
commits at osafoundation.org
commits at osafoundation.org
Sun Aug 8 19:13:26 PDT 2004
Commit by: donn
Modified files:
chandler/parcels/osaf/contentmodel/ContentModel.py 1.22 1.23
chandler/parcels/osaf/contentmodel/tasks/parcel.xml 1.27 1.28
chandler/parcels/osaf/contentmodel/tests/TestStamping.py 1.1 1.2
Log message:
Updated Stamping for robustness
* fixed a bug copying collection data during stamping
* improved robustness and warning messages
* stamping was broken when _getSuperKinds() was removed, now fixed
* rewrote the test cases
* Changed the ContentModel so "requestee" on a Task could be a list
ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/contentmodel/ContentModel.py.diff?r1=text&tr1=1.22&r2=text&tr2=1.23
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/contentmodel/tasks/parcel.xml.diff?r1=text&tr1=1.27&r2=text&tr2=1.28
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/contentmodel/tests/TestStamping.py.diff?r1=text&tr1=1.1&r2=text&tr2=1.2
Index: chandler/parcels/osaf/contentmodel/tasks/parcel.xml
diff -u chandler/parcels/osaf/contentmodel/tasks/parcel.xml:1.27 chandler/parcels/osaf/contentmodel/tasks/parcel.xml:1.28
--- chandler/parcels/osaf/contentmodel/tasks/parcel.xml:1.27 Tue Aug 3 09:58:58 2004
+++ chandler/parcels/osaf/contentmodel/tasks/parcel.xml Sun Aug 8 19:13:24 2004
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="iso-8859-1"?>
-<!-- $Revision: 1.27 $ -->
-<!-- $Date: 2004/08/03 16:58:58 $ -->
+<!-- $Revision: 1.28 $ -->
+<!-- $Date: 2004/08/09 02:13:24 $ -->
<!-- Copyright (c) 2004 Open Source Applications Foundation -->
<!-- License: http://osafoundation.org/Chandler_0.1_license_terms.htm -->
@@ -44,6 +44,7 @@
<displayName>Requestee</displayName>
<issues>Type could be Contact, EmailAddress or String</issues>
<issues>Think about using the icalendar terminology</issues>
+ <cardinality>list</cardinality>
<type itemref="content:ContentItem"/>
<inverseAttribute itemref="tasks:taskRequests"/>
</Attribute>
Index: chandler/parcels/osaf/contentmodel/ContentModel.py
diff -u chandler/parcels/osaf/contentmodel/ContentModel.py:1.22 chandler/parcels/osaf/contentmodel/ContentModel.py:1.23
--- chandler/parcels/osaf/contentmodel/ContentModel.py:1.22 Sat Jul 17 15:22:54 2004
+++ chandler/parcels/osaf/contentmodel/ContentModel.py Sun Aug 8 19:13:23 2004
@@ -1,14 +1,15 @@
""" Classes used for contentmodel parcel and kinds.
"""
-__revision__ = "$Revision: 1.22 $"
-__date__ = "$Date: 2004/07/17 22:22:54 $"
+__revision__ = "$Revision: 1.23 $"
+__date__ = "$Date: 2004/08/09 02:13:23 $"
__copyright__ = "Copyright (c) 2003 Open Source Applications Foundation"
__license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
from application.Parcel import Parcel
import repository.item.Item as Item
import repository.item.Query as Query
+import logging
import application.Globals as Globals
@@ -87,14 +88,56 @@
getConversationKind = classmethod(getConversationKind)
-class StampError(ValueError):
- "Can't stamp Item with the requested Mixin Kind"
+class SuperKindSignature(list):
+ """
+ A list of unique superkinds, used as a signature to identify
+ the structure of a Kind for stamping.
+ The signature is the list of twig node superkinds of the Kind.
+ Using a tree analogy, a twig is the part farthest from the leaf
+ that has no braching.
+ Specifically, a twig node in the SuperKind hierarchy is a node
+ that has at most one superkind, and whose superkind has at
+ most one superkind, all they way up.
+ The twig superkinds list makes the best signature for two reasons:
+ 1) it bypasses all the branching, allowing (A, (B,C)) to match
+ ((A, B), C)
+ 2) it uses the most specialized form when there is no branching,
+ thus if D has superKind B, and B has no superKinds,
+ D is more specialized, so we want to use it.
+ """
+ def __init__(self, aKind, *args, **kwds):
+ """
+ construct with a single Kind
+ """
+ super(SuperKindSignature, self).__init__(*args, **kwds)
+ onTwig = self.appendTwigSuperkinds(aKind)
+ if onTwig:
+ assert len(self) == 0, "Error building superKind Signature"
+ self.append(aKind)
+
+ def appendTwigSuperkinds(self, aKind):
+ """
+ called with a kind, appends all the twig superkinds
+ and returns True iff there's been no branching within
+ this twig
+ """
+ supers = aKind.getAttributeValue('superKinds', default = [])
+ numSupers = len(supers)
+ onTwig = True
+ for kind in supers:
+ onTwig = self.appendTwigSuperkinds(kind)
+ if onTwig and numSupers > 1:
+ self.appendUnique(kind)
+ return numSupers < 2 and onTwig
-class KindList(list):
def appendUnique(self, item):
if not item in self:
self.append(item)
+ def extend(self, sequence):
+ for item in sequence:
+ self.appendUnique(item)
+
def properSubsetOf(self, sequence):
"""
return True if self is a proper subset of sequence
@@ -105,26 +148,13 @@
return False
return True
- def allLeafSuperKinds(cls, aKind):
- """
- Return all the leaf node SuperKinds of a Kind in a list.
- """
- def leafNode(kind):
- supers = kind.getAttributeValue('superKinds', default = None)
- return supers is None
- def appendSuperkinds(aKind, supersList):
- supers = aKind.getAttributeValue('superKinds', default = [])
- for kind in supers:
- # all Kinds have Item as their superKind, so the
- # kinds we want are ones just below the leaf.
- if leafNode(kind):
- supersList.appendUnique(aKind)
- appendSuperkinds(kind, supersList)
- supersList = KindList()
- appendSuperkinds(aKind, supersList)
- return supersList
- allLeafSuperKinds = classmethod(allLeafSuperKinds)
-
+ def __str__(self):
+ readable = []
+ for item in self:
+ readable.append(item.itsName)
+ theList = ', '.join(readable)
+ return '['+theList+']'
+
class ContentItem(Item.Item):
def __init__(self, name=None, parent=None, kind=None):
if not parent:
@@ -154,7 +184,55 @@
self.StampPostProcess(futureKind, dataCarryOver)
Globals.repository.commit()
- def FindStampedKind(self, operation, mixinKind):
+ def CandidateStampedKinds(self):
+ """
+ return the list of candidate kinds for stamping
+ right now, we consider all kinds
+ """
+ kindKind = Globals.repository.findPath('//Schema/Core/Kind')
+ allKinds = Query.KindQuery().run([kindKind])
+ return allKinds
+
+ def ComputeTargetKindSignature(self, operation, stampKind):
+ """
+ Compute the Kind Signature for stamping.
+ Takes the operation, the kind of self, the stampKind,
+ and computes a target kind signature, which is a list
+ of superKinds.
+ returns a tuple with the signature, and the allowed
+ extra kinds
+ @return: a C{Tuple} (kindSignature, allowedExtra)
+ where kindSignature is a list of kinds, and
+ allowedExtra is an integer telling how many
+ extra kinds are allowed beyond what's in the target.
+ """
+ myKind = self.itsKind
+ soughtSignature = SuperKindSignature(myKind)
+ stampSignature = SuperKindSignature(stampKind)
+ if operation == 'add':
+ for stampSuperKind in stampSignature:
+ if stampSuperKind in soughtSignature:
+ logging.warning("Trying to stamp with a Kind Signature already present.")
+ logging.warning("%s has signature %s which overlaps with %s whose signature is %s)" % \
+ (stampKind.itsName, stampSignature, \
+ myKind.itsName, soughtSignature))
+ return None # in case this method is overloaded
+ soughtSignature.extend(stampSignature)
+ extrasAllowed = 1
+ else:
+ assert operation == 'remove', "invalid Stamp operation in ContentItem.NewStampedKind: "+operation
+ if not stampSignature.properSubsetOf(soughtSignature):
+ logging.warning("Trying to unstamp with a Kind Signature not already present.")
+ logging.warning("%s has signature %s which is not present in %s: %s" % \
+ (stampKind.itsName, stampSignature, \
+ myKind.itsName, soughtSignature))
+ return None # in case this method is overloaded
+ for stampSuperKind in stampSignature:
+ soughtSignature.remove(stampSuperKind)
+ extrasAllowed = -1
+ return (soughtSignature, extrasAllowed)
+
+ def FindStampedKind(self, operation, stampKind):
"""
Return the new Kind that results from self being
stamped with the Mixin Kind specified.
@@ -166,47 +244,48 @@
@type mixinKind: C{Kind} of the Mixin
@return: a C{Kind}
"""
- myKind = self.itsKind
- soughtMixins = KindList.allLeafSuperKinds(myKind)
- if operation == 'add':
- assert not mixinKind in soughtMixins, "Trying to stamp with a Mixin Kind already present"
- soughtMixins.append(mixinKind)
- extrasAllowed = 1
- else:
- assert operation == 'remove', "invalid Stamp operation in ContentItem.NewStampedKind: "+operation
- if not mixinKind in soughtMixins:
- return None
- soughtMixins.remove(mixinKind)
- extrasAllowed = -1
-
- qualified = []
- kindKind = Globals.repository.findPath('//Schema/Core/Kind')
- allKinds = Query.KindQuery().run([kindKind])
- for candidate in allKinds:
- superKinds = KindList.allLeafSuperKinds(candidate)
- extras = len(superKinds) - len(soughtMixins)
+ signature = self.ComputeTargetKindSignature(operation, stampKind)
+ if signature is None:
+ return None
+ soughtSignature, extrasAllowed = signature
+ exactMatches = []
+ closeMatches = []
+ candidates = self.CandidateStampedKinds()
+ for candidate in candidates:
+ candidateSignature = SuperKindSignature(candidate)
+ extras = len(candidateSignature) - len(soughtSignature)
if extras != 0 and (extras - extrasAllowed) != 0:
continue
- shortList = soughtMixins
- longList = superKinds
+ shortList = soughtSignature
+ longList = candidateSignature
if extras < 0:
- shortList = superKinds
- longList = soughtMixins
+ shortList = candidateSignature
+ longList = soughtSignature
if shortList.properSubsetOf(longList):
# found a potential match
if extras == 0:
# exact match
- return candidate
+ exactMatches.append(candidate)
else:
# close match - keep searching for a better match
- qualified.append(candidate)
+ closeMatches.append(candidate)
- # finished search with no exact matches. Better have only one candidate.
- if len(qualified) == 1:
- return qualified[0]
- # couldn't find a match, just ReKind with the Mixin Kind
+ # finished search. Better have only one exact match or else the match is ambiguous.
+ if len(exactMatches) == 1:
+ return exactMatches[0]
+ elif len(exactMatches) == 0:
+ if len(closeMatches) == 1:
+ # zero exact matches is OK when "mixin synergy" is involved.
+ return closeMatches[0]
+
+ # Couldn't find a single exact match or a single close match.
+ logging.warning ("Couldn't find suitable candidates for stamping %s with %s." \
+ % (self.itsKind.itsName, stampKind.itsName))
+ logging.warning ("Exact matches: %s" % exactMatches)
+ logging.warning ("Close matches: %s" % closeMatches)
+ # ReKind with the Mixin Kind on-the-fly
return None
-
+
def AddedRemovedKinds(self, futureKind):
"""
@@ -256,7 +335,13 @@
break
if self.hasAttributeAspect(name, 'redirectTo'):
try:
- carryOver[name] = self.getAttributeValue(name)
+ value = self.getAttributeValue(name)
+ # collections need to deep copy their attribute value
+ # otherwise there will be two references to the collection,
+ # which will go away when the first reference goes away.
+ value = self.CloneCollectionValue(name, value)
+ oldRedirect = self.getAttributeAspect(name, 'redirectTo')
+ carryOver[name] = (value, oldRedirect)
except AttributeError:
carryOver[name] = None
return carryOver
@@ -284,8 +369,56 @@
"""
for key, value in carryOver.items():
if value is not None:
- self.setAttributeValue(key, value)
+ value, redirect = value
+ # if the redirect has changed, set the value to the new attribute
+ if self.hasAttributeAspect(key, 'redirectTo'):
+ newRedirect = self.getAttributeAspect(key, 'redirectTo')
+ if redirect != newRedirect:
+ try:
+ self.setAttributeValue(key, value)
+ except AttributeError:
+ pass
+ def CloneCollectionValue(self, key, value):
+ """
+ If the value is some kind of collection, we need to make a shallow copy
+ so the collection isn't destroyed when the reference in the other attribute
+ is destroyed.
+
+ @param key: the name of the indirect attribute.
+ @type name: a string.
+ @param value: the value, already set, in the attribute
+ @type value: anything compatible with the attribute's type
+
+ I made this a separate method for easy overloading.
+ """
+ # don't need to clone single items
+ if self.getAttributeAspect(key, 'cardinality') == 'single':
+ return value
+
+ # check the first item to see if it has an alias
+ try:
+ alias = value.getAlias(value[0])
+ hasAlias = alias is not None
+ except:
+ hasAlias = False
+
+ # create the clone
+ if hasAlias:
+ clone = {}
+ else:
+ clone = []
+
+ # copy each item, using alias if available
+ for item in value:
+ if hasAlias:
+ alias = value.getAlias(item)
+ clone[alias] = item
+ else:
+ clone.append(item)
+
+ return clone
+
class Project(Item.Item):
def __init__(self, name=None, parent=None, kind=None):
if not parent:
Index: chandler/parcels/osaf/contentmodel/tests/TestStamping.py
diff -u chandler/parcels/osaf/contentmodel/tests/TestStamping.py:1.1 chandler/parcels/osaf/contentmodel/tests/TestStamping.py:1.2
--- chandler/parcels/osaf/contentmodel/tests/TestStamping.py:1.1 Sat Jul 17 12:26:48 2004
+++ chandler/parcels/osaf/contentmodel/tests/TestStamping.py Sun Aug 8 19:13:24 2004
@@ -2,8 +2,8 @@
Unit tests for notes parcel
"""
-__revision__ = "$Revision: 1.1 $"
-__date__ = "$Date: 2004/07/17 19:26:48 $"
+__revision__ = "$Revision: 1.2 $"
+__date__ = "$Date: 2004/08/09 02:13:24 $"
__copyright__ = "Copyright (c) 2003 Open Source Applications Foundation"
__license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
@@ -17,164 +17,224 @@
import osaf.contentmodel.calendar.Calendar as Calendar
import osaf.contentmodel.tests.GenerateItems as GenerateItems
import mx.DateTime as DateTime
+import logging
from repository.util.Path import Path
+verbose = False
+compareWhos = True
+testFailureCases = True
+
+class SavedAttrs:
+ """
+ Saved Attributes stored in one of these
+ """
+ pass
class StampingTest(TestContentModel.ContentModelTestCase):
""" Test Stamping in the Content Model """
+ def setAttributes(self, item, doWho=True):
+ anAbout = 'aTitleOrHeadline'
+ item.about = anAbout
+ aDate = DateTime.now()
+ item.date = aDate
+ aWhoList = []
+ if doWho:
+ aWhoList.append(GenerateItems.GenerateCalendarParticipant())
+ aWhoList.append(GenerateItems.GenerateCalendarParticipant())
+ if compareWhos:
+ item.who = aWhoList
+
+ savedAttrs = SavedAttrs()
+ try:
+ savedItems = self.savedAttrs
+ except AttributeError:
+ self.savedAttrs = {}
+ self.savedAttrs[item.itsName] = savedAttrs
+
+ savedAttrs.about = anAbout
+ savedAttrs.date = aDate
+ savedAttrs.who = aWhoList
+
+ def assertAttributes(self, item):
+ itemAttrs = self.savedAttrs[item.itsName]
+ self.assertEqual(item.about, itemAttrs.about)
+ self.assertEqual(item.date, itemAttrs.date)
+ # compare the whos
+ if compareWhos:
+ self.assertEqual(len(item.who), len(itemAttrs.who))
+ i = 0
+ for whom in item.who:
+ self.assertEqual(whom, itemAttrs.who[i])
+ i += 1
+
+ def assertKinds(self, item, kindsList):
+ self.assertAttributes(item)
+ for kind in kindsList:
+ self.assert_(item.itsKind.isKindOf(kind))
+
+ def traverseStampSquence(self, item, sequence):
+ for operation, stampKind in sequence:
+ if verbose:
+ message = "stamping %s with %s %s" % \
+ (item.itsKind.itsName,
+ operation,
+ stampKind.itsName)
+ print message
+ logging.warning(message)
+ item.StampKind(operation, stampKind)
+ self.assertAttributes(item)
+ if operation == 'add':
+ self.assert_(item.itsKind.isKindOf(stampKind))
def testStamping(self):
- """ Simple test for creating instances of notes and stamping to related kinds """
-
+ # Make sure the contentModel is loaded.
self.loadParcel("http://osafoundation.org/parcels/osaf/contentmodel")
- # Construct sample items
- noteItem1 = Notes.Note("noteItem1")
- noteItem2 = Notes.Note("noteItem2")
- noteItem3 = Notes.Note("noteItem3")
-
- # Double check kinds
- self.assertEqual(noteItem1.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem2.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem3.itsKind, ContentModel.ContentModel.getNoteKind())
-
- # Add some attributes
- note1About = 'note1About'
- noteItem1.about = note1About
- note2About = 'note2About'
- noteItem2.about = note2About
- note3About = 'note3About'
- noteItem3.about = note3About
- note1Date = DateTime.now()
- noteItem1.date = note1Date
- note2Date = DateTime.now()
- noteItem2.date = note2Date
- note3Date = DateTime.now()
- noteItem3.date = note3Date
-
# Get the stamp kinds
mailMixin = Mail.MailParcel.getMailMessageMixinKind()
taskMixin = Task.TaskParcel.getTaskMixinKind()
eventMixin = Calendar.CalendarParcel.getCalendarEventMixinKind()
-
- # Stamp to Mail, Task, Event
- addOperation = 'add'
- noteItem1.StampKind(addOperation, mailMixin)
- noteItem2.StampKind(addOperation, taskMixin)
- noteItem3.StampKind(addOperation, eventMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # Stamp additional Task, Event, Mail
- noteItem1.StampKind(addOperation, taskMixin)
- noteItem2.StampKind(addOperation, eventMixin)
- noteItem3.StampKind(addOperation, mailMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # Stamp additional, so they have all three
- noteItem1.StampKind(addOperation, eventMixin)
- noteItem2.StampKind(addOperation, mailMixin)
- noteItem3.StampKind(addOperation, taskMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # unstamp in different orders - mail
- removeOperation = 'remove'
- noteItem1.StampKind(removeOperation, mailMixin)
- noteItem2.StampKind(removeOperation, mailMixin)
- noteItem3.StampKind(removeOperation, mailMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # unstamp in different orders - event
- noteItem1.StampKind(removeOperation, eventMixin)
- noteItem2.StampKind(removeOperation, eventMixin)
- noteItem3.StampKind(removeOperation, eventMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # unstamp in different orders - task
- noteItem1.StampKind(removeOperation, taskMixin)
- noteItem2.StampKind(removeOperation, taskMixin)
- noteItem3.StampKind(removeOperation, taskMixin)
-
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # should all be back to Note again
- self.assertEqual(noteItem1.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem2.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem3.itsKind, ContentModel.ContentModel.getNoteKind())
+ taskKind = Task.TaskParcel.getTaskKind()
+ mailKind = Mail.MailParcel.getMailMessageKind()
+ eventKind = Calendar.CalendarParcel.getCalendarEventKind()
+ noteKind = ContentModel.ContentModel.getNoteKind()
+
+ # start out with a Note
+ aNote = Notes.Note("noteItem1")
+ self.setAttributes(aNote, doWho=False)
+ self.assertAttributes(aNote)
+ add = 'add'
+ remove = 'remove'
+
+ # stamp everything on and off the note
+ self.traverseStampSquence(aNote, ((add, mailMixin),
+ (add, taskMixin),
+ (add, eventMixin),
+ (remove, eventMixin),
+ (remove, taskMixin),
+ (remove, mailMixin)))
+
+ # stamp everything on again, remove in a different order
+ self.traverseStampSquence(aNote, ((add, mailMixin),
+ (add, taskMixin),
+ (add, eventMixin),
+ (remove, mailMixin),
+ (remove, taskMixin),
+ (remove, eventMixin)))
+ self.assertAttributes(aNote)
+
+ # Create a Task, and do all kinds of stamping on it
+ aTask = Task.Task("aTask")
+ self.setAttributes(aTask)
+
+ self.traverseStampSquence(aTask, ((add, eventMixin),
+ (remove, taskMixin)))
+ # now it's an Event
+
+ self.traverseStampSquence(aTask, ((add, mailMixin),
+ (remove, mailMixin)))
+
+ self.traverseStampSquence(aTask, ((add, mailMixin),
+ (add, taskMixin),
+ (remove, mailMixin),
+ (remove, taskMixin)))
+
+ self.traverseStampSquence(aTask, ((add, taskMixin),
+ (add, mailMixin),
+ (remove, mailMixin),
+ (remove, taskMixin)))
+
+ self.traverseStampSquence(aTask, ((add, mailMixin),
+ (remove, eventMixin)))
+ # now it's a Mail
+
+ self.traverseStampSquence(aTask, ((add, taskMixin),
+ (remove, mailMixin)))
+ # it's a Task again
+
+ self.traverseStampSquence(aTask, ((add, mailMixin),
+ (remove, taskMixin)))
+
+ self.traverseStampSquence(aTask, ((add, taskMixin),
+ (remove, mailMixin)))
+ # it's a Task again
+
+ self.traverseStampSquence(aTask, ((add, eventMixin),
+ (remove, taskMixin),
+ (add, mailMixin),
+ (remove, eventMixin),
+ (add, taskMixin),
+ (remove, mailMixin)))
+ self.assert_(aTask.itsKind.isKindOf(taskKind))
+
+ # check stamping on an Event
+ anEvent = Calendar.CalendarEvent("anEvent")
+ self.setAttributes(anEvent)
+
+ # round-robin it's Kind back to event
+ self.traverseStampSquence(anEvent, ((add, mailMixin),
+ (remove, eventMixin),
+ (add, taskMixin),
+ (remove, mailMixin),
+ (add, eventMixin),
+ (remove, taskMixin)))
+ self.assert_(anEvent.itsKind.isKindOf(eventKind))
+
+ # check stamping on a Mail Message
+ aMessage = Mail.MailMessage("aMessage")
+ self.setAttributes(aMessage)
+ self.traverseStampSquence(aMessage, ((add, eventMixin),
+ (add, taskMixin),
+ (remove, eventMixin),
+ (remove, taskMixin)))
+ self.assert_(aMessage.itsKind.isKindOf(mailKind))
# now mixin some arbitrary Kind
- anotherKind = ContentModel.ContentModel.getConversationKind()
+ anotherKind = self.rep.findPath('//parcels/osaf/framework/blocks/Block')
# stamp an event, mail, task with another kind
- noteItem1.StampKind(addOperation, eventMixin)
- noteItem2.StampKind(addOperation, mailMixin)
- noteItem3.StampKind(addOperation, taskMixin)
-
- noteItem1.StampKind(addOperation, anotherKind)
- noteItem2.StampKind(addOperation, anotherKind)
- noteItem3.StampKind(addOperation, anotherKind)
-
- noteItem1.StampKind(removeOperation, anotherKind)
- noteItem2.StampKind(removeOperation, anotherKind)
- noteItem3.StampKind(removeOperation, anotherKind)
-
- noteItem1.StampKind(removeOperation, eventMixin)
- noteItem2.StampKind(removeOperation, mailMixin)
- noteItem3.StampKind(removeOperation, taskMixin)
+ aNote.StampKind(add, anotherKind)
+ aTask.StampKind(add, anotherKind)
+ anEvent.StampKind(add, anotherKind)
+ aMessage.StampKind(add, anotherKind)
+
+ self.assertKinds(aNote, (noteKind, anotherKind))
+ self.assertKinds(aTask, (taskKind, anotherKind))
+ self.assertKinds(anEvent, (eventKind, anotherKind))
+ self.assertKinds(aMessage, (mailKind, anotherKind))
+
+ # unstamp with another kind
+ aNote.StampKind(remove, anotherKind)
+ aTask.StampKind(remove, anotherKind)
+ anEvent.StampKind(remove, anotherKind)
+ aMessage.StampKind(remove, anotherKind)
+
+ # see that they still have their attributes
+ self.assertKinds(aNote, (noteKind, ))
+ self.assertKinds(aTask, (taskKind, ))
+ self.assertKinds(anEvent, (eventKind, ))
+ self.assertKinds(aMessage, (mailKind, ))
+
+ # Test some failure cases
+ # These cases should produce suitable warning messages in Chandler.log
+ if testFailureCases:
+ anotherEvent = Calendar.CalendarEvent("anotherEvent")
+ self.setAttributes(anotherEvent)
+ self.assert_(anotherEvent.itsKind.isKindOf(eventKind))
+ try:
+ # double stamping
+ self.traverseStampSquence(anotherEvent, ((add, mailMixin),
+ (add, mailMixin)))
+ except:
+ pass
+
+ try:
+ # unstamping something not present
+ self.traverseStampSquence(anotherEvent, ((remove, taskMixin), ))
+ except:
+ pass
- # see that they still have their attributes
- self.assertEqual(noteItem1.about, note1About)
- self.assertEqual(noteItem1.date, note1Date)
- self.assertEqual(noteItem2.about, note2About)
- self.assertEqual(noteItem2.date, note2Date)
- self.assertEqual(noteItem3.about, note3About)
- self.assertEqual(noteItem3.date, note3Date)
-
- # should all be back to Note again
- self.assertEqual(noteItem1.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem2.itsKind, ContentModel.ContentModel.getNoteKind())
- self.assertEqual(noteItem3.itsKind, ContentModel.ContentModel.getNoteKind())
if __name__ == "__main__":
unittest.main()
More information about the Commits
mailing list