1#!/pxrpythonsubst 2# 3# Copyright 2016 Pixar 4# 5# Licensed under the Apache License, Version 2.0 (the "Apache License") 6# with the following modification; you may not use this file except in 7# compliance with the Apache License and the following modification to it: 8# Section 6. Trademarks. is deleted and replaced with: 9# 10# 6. Trademarks. This License does not grant permission to use the trade 11# names, trademarks, service marks, or product names of the Licensor 12# and its affiliates, except as required to comply with Section 4(c) of 13# the License and to reproduce the content of the NOTICE file. 14# 15# You may obtain a copy of the Apache License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, software 20# distributed under the Apache License with the above modification is 21# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the Apache License for the specific 23# language governing permissions and limitations under the Apache License. 24# 25 26from __future__ import print_function 27 28import sys 29from pxr import Tf 30 31import unittest 32 33status = 0 34 35f1called = False 36def f1(): 37 global f1called 38 print('called to python!') 39 f1called = True 40 41def f2(): 42 return 'called to python, return string!' 43 44def f3(arg1, arg2): 45 print('args', arg1, arg2) 46 47def f4(stringArg): 48 return 'got string ' + stringArg 49 50class MyBase(Tf._TestBase): 51 def __init__(self): 52 Tf._TestBase.__init__(self) 53 def Virtual(self): 54 return 'python base' 55 def Virtual2(self): 56 print('python base v2') 57 def Virtual3(self, arg): 58 print('python base v3', arg) 59 def UnwrappedVirtual(self): 60 return 'unwrapped virtual' 61 62class MyDerived(Tf._TestDerived): 63 def Virtual(self): 64 return 'python derived' 65 def Virtual2(self): 66 print('python derived v2') 67 def Virtual3(self, arg): 68 print('python derived v3', arg) 69 def Virtual4(self): 70 return 'python derived v4' 71 72class Raiser(Tf._TestBase): 73 def __init__(self): 74 Tf._TestBase.__init__(self) 75 def Virtual(self): 76 raise RuntimeError('testing exception stuff') 77 def Virtual2(self): 78 print('raiser v2') 79 def Virtual3(self, arg): 80 print('py virtual 3', arg) 81 82class TestPython(unittest.TestCase): 83 84 def test_BaseDerived(self): 85 mb = MyBase() 86 d = Tf._TestDerived() 87 md = MyDerived() 88 89 self.assertEqual('unwrapped virtual', mb.TestCallVirtual()) 90 91 self.assertEqual('cpp base', mb.Virtual4()) 92 self.assertEqual('python derived v4', md.Virtual4()) 93 94 self.assertEqual((False, 'python base'), Tf._TakesBase(mb)) 95 self.assertEqual((True, 'cpp derived'), Tf._TakesBase(d)) 96 self.assertEqual((True, 'python derived'), Tf._TakesBase(md)) 97 98 self.assertEqual('python base', Tf._TakesConstBase(mb)) 99 self.assertEqual('cpp derived', Tf._TakesConstBase(d)) 100 self.assertEqual('python derived', Tf._TakesConstBase(md)) 101 102 self.assertEqual('cpp derived', Tf._TakesDerived(d)) 103 self.assertEqual('python derived', Tf._TakesDerived(md)) 104 105 self.assertIs(Tf._ReturnsConstBase(md), md) 106 self.assertIs(Tf._ReturnsBase(md), md) 107 self.assertIs(Tf._ReturnsBaseRefPtr(md), md) 108 109 try: 110 Tf._TestBase().Virtual() 111 assert False, 'calling pure virtual' 112 except: 113 pass 114 115 116 def test_Factory(self): 117 df = Tf._DerivedFactory() 118 self.assertIsInstance(df, Tf._TestDerived) 119 self.assertTrue(hasattr(df, '__owner')) 120 self.assertEqual((True, 'cpp derived'), Tf._TakesBase(df)) 121 Tf._TakesReference(df) 122 self.assertFalse(hasattr(df, '__owner')) 123 124 self.assertIs(Tf._DerivedNullFactory(), None) 125 126 127 def test_ErrorException(self): 128 with self.assertRaises(RuntimeError): 129 Tf._TakesBase(Raiser()) 130 131 with self.assertRaises(Tf.ErrorException) as cm: 132 Tf._mightRaise(True) 133 for x in cm.exception.args: 134 self.assertTrue(len(repr(x))) 135 136 with self.assertRaises(Tf.ErrorException): 137 Tf.RaiseCodingError("some error") 138 139 with self.assertRaises(Tf.ErrorException): 140 Tf.RaiseRuntimeError("some error") 141 142 with self.assertRaises(Tf.ErrorException): 143 Tf._doErrors() 144 145 def test_CppException(self): 146 with self.assertRaises(Tf.CppException) as cm: 147 Tf._ThrowTest('hello') 148 print(cm.exception) 149 150 with self.assertRaises(Tf.CppException) as cm: 151 Tf._CallThrowTest(lambda : Tf._ThrowTest('py-to-cpp-to-py')) 152 print(cm.exception) 153 154 def test_StaticMethodPosting(self): 155 with self.assertRaises(Tf.ErrorException): 156 Tf._TestStaticMethodError.Error() 157 158 Tf.Warn("expected warning, for coverage") 159 160 Tf.Status("expected status message, for coverage") 161 162 def test_NoticeListener(self): 163 global noticesHandled 164 noticesHandled = 0 165 166 def HandleNotice(notice, sender): 167 global noticesHandled 168 noticesHandled += 1 169 170 listener = Tf.Notice.RegisterGlobally('TfNotice', HandleNotice) 171 self.assertEqual(0, noticesHandled) 172 Tf.Notice().SendGlobally() 173 self.assertEqual(1, noticesHandled) 174 listener.Revoke() 175 Tf.Notice().SendGlobally() 176 self.assertEqual(1, noticesHandled) 177 178 # Raise in notice listener 179 def RaiseOnNotice(notice, sender): 180 raise RuntimeError('got notice!') 181 182 class CustomTestNotice(Tf.Notice): 183 pass 184 Tf.Type.Define(CustomTestNotice) 185 186 listener = Tf.Notice.RegisterGlobally(CustomTestNotice, RaiseOnNotice) 187 with self.assertRaises(RuntimeError): 188 CustomTestNotice().SendGlobally() 189 190 # Register a bad notice 191 with self.assertRaises(TypeError): 192 listener = Tf.Notice.RegisterGlobally('BogusNotice', HandleNotice) 193 194 195 def test_Enums(self): 196 Tf._takesTfEnum(Tf._Alpha) 197 Tf._takesTfEnum(Tf._Delta) 198 199 self.assertEqual(Tf._Delta, Tf._returnsTfEnum(Tf._Delta)) 200 self.assertIs(Tf._returnsTfEnum(Tf._Delta), Tf._Delta) 201 202 Tf._takesTfEnum(Tf._Enum.One) 203 204 Tf._takesTestEnum(Tf._Alpha) 205 Tf._takesTestEnum2(Tf._Enum.One) 206 207 self.assertEqual(Tf._TestScopedEnum.Boron, 208 Tf._TestScopedEnum.GetValueFromName('Boron')) 209 self.assertEqual(Tf._TestScopedEnum.Hydrogen, 210 Tf._TestScopedEnum.GetValueFromName('Hydrogen')) 211 self.assertNotEqual(Tf._TestScopedEnum.Hydrogen, 212 Tf._TestScopedEnum.GetValueFromName('Boron')) 213 214 def testRepr(s): 215 self.assertEqual(s, repr(eval(s))) 216 217 testRepr("Tf._Alpha") 218 testRepr("Tf._TestScopedEnum.Hydrogen") 219 testRepr("Tf._Enum.One") 220 testRepr("Tf._Enum.TestScopedEnum.Alef") 221 222 223 224 def test_EnumComparisons(self): 225 self.assertTrue(Tf._Alpha < Tf._Bravo < Tf._Charlie < Tf._Delta) 226 self.assertTrue(Tf._Delta > Tf._Charlie > Tf._Bravo > Tf._Alpha) 227 self.assertTrue(Tf._Alpha != Tf._Bravo != Tf._Charlie != Tf._Delta) 228 self.assertTrue(Tf._Alpha != Tf._Enum.One) 229 self.assertTrue(Tf._Alpha > Tf._Enum.One) 230 self.assertTrue(Tf._Alpha >= Tf._Alpha) 231 self.assertTrue(Tf._Alpha <= Tf._Delta) 232 self.assertTrue(Tf._Charlie >= Tf._Bravo) 233 234 235 def test_EnumValuesRemovedFromTypeScope(self): 236 with self.assertRaises(AttributeError): 237 Tf._takesTestEnum(Tf._TestEnum._Alpha) 238 239 self.assertEqual((Tf._Alpha, Tf._Bravo, Tf._Charlie, Tf._Delta), 240 Tf._TestEnum.allValues) 241 242 with self.assertRaises(TypeError): 243 Tf._takesTestEnum(Tf._Enum.One) 244 with self.assertRaises(TypeError): 245 Tf._takesTestEnum2(Tf._Alpha) 246 247 self.assertEqual((Tf._Enum.One, Tf._Enum.Two, Tf._Enum.Three), 248 Tf._Enum.TestEnum2.allValues) 249 250 self.assertEqual((Tf._Enum.TestScopedEnum.Alef, 251 Tf._Enum.TestScopedEnum.Bet, 252 Tf._Enum.TestScopedEnum.Gimel), 253 Tf._Enum.TestScopedEnum.allValues) 254 255 self.assertEqual(1, Tf._Enum.One.value) 256 self.assertEqual(2, Tf._Enum.Two.value) 257 self.assertEqual(3, Tf._Alpha.value) 258 self.assertEqual('A', Tf._Alpha.displayName) 259 self.assertEqual('B', Tf._Bravo.displayName) 260 self.assertEqual(Tf._Alpha, Tf.Enum.GetValueFromFullName(Tf._Alpha.fullName)) 261 self.assertEqual(None, Tf.Enum.GetValueFromFullName("invalid_enum_name")) 262 263 self.assertTrue(Tf._Enum.One == 1) 264 self.assertTrue(Tf._Enum.Two == 2) 265 self.assertTrue(Tf._Alpha == 3) 266 267 self.assertTrue(1 == Tf._Enum.One) 268 self.assertTrue(2 == Tf._Enum.Two) 269 self.assertTrue(3 == Tf._Alpha) 270 271 self.assertTrue(Tf._Alpha | Tf._Alpha is Tf._Alpha) 272 self.assertTrue(Tf._Alpha & Tf._Alpha is Tf._Alpha) 273 self.assertTrue(Tf._Alpha == 3) 274 self.assertTrue(Tf._Alpha | 1 is Tf._Alpha) 275 self.assertTrue(2 | Tf._Alpha is Tf._Alpha) 276 self.assertTrue(4 | Tf._Alpha == 7) 277 278 self.assertTrue(Tf._Alpha & 3 is Tf._Alpha) 279 self.assertTrue(3 & Tf._Alpha is Tf._Alpha) 280 self.assertTrue(2 & Tf._Alpha == 2) 281 282 self.assertTrue(Tf._Enum.One ^ Tf._Enum.Two == 3) 283 self.assertTrue(4 ^ Tf._Alpha == 7) 284 self.assertTrue(Tf._Alpha ^ 4 == 7) 285 286 287 def test_EnumRegistrationCollision(self): 288 with self.assertRaises(Tf.ErrorException): 289 Tf._registerInvalidEnum(Tf) 290 291 292 def test_EnumInvalidBitwiseOperations(self): 293 '''Bitwise operations are not permitted between enum values of different types.''' 294 with self.assertRaises(TypeError): 295 Tf._Alpha & Tf._Enum.Two 296 assert False, "Should not permit bitwise AND between different enum types" 297 298 with self.assertRaises(TypeError): 299 Tf._Alpha | Tf._Enum.Two 300 301 with self.assertRaises(TypeError): 302 Tf._Alpha ^ Tf._Enum.Two 303 304 def test_EnumOneObjectPerUniqueValue(self): 305 '''Only one object should be created for each unique value per type.''' 306 value1 = Tf._Alpha | Tf._Delta 307 value2 = Tf._Alpha | Tf._Delta 308 self.assertIs(value1, value2) 309 310 def test_EnumConversion(self): 311 value1 = Tf._Alpha | Tf._Delta 312 # Conversions of TfEnum objects to python should retain the correct type.''' 313 self.assertIs(Tf._returnsTfEnum(value1), value1) 314 315 # The auto-generated python object should be convertible to the original type. 316 Tf._takesTestEnum(value1) 317 318 def test_ByteArrayConversion(self): 319 '''Verify we can convert buffers to byte arrays.''' 320 ba = Tf._ConvertByteListToByteArray(['a', 'b', 'c']) 321 self.assertEqual(ba, bytearray([ord('a'), ord('b'), ord('c')])) 322 323 numbers = [str(x % 10) for x in range(256)] 324 ba = Tf._ConvertByteListToByteArray(numbers) 325 self.assertEqual(ba, bytearray([ord(x) for x in numbers])) 326 327 zeroes = ['\x00', '\x00', '\x00'] 328 ba = Tf._ConvertByteListToByteArray(zeroes) 329 self.assertEqual(ba, bytearray([0, 0, 0])) 330 331 ba = Tf._ConvertByteListToByteArray([]) 332 self.assertEqual(ba, bytearray()) 333 self.assertTrue(isinstance(ba, bytearray)) 334 335 def test_Callbacks(self): 336 global f1called 337 Tf._callback(f1) 338 self.assertTrue(f1called) 339 340 self.assertEqual('called to python, return string!', Tf._stringCallback(f2)) 341 342 self.assertEqual('got string c++ is calling...', Tf._stringStringCallback(f4)) 343 344 with self.assertRaises(TypeError): 345 Tf._callback(f3) 346 with self.assertRaises(TypeError): 347 Tf._stringCallback(f1) 348 349 350 def test_WeakStrongRefCallbacks(self): 351 class Foo(object): 352 def method(self): 353 return 'python method' 354 f = Foo() 355 m = f.method 356 357 # the callback is an instancemethod, it should not keep the object 358 # alive. 359 Tf._setTestCallback(m) 360 self.assertEqual('python method', Tf._invokeTestCallback()) 361 del f 362 del m 363 print('expected warning...') 364 self.assertEqual('', Tf._invokeTestCallback()) 365 print('end of expected warning') 366 367 l = lambda : 'python lambda' 368 369 # the callback is a lambda, it should stay alive (and will keep f alive) 370 Tf._setTestCallback(l) 371 self.assertEqual('python lambda', Tf._invokeTestCallback()) 372 del l 373 self.assertEqual('python lambda', Tf._invokeTestCallback()) 374 375 # Test unbound instance method. 376 self.assertEqual('test', Tf._callUnboundInstance(str.lower, "TEST")) 377 378 # the callback is a function, it should not stay alive 379 def func(): 380 return 'python func' 381 382 Tf._setTestCallback(func) 383 self.assertEqual('python func', Tf._invokeTestCallback()) 384 del func 385 print('expected warning...') 386 self.assertEqual('', Tf._invokeTestCallback()) 387 print('end of expected warning') 388 389 del Foo 390 391 392 def test_Singleton(self): 393 class Foo(Tf.Singleton): 394 def init(self): 395 print('Initializing singleton') 396 self._value = 100 397 def GetValue(self): 398 return self._value 399 def SetValue(self, value): 400 self._value = value 401 402 # Get the singleton instance (first time causes creation) 403 f = Foo() 404 405 # Subsequent times do not cause creation 406 f = Foo() 407 408 # Always get same instance (there is only one) 409 f is Foo() and Foo() is Foo() 410 411 self.assertEqual(100, Foo().GetValue()) 412 413 Foo().SetValue(123) 414 415 self.assertEqual(123, Foo().GetValue()) 416 417 418 def test_TfPyClassMethod(self): 419 c = Tf._ClassWithClassMethod() 420 421 # Test classmethod invokation. 422 def _TestCallable(): 423 return 123 424 self.assertEqual((Tf._ClassWithClassMethod, 123), c.Test(_TestCallable)) 425 426 # Test classmethod error handling. 427 class _TestException(Exception): 428 '''A sample exception to raise.''' 429 pass 430 def _TestCallableWithException(): 431 raise _TestException() 432 433 with self.assertRaises(_TestException): 434 c.Test(_TestCallableWithException) 435 436 437 def test_Debug(self): 438 # should allow setting TfDebug's output file to either stdout or 439 # stderr, but not other files. 440 Tf.Debug.SetOutputFile(sys.__stdout__) 441 Tf.Debug.SetOutputFile(sys.__stderr__) 442 443 # other files not allowed. 444 import tempfile 445 with tempfile.NamedTemporaryFile() as f: 446 with self.assertRaises(Tf.ErrorException): 447 Tf.Debug.SetOutputFile(f.file) 448 449 # argument checking. 450 # Will raise Tf.ErrorException. 451 with self.assertRaises(Tf.ErrorException): 452 Tf.Debug.SetOutputFile(1234) 453 454 455 def test_ExceptionWithoutCurrentThreadState(self): 456 with self.assertRaises(RuntimeError): 457 Tf._ThrowCppException() 458 459 460 def test_TakeVectorOfVectorOfStrings(self): 461 self.assertEqual(4, Tf._TakesVecVecString([['1', '2', '3'], ['4', '5'], [], ['6']])) 462 463 464 def test_TfPyObjWrapper(self): 465 self.assertEqual('a', Tf._RoundTripWrapperTest('a')) 466 self.assertEqual(1234, Tf._RoundTripWrapperTest(1234)) 467 self.assertEqual([], Tf._RoundTripWrapperTest([])) 468 self.assertEqual(None, Tf._RoundTripWrapperTest(None)) 469 470 self.assertEqual('a', Tf._RoundTripWrapperCallTest(lambda: 'a')) 471 self.assertEqual(1234, Tf._RoundTripWrapperCallTest(lambda: 1234)) 472 self.assertEqual([], Tf._RoundTripWrapperCallTest(lambda: [])) 473 self.assertEqual(None, Tf._RoundTripWrapperCallTest(lambda: None)) 474 475 self.assertEqual('a', Tf._RoundTripWrapperIndexTest(['a','b'], 0)) 476 self.assertEqual('b', Tf._RoundTripWrapperIndexTest(['a','b'], 1)) 477 self.assertEqual(4, Tf._RoundTripWrapperIndexTest([1,2,3,4], -1)) 478 479 def test_TfMakePyConstructorWithVarArgs(self): 480 with self.assertRaises(TypeError): 481 Tf._ClassWithVarArgInit() 482 483 def CheckResults(c, allowExtraArgs, args, kwargs): 484 self.assertEqual(c.allowExtraArgs, allowExtraArgs) 485 self.assertEqual(c.args, args) 486 self.assertEqual(c.kwargs, kwargs) 487 488 def StandardTests(allowExtraArgs): 489 CheckResults(Tf._ClassWithVarArgInit(allowExtraArgs), 490 allowExtraArgs, (), {}) 491 CheckResults(Tf._ClassWithVarArgInit(allowExtraArgs, 1), 492 allowExtraArgs, (), {'a':1}) 493 CheckResults(Tf._ClassWithVarArgInit(allowExtraArgs, 1, 2, 3), 494 allowExtraArgs, (), {'a':1, 'b':2, 'c':3}) 495 CheckResults(Tf._ClassWithVarArgInit(allowExtraArgs, 1, 2, c=3), 496 allowExtraArgs, (), {'a':1, 'b':2, 'c':3}) 497 498 # Tests with extra arguments disallowed. 499 StandardTests(allowExtraArgs=False) 500 501 # These cases should emit an error because there are unexpected 502 # arguments 503 with self.assertRaises(TypeError): 504 Tf._ClassWithVarArgInit(False, 1, 2, 3, 4) 505 506 with self.assertRaises(TypeError): 507 Tf._ClassWithVarArgInit(False, d=4) 508 509 # This should emit an error because we have multiple values for a single 510 # arg. 511 with self.assertRaises(TypeError): 512 Tf._ClassWithVarArgInit(False, 1, 2, 3, b=4) 513 514 # Tests with extra arguments allowed. 515 StandardTests(allowExtraArgs=True) 516 517 CheckResults(Tf._ClassWithVarArgInit(True, 1, 2, 3, 4, 5), 518 True, (4,5), {'a':1, 'b':2, 'c':3}) 519 CheckResults(Tf._ClassWithVarArgInit(True, 1, 2, c=3, d=6, f=8), 520 True, (), {'a':1, 'b':2, 'c':3, 'd':6, 'f':8}) 521 CheckResults(Tf._ClassWithVarArgInit(True, 1, 2, 3, 4, d=8), 522 True, (4,), {'a':1, 'b':2, 'c':3, 'd':8}) 523 524if __name__ == '__main__': 525 unittest.main() 526