Added CTest to run most operators in background mode under various conditions to...
[blender.git] / source / tests / bl_run_operators.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 # <pep8 compliant>
20
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.
23 #
24 # only error checked for here is a segfault.
25
26 import bpy
27 import sys
28
29 op_blacklist = (
30     "script.reload",
31     "script.reload",
32     "export*.*",
33     "import*.*",
34     "*.save_*",
35     "*.read_*",
36     "*.open_*",
37     "*.link_append",
38     "render.render",
39     "*.*_export",
40     "*.*_import",
41     "wm.url_open",
42     "wm.doc_view",
43     "wm.path_open",
44     "help.operator_cheat_sheet",
45     )
46
47
48 def filter_op_list(operators):
49     from fnmatch import fnmatchcase
50
51     def is_op_ok(op):
52         for op_match in op_blacklist:
53             if fnmatchcase(op, op_match):
54                 print("    skipping: %s (%s)" % (op, op_match))
55                 return False
56         return True
57
58     operators[:] = [op for op in operators if is_op_ok(op[0])]
59
60
61 def run_ops(operators, setup_func=None):
62     print("\ncontext:", setup_func.__name__)
63     # first invoke
64     for op_id, op in operators:
65         if op.poll():
66             print("    operator:", op_id)
67             sys.stdout.flush()  # incase of crash
68
69             # disable will get blender in a bad state and crash easy!
70             bpy.ops.wm.read_factory_settings()
71
72             setup_func()
73             continue
74             for mode in ('EXEC_DEFAULT', 'INVOKE_DEFAULT'):
75                 try:
76                     op(mode)
77                     #print(" - pass")
78                 except:
79                     #print(" - fail")
80                     #import traceback
81                     #traceback.print_exc()
82                     pass
83
84
85 # contexts
86 def ctx_clear_scene():  # copied from batch_import.py
87     unique_obs = set()
88     for scene in bpy.data.scenes:
89         for obj in scene.objects[:]:
90             scene.objects.unlink(obj)
91             unique_obs.add(obj)
92
93     # remove obdata, for now only worry about the startup scene
94     for bpy_data_iter in (bpy.data.objects, bpy.data.meshes, bpy.data.lamps, bpy.data.cameras):
95         for id_data in bpy_data_iter:
96             bpy_data_iter.remove(id_data)
97
98
99 def ctx_editmode_mesh():
100     bpy.ops.object.mode_set(mode='EDIT')
101     bpy.ops.object.vertex_group_add()
102
103
104 def ctx_editmode_curves():
105     bpy.ops.curve.primitive_nurbs_circle_add()
106     bpy.ops.object.mode_set(mode='EDIT')
107
108
109 def ctx_editmode_surface():
110     bpy.ops.surface.primitive_nurbs_surface_torus_add()
111     bpy.ops.object.mode_set(mode='EDIT')
112
113
114 def ctx_editmode_mball():
115     bpy.ops.object.metaball_add()
116     bpy.ops.object.mode_set(mode='EDIT')
117
118
119 def ctx_editmode_mball():
120     bpy.ops.object.metaball_add()
121     bpy.ops.object.mode_set(mode='EDIT')
122
123
124 def ctx_editmode_text():
125     bpy.ops.object.text_add()
126     bpy.ops.object.mode_set(mode='EDIT')
127
128
129 def ctx_editmode_armature():
130     bpy.ops.object.armature_add()
131     bpy.ops.object.mode_set(mode='EDIT')
132     bpy.ops.armature.select_all(action='TOGGLE')
133     bpy.ops.armature.delete()
134
135
136 def ctx_editmode_lattice():
137     bpy.ops.object.add(type='LATTICE')
138     bpy.ops.object.mode_set(mode='EDIT')
139     # bpy.ops.object.vertex_group_add()
140
141
142 def ctx_object_empty():
143     bpy.ops.object.add(type='EMPTY')
144
145
146 def ctx_weightpaint():
147     bpy.ops.object.mode_set(mode='WEIGHT_PAINT')
148
149
150 def main():
151     # bpy.ops.wm.read_factory_settings()
152     import bpy
153     operators = []
154     for mod_name in dir(bpy.ops):
155         mod = getattr(bpy.ops, mod_name)
156         for submod_name in dir(mod):
157             op = getattr(mod, submod_name)
158             operators.append(("%s.%s" % (mod_name, submod_name), op))
159
160     operators.sort(key=lambda op: op[0])
161
162     filter_op_list(operators)
163
164     # for testing, mix the list up.
165     #operators.reverse()
166
167     #import random
168     #random.shuffle(operators)
169
170     # Run the operator tests in different contexts
171     run_ops(operators, setup_func=lambda: None)
172     run_ops(operators, setup_func=ctx_editmode_surface)
173     run_ops(operators, setup_func=ctx_object_empty)
174     run_ops(operators, setup_func=ctx_editmode_armature)
175     run_ops(operators, setup_func=ctx_editmode_mesh)
176     run_ops(operators, setup_func=ctx_clear_scene)
177     run_ops(operators, setup_func=ctx_editmode_curves)
178     run_ops(operators, setup_func=ctx_editmode_mball)
179     run_ops(operators, setup_func=ctx_editmode_text)
180     run_ops(operators, setup_func=ctx_weightpaint)
181     run_ops(operators, setup_func=ctx_editmode_lattice)
182
183     print("finished")
184
185 if __name__ == "__main__":
186     main()