This file is indexed.

/usr/lib/python2.7/dist-packages/woo/tests/core.py is in python-woo 1.0+dfsg1-2.

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")