Merge branch 'master' into blender2.8
[blender.git] / tests / python / bl_rna_defaults.py
1 # Apache License, Version 2.0
2
3 # ./blender.bin --background -noaudio --factory-startup --python tests/python/bl_rna_defaults.py
4
5 import bpy
6
7 DUMMY_NAME = "Untitled"
8 DUMMY_PATH = __file__
9 GLOBALS = {
10     "error_num": 0,
11 }
12
13
14 def as_float_32(f):
15     from struct import pack, unpack
16     return unpack("f", pack("f", f))[0]
17
18
19 def repr_float_precision(f, round_fn):
20     """
21     Get's the value which was most likely entered by a human in C.
22
23     Needed since Python will show trailing precision from a 32bit float.
24     """
25     f_round = round_fn(f)
26     f_str = repr(f)
27     f_str_frac = f_str.partition(".")[2]
28     if not f_str_frac:
29         return f_str
30     for i in range(1, len(f_str_frac)):
31         f_test = round(f, i)
32         f_test_round = round_fn(f_test)
33         if f_test_round == f_round:
34             return "%.*f" % (i, f_test)
35     return f_str
36
37
38 def repr_float_32(f):
39     return repr_float_precision(f, as_float_32)
40
41
42 def validate_defaults(test_id, o):
43
44     def warning(prop_id, val_real, val_default, *, repr_fn=repr):
45         print("Error %s: '%s.%s' is:%s, expected:%s" %
46               (test_id, o.__class__.__name__, prop_id,
47                repr_fn(val_real), repr_fn(val_default)))
48         GLOBALS["error_num"] += 1
49
50     properties = type(o).bl_rna.properties.items()
51     for prop_id, prop in properties:
52         if prop_id == "rna_type":
53             continue
54         prop_type = prop.type
55         if prop_type in {'STRING', 'COLLECTION'}:
56             continue
57
58         if prop_type == 'POINTER':
59             # traverse down pointers if they're set
60             val_real = getattr(o, prop_id)
61             if (val_real is not None) and (not isinstance(val_real, bpy.types.ID)):
62                 validate_defaults("%s.%s" % (test_id, prop_id), val_real)
63         elif prop_type in {'INT', 'BOOL'}:
64             # array_length = prop.array_length
65             if not prop.is_array:
66                 val_real = getattr(o, prop_id)
67                 val_default = prop.default
68                 if val_real != val_default:
69                     warning(prop_id, val_real, val_default)
70             else:
71                 pass  # TODO, array defaults
72         elif prop_type == 'FLOAT':
73             # array_length = prop.array_length
74             if not prop.is_array:
75                 val_real = getattr(o, prop_id)
76                 val_default = prop.default
77                 if val_real != val_default:
78                     warning(prop_id, val_real, val_default, repr_fn=repr_float_32)
79             else:
80                 pass  # TODO, array defaults
81         elif prop_type == 'ENUM':
82             val_real = getattr(o, prop_id)
83             if prop.is_enum_flag:
84                 val_default = prop.default_flag
85             else:
86                 val_default = prop.default
87             if val_real != val_default:
88                 warning(prop_id, val_real, val_default)
89
90         # print(prop_id, prop_type)
91
92
93 def _test_id_gen(data_attr, args_create=(DUMMY_NAME,), create_method="new"):
94     def test_gen(test_id):
95         id_collection = getattr(bpy.data, data_attr)
96         create_fn = getattr(id_collection, create_method)
97         o = create_fn(*args_create)
98         o.user_clear()
99         validate_defaults(test_id, o)
100         id_collection.remove(o)
101     return test_gen
102
103
104 test_Action = _test_id_gen("actions")
105 test_Armature = _test_id_gen("armatures")
106 test_Camera = _test_id_gen("cameras")
107 test_Group = _test_id_gen("groups")
108 test_Lattice = _test_id_gen("lattices")
109 test_LineStyle = _test_id_gen("linestyles")
110 test_Mask = _test_id_gen("masks")
111 test_Material = _test_id_gen("materials")
112 test_Mesh = _test_id_gen("meshes")
113 test_MetaBall = _test_id_gen("metaballs")
114 test_MovieClip = _test_id_gen("movieclips", args_create=(DUMMY_PATH,), create_method="load")
115 test_Object = _test_id_gen("objects", args_create=(DUMMY_NAME, None))
116 test_Palette = _test_id_gen("palettes")
117 test_Particle = _test_id_gen("particles")
118 test_Scene = _test_id_gen("scenes")
119 test_Sound = _test_id_gen("sounds", args_create=(DUMMY_PATH,), create_method="load")
120 test_Speaker = _test_id_gen("speakers")
121 test_Text = _test_id_gen("texts")
122 test_VectorFont = _test_id_gen("fonts", args_create=("<builtin>",), create_method="load")
123 test_World = _test_id_gen("worlds")
124
125 ns = globals()
126 for t in bpy.data.curves.bl_rna.functions["new"].parameters["type"].enum_items.keys():
127     ns["test_Curve_%s" % t] = _test_id_gen("curves", args_create=(DUMMY_NAME, t))
128 for t in bpy.data.lights.bl_rna.functions["new"].parameters["type"].enum_items.keys():
129     ns["test_Light_%s" % t] = _test_id_gen("lights", args_create=(DUMMY_NAME, t))
130 # types are a dynamic enum, have to hard-code.
131 for t in "ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree":
132     ns["test_NodeGroup_%s" % t] = _test_id_gen("node_groups", args_create=(DUMMY_NAME, t))
133 for t in bpy.data.textures.bl_rna.functions["new"].parameters["type"].enum_items.keys():
134     ns["test_Texture_%s" % t] = _test_id_gen("textures", args_create=(DUMMY_NAME, t))
135 del ns
136
137
138 def main():
139     for fn_id, fn_val in sorted(globals().items()):
140         if fn_id.startswith("test_") and callable(fn_val):
141             fn_val(fn_id)
142
143     print("Error (total): %d" % GLOBALS["error_num"])
144
145
146 if __name__ == "__main__":
147     main()