Source code for woo.tests.core

# encoding: utf-8
# 2009 © Václav Šmilauer <eudoxos@arcig.cz>

"""
Core functionality (Scene in c++), such as accessing bodies, materials, interactions. Specific functionality tests should go to engines.py or elsewhere, not here.
"""
import unittest
import random
import os
from minieigen import *
from math import *
import woo
from woo import utils
from woo.core import *
from woo.dem import *
from woo.pre import *
try: from woo.sparc import *
except: pass
try: from woo.gl import *
except: pass
try: from woo.voro import *
except: pass
try: from woo.cld import *
except: pass
try: from woo.qt import *
except: pass

import woo
woo.master.usesApi=10104

## TODO tests
[docs]class TestInteractions(unittest.TestCase): pass
[docs]class TestForce(unittest.TestCase): pass
[docs]class TestTags(unittest.TestCase): pass
[docs]class TestScene(unittest.TestCase):
[docs] def setUp(self): self.scene=woo.core.Scene()
#def _testTags(self): # 'Core: Scene.tags are str (not unicode) objects' # S=self.scene # S.tags['str']='asdfasd' # S.tags['uni']=u'→ Σ' # self.assertTrue(type(S.tags['str']==unicode)) # def tagError(S): S.tags['error']=234 # self.assertTrue(type(S.tags['uni']==unicode)) # self.assertRaises(TypeError,lambda: tagError(S))
[docs]class TestObjectInstantiation(unittest.TestCase):
[docs] def setUp(self): self.t=woo.core.WooTestClass() # usually disabled and will be removed completely if hasattr(woo.core,'WooTestClassStatic'): self.ts=woo.core.WooTestClassStatic()
[docs] def testClassCtors(self): "Core: correct types are instantiated" # correct instances created with Foo() syntax import woo.system for r in woo.system.childClasses(woo.core.Object): obj=r(); self.assertTrue(obj.__class__==r,'Failed for '+r.__name__)
[docs] def testRootDerivedCtors_attrs_few(self): "Core: class ctor's attributes" # attributes passed when using the Foo(attr1=value1,attr2=value2) syntax gm=Shape(color=1.); self.assertTrue(gm.color==1.)
[docs] def testDeepcopy(self): 'Core: Object.deepcopy' t=woo.core.WooTestClass() t.mass=0. t2=t.deepcopy(mass=1.0) self.assertTrue(t2.mass==1.0 and t.mass==0.)
[docs] def testDispatcherCtor(self): "Core: dispatcher ctors with functors" # dispatchers take list of their functors in the ctor # same functors are collapsed in one cld1=LawDispatcher([Law2_L6Geom_FrictPhys_IdealElPl(),Law2_L6Geom_FrictPhys_IdealElPl()]); self.assertTrue(len(cld1.functors)==1) # two different make two different cld2=LawDispatcher([Law2_L6Geom_FrictPhys_IdealElPl(),Law2_L6Geom_FrictPhys_LinEl6()]); self.assertTrue(len(cld2.functors)==2)
[docs] def testContactLoopCtor(self): "Core: ContactLoop special ctor" # ContactLoop takes 3 lists id=ContactLoop([Cg2_Facet_Sphere_L6Geom(),Cg2_Sphere_Sphere_L6Geom()],[Cp2_FrictMat_FrictPhys()],[Law2_L6Geom_FrictPhys_IdealElPl()],) self.assertTrue(len(id.geoDisp.functors)==2) self.assertTrue(id.geoDisp.__class__==CGeomDispatcher) self.assertTrue(id.phyDisp.functors[0].__class__==Cp2_FrictMat_FrictPhys) self.assertTrue(id.lawDisp.functors[0].__class__==Law2_L6Geom_FrictPhys_IdealElPl)
[docs] @unittest.skipIf('TRAVIS' in os.environ,'This test would fail at Travis-CI for unknown reason.') def testParallelEngineCtor(self): "Core: ParallelEngine special ctor" pe=ParallelEngine([InsertionSortCollider(),[BoundDispatcher(),ForceResetter()]]) self.assertTrue(pe.slaves[0].__class__==InsertionSortCollider) self.assertTrue(len(pe.slaves[1])==2) pe.slaves=[] self.assertTrue(len(pe.slaves)==0)
## ## testing incorrect operations that should raise exceptions ##
[docs] def testWrongFunctorType(self): "Core: dispatcher and functor type mismatch is detected" # dispatchers accept only correct functors # pybind11: RuntimeError self.assertRaises((TypeError,RuntimeError),lambda: LawDispatcher([Bo1_Sphere_Aabb()]))
[docs] def testInvalidAttr(self): 'Core: invalid attribute access raises AttributeError' # accessing invalid attributes raises AttributeError self.assertRaises(AttributeError,lambda: Sphere(attributeThatDoesntExist=42)) self.assertRaises(AttributeError,lambda: Sphere().attributeThatDoesntExist)
### ### shared ownership semantics ###
[docs] def testShared(self): "Core: shared_ptr really shared" m=woo.utils.defaultMaterial() s1,s2=woo.utils.sphere((0,0,0),1,mat=m),woo.utils.sphere((0,0,0),2,mat=m) s1.mat.young=2342333 self.assertTrue(s1.mat.young==s2.mat.young)
[docs] def testSharedAfterReload(self): "Core: shared_ptr preserved when saving/loading" S=Scene(fields=[DemField()]) m=woo.utils.defaultMaterial() S.dem.par.add([woo.utils.sphere((0,0,0),1,mat=m),woo.utils.sphere((0,0,0),2,mat=m)]) S.saveTmp(quiet=True); S=Scene.loadTmp() S.dem.par[0].mat.young=9087438484 self.assertTrue(S.dem.par[0].mat.young==S.dem.par[1].mat.young)
## ## attribute flags ##
[docs] def testNoSave(self): 'Core: Attr::noSave' # update bound of the particle t=self.t i0=t.noSaveAttr t.noSaveAttr=i0+10 t.saveTmp('t') t2=woo.core.WooTestClass.loadTmp('t') # loaded copy has the default value self.assertTrue(t2.noSaveAttr==i0)
[docs] @unittest.skipIf(not hasattr(woo.core,'WooTestClassStatic'),'Built without static attrs') def testNoSave_static(self): 'Core: Attr::noSave (static)' ts=self.ts ts.noSave=344 ts.namedEnum='one' ts.saveTmp('ts') ts.noSave=123 self.assertTrue(ts.noSave==123) ts2=woo.core.WooTestClassStatic.loadTmp('ts') self.assertTrue(ts2.noSave==123) # was not saved self.assertTrue(ts2.namedEnum=='one') # was saved # since it is static, self.ts is changed as well now self.assertTrue(ts.namedEnum==ts2.namedEnum) self.assertTrue(id(ts.namedEnum)==id(ts2.namedEnum))
[docs] def testReadonly(self): 'Core: Attr::readonly' self.assertTrue(self.t.meaning42==42) self.assertRaises(AttributeError,lambda: setattr(self.t,'meaning42',43))
[docs] @unittest.skipIf(not hasattr(woo.core,'WooTestClassStatic'),'Built without static attrs') def testReaonly_static(self): 'Core: Attr::readonly (static)' self.assertRaises(AttributeError,lambda: setattr(self.ts,'readonly',2))
[docs] def testTriggerPostLoad(self): 'Core: postLoad & Attr::triggerPostLoad' WTC=woo.core.WooTestClass # stage right after construction self.assertEqual(self.t.postLoadStage,WTC.postLoad_ctor) baz0=self.t.baz self.t.foo_incBaz=1 # assign whatever, baz should be incremented self.assertEqual(self.t.baz,baz0+1) self.assertEqual(self.t.postLoadStage,WTC.postLoad_foo) self.t.foo_incBaz=1 # again self.assertEqual(self.t.baz,baz0+2) self.t.bar_zeroBaz=1 # assign to reset baz self.assertEqual(self.t.baz,0) self.assertEqual(self.t.postLoadStage,WTC.postLoad_bar) # assign to baz to test postLoad again self.t.baz=-1 self.assertTrue(self.t.postLoadStage==WTC.postLoad_baz)
[docs] def testUnicodeStrConverter(self): 'Core: std::string attributes can be assigned unicode string' t=woo.core.WooTestClass(strVar=u'abc') t.strVar=u'abc' self.assertTrue(t.strVar=='abc')
[docs] def testNamedEnum(self): 'Core: Attr::namedEnum' t=woo.core.WooTestClass() self.assertRaises(ValueError,lambda: setattr(t,'namedEnum','nonsense')) self.assertRaises(ValueError,lambda: setattr(t,'namedEnum',-2)) self.assertRaises(TypeError,lambda: setattr(t,'namedEnum',[])) t.namedEnum='zero' self.assertTrue(t.namedEnum=='zero') # try with unicode string t.namedEnum=u'zero' self.assertTrue(t.namedEnum=='zero') t.namedEnum='nothing' self.assertTrue(t.namedEnum=='zero') t.namedEnum=0 self.assertTrue(t.namedEnum=='zero') # ctor self.assertRaises(ValueError,lambda: woo.core.WooTestClass(namedEnum='nonsense')) tt=woo.core.WooTestClass(namedEnum='single') self.assertTrue(tt.namedEnum=='one')
[docs] @unittest.skipIf(not hasattr(woo.core,'WooTestClassStatic'),'Built without static attrs') def testNamedEnum_static(self): 'Core: Attr::namedEnum (static)' ts=self.ts self.assertRaises(ValueError,lambda: setattr(ts,'namedEnum','nonsense')) self.assertRaises(ValueError,lambda: setattr(ts,'namedEnum',-2)) self.assertRaises(TypeError,lambda: setattr(ts,'namedEnum',[])) ts.namedEnum='zero' self.assertTrue(ts.namedEnum=='zero') ts.namedEnum=-1 self.assertTrue(ts.namedEnum=='minus one') # test passing it in the ctor tt=woo.core.WooTestClass(namedEnum='NULL') # use the alternative name self.assertTrue(tt.namedEnum=='zero') tt=woo.core.WooTestClass(namedEnum=1) # use number self.assertTrue(tt.namedEnum=='one')
[docs] def testBits(self): 'Core: AttrTrait.bits accessors' t=self.t # flags and bits read-write t.bits=1 self.assertTrue(t.bit0) t.bit2=True self.assertTrue(t.bits==5) # flags read-only, bits midifiable self.assertRaises(AttributeError,lambda: setattr(t,'bitsRw',1)) t.bit2rw=True t.bit3rw=True self.assertTrue(t.bitsRw==12) # both flags and bits read-only self.assertRaises(AttributeError,lambda: setattr(t,'bitsRo',1)) self.assertRaises(AttributeError,lambda: setattr(t,'bit1ro',True)) self.assertTrue(t.bitsRo==3) self.assertTrue((t.bit0ro,t.bit1ro,t.bit2ro)==(True,True,False))
[docs] def testDeprecated(self): 'Core: AttrTrait.deprecated raises exception on access' self.assertRaises(ValueError,lambda: getattr(self.t,'deprecatedAttr')) self.assertRaises(ValueError,lambda: setattr(self.t,'deprecatedAttr',1))
## not (yet) supported for static attributes # def testBits_static(self):
[docs] def testHidden(self): 'Core: Attr::hidden' # hidden attributes are not wrapped in python at all self.assertTrue(not hasattr(self.t,'hiddenAttr'))
[docs] @unittest.skipIf(not hasattr(woo.core,'WooTestClassStatic'),'Built without static attrs') def testHidden_static(self): 'Core: Attr::hidden (static)' self.assertRaises(AttributeError,lambda: getattr(self.ts,'hidden'))
[docs] def testNotifyDead(self): 'Core: PeriodicEngine::notifyDead' e=woo.core.WooTestPeriodicEngine() self.assertTrue(e.deadCounter==0) prev=e.deadCounter e.dead=True self.assertTrue(e.deadCounter>prev) # ideally, this should be 1, not 4 by now!! prev=e.deadCounter e.dead=True self.assertTrue(e.deadCounter>prev) prev=e.deadCounter e.dead=False self.assertTrue(e.deadCounter>prev)
[docs] def testNodeDataCtorAssign(self): 'Core: assign node data using shorthands in the ctor' n=woo.core.Node(pos=(1,2,3),dem=woo.dem.DemData(mass=100)) self.assertTrue(n.dem.mass==100) # pybind11: RuntimeError self.assertRaises((TypeError,RuntimeError),lambda: woo.core.Node(dem=1)) if 'gl' in woo.config.features: # type mismatch self.assertRaises(RuntimeError,lambda: woo.core.Node(dem=woo.gl.GlData()))
[docs]class TestLoop(unittest.TestCase):
[docs] def setUp(self): woo.master.reset() woo.master.scene.fields=[DemField()] woo.master.scene.dt=1e-8
[docs] def testSubstepping(self): 'Loop: substepping' S=woo.master.scene S.engines=[PyRunner(1,'pass'),PyRunner(1,'pass'),PyRunner(1,'pass')] # value outside the loop self.assertTrue(S.subStep==-1) # O.subStep is meaningful when substepping S.subStepping=True S.one(); self.assertTrue(S.subStep==0) S.one(); self.assertTrue(S.subStep==1) # when substepping is turned off in the middle of the loop, the next step finishes the loop S.subStepping=False S.one(); self.assertTrue(S.subStep==-1) # subStep==0 inside the loop without substepping S.engines=[PyRunner(1,'if scene.subStep!=0: raise RuntimeError("scene.subStep!=0 inside the loop with Scene.subStepping==False!")')] S.one()
[docs] def testEnginesModificationInsideLoop(self): 'Loop: Scene.engines can be modified inside the loop transparently.' S=woo.master.scene S.engines=[ PyRunner(stepPeriod=1,command='from woo.core import *; from woo.dem import *; scene.engines=[ForceResetter(),ForceResetter(),Leapfrog(reset=False)]'), # change engines here ForceResetter() # useless engine ] S.subStepping=True # run prologue and the first engine, which modifies O.engines S.one(); S.one(); self.assertTrue(S.subStep==1) self.assertTrue(len(S.engines)==3) # gives modified engine sequence transparently self.assertTrue(len(S._nextEngines)==3) self.assertTrue(len(S._currEngines)==2) S.one(); S.one(); # run the 2nd ForceResetter, and epilogue self.assertTrue(S.subStep==-1) # start the next step, nextEngines should replace engines automatically S.one() self.assertTrue(S.subStep==0) self.assertTrue(len(S._nextEngines)==0) self.assertTrue(len(S.engines)==3) self.assertTrue(len(S._currEngines)==3)
[docs] def testDead(self): 'Loop: dead engines are not run' S=woo.master.scene S.engines=[PyRunner(1,'pass',dead=True)] S.one(); self.assertTrue(S.engines[0].nDone==0)
[docs] def testPausedContext(self): 'Loop: "with Scene.paused()" context manager' import time S=woo.master.scene S.engines=[] S.run() with S.paused(): i=S.step time.sleep(.1) self.assertTrue(i==S.step) # check there was no advance during those .1 secs self.assertTrue(S.running) # running should return true nevertheless time.sleep(.1) self.assertTrue(i<S.step) # check we run during those .1 secs again S.stop()
[docs] def testNoneEngine(self): 'Loop: None engine raises exception.' S=woo.master.scene self.assertRaises(RuntimeError,lambda: setattr(S,'engines',[ContactLoop(),None,ContactLoop()]))
[docs] def testStopAtStep(self): 'Loop: S.stopAtStep and S.run(steps=...)' S=woo.core.Scene(dt=1.) S.stopAtStep=100 # absolute value S.run(wait=True) self.assertEqual(S.step,100) S.run(steps=100,wait=True) # relative value self.assertEqual(S.step,200)
[docs] def testStopAtTime(self): 'Loop: S.stopAtTime and S.run(time=...)' S=woo.core.Scene(dt=1e-3) S.stopAtTime=1.0001 # absolute value S.run(wait=True) self.assertAlmostEqual(S.time,1.001,delta=1e-3) S.run(time=.5,wait=True) # relative value self.assertAlmostEqual(S.time,1.501,delta=1e-3)
[docs] def testStopAtHook(self): 'Loop: S.stopAtHook' S=woo.core.Scene(dt=1e-3) # both of them should trivver stopAtHook S.stopAtTime=10e-3 S.stopAtStep=1000 S.lab.a=1 S.lab._setWritable('a') S.stopAtHook='S.lab.a+=1' S.run(wait=True) # stopAtTime applies first self.assertEqual(S.lab.a,2) S.run(wait=True) # stopAtStep applies now self.assertEqual(S.lab.a,3)
[docs] def testWait(self): 'Loop: Scene.wait() returns only after the current step finished' S=woo.core.Scene(dt=1e-3,engines=[PyRunner(1,'import time; S.stop(); time.sleep(.3); S.lab.aa=True')]) S.run(wait=True) self.assertTrue(hasattr(S.lab,'aa'))
[docs] def testWaitForScenes(self): 'Loop: Master.waitForScenes correctly handles reassignment of the master scene' S=woo.master.scene=woo.core.Scene(dt=1e-3,engines=[PyRunner(1,'import time; S.stop(); time.sleep(.3); woo.master.scene=woo.core.Scene(dt=1e-4,engines=[woo.core.PyRunner(1,"S.stop()")])')]) S.run() woo.master.waitForScenes() self.assertTrue(woo.master.scene.dt==1e-4) # check master scene is the second one
[docs]class TestIO(unittest.TestCase):
[docs] def testSaveAllClasses(self): 'I/O: All classes can be saved and loaded with boost::serialization' import woo.system failed=set() for c in woo.system.childClasses(woo.core.Object): t=woo.core.WooTestClass() try: t.any=c() t.saveTmp(quiet=True) woo.master.loadTmpAny() except (RuntimeError,ValueError): print(20*'*'+' error with class '+c.__name__) import traceback traceback.print_exc() failed.add(c.__name__) failed=list(failed); failed.sort() if failed: print(80*'#'+'\nFailed classes were: '+' '.join(failed)) self.assertTrue(len(failed)==0,'Failed classes were: '+' '.join(failed)+'\n'+80*'#')
[docs]class TestContact(unittest.TestCase):
[docs] def setUp(self): self.S=S=woo.core.Scene(fields=[DemField()],engines=DemField.minimalEngines()) for i in range(0,10): S.dem.par.add(woo.dem.Sphere.make((0,0,i),1.1)) S.one()
[docs] def testForceSign(self): 'Contact: forceSign' S=self.S c45=S.dem.con[4,5] inc=(c45.id1==4) self.assertTrue(c45.forceSign(4)==(1 if inc else -1)) self.assertTrue(c45.forceSign(5)==(-1 if inc else 1)) self.assertRaises(RuntimeError,lambda: c45.forceSign(6)) self.assertTrue(c45.forceSign(S.dem.par[4])==(1 if inc else -1)) self.assertTrue(c45.forceSign(S.dem.par[5])==(-1 if inc else 1)) self.assertRaises(RuntimeError,lambda: c45.forceSign(S.dem.par[6]))
[docs]class TestParticles(unittest.TestCase):
[docs] def setUp(self): woo.master.reset() woo.master.scene.fields=[DemField()] S=woo.master.scene self.count=100 S.dem.par.add([utils.sphere([random.random(),random.random(),random.random()],random.random()) for i in range(0,self.count)]) random.seed()
[docs] def testIterate(self): "Particles: iteration" counted=0 S=woo.master.scene for b in S.dem.par: counted+=1 self.assertTrue(counted==self.count)
[docs] def testLen(self): "Particles: len(S.dem.par)" S=woo.master.scene self.assertTrue(len(S.dem.par)==self.count)
[docs] def testRemove(self): "Particles: acessing removed particles raises IndexError" S=woo.master.scene S.dem.par.remove(0) self.assertRaises(IndexError,lambda: S.dem.par[0])
[docs] def testRemoveShrink(self): "Particles: removing particles shrinks storage size" S=woo.master.scene for i in range(self.count-1,-1,-1): S.dem.par.remove(i) self.assertTrue(len(S.dem.par)==i)
[docs] def testNegativeIndex(self): "Particles: negative index counts backwards (like python sequences)." S=woo.master.scene self.assertTrue(S.dem.par[-1]==S.dem.par[self.count-1])
[docs] def testRemovedIterate(self): "Particles: iterator silently skips erased particles" S=woo.master.scene removed,counted=0,0 for i in range(0,10): id=random.randint(0,self.count-1) if S.dem.par.exists(id): S.dem.par.remove(id); removed+=1 for b in S.dem.par: counted+=1 self.assertTrue(counted==self.count-removed)
[docs]class TestArrayAccu(unittest.TestCase):
[docs] def setUp(self): self.t=woo.core.WooTestClass() self.N=woo.master.numThreads
#def testRead(self): # 'OpenMP array accu: implicit conversion to python list' # print(self.t.aaccu)
[docs] def testResize(self): 'OpenMP array accu: resizing' self.assertEqual(len(self.t.aaccuRaw),0) # initial zero size for sz in (4,8,16,32,33): self.t.aaccuRaw=[i for i in range(0,sz)] r=self.t.aaccuRaw for i in range(0,sz): self.assertTrue(r[i][0]==i) # first thread is assigned the value for n in range(1,self.N): self.assertTrue(r[i][n]==0) # other threads are reset
[docs] def testPreserveResize(self): 'OpenMP array accu: preserve old data on resize' self.t.aaccuRaw=(0,1) self.t.aaccuWriteThreads(2,[2]) # write whatever to index 2: resizes, but should preserve 0,1 self.assertEqual(self.t.aaccuRaw[0][0],0) self.assertEqual(self.t.aaccuRaw[1][0],1)
[docs] def testThreadWrite(self): 'OpenMP array accu: concurrent writes' self.t.aaccuWriteThreads(0,list(range(self.N))) for i in range(0,self.N): self.assertEqual(self.t.aaccuRaw[0][i],i) # each thread has written its own index
[docs]class _TestPyClass(woo.core.Object,woo.pyderived.PyWooObject): 'Sample pure-python class integrated into woo (mainly used with preprocessors), for use with :obj:`TestPyDerived`.' PAT=woo.pyderived.PyAttrTrait _attrTraits=[ PAT(float,'aF',1.,'float attr'), PAT([float,],'aFF',[0.,1.,2.],'list of floats attr'), PAT(Vector2,'aV2',(0.,1.),'vector2 attr'), PAT([Vector2,],'aVV2',[(0.,0.),(1.,1.)],'list of vector2 attr'), PAT(woo.core.Node,'aNode',woo.core.Node(pos=(1,1,1)),'node attr'), PAT(woo.core.Node,'aNodeNone',None,'node attr, uninitialized'), PAT([woo.core.Node,],'aNNode',[woo.core.Node(pos=(1,1,1)),woo.core.Node(pos=(2,2,2))],'List of nodes'), PAT(float,'aF_trigger',1.,triggerPostLoad=True,doc='Float triggering postLoad, copying its value to aF'), PAT(int,'postLoadCounter',0,doc='counter for postLoad (readonly). Incremented by 1 after construction, incremented by 10 when assigning to aF_trigger.'), PAT(int,'deprecAttr',-1,deprecated=True,doc='deprecated, here should be the explanation.'), PAT(str,'sChoice','aa',choice=['aa','bb','cc'],doc='String choice attribute') ] def postLoad(self,I): if I is None: self.postLoadCounter+=1 elif I=='aF_trigger': # print 'postLoad / aF_trigger' self.postLoadCounter+=10 self.aF=self.aF_trigger else: raise RuntimeError(self.__class__.__name__+'.postLoad called with unknown attribute id %s'%I)
[docs] def __new__(klass,**kw): self=super().__new__(klass) self.wooPyInit(klass,woo.core.Object,**kw) return self
def __init__(self,**kw): woo.core.Object.__init__(self) self.wooPyInit(_TestPyClass,woo.core.Object,**kw)
[docs]class _TestPyClass2(_TestPyClass): 'Python class deriving from python base class (which in turn derives from c++).' _PAT=woo.pyderived.PyAttrTrait def postLoad(self,I): if I=='f2' or I is None: self.f2counter+=1 else: super(_TestPyClass2,self).postLoad(I) _attrTraits=[ _PAT(int,'f2',0,triggerPostLoad=True,doc='Float attr in derived class'), _PAT(int,'f2counter',0,doc='Count how many times was f2 manipulated (to test triggerPostLoad in class with python parent)'), ]
[docs] def __new__(klass,**kw): self=super().__new__(klass) self.wooPyInit(klass,_TestPyClass,**kw) return self
def __init__(self,**kw): _TestPyClass.__init__(self) self.wooPyInit(_TestPyClass2,_TestPyClass,**kw)
[docs]class TestPyDerived(unittest.TestCase): import woo.pyderived, woo.core
[docs] def setUp(self): self.t=_TestPyClass() self.t2=_TestPyClass2()
def testTrigger(self): 'PyDerived: postLoad triggers' # print 'postLoadCounter after ctor:',self.t.postLoadCounter self.assertTrue(self.t.postLoadCounter==1) self.t.aF_trigger=514. self.assertTrue(self.t.aF_trigger==514.) self.assertTrue(self.t.aF==514.) self.assertTrue(self.t.postLoadCounter==11)
[docs] def testPickle(self): 'PyDerived: deepcopy' self.assertTrue(self.t.aF==1.) self.assertTrue(self.t.aNode.pos==Vector3(1,1,1)) self.t.aF=2. self.t.aNode.pos=Vector3(0,0,0) self.assertTrue(self.t.aF==2.) self.assertTrue(self.t.aNode.pos==Vector3(0,0,0)) # pickle needs the class to be found in the module itself # PicklingError: Can't pickle <class 'woo.tests.core._TestPyClass'>: it's not found as woo.tests.core._TestPyClass # this is fixed now the class is defined at the module level t2=self.t.deepcopy() self.assertTrue(t2.aF==2.) self.assertTrue(t2.aNode.pos==Vector3(0,0,0)) self.t.aF=0. t3=self.t.deepcopy(aF=1.0) # with kw arg self.assertTrue(t3.aF==1. and self.t.aF==0.)
[docs] def testTypeCoerceFloats(self): 'PyDerived: type coercion (primitive types)' # numbers and number sequences self.assertRaises(TypeError,lambda:setattr(self.t,'aF','asd')) self.assertRaises(TypeError,lambda:setattr(self.t,'aF','123')) # disallow conversion from strings self.assertRaises(TypeError,lambda:setattr(self.t,'aFF',(1,2,'ab'))) self.assertRaises(TypeError,lambda:setattr(self.t,'aFF','ab')) self.assertRaises(TypeError,lambda:setattr(self.t,'aFF',[(1,2,3),(4,5,6)])) try: self.t.aFF=[] except: self.fail("Empty list not accepter for list of floats") try: self.t.aFF=Vector3(1,2,3) except: self.fail("Vector3 not accepted for list of floats") try: self.t.aV2=(0,1.) except: self.fail("2-tuple not accepted for Vector2")
[docs] def testTypeCoerceObject(self): 'PyDerived: type coercion (woo.core.Object)' # c++ objects try: self.t.aNode=None except: self.fail("None not accepted as woo.core.Node") self.assertRaises(TypeError,lambda:setattr(self.t,'aNode',woo.core.Scene())) # list of c++ objects self.assertRaises(TypeError,lambda:setattr(self.t,'aNNode',(woo.core.Node(),woo.core.Scene()))) try: self.t.aNNode=[None,woo.core.Node()] except: self.fail("[None,Node] not accepted for list of Nodes")
[docs] def testTypeCoerceCtor(self): 'PyDerived: type coercion (ctor)' self.assertRaises(TypeError,lambda:_TestPyClass(aF='abc'))
[docs] def testTraits(self): 'PyDerived: PyAttrTraits' self.assertTrue(self.t._attrTraits[0].ini==1.) self.assertTrue(self.t._attrTraits[0].pyType==float)
[docs] def testIniDefault(self): 'PyDerived: default initialization' self.assertTrue(self.t.aF==1.) self.assertTrue(self.t.aFF==[0.,1.,2.]) self.assertTrue(self.t.aNodeNone==None)
def testIniUser(self): 'PyDerived: user initialization' t2=_TestPyClass(aF=2.) self.assertTrue(t2.aF==2.) self.assertRaises(AttributeError,lambda: _TestPyClass(nonsense=123))
[docs] def testStrValidation(self): 'PyDerived: string choice is validated' try: self.t.sChoice='bb' except: self.fail("Valid choice value not accepted as new attribute value") self.assertRaises(ValueError, lambda: setattr(self.t,'sChoice','abc')) self.assertRaises(ValueError, lambda: _TestPyClass(sChoice='abc')) try: _TestPyClass(sChoice='bb') except: self.fail("Valid choice value not accepted in ctor")
[docs] def testBoostSaveError(self): 'PyDerived: refuses to save via Object::save (data loss; dump must be used instead)' self.assertRaises(IOError,lambda: self.t.save('whatever')) self.assertRaises(IOError,lambda: self.t2.save('whatever'))
[docs] def testBoostDumpError(self): 'PyDerived: refuses to dump with boost::serialization format (data loss)' self.assertRaises(IOError,lambda: self.t.dump('whatever.xml')) self.assertRaises(IOError,lambda: self.t2.dump('whatever.xml'))
[docs] def testDeprecated(self): 'PyDerived: deprecated attribute raises ValueError on access' self.assertRaises(ValueError,lambda: getattr(self.t,'deprecAttr')) self.assertRaises(ValueError,lambda: setattr(self.t,'deprecAttr',1))
[docs] def testPickle2(self): 'PyDerived: deepcopy (python parent)' self.t2.f2=3 self.t2.aF=0 tt=self.t2.deepcopy() self.assertTrue(tt.aF==0) self.assertTrue(tt.f2==3)
[docs] def testIniUser(self): 'PyDerived: user initialization (python parent)' t2=_TestPyClass2(aF=0,f2=3) self.assertTrue(t2.aF==0) self.assertTrue(t2.f2==3)
[docs] def testTrigger(self): 'PyDerived: postLoad trigger (python parent)' c1=self.t2.postLoadCounter c2=self.t2.f2counter self.t2.f2=4. self.t2.aF_trigger=5. self.assertTrue(self.t2.f2counter>c2) self.assertTrue(self.t2.postLoadCounter>c1)