/usr/lib/python2.7/dist-packages/woo/tests/core.py is in python-woo 1.0+dfsg1-1+b4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 | # 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
from minieigen import *
from math import *
import woo
from woo._customConverters import *
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
## TODO tests
class TestInteractions(unittest.TestCase): pass
class TestForce(unittest.TestCase): pass
class TestTags(unittest.TestCase): pass
class TestScene(unittest.TestCase):
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.assert_(type(S.tags['str']==unicode))
# def tagError(S): S.tags['error']=234
# self.assert_(type(S.tags['uni']==unicode))
# self.assertRaises(TypeError,lambda: tagError(S))
class TestObjectInstantiation(unittest.TestCase):
def setUp(self):
self.t=woo.core.WooTestClass()
self.ts=woo.core.WooTestClassStatic()
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.assert_(obj.__class__==r,'Failed for '+r.__name__)
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.assert_(gm.color==1.)
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.assert_(len(cld1.functors)==1)
# two different make two different
cld2=LawDispatcher([Law2_L6Geom_FrictPhys_IdealElPl(),Law2_L6Geom_FrictPhys_LinEl6()]); self.assert_(len(cld2.functors)==2)
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.assert_(len(id.geoDisp.functors)==2)
self.assert_(id.geoDisp.__class__==CGeomDispatcher)
self.assert_(id.phyDisp.functors[0].__class__==Cp2_FrictMat_FrictPhys)
self.assert_(id.lawDisp.functors[0].__class__==Law2_L6Geom_FrictPhys_IdealElPl)
def testParallelEngineCtor(self):
"Core: ParallelEngine special ctor"
pe=ParallelEngine([InsertionSortCollider(),[BoundDispatcher(),ForceResetter()]])
self.assert_(pe.slaves[0].__class__==InsertionSortCollider)
self.assert_(len(pe.slaves[1])==2)
pe.slaves=[]
self.assert_(len(pe.slaves)==0)
##
## testing incorrect operations that should raise exceptions
##
def testWrongFunctorType(self):
"Core: dispatcher and functor type mismatch is detected"
# dispatchers accept only correct functors
self.assertRaises(TypeError,lambda: LawDispatcher([Bo1_Sphere_Aabb()]))
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
###
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.assert_(s1.mat.young==s2.mat.young)
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.assert_(S.dem.par[0].mat.young==S.dem.par[1].mat.young)
##
## attribute flags
##
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.assert_(t2.noSaveAttr==i0)
def testNoSave_static(self):
'Core: Attr::noSave (static)'
ts=self.ts
ts.noSave=344
ts.namedEnum='one'
ts.saveTmp('ts')
ts.noSave=123
self.assert_(ts.noSave==123)
ts2=woo.core.WooTestClassStatic.loadTmp('ts')
self.assert_(ts2.noSave==123) # was not saved
self.assert_(ts2.namedEnum=='one') # was saved
# since it is static, self.ts is changed as well now
self.assert_(ts.namedEnum==ts2.namedEnum)
self.assert_(id(ts.namedEnum)==id(ts2.namedEnum))
def testReadonly(self):
'Core: Attr::readonly'
self.assert_(self.t.meaning42==42)
self.assertRaises(AttributeError,lambda: setattr(self.t,'meaning42',43))
def testReaonly_static(self):
'Core: Attr::readonly (static)'
self.assertRaises(AttributeError,lambda: setattr(self.ts,'readonly',2))
def testTriggerPostLoad(self):
'Core: postLoad & Attr::triggerPostLoad'
WTC=woo.core.WooTestClass
# stage right after construction
self.assert_(self.t.postLoadStage==WTC.postLoad_ctor)
baz0=self.t.baz
self.t.foo_incBaz=1 # assign whatever, baz should be incremented
self.assert_(self.t.baz==baz0+1)
self.assert_(self.t.postLoadStage==WTC.postLoad_foo)
self.t.foo_incBaz=1 # again
self.assert_(self.t.baz==baz0+2)
self.t.bar_zeroBaz=1 # assign to reset baz
self.assert_(self.t.baz==0)
self.assert_(self.t.postLoadStage==WTC.postLoad_bar)
# assign to baz to test postLoad again
self.t.baz=-1
self.assert_(self.t.postLoadStage==WTC.postLoad_baz)
def testTriggerPostLoad_static(self):
'Core: Attr::triggerPostLoad (static)'
ts=self.ts
self.assert_(ts.numTriggered==0)
ts.trigger=1
self.assert_(ts.numTriggered==1)
ts.trigger=-1
self.assert_(ts.numTriggered==2)
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.assert_(t.namedEnum=='zero')
t.namedEnum='nothing'
self.assert_(t.namedEnum=='zero')
t.namedEnum=0
self.assert_(t.namedEnum=='zero')
# ctor
self.assertRaises(ValueError,lambda: woo.core.WooTestClass(namedEnum='nonsense'))
tt=woo.core.WooTestClass(namedEnum='single')
self.assert_(tt.namedEnum=='one')
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.assert_(ts.namedEnum=='zero')
ts.namedEnum=-1
self.assert_(ts.namedEnum=='minus one')
# test passing it in the ctor
tt=woo.core.WooTestClass(namedEnum='NULL') # use the alternative name
self.assert_(tt.namedEnum=='zero')
tt=woo.core.WooTestClass(namedEnum=1) # use number
self.assert_(tt.namedEnum=='one')
def testBits(self):
'Core: AttrTrait.bits accessors'
t=self.t
# flags and bits read-write
t.bits=1
self.assert_(t.bit0)
t.bit2=True
self.assert_(t.bits==5)
# flags read-only, bits midifiable
self.assertRaises(AttributeError,lambda: setattr(t,'bitsRw',1))
t.bit2rw=True
t.bit3rw=True
self.assert_(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.assert_(t.bitsRo==3)
self.assert_((t.bit0ro,t.bit1ro,t.bit2ro)==(True,True,False))
## not (yet) supported for static attributes
# def testBits_static(self):
def testHidden(self):
'Core: Attr::hidden'
# hidden attributes are not wrapped in python at all
self.assert_(not hasattr(self.t,'hiddenAttr'))
def testHidden_static(self):
'Core: Attr::hidden (static)'
self.assertRaises(AttributeError,lambda: getattr(self.ts,'hidden'))
def testNotifyDead(self):
'Core: PeriodicEngine::notifyDead'
e=woo.core.WooTestPeriodicEngine()
self.assert_(e.deadCounter==0)
prev=e.deadCounter
e.dead=True
self.assert_(e.deadCounter>prev) # ideally, this should be 1, not 4 by now!!
prev=e.deadCounter
e.dead=True
self.assert_(e.deadCounter>prev)
prev=e.deadCounter
e.dead=False
self.assert_(e.deadCounter>prev)
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.assert_(n.dem.mass==100)
self.assertRaises(TypeError,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()))
class TestLoop(unittest.TestCase):
def setUp(self):
woo.master.reset()
woo.master.scene.fields=[DemField()]
woo.master.scene.dt=1e-8
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.assert_(S.subStep==-1)
# O.subStep is meaningful when substepping
S.subStepping=True
S.one(); self.assert_(S.subStep==0)
S.one(); self.assert_(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.assert_(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()
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.assert_(S.subStep==1)
self.assert_(len(S.engines)==3) # gives modified engine sequence transparently
self.assert_(len(S._nextEngines)==3)
self.assert_(len(S._currEngines)==2)
S.one(); S.one(); # run the 2nd ForceResetter, and epilogue
self.assert_(S.subStep==-1)
# start the next step, nextEngines should replace engines automatically
S.one()
self.assert_(S.subStep==0)
self.assert_(len(S._nextEngines)==0)
self.assert_(len(S.engines)==3)
self.assert_(len(S._currEngines)==3)
def testDead(self):
'Loop: dead engines are not run'
S=woo.master.scene
S.engines=[PyRunner(1,'pass',dead=True)]
S.one(); self.assert_(S.engines[0].nDone==0)
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.assert_(i==S.step) # check there was no advance during those .1 secs
self.assert_(S.running) # running should return true nevertheless
time.sleep(.1)
self.assert_(i<S.step) # check we run during those .1 secs again
S.stop()
def testNoneEngine(self):
'Loop: None engine raises exception.'
S=woo.master.scene
self.assertRaises(RuntimeError,lambda: setattr(S,'engines',[ContactLoop(),None,ContactLoop()]))
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.assert_(S.step==100)
S.run(steps=100,wait=True) # relative value
self.assert_(S.step==200)
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)
print 'TIME',S.time
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)
class TestIO(unittest.TestCase):
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):
woo.master.reset()
S=woo.master.scene
try:
S.any=[c()]
S.saveTmp(quiet=True)
S=Scene.loadTmp()
except (RuntimeError,ValueError):
failed.add(c.__name__)
failed=list(failed); failed.sort()
self.assert_(len(failed)==0,'Failed classes were: '+' '.join(failed))
class TestParticles(unittest.TestCase):
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()
def testIterate(self):
"Particles: iteration"
counted=0
S=woo.master.scene
for b in S.dem.par: counted+=1
self.assert_(counted==self.count)
def testLen(self):
"Particles: len(S.dem.par)"
S=woo.master.scene
self.assert_(len(S.dem.par)==self.count)
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])
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.assert_(len(S.dem.par)==i)
def testNegativeIndex(self):
"Particles: negative index counts backwards (like python sequences)."
S=woo.master.scene
self.assert_(S.dem.par[-1]==S.dem.par[self.count-1])
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.assert_(counted==self.count-removed)
class TestArrayAccu(unittest.TestCase):
def setUp(self):
self.t=woo.core.WooTestClass()
self.N=woo.master.numThreads
def testResize(self):
'OpenMP array accu: resizing'
self.assert_(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.assert_(r[i][0]==i) # first thread is assigned the value
for n in range(1,self.N): self.assert_(r[i][n]==0) # other threads are reset
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.assert_(self.t.aaccuRaw[0][0]==0)
self.assert_(self.t.aaccuRaw[1][0]==1)
def testThreadWrite(self):
'OpenMP array accu: concurrent writes'
self.t.aaccuWriteThreads(0,[i for i in range(0,self.N)])
for i in range(0,self.N):
self.assert_(self.t.aaccuRaw[0][i]==i) # each thread has written its own index
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(str,'sChoice','aa',choice=['aa','bb','cc'],doc='String choice attribute')
]
def postLoad(self,I):
# print '_TestPyClass.postLoad(%s)'%I
if I==None:
self.postLoadCounter+=1
elif I==id(self.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)
def __init__(self,**kw):
woo.core.Object.__init__(self)
self.wooPyInit(self.__class__,woo.core.Object,**kw)
class TestPyDerived(unittest.TestCase):
import woo.pyderived, woo.core
def setUp(self):
self.t=_TestPyClass()
def testTrigger(self):
'PyDerived: postLoad triggers'
# print 'postLoadCounter after ctor:',self.t.postLoadCounter
self.assert_(self.t.postLoadCounter==1)
self.t.aF_trigger=514.
self.assert_(self.t.aF_trigger==514.)
self.assert_(self.t.aF==514.)
self.assert_(self.t.postLoadCounter==11)
def testPickle(self):
'PyDerived: deepcopy'
self.assert_(self.t.aF==1.)
self.assert_(self.t.aNode.pos==Vector3(1,1,1))
self.t.aF=2.
self.t.aNode.pos=Vector3(0,0,0)
self.assert_(self.t.aF==2.)
self.assert_(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.assert_(t2.aF==2.)
self.assert_(t2.aNode.pos==Vector3(0,0,0))
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")
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")
def testTypeCoerceCtor(self):
'PyDerived: type coercion (ctor)'
self.assertRaises(TypeError,lambda:_TestPyClass(aF='abc'))
def testTraits(self):
'PyDerived: PyAttrTraits'
self.assert_(self.t._attrTraits[0].ini==1.)
self.assert_(self.t._attrTraits[0].pyType==float)
def testIniDefault(self):
'PyDerived: default initialization'
self.assert_(self.t.aF==1.)
self.assert_(self.t.aFF==[0.,1.,2.])
self.assert_(self.t.aNodeNone==None)
def testIniUser(self):
'PyDerived: user initialization'
t2=_TestPyClass(aF=2.)
self.assert_(t2.aF==2.)
self.assertRaises(AttributeError,lambda: _TestPyClass(nonsense=123))
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")
|