Alembic unit tests: fixed compiler errors
[blender.git] / tests / python / bl_pyapi_idprop_datablock.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 import bpy
20 import sys
21 import os
22 import tempfile
23 import traceback
24 import inspect
25 from bpy.types import UIList
26
27 arr_len = 100
28 ob_cp_count = 100
29 lib_path = os.path.join(tempfile.gettempdir(), "lib.blend")
30 test_path = os.path.join(tempfile.gettempdir(), "test.blend")
31
32
33 def print_fail_msg_and_exit(msg):
34     def __LINE__():
35         try:
36             raise Exception
37         except:
38             return sys.exc_info()[2].tb_frame.f_back.f_back.f_back.f_lineno
39
40     def __FILE__():
41         return inspect.currentframe().f_code.co_filename
42
43     print("'%s': %d >> %s" % (__FILE__(), __LINE__(), msg), file=sys.stderr)
44     sys.stderr.flush()
45     sys.stdout.flush()
46     os._exit(1)
47
48
49 def abort_if_false(expr, msg=None):
50     if not expr:
51         if not msg:
52             msg = "test failed"
53         print_fail_msg_and_exit(msg)
54
55
56 class TestClass(bpy.types.PropertyGroup):
57     test_prop = bpy.props.PointerProperty(type=bpy.types.Object)
58     name = bpy.props.StringProperty()
59
60
61 def get_scene(lib_name, sce_name):
62     for s in bpy.data.scenes:
63         if s.name == sce_name:
64             if (s.library and s.library.name == lib_name) or \
65                     (lib_name == None and s.library == None):
66                 return s
67
68
69 def check_crash(fnc, args=None):
70     try:
71         fnc(args) if args else fnc()
72     except:
73         return
74     print_fail_msg_and_exit("test failed")
75
76
77 def init():
78     bpy.utils.register_class(TestClass)
79     bpy.types.Object.prop_array = bpy.props.CollectionProperty(
80         name="prop_array",
81         type=TestClass)
82     bpy.types.Object.prop = bpy.props.PointerProperty(type=bpy.types.Object)
83
84
85 def make_lib():
86     bpy.ops.wm.read_factory_settings()
87
88     # datablock pointer to the Camera object
89     bpy.data.objects["Cube"].prop = bpy.data.objects['Camera']
90
91     # array of datablock pointers to the Lamp object
92     for i in range(0, arr_len):
93         a = bpy.data.objects["Cube"].prop_array.add()
94         a.test_prop = bpy.data.objects['Lamp']
95         a.name = a.test_prop.name
96
97     # make unique named copy of the cube
98     ob = bpy.data.objects["Cube"].copy()
99     bpy.context.scene.objects.link(ob)
100
101     bpy.data.objects["Cube.001"].name = "Unique_Cube"
102
103     # duplicating of Cube
104     for i in range(0, ob_cp_count):
105         ob = bpy.data.objects["Cube"].copy()
106         bpy.context.scene.objects.link(ob)
107
108     # nodes
109     bpy.data.scenes["Scene"].use_nodes = True
110     bpy.data.scenes["Scene"].node_tree.nodes['Render Layers']["prop"] =\
111         bpy.data.objects['Camera']
112
113     # rename scene and save
114     bpy.data.scenes["Scene"].name = "Scene_lib"
115     bpy.ops.wm.save_as_mainfile(filepath=lib_path)
116
117
118 def check_lib():
119     # check pointer
120     abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera'])
121
122     # check array of pointers in duplicated object
123     for i in range(0, arr_len):
124         abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop ==
125                        bpy.data.objects['Lamp'])
126
127
128 def check_lib_linking():
129     # open startup file
130     bpy.ops.wm.read_factory_settings()
131
132     # link scene to the startup file
133     with bpy.data.libraries.load(lib_path, link=True) as (data_from, data_to):
134         data_to.scenes = ["Scene_lib"]
135
136     o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube']
137
138     abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Lamp'])
139     abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera'])
140     abort_if_false(o.prop.library == o.library)
141
142     bpy.ops.wm.save_as_mainfile(filepath=test_path)
143
144
145 def check_linked_scene_copying():
146     # full copy of the scene with datablock props
147     bpy.ops.wm.open_mainfile(filepath=test_path)
148     bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
149     bpy.ops.scene.new(type='FULL_COPY')
150
151     # check save/open
152     bpy.ops.wm.save_as_mainfile(filepath=test_path)
153     bpy.ops.wm.open_mainfile(filepath=test_path)
154
155     intern_sce = get_scene(None, "Scene_lib")
156     extern_sce = get_scene("Lib", "Scene_lib")
157
158     # check node's props
159     # we made full copy from linked scene, so pointers must equal each other
160     abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
161                    intern_sce.node_tree.nodes['Render Layers']["prop"] ==
162                    extern_sce.node_tree.nodes['Render Layers']["prop"])
163
164
165 def check_scene_copying():
166     # full copy of the scene with datablock props
167     bpy.ops.wm.open_mainfile(filepath=lib_path)
168     bpy.data.screens['Default'].scene = bpy.data.scenes["Scene_lib"]
169     bpy.ops.scene.new(type='FULL_COPY')
170
171     path = test_path + "_"
172     # check save/open
173     bpy.ops.wm.save_as_mainfile(filepath=path)
174     bpy.ops.wm.open_mainfile(filepath=path)
175
176     first_sce = get_scene(None, "Scene_lib")
177     second_sce = get_scene(None, "Scene_lib.001")
178
179     # check node's props
180     # must point to own scene camera
181     abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] ==
182                         second_sce.node_tree.nodes['Render Layers']["prop"]))
183
184
185 # count users
186 def test_users_counting():
187     bpy.ops.wm.read_factory_settings()
188     lamp_us = bpy.data.objects["Lamp"].data.users
189     n = 1000
190     for i in range(0, n):
191         bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Lamp"].data
192     abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + n)
193
194     for i in range(0, int(n / 2)):
195         bpy.data.objects["Cube"]["a%s" % i] = 1
196     abort_if_false(bpy.data.objects["Lamp"].data.users == lamp_us + int(n / 2))
197
198
199 # linking
200 def test_linking():
201     make_lib()
202     check_lib()
203     check_lib_linking()
204     check_linked_scene_copying()
205     check_scene_copying()
206
207
208 # check restrictions for datablock pointers for some classes; GUI for manual testing
209 def test_restrictions1():
210     class TEST_Op(bpy.types.Operator):
211         bl_idname = 'scene.test_op'
212         bl_label = 'Test'
213         bl_options = {"INTERNAL"}
214         str_prop = bpy.props.StringProperty(name="str_prop")
215
216         # disallow registration of datablock properties in operators
217         # will be checked in the draw method (test manually)
218         # also, see console:
219         #   ValueError: bpy_struct "SCENE_OT_test_op" doesn't support datablock properties
220         id_prop = bpy.props.PointerProperty(type=bpy.types.Object)
221
222         def execute(self, context):
223             return {'FINISHED'}
224
225     # just panel for testing the poll callback with lots of objects
226     class TEST_PT_DatablockProp(bpy.types.Panel):
227         bl_label = "Datablock IDProp"
228         bl_space_type = "PROPERTIES"
229         bl_region_type = "WINDOW"
230         bl_context = "render"
231
232         def draw(self, context):
233             self.layout.prop_search(context.scene, "prop", bpy.data,
234                                     "objects")
235             self.layout.template_ID(context.scene, "prop1")
236             self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups")
237
238             op = self.layout.operator("scene.test_op")
239             op.str_prop = "test string"
240
241             def test_fnc(op):
242                 op["ob"] = bpy.data.objects['Unique_Cube']
243             check_crash(test_fnc, op)
244             abort_if_false(not hasattr(op, "id_prop"))
245
246     bpy.utils.register_class(TEST_PT_DatablockProp)
247     bpy.utils.register_class(TEST_Op)
248
249     def poll(self, value):
250         return value.name in bpy.data.scenes["Scene_lib"].objects
251
252     def poll1(self, value):
253         return True
254
255     bpy.types.Scene.prop = bpy.props.PointerProperty(type=bpy.types.Object)
256     bpy.types.Scene.prop1 = bpy.props.PointerProperty(type=bpy.types.Object, poll=poll)
257     bpy.types.Scene.prop2 = bpy.props.PointerProperty(type=bpy.types.NodeTree, poll=poll1)
258
259     # check poll effect on UI (poll returns false => red alert)
260     bpy.context.scene.prop = bpy.data.objects["Lamp.001"]
261     bpy.context.scene.prop1 = bpy.data.objects["Lamp.001"]
262
263     # check incorrect type assignment
264     def sub_test():
265         # NodeTree id_prop
266         bpy.context.scene.prop2 = bpy.data.objects["Lamp.001"]
267
268     check_crash(sub_test)
269
270     bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree")
271
272     print("Please, test GUI performance manually on the Render tab, '%s' panel" %
273           TEST_PT_DatablockProp.bl_label, file=sys.stderr)
274     sys.stderr.flush()
275
276
277 # check some possible regressions
278 def test_regressions():
279     bpy.types.Object.prop_str = bpy.props.StringProperty(name="str")
280     bpy.data.objects["Unique_Cube"].prop_str = "test"
281
282     bpy.types.Object.prop_gr = bpy.props.PointerProperty(
283         name="prop_gr",
284         type=TestClass,
285         description="test")
286
287     bpy.data.objects["Unique_Cube"].prop_gr = None
288
289
290 # test restrictions for datablock pointers
291 def test_restrictions2():
292     class TestClassCollection(bpy.types.PropertyGroup):
293         prop = bpy.props.CollectionProperty(
294             name="prop_array",
295             type=TestClass)
296     bpy.utils.register_class(TestClassCollection)
297
298     class TestPrefs(bpy.types.AddonPreferences):
299         bl_idname = "testprefs"
300         # expecting crash during registering
301         my_prop2 = bpy.props.PointerProperty(type=TestClass)
302
303         prop = bpy.props.PointerProperty(
304             name="prop",
305             type=TestClassCollection,
306             description="test")
307
308     bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object)
309
310     class TestUIList(UIList):
311         test = bpy.props.PointerProperty(type=bpy.types.Object)
312         def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
313             layout.prop(item, "name", text="", emboss=False, icon_value=icon)
314
315     check_crash(bpy.utils.register_class, TestPrefs)
316     check_crash(bpy.utils.register_class, TestUIList)
317
318     bpy.utils.unregister_class(TestClassCollection)
319
320
321 def main():
322     init()
323     test_users_counting()
324     test_linking()
325     test_restrictions1()
326     check_crash(test_regressions)
327     test_restrictions2()
328
329
330 if __name__ == "__main__":
331     try:
332         main()
333     except:
334         import traceback
335
336         traceback.print_exc()
337         sys.stderr.flush()
338         os._exit(1)