6b5335d4a8e344a6c042fce6b7d0c61a055aa262
[blender.git] / tests / python / bl_pyapi_idprop.py
1 # Apache License, Version 2.0
2
3 # ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
4 import bpy
5 import unittest
6 import numpy as np
7 from array import array
8
9
10 class TestHelper:
11
12     @property
13     def id(self):
14         return self._id
15
16     def setUp(self):
17         self._id = bpy.context.scene
18         self._removeProperties()
19         assert len(self._id.keys()) == 0
20
21     def tearDown(self):
22         self._removeProperties()
23
24     def _removeProperties(self):
25         for key in list(self._id.keys()):
26             del self._id[key]
27
28     def assertAlmostEqualSeq(self, list1, list2):
29         self.assertEqual(len(list1), len(list2))
30         for v1, v2 in zip(list1, list2):
31             self.assertAlmostEqual(v1, v2, places=5)
32
33
34 class TestIdPropertyCreation(TestHelper, unittest.TestCase):
35
36     def test_name_empty(self):
37         self.id[""] = 4
38         self.assertEqual(self.id[""], 4)
39
40     def test_name_too_long(self):
41         with self.assertRaises(KeyError):
42             self.id["name" * 30] = 4
43
44     def test_int(self):
45         self.id["a"] = 2
46         self.assertEqual(self.id["a"], 2)
47         self.assertTrue(isinstance(self.id["a"], int))
48
49         with self.assertRaises(OverflowError):
50             self.id["a"] = 2 ** 31  # integer <= 2 ** 31-1
51
52     def test_double(self):
53         self.id["a"] = 2.5
54         self.assertEqual(self.id["a"], 2.5)
55         self.assertTrue(isinstance(self.id["a"], float))
56
57     def test_unicode(self):
58         self.id["a"] = "Hello World"
59         self.assertEqual(self.id["a"], "Hello World")
60         self.assertTrue(isinstance(self.id["a"], str))
61
62     def test_bytes(self):
63         self.id["a"] = b"Hello World"
64         self.assertEqual(self.id["a"], b"Hello World")
65         self.assertTrue(isinstance(self.id["a"], bytes))
66
67     def test_sequence_double_list(self):
68         mylist = [1.2, 3.4, 5.6]
69         self.id["a"] = mylist
70         self.assertEqual(self.id["a"].to_list(), mylist)
71         self.assertEqual(self.id["a"].typecode, "d")
72
73     def test_sequence_int_list(self):
74         mylist = [1, 2, 3]
75         self.id["a"] = mylist
76         self.assertEqual(self.id["a"].to_list(), mylist)
77         self.assertEqual(self.id["a"].typecode, "i")
78
79     def test_sequence_float_array(self):
80         mylist = [1.2, 3.4, 5.6]
81         self.id["a"] = array("f", mylist)
82         self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
83         self.assertEqual(self.id["a"].typecode, "f")
84
85     def test_sequence_double_array(self):
86         mylist = [1.2, 3.4, 5.6]
87         self.id["a"] = array("d", mylist)
88         self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
89         self.assertEqual(self.id["a"].typecode, "d")
90
91     def test_sequence_int_array(self):
92         mylist = [1, 2, 3]
93         self.id["a"] = array("i", mylist)
94         self.assertAlmostEqualSeq(self.id["a"].to_list(), mylist)
95         self.assertEqual(self.id["a"].typecode, "i")
96
97     def test_sequence_other_array(self):
98         mylist = [1, 2, 3]
99         self.id["a"] = array("Q", mylist)
100         self.assertEqual(self.id["a"].to_list(), mylist)
101
102     def test_sequence_mixed_numerical_type(self):
103         self.id["a"] = [1, 2, 3.4, 5]
104         self.assertAlmostEqualSeq(self.id["a"].to_list(), [1.0, 2.0, 3.4, 5.0])
105         self.assertEqual(self.id["a"].typecode, "d")
106
107     def test_sequence_str_list(self):
108         # I'm a bit surprised that this works
109         mylist = ["abc", "qwe"]
110         self.id["a"] = mylist
111         self.assertEqual(self.id["a"], mylist)
112
113     def test_sequence_mixed_type(self):
114         with self.assertRaises(TypeError):
115             mylist = ["abc", 3, "qwe", 3.4]
116             self.id["a"] = mylist
117
118     def test_mapping_simple(self):
119         mydict = {"1": 10, "2": "20", "3": 30.5}
120         self.id["a"] = mydict
121         self.assertEqual(self.id["a"]["1"], mydict["1"])
122         self.assertEqual(self.id["a"]["2"], mydict["2"])
123         self.assertEqual(self.id["a"]["3"], mydict["3"])
124
125     def test_mapping_complex(self):
126         mydict = {
127             "1": [1, 2, 3],
128             "2": {"1": "abc", "2": array("i", [4, 5, 6])},
129             "3": {"1": {"1": 10}, "2": b"qwe"},
130         }
131         self.id["a"] = mydict
132         self.assertEqual(self.id["a"]["1"].to_list(), [1, 2, 3])
133         self.assertEqual(self.id["a"]["2"]["1"], "abc")
134         self.assertEqual(self.id["a"]["2"]["2"].to_list(), [4, 5, 6])
135         self.assertEqual(self.id["a"]["3"]["1"]["1"], 10)
136         self.assertEqual(self.id["a"]["3"]["2"], b"qwe")
137
138         with self.assertRaises(KeyError):
139             a = self.id["a"]["2"]["a"]
140
141     def test_invalid_type(self):
142         with self.assertRaises(TypeError):
143             self.id["a"] = self
144
145
146 class TestBufferProtocol(TestHelper, unittest.TestCase):
147
148     def test_int(self):
149         self.id["a"] = array("i", [1, 2, 3, 4, 5])
150         a = np.frombuffer(self.id["a"], self.id["a"].typecode)
151         self.assertEqual(len(a), 5)
152         a[2] = 10
153         self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5])
154
155     def test_float(self):
156         self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0])
157         a = np.frombuffer(self.id["a"], self.id["a"].typecode)
158         self.assertEqual(len(a), 4)
159         a[-1] = 10
160         self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0])
161
162     def test_double(self):
163         self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0])
164         a = np.frombuffer(self.id["a"], self.id["a"].typecode)
165         a[1] = 10
166         self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0])
167
168     def test_full_update(self):
169         self.id["a"] = array("i", [1, 2, 3, 4, 5, 6])
170         a = np.frombuffer(self.id["a"], self.id["a"].typecode)
171         a[:] = [10, 20, 30, 40, 50, 60]
172         self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60])
173
174     def test_partial_update(self):
175         self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8])
176         a = np.frombuffer(self.id["a"], self.id["a"].typecode)
177         a[1:5] = [10, 20, 30, 40]
178         self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8])
179
180     def test_copy(self):
181         self.id["a"] = array("i", [1, 2, 3, 4, 5])
182         self.id["b"] = self.id["a"]
183         self.assertEqual(self.id["a"].to_list(), self.id["b"].to_list())
184
185     def test_memview_attributes(self):
186         mylist = [1, 2, 3]
187         self.id["a"] = mylist
188
189         view1 = memoryview(self.id["a"])
190         view2 = memoryview(array("i", mylist))
191
192         self.assertEqualMemviews(view1, view2)
193
194     def assertEqualMemviews(self, view1, view2):
195         props_to_compare = (
196             "contiguous", "format", "itemsize", "nbytes", "ndim",
197             "readonly", "shape", "strides", "suboffsets"
198         )
199         for attr in props_to_compare:
200             self.assertEqual(getattr(view1, attr), getattr(view2, attr))
201
202         self.assertEqual(list(view1), list(view2))
203         self.assertEqual(view1.tobytes(), view2.tobytes())
204
205
206 if __name__ == '__main__':
207     import sys
208     sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
209     unittest.main()