test_property.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. import sys
  2. import pytest
  3. from panda3d import core
  4. from contextlib import contextmanager
  5. import collections.abc as collections_abc
  6. @contextmanager
  7. def constant_refcount(var):
  8. """ with block that checks that the Python refcount remains the same. """
  9. rc = sys.getrefcount(var)
  10. yield
  11. assert sys.getrefcount(var) == rc
  12. def test_property():
  13. # This is a property defined by MAKE_PROPERTY.
  14. np = core.PandaNode("")
  15. # Getter
  16. transform = np.get_transform()
  17. assert transform == np.transform
  18. # Setter
  19. new_transform = transform.set_pos((1, 0, 0))
  20. np.transform = new_transform
  21. assert np.transform == new_transform
  22. # Invalid assignments
  23. with pytest.raises(TypeError):
  24. np.transform = None
  25. with pytest.raises(TypeError):
  26. np.transform = "nonsense"
  27. with pytest.raises(TypeError):
  28. del np.transform
  29. def test_property2():
  30. # This is a property defined by MAKE_PROPERTY2, that can be None.
  31. mat = core.Material()
  32. mat.ambient = (1, 0, 0, 1)
  33. assert mat.ambient == (1, 0, 0, 1)
  34. mat.ambient = None
  35. assert mat.ambient is None
  36. with pytest.raises(TypeError):
  37. mat.ambient = "nonsense"
  38. with pytest.raises(TypeError):
  39. del mat.ambient
  40. # The next tests are for MAKE_SEQ_PROPERTY.
  41. def seq_property(*items):
  42. """ Returns a sequence property initialized with the given items. """
  43. # It doesn't matter which property we use; I just happened to pick
  44. # CollisionNode.solids.
  45. cn = core.CollisionNode("")
  46. append = cn.add_solid
  47. for item in items:
  48. append(item)
  49. assert len(cn.solids) == len(items)
  50. return cn.solids
  51. # Arbitrary items we can use as items for the above seq property.
  52. item_a = core.CollisionSphere((0, 0, 0), 1)
  53. item_b = core.CollisionSphere((0, 0, 0), 2)
  54. item_c = core.CollisionSphere((0, 0, 0), 3)
  55. def test_seq_property_abc():
  56. prop = seq_property()
  57. assert isinstance(prop, collections_abc.Container)
  58. assert isinstance(prop, collections_abc.Sized)
  59. assert isinstance(prop, collections_abc.Iterable)
  60. assert isinstance(prop, collections_abc.MutableSequence)
  61. assert isinstance(prop, collections_abc.Sequence)
  62. def test_seq_property_empty():
  63. prop = seq_property()
  64. assert not prop
  65. assert len(prop) == 0
  66. with pytest.raises(IndexError):
  67. prop[0]
  68. with pytest.raises(IndexError):
  69. prop[-1]
  70. def test_seq_property_iter():
  71. prop = seq_property(item_a, item_b, item_b)
  72. assert prop
  73. assert len(prop) == 3
  74. assert tuple(prop) == (item_a, item_b, item_b)
  75. assert item_a in prop
  76. assert item_c not in prop
  77. assert None not in prop
  78. def test_seq_property_reversed():
  79. prop = seq_property(item_a, item_b, item_b)
  80. assert tuple(reversed(prop)) == tuple(reversed(tuple(prop)))
  81. def test_seq_property_getitem():
  82. prop = seq_property(item_a, item_b, item_b)
  83. assert prop[0] == item_a
  84. assert prop[1] == item_b
  85. assert prop[2] == item_b
  86. # Reverse index
  87. assert prop[-1] == item_b
  88. assert prop[-2] == item_b
  89. assert prop[-3] == item_a
  90. # Out of bounds access
  91. with pytest.raises(IndexError):
  92. prop[-4]
  93. with pytest.raises(IndexError):
  94. prop[3]
  95. with pytest.raises(IndexError):
  96. prop[2**63]
  97. # Invalid index
  98. with pytest.raises(TypeError):
  99. prop[None]
  100. with pytest.raises(TypeError):
  101. prop[item_a]
  102. with pytest.raises(TypeError):
  103. prop["nonsense"]
  104. # Reference count check
  105. i = 1
  106. with constant_refcount(i):
  107. prop[i]
  108. # Make sure it preserves refcount of invalid indices
  109. i = "nonsense195641"
  110. with constant_refcount(i):
  111. try:
  112. prop[i]
  113. except TypeError:
  114. pass
  115. def test_seq_property_setitem():
  116. prop = seq_property(item_c, item_c, item_c)
  117. prop[0] = item_a
  118. prop[1] = item_b
  119. assert tuple(prop) == (item_a, item_b, item_c)
  120. # Refcount of key and value stays the same?
  121. i = 0
  122. with constant_refcount(i):
  123. with constant_refcount(item_a):
  124. prop[0] = item_a
  125. # Reverse index
  126. prop[-1] = item_a
  127. prop[-2] = item_b
  128. prop[-3] = item_c
  129. assert tuple(prop) == (item_c, item_b, item_a)
  130. # Out of bounds access
  131. with pytest.raises(IndexError):
  132. prop[-4] = item_c
  133. with pytest.raises(IndexError):
  134. prop[3] = item_c
  135. with pytest.raises(IndexError):
  136. prop[2**63] = item_c
  137. # Invalid index
  138. with pytest.raises(TypeError):
  139. prop[None] = item_c
  140. with pytest.raises(TypeError):
  141. prop[item_a] = item_c
  142. with pytest.raises(TypeError):
  143. prop["nonsense"] = item_c
  144. # Invalid type
  145. with pytest.raises(TypeError):
  146. prop[0] = None
  147. with pytest.raises(TypeError):
  148. prop[0] = "nonsense"
  149. def test_seq_property_delitem():
  150. prop = seq_property(item_a, item_b, item_c)
  151. # Out of bounds
  152. with pytest.raises(IndexError):
  153. prop[3]
  154. with pytest.raises(IndexError):
  155. prop[-4]
  156. # Positive index
  157. del prop[0]
  158. assert tuple(prop) == (item_b, item_c)
  159. # Negative index
  160. del prop[-2]
  161. assert tuple(prop) == (item_c,)
  162. # Invalid index
  163. with pytest.raises(TypeError):
  164. del prop[None]
  165. def test_seq_property_index():
  166. prop = seq_property(item_a, item_b, item_b)
  167. assert prop.index(item_a) == 0
  168. assert prop.index(item_b) == 1
  169. with pytest.raises(ValueError):
  170. prop.index(item_c)
  171. with pytest.raises(ValueError):
  172. prop.index(None)
  173. with pytest.raises(ValueError):
  174. prop.index("nonsense")
  175. # Refcount is properly decreased
  176. with constant_refcount(item_b):
  177. prop.index(item_b)
  178. with constant_refcount(item_c):
  179. try:
  180. prop.index(item_c)
  181. except ValueError:
  182. pass
  183. nonsense = "nonsense854371"
  184. with constant_refcount(nonsense):
  185. try:
  186. prop.index(nonsense)
  187. except ValueError:
  188. pass
  189. def test_seq_property_count():
  190. prop = seq_property(item_a, item_b, item_b)
  191. prop.count(item_a) == 1
  192. prop.count(item_b) == 2
  193. prop.count(item_c) == 0
  194. prop.count(None) == 0
  195. prop.count(("nonsense", -3.5)) == 0
  196. # Refcount does not leak
  197. with constant_refcount(item_b):
  198. prop.count(item_b)
  199. nonsense = "nonsense219449"
  200. with constant_refcount(nonsense):
  201. prop.count(nonsense)
  202. def test_seq_property_clear():
  203. prop = seq_property(item_a, item_b, item_b)
  204. prop.clear()
  205. assert not prop
  206. assert len(prop) == 0
  207. assert tuple(prop) == ()
  208. def test_seq_property_pop():
  209. prop = seq_property(item_a, item_b, item_c, item_b)
  210. # Test out of bounds pop
  211. with pytest.raises(IndexError):
  212. prop.pop(4)
  213. with pytest.raises(IndexError):
  214. prop.pop(-5)
  215. assert prop.pop(1) == item_b
  216. assert prop.pop(-1) == item_b
  217. assert prop.pop() == item_c
  218. assert prop.pop(0) == item_a
  219. # Wrong args
  220. with pytest.raises(TypeError):
  221. prop.pop(0, 0)
  222. # Pop on empty sequence
  223. with pytest.raises(IndexError):
  224. prop.pop()
  225. with pytest.raises(IndexError):
  226. prop.pop(0)
  227. with pytest.raises(IndexError):
  228. prop.pop(-1)
  229. def test_seq_property_remove():
  230. prop = seq_property(item_a, item_b, item_c, item_b)
  231. with constant_refcount(item_b):
  232. prop.remove(item_b)
  233. assert tuple(prop) == (item_a, item_c, item_b)
  234. prop.remove(item_b)
  235. assert tuple(prop) == (item_a, item_c)
  236. with pytest.raises(ValueError):
  237. prop.remove(item_b)
  238. with pytest.raises(ValueError):
  239. prop.remove(None)
  240. with pytest.raises(ValueError):
  241. prop.remove("nonsense")
  242. def test_seq_property_append():
  243. prop = seq_property(item_a, item_b)
  244. with constant_refcount(item_c):
  245. prop.append(item_c)
  246. assert tuple(prop) == (item_a, item_b, item_c)
  247. with pytest.raises(TypeError):
  248. prop.append(None)
  249. with pytest.raises(TypeError):
  250. prop.append("nonsense")
  251. def test_seq_property_insert():
  252. # Adding at the beginning
  253. prop = seq_property(item_a, item_a, item_a)
  254. with constant_refcount(item_b):
  255. prop.insert(0, item_b)
  256. assert tuple(prop) == (item_b, item_a, item_a, item_a)
  257. # Adding in the middle
  258. prop = seq_property(item_a, item_a, item_a)
  259. with constant_refcount(item_b):
  260. prop.insert(2, item_b)
  261. assert tuple(prop) == (item_a, item_a, item_b, item_a)
  262. # Adding at the end
  263. prop = seq_property(item_a, item_a, item_a)
  264. with constant_refcount(item_b):
  265. prop.insert(len(prop), item_b)
  266. assert tuple(prop) == (item_a, item_a, item_a, item_b)
  267. # Adding with negative index
  268. prop = seq_property(item_a, item_a, item_a)
  269. with constant_refcount(item_b):
  270. prop.insert(-2, item_b)
  271. assert tuple(prop) == (item_a, item_b, item_a, item_a)
  272. # Adding at the end with overflowing index
  273. prop = seq_property(item_a, item_a, item_a)
  274. with constant_refcount(item_b):
  275. prop.insert(2345, item_b)
  276. assert tuple(prop) == (item_a, item_a, item_a, item_b)
  277. # Adding at the beginning with negative overflowing index
  278. prop = seq_property(item_a, item_a, item_a)
  279. with constant_refcount(item_b):
  280. prop.insert(-2345, item_b)
  281. assert tuple(prop) == (item_b, item_a, item_a, item_a)
  282. def test_seq_property_extend():
  283. prop = seq_property(item_a)
  284. with constant_refcount(item_b):
  285. prop.extend((item_b, item_c))
  286. assert tuple(prop) == (item_a, item_b, item_c)
  287. with pytest.raises(TypeError):
  288. prop.extend(None)
  289. with pytest.raises(TypeError):
  290. prop.extend("nonsense")
  291. with pytest.raises(TypeError):
  292. prop.extend(item_a)
  293. with pytest.raises(TypeError):
  294. prop.extend(item_a, item_b)
  295. with pytest.raises(TypeError):
  296. prop.extend()
  297. with pytest.raises(TypeError):
  298. prop.extend((item_a, None))
  299. with pytest.raises(TypeError):
  300. prop.extend(["nonsense"])
  301. # The next tests are for MAKE_MAP_PROPERTY.
  302. def map_property(**items):
  303. """ Returns a mapping property initialized with the given values. """
  304. # It doesn't matter which property we use; I just happened to pick
  305. # NodePath.tags.
  306. np = core.NodePath("")
  307. for k, v in items.items():
  308. np.set_tag(k, v)
  309. return np.tags
  310. def test_map_property_abc():
  311. prop = map_property()
  312. assert isinstance(prop, collections_abc.Container)
  313. assert isinstance(prop, collections_abc.Sized)
  314. assert isinstance(prop, collections_abc.Iterable)
  315. assert isinstance(prop, collections_abc.MutableMapping)
  316. assert isinstance(prop, collections_abc.Mapping)
  317. def test_map_property_empty():
  318. prop = map_property()
  319. assert not prop
  320. assert len(prop) == 0
  321. with pytest.raises(KeyError):
  322. prop.popitem()
  323. with pytest.raises(KeyError):
  324. prop['nonsense']
  325. def test_map_property_getitem():
  326. key = 'key162732'
  327. value = 'value162732'
  328. prop = map_property(**{key: value})
  329. with constant_refcount(key):
  330. with constant_refcount(value):
  331. assert prop[key] == value
  332. with pytest.raises(KeyError):
  333. prop['nonsense']
  334. with pytest.raises(TypeError):
  335. prop[None]
  336. def test_map_property_setitem():
  337. key = 'key559075'
  338. value = 'value559075'
  339. prop = map_property()
  340. # Setting new key
  341. with constant_refcount(key):
  342. with constant_refcount(value):
  343. prop[key] = value
  344. assert prop[key] == value
  345. # Setting existing key
  346. with constant_refcount(key):
  347. with constant_refcount(value):
  348. prop[key] = value
  349. assert prop[key] == value
  350. # Unknown key
  351. with pytest.raises(TypeError):
  352. prop[None] = value
  353. # Unknown value
  354. with pytest.raises(TypeError):
  355. prop[key] = None
  356. def test_map_property_delitem():
  357. key = 'key731691'
  358. value = 'value731691'
  359. prop = map_property(**{key: value})
  360. with constant_refcount(key):
  361. with constant_refcount(value):
  362. del prop[key]
  363. with pytest.raises(KeyError):
  364. assert prop[key]
  365. # Nonexistent key
  366. with pytest.raises(KeyError):
  367. del prop['nonsense']
  368. # Invalid type key
  369. with pytest.raises(TypeError):
  370. del prop[None]
  371. def test_map_property_contains():
  372. prop = map_property(key='value')
  373. assert 'key' in prop
  374. assert None not in prop
  375. assert 'value' not in prop
  376. def test_map_property_get():
  377. key = 'key861801'
  378. value = 'value861801'
  379. prop = map_property(**{key: value})
  380. default = 'default861801'
  381. with constant_refcount(key):
  382. with constant_refcount(default):
  383. assert prop.get(key) == value
  384. with constant_refcount(key):
  385. with constant_refcount(default):
  386. assert prop.get(key, default) == value
  387. assert prop.get('unknown') is None
  388. with constant_refcount(default):
  389. assert prop.get('unknown', default) == default
  390. def test_map_property_pop():
  391. key = 'key'
  392. value = 'value'
  393. prop = map_property(**{key: value})
  394. assert prop.pop('nonsense', None) is None
  395. assert prop.pop('nonsense', 'default') == 'default'
  396. assert prop.pop('key', 'default') == 'value'
  397. assert 'key' not in prop
  398. def test_map_property_popitem():
  399. key = 'key'
  400. value = 'value'
  401. prop = map_property(**{key: value})
  402. assert prop.popitem() == (key, value)
  403. with pytest.raises(KeyError):
  404. assert prop.popitem()
  405. def test_map_property_clear():
  406. prop = map_property(key='value', key2='value2')
  407. prop.clear()
  408. assert len(prop) == 0
  409. def test_map_property_setdefault():
  410. prop = map_property(key='value')
  411. # Don't change value of key that already exists
  412. prop.setdefault('key', 'value2')
  413. assert prop['key'] == 'value'
  414. # Change values of nonexistent key
  415. prop.setdefault('key2', 'value2')
  416. assert prop['key2'] == 'value2'
  417. # These should error because you can't set None on this property
  418. with pytest.raises(TypeError):
  419. prop.setdefault('key3', None)
  420. with pytest.raises(TypeError):
  421. prop.setdefault('key3')
  422. def test_map_property_update():
  423. prop = map_property()
  424. # Empty update
  425. prop.update()
  426. # Passing in dictionary
  427. prop.update({'key': 'value'})
  428. # Passing in keywords
  429. prop.update(key2='value2')
  430. # Don't pass in both!
  431. with pytest.raises(TypeError):
  432. prop.update({}, k='v')
  433. assert prop['key'] == 'value'
  434. assert prop['key2'] == 'value2'
  435. def test_map_property_keys():
  436. prop = map_property(key='value', key2='value2')
  437. assert isinstance(prop.keys(), collections_abc.MappingView)
  438. assert frozenset(prop.keys()) == frozenset(('key', 'key2'))
  439. def test_map_property_values():
  440. prop = map_property(key='value', key2='value2')
  441. assert isinstance(prop.values(), collections_abc.ValuesView)
  442. assert frozenset(prop.values()) == frozenset(('value', 'value2'))
  443. def test_map_property_items():
  444. prop = map_property(key='value', key2='value2')
  445. assert isinstance(prop.items(), collections_abc.MappingView)
  446. assert frozenset(prop.items()) == frozenset((('key', 'value'), ('key2', 'value2')))
  447. def test_static_property():
  448. v1 = core.PandaSystem.version_string
  449. v2 = core.PandaSystem.get_version_string()
  450. assert v1 == v2