test_property.py 15 KB

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