Fix for memory leak caused by re-making mipmaps
[blender.git] / source / tests / bl_test.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 import sys
22 import os
23
24
25 # may split this out into a new file
26 def replace_bpy_app_version():
27     """ So MD5's are predictable from output which uses blenders versions.
28     """
29
30     import bpy
31
32     app = bpy.app
33     app_fake = type(bpy)("bpy.app")
34
35     for attr in dir(app):
36         if not attr.startswith("_"):
37             setattr(app_fake, attr, getattr(app, attr))
38
39     app_fake.version = 0, 0, 0
40     app_fake.version_string = "0.00 (sub 0)"
41     bpy.app = app_fake
42
43
44 def clear_startup_blend():
45     import bpy
46
47     for scene in bpy.data.scenes:
48         for obj in scene.objects:
49             scene.objects.unlink(obj)
50
51
52 def blend_to_md5():
53     import bpy
54     scene = bpy.context.scene
55     ROUND = 4
56
57     def matrix2str(matrix):
58         return "".join([str(round(axis, ROUND)) for vector in matrix for axis in vector]).encode('ASCII')
59
60     def coords2str(seq, attr):
61         return "".join([str(round(axis, ROUND)) for vertex in seq for axis in getattr(vertex, attr)]).encode('ASCII')
62
63     import hashlib
64
65     md5 = hashlib.new("md5")
66     md5_update = md5.update
67
68     for obj in scene.objects:
69         md5_update(matrix2str(obj.matrix_world))
70         data = obj.data
71
72         if type(data) == bpy.types.Mesh:
73             md5_update(coords2str(data.vertices, "co"))
74         elif type(data) == bpy.types.Curve:
75             for spline in data.splines:
76                 md5_update(coords2str(spline.bezier_points, "co"))
77                 md5_update(coords2str(spline.points, "co"))
78
79     return md5.hexdigest()
80
81
82 def main():
83     argv = sys.argv
84     print("  args:", " ".join(argv))
85     argv = argv[argv.index("--") + 1:]
86
87     def arg_extract(arg, optional=True, array=False):
88         arg += "="
89         if array:
90             value = []
91         else:
92             value = None
93
94         i = 0
95         while i < len(argv):
96             if argv[i].startswith(arg):
97                 item = argv[i][len(arg):]
98                 del argv[i]
99                 i -= 1
100
101                 if array:
102                     value.append(item)
103                 else:
104                     value = item
105                     break
106
107             i += 1
108
109         if (not value) and (not optional):
110             print("  '%s' not set" % arg)
111             sys.exit(1)
112
113         return value
114
115     run = arg_extract("--run", optional=False)
116     md5 = arg_extract("--md5", optional=False)
117     md5_method = arg_extract("--md5_method", optional=False)  # 'SCENE' / 'FILE'
118
119     # only when md5_method is 'FILE'
120     md5_source = arg_extract("--md5_source", optional=True, array=True)
121
122     # save blend file, for testing
123     write_blend = arg_extract("--write-blend", optional=True)
124
125     # ensure files are written anew
126     for f in md5_source:
127         if os.path.exists(f):
128             os.remove(f)
129
130     import bpy
131
132     replace_bpy_app_version()
133     if not bpy.data.filepath:
134         clear_startup_blend()
135
136     print("  Running: '%s'" % run)
137     print("  MD5: '%s'!" % md5)
138
139     try:
140         result = eval(run)
141     except:
142         import traceback
143         traceback.print_exc()
144         sys.exit(1)
145
146     if write_blend is not None:
147         print("  Writing Blend: %s" % write_blend)
148         bpy.ops.wm.save_mainfile(filepath=write_blend, check_existing=False)
149
150     print("  Result: '%s'" % str(result))
151     if not result:
152         print("  Running: %s -> False" % run)
153         sys.exit(1)
154
155     if md5_method == 'SCENE':
156         md5_new = blend_to_md5()
157     elif md5_method == 'FILE':
158         if not md5_source:
159             print("  Missing --md5_source argument")
160             sys.exit(1)
161
162         for f in md5_source:
163             if not os.path.exists(f):
164                 print("  Missing --md5_source=%r argument does not point to a file")
165                 sys.exit(1)
166
167         import hashlib
168
169         md5_instance = hashlib.new("md5")
170         md5_update = md5_instance.update
171
172         for f in md5_source:
173             filehandle = open(f, "rb")
174             md5_update(filehandle.read())
175             filehandle.close()
176
177         md5_new = md5_instance.hexdigest()
178
179     else:
180         print("  Invalid --md5_method=%s argument is not a valid source")
181         sys.exit(1)
182
183     if md5 != md5_new:
184         print("  Running: %s\n    MD5 Recieved: %s\n    MD5 Expected: %s" % (run, md5_new, md5))
185         sys.exit(1)
186
187     print("  Success: %s" % run)
188
189
190 if __name__ == "__main__":
191     # So a python error exits(1)
192     try:
193         main()
194     except:
195         import traceback
196         traceback.print_exc()
197         sys.exit(1)