1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
21 # semi-useful script, runs all operators in a number of different
22 # contexts, cheap way to find misc small bugs but is in no way a complete test.
24 # only error checked for here is a segfault.
30 USE_FILES = "" # "/mango/"
32 USE_RANDOM_SCREEN = False
33 RANDOM_SEED = [1] # so we can redo crashes
34 RANDOM_RESET = 0.1 # 10% chance of resetting on each new operator
51 "render.play_rendered_anim",
52 "sound.bake_animation", # OK but slow
53 "sound.mixdown", # OK but slow
54 "object.bake_image", # OK but slow
55 "object.paths_calculate", # OK but slow
56 "object.paths_update", # OK but slow
57 "ptcache.bake_all", # OK but slow
58 "nla.bake", # OK but slow
63 "script.autoexec_warn_clear",
64 "screen.delete", # already used for random screens
65 "wm.blenderplayer_start",
66 "wm.recover_auto_save",
74 "wm.copy_prev_settings",
78 "wm.properties_remove",
80 "wm.properties_context_change",
81 "wm.operator_cheat_sheet",
82 "wm.interface_theme_*",
83 "wm.previews_ensure", # slow - but harmless
84 "wm.keyitem_add", # just annoying - but harmless
85 "wm.keyconfig_activate", # just annoying - but harmless
86 "wm.keyconfig_preset_add", # just annoying - but harmless
87 "wm.keyconfig_test", # just annoying - but harmless
88 "wm.memory_statistics", # another annoying one
89 "wm.dependency_relations", # another annoying one
90 "wm.keymap_restore", # another annoying one
91 "wm.addon_*", # harmless, but dont change state
92 "console.*", # just annoying - but harmless
96 def blend_list(mainpath):
98 from os.path import join, splitext
100 def file_list(path, filename_check=None):
101 for dirpath, dirnames, filenames in os.walk(path):
103 dirnames[:] = [d for d in dirnames if not d.startswith(".")]
105 for filename in filenames:
106 filepath = join(dirpath, filename)
107 if filename_check is None or filename_check(filepath):
110 def is_blend(filename):
111 ext = splitext(filename)[1]
112 return (ext in {".blend", })
114 return list(sorted(file_list(mainpath, is_blend)))
118 USE_FILES_LS = blend_list(USE_FILES)
119 # print(USE_FILES_LS)
122 def filter_op_list(operators):
123 from fnmatch import fnmatchcase
126 for op_match in op_blacklist:
127 if fnmatchcase(op, op_match):
128 print(" skipping: %s (%s)" % (op, op_match))
132 operators[:] = [op for op in operators if is_op_ok(op[0])]
136 bpy.ops.wm.read_factory_settings()
137 for scene in bpy.data.scenes:
138 # reduce range so any bake action doesn't take too long
139 scene.frame_start = 1
142 if USE_RANDOM_SCREEN:
144 for i in range(random.randint(0, len(bpy.data.screens))):
145 bpy.ops.screen.delete()
146 print("Scree IS", bpy.context.screen)
151 f = USE_FILES_LS[random.randint(0, len(USE_FILES_LS) - 1)]
152 bpy.ops.wm.open_mainfile(filepath=f)
156 def build_property_typemap(skip_classes):
158 property_typemap = {}
160 for attr in dir(bpy.types):
161 cls = getattr(bpy.types, attr)
162 if issubclass(cls, skip_classes):
165 # # to support skip-save we cant get all props
166 # properties = cls.bl_rna.properties.keys()
168 for prop_id, prop in cls.bl_rna.properties.items():
169 if not prop.is_skip_save:
170 properties.append(prop_id)
172 properties.remove("rna_type")
173 property_typemap[attr] = properties
175 return property_typemap
177 bpy.types.BrushTextureSlot,
180 property_typemap = build_property_typemap(CLS_BLACKLIST)
181 bpy_struct_type = bpy.types.Struct.__base__
183 def id_walk(value, parent):
184 value_type = type(value)
185 value_type_name = value_type.__name__
187 value_id = getattr(value, "id_data", Ellipsis)
188 value_props = property_typemap.get(value_type_name, ())
190 for prop in value_props:
191 subvalue = getattr(value, prop)
193 if subvalue == parent:
196 if prop == "point_caches":
198 subvalue_type = type(subvalue)
199 yield value, prop, subvalue_type
200 subvalue_id = getattr(subvalue, "id_data", Ellipsis)
202 if value_id == subvalue_id:
203 if subvalue_type == float:
205 elif subvalue_type == int:
207 elif subvalue_type == bool:
209 elif subvalue_type == str:
211 elif hasattr(subvalue, "__len__"):
212 for sub_item in subvalue[:]:
213 if isinstance(sub_item, bpy_struct_type):
214 subitem_id = getattr(sub_item, "id_data", Ellipsis)
215 if subitem_id == subvalue_id:
216 yield from id_walk(sub_item, value)
218 if subvalue_type.__name__ in property_typemap:
219 yield from id_walk(subvalue, value)
224 1, 0.1, -1, # float("nan"),
225 "", "test", b"", b"test",
227 (10,), (10, 20), (0, 0, 0),
228 {0: "", 1: "hello", 2: "test"}, {"": 0, "hello": 1, "test": 2},
229 set(), {"", "test", "."}, {None, ..., type},
230 range(10), (" " * i for i in range(10)),
234 for attr in dir(bpy.data):
235 if attr == "window_managers":
237 seq = getattr(bpy.data, attr)
238 if seq.__class__.__name__ == 'bpy_prop_collection':
240 for val, prop, tp in id_walk(id_data, bpy.data):
242 for val_rnd in _random_values:
244 setattr(val, prop, val_rnd)
249 def run_ops(operators, setup_func=None, reset=True):
250 print("\ncontext:", setup_func.__name__)
253 for op_id, op in operators:
255 print(" operator: %4d, %s" % (STATE["counter"], op_id))
256 STATE["counter"] += 1
257 sys.stdout.flush() # in case of crash
259 # disable will get blender in a bad state and crash easy!
264 if random.random() < (1.0 - RANDOM_RESET):
275 # we can't be sure it will work
283 for mode in {'EXEC_DEFAULT', 'INVOKE_DEFAULT'}:
288 # traceback.print_exc()
299 # we can't be sure it will work
309 def ctx_clear_scene(): # copied from batch_import.py
310 bpy.ops.wm.read_factory_settings(use_empty=True)
313 def ctx_editmode_mesh():
314 bpy.ops.object.mode_set(mode='EDIT')
317 def ctx_editmode_mesh_extra():
318 bpy.ops.object.vertex_group_add()
319 bpy.ops.object.shape_key_add(from_mix=False)
320 bpy.ops.object.shape_key_add(from_mix=True)
321 bpy.ops.mesh.uv_texture_add()
322 bpy.ops.mesh.vertex_color_add()
323 bpy.ops.object.material_slot_add()
325 bpy.ops.object.mode_set(mode='EDIT')
328 def ctx_editmode_mesh_empty():
329 bpy.ops.object.mode_set(mode='EDIT')
330 bpy.ops.mesh.select_all(action='SELECT')
331 bpy.ops.mesh.delete()
334 def ctx_editmode_curves():
335 bpy.ops.curve.primitive_nurbs_circle_add()
336 bpy.ops.object.mode_set(mode='EDIT')
339 def ctx_editmode_curves_empty():
340 bpy.ops.curve.primitive_nurbs_circle_add()
341 bpy.ops.object.mode_set(mode='EDIT')
342 bpy.ops.curve.select_all(action='SELECT')
343 bpy.ops.curve.delete(type='VERT')
346 def ctx_editmode_surface():
347 bpy.ops.surface.primitive_nurbs_surface_torus_add()
348 bpy.ops.object.mode_set(mode='EDIT')
351 def ctx_editmode_mball():
352 bpy.ops.object.metaball_add()
353 bpy.ops.object.mode_set(mode='EDIT')
356 def ctx_editmode_text():
357 bpy.ops.object.text_add()
358 bpy.ops.object.mode_set(mode='EDIT')
361 def ctx_editmode_armature():
362 bpy.ops.object.armature_add()
363 bpy.ops.object.mode_set(mode='EDIT')
366 def ctx_editmode_armature_empty():
367 bpy.ops.object.armature_add()
368 bpy.ops.object.mode_set(mode='EDIT')
369 bpy.ops.armature.select_all(action='SELECT')
370 bpy.ops.armature.delete()
373 def ctx_editmode_lattice():
374 bpy.ops.object.add(type='LATTICE')
375 bpy.ops.object.mode_set(mode='EDIT')
376 # bpy.ops.object.vertex_group_add()
379 def ctx_object_empty():
380 bpy.ops.object.add(type='EMPTY')
383 def ctx_object_pose():
384 bpy.ops.object.armature_add()
385 bpy.ops.object.mode_set(mode='POSE')
386 bpy.ops.pose.select_all(action='SELECT')
389 def ctx_object_paint_weight():
390 bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
393 def ctx_object_paint_vertex():
394 bpy.ops.object.mode_set(mode='VERTEX_PAINT')
397 def ctx_object_paint_sculpt():
398 bpy.ops.object.mode_set(mode='SCULPT')
401 def ctx_object_paint_texture():
402 bpy.ops.object.mode_set(mode='TEXTURE_PAINT')
405 def bpy_check_type_duplicates():
406 # non essential sanity check
407 bl_types = dir(bpy.types)
408 bl_types_unique = set(bl_types)
410 if len(bl_types) != len(bl_types_unique):
411 print("Error, found duplicates in 'bpy.types'")
412 for t in sorted(bl_types_unique):
413 tot = bl_types.count(t)
415 print(" '%s', %d" % (t, tot))
422 bpy_check_type_duplicates()
427 for mod_name in dir(bpy.ops):
428 mod = getattr(bpy.ops, mod_name)
429 for submod_name in dir(mod):
430 op = getattr(mod, submod_name)
431 operators.append(("%s.%s" % (mod_name, submod_name), op))
433 operators.sort(key=lambda op: op[0])
435 filter_op_list(operators)
437 # for testing, mix the list up.
438 # operators.reverse()
442 random.seed(RANDOM_SEED[0])
443 operators = operators * RANDOM_MULTIPLY
444 random.shuffle(operators)
446 # 2 passes, first just run setup_func to make sure they are ok
447 for operators_test in ((), operators):
448 # Run the operator tests in different contexts
449 run_ops(operators_test, setup_func=lambda: None)
454 run_ops(operators_test, setup_func=ctx_clear_scene)
456 run_ops(operators_test, setup_func=ctx_object_empty)
457 run_ops(operators_test, setup_func=ctx_object_pose)
458 run_ops(operators_test, setup_func=ctx_object_paint_weight)
459 run_ops(operators_test, setup_func=ctx_object_paint_vertex)
460 run_ops(operators_test, setup_func=ctx_object_paint_sculpt)
461 run_ops(operators_test, setup_func=ctx_object_paint_texture)
463 run_ops(operators_test, setup_func=ctx_editmode_mesh)
464 run_ops(operators_test, setup_func=ctx_editmode_mesh_extra)
465 run_ops(operators_test, setup_func=ctx_editmode_mesh_empty)
467 run_ops(operators_test, setup_func=ctx_editmode_armature)
468 run_ops(operators_test, setup_func=ctx_editmode_armature_empty)
470 run_ops(operators_test, setup_func=ctx_editmode_curves)
471 run_ops(operators_test, setup_func=ctx_editmode_curves_empty)
472 run_ops(operators_test, setup_func=ctx_editmode_surface)
474 run_ops(operators_test, setup_func=ctx_editmode_mball)
475 run_ops(operators_test, setup_func=ctx_editmode_text)
476 run_ops(operators_test, setup_func=ctx_editmode_lattice)
478 if not operators_test:
479 print("All setup functions run fine!")
481 print("Finished %r" % __file__)
484 if __name__ == "__main__":
485 # ~ for i in range(200):
486 # ~ RANDOM_SEED[0] += 1