[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [61281] trunk/lib/tests/modeling: Added regression test for bevel.
Howard Trickey
howard.trickey at gmail.com
Sun Feb 2 18:37:57 CET 2014
Revision: 61281
https://developer.blender.org/rBL61281
Author: howardt
Date: 2014-02-02 17:37:56 +0000 (Sun, 02 Feb 2014)
Log Message:
-----------
Added regression test for bevel.
The tests are run by executing the script run_tests
which is in the Text Editor.
That depends on mesh_ops_test.py, which is a general
framework useful for mesh ops testing: given a spec
of a 'before' and 'after' object, what mesh elements
to select, a mesh op to run, and its parameters,
it will check that running the operator on the 'before'
mesh yields the 'after' one.
Added Paths:
-----------
trunk/lib/tests/modeling/bevel_regression.blend
trunk/lib/tests/modeling/mesh_ops_test.py
Added: trunk/lib/tests/modeling/bevel_regression.blend
===================================================================
(Binary files differ)
Property changes on: trunk/lib/tests/modeling/bevel_regression.blend
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Added: trunk/lib/tests/modeling/mesh_ops_test.py
===================================================================
--- trunk/lib/tests/modeling/mesh_ops_test.py (rev 0)
+++ trunk/lib/tests/modeling/mesh_ops_test.py 2014-02-02 17:37:56 UTC (rev 61281)
@@ -0,0 +1,285 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+# A framework to run regression tests on mesh operators
+#
+# General idea:
+# A test is:
+# Object mode
+# Select <test object>
+# Duplicate the object
+# Edit mode
+# Select None
+# Select according to <select spec>
+# Apply <mesh operator> with <test params>
+# Object mode
+# test_mesh = <test object>.data
+# run test_mesh.validate()
+# run test_mesh.unit_test_compare(<expected object>.data)
+# delete the duplicate object
+#
+# The things in angle brackets are parameters of the test, and are specified in
+# a declarative TestSpec.
+# We need to run this in a blend file that has the given <test object> and <expect object>,
+# with identical locations (will probably be convenient to put these on separate layers).
+
+import bpy
+
+
+class TestSpec:
+ """Test specification.
+
+ Holds names of test and expected result objects,
+ a selection specification,
+ a mesh op to run and its arguments
+ """
+
+ def __init__(self, op, test_obj, expected_obj, select, params):
+ """Construct a test spec.
+
+ Args:
+ op: string - name of a function in bpy.ops.mesh
+ test_obj: string - name of the object to apply the test to
+ expected_obj: string - name of the object that has the expected result
+ select: string - should be V, E, or F followed by space seperated indices of desired selection
+ params: string - space-separated name=val pairs giving operator arguments
+ """
+
+ self.test_obj = test_obj
+ self.expected_obj = expected_obj
+ self.select = select
+ self.op = op
+ self.params = params
+
+ def __str__(self):
+ return self.op + "(" + self.params + ") on " + self.test_obj + " selecting " + self.select
+
+ def ParseParams(self, verbose=False):
+ """Parse a space-separated list of name=value pairs.
+
+ Args:
+ self: TestSpec
+ Returns:
+ dict - the parsed self.params
+ """
+
+ ans = {}
+ nvs = self.params.split()
+ for nv in nvs:
+ parts = nv.split('=')
+ if len(parts) != 2:
+ if verbose:
+ print('Parameter syntax error at', nv)
+ break
+ name = parts[0]
+ try:
+ val = eval(parts[1])
+ except SyntaxError:
+ if verbose:
+ print('Parameter value syntax error at', nv)
+ break
+ ans[name] = val
+ return ans
+
+
+# This only works if there is a 3D View area visible somewhere in current window
+def GetView3DContext(verbose=False):
+ """Get a context dictionary for a View3D window.
+
+ This can be used as a context override to ensure that an operator will
+ be executed with a View3D window and other variables implied by that context.
+
+ Args:
+ verbose: bool - should we be wordy about errors
+ Returns:
+ dict - with keys for window, screen, area, region, and scene
+ """
+
+ win = bpy.context.window
+ scr = win.screen
+ a3d = None
+ for a in scr.areas:
+ if a.type == 'VIEW_3D':
+ a3d = a
+ break
+ if a3d is None:
+ if verbose:
+ print('No 3d view area')
+ return None
+ rwin = None
+ for r in a3d.regions:
+ if r.type == 'WINDOW':
+ rwin = r
+ break
+ if rwin is None:
+ if verbose:
+ print('No window in 3d view area')
+ return None
+ return {'window': win, 'screen': scr, 'area': a3d, 'region': rwin, 'scene': bpy.context.scene}
+
+
+def DoMeshSelect(select, verbose=False):
+ """Given a selection spec string, switch to the desired selection mode and do the select.
+
+ Assume we are in Object mode on a mesh object.
+ The selection spec string should start with V, E, or F (for vertex, edge, face) to choose
+ the kind of element selected, and the is followed with a list of element indices
+ (use --debug to Blender to visualize these).
+
+ Args:
+ select: string - see comment above for syntax
+ verbose: bool - should we be wordy
+ Returns:
+ string - which select_mode to switch to
+ """
+
+ m = bpy.context.active_object.data
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.select_all(action='DESELECT')
+ bpy.ops.object.mode_set(mode='OBJECT')
+ if m.is_editmode:
+ if verbose:
+ print('Select failed, not in Object mode')
+ return 'VERT'
+ if not select:
+ if verbose:
+ print('No select spec')
+ return 'VERT'
+ if select[0] == 'V':
+ seltype = 'VERT'
+ elif select[0] == 'E':
+ seltype = 'EDGE'
+ elif select[0] == 'F':
+ seltype = 'FACE'
+ else:
+ if verbose:
+ print('Bad select type', select[0])
+ return 'VERT'
+ parts = select[1:].split()
+ try:
+ elems = set([int(p) for p in parts])
+ except ValueError:
+ if verbose:
+ print('Bad syntax in select spec', select)
+ return
+ for i in elems:
+ if seltype == 'VERT':
+ m.vertices[i].select = True
+ elif seltype == 'EDGE':
+ m.edges[i].select = True
+ else:
+ # for faces, select all the edges around the face
+ for j in m.polygons[i].loop_indices:
+ ei = m.loops[j].edge_index
+ m.edges[ei].select = True
+ return seltype
+
+
+def RunTest(t, cleanup=True, verbose=False):
+ """Run the test specified by given TestSpec.
+
+ Args:
+ t: TestSpec
+ cleanup: bool - should we clean up duplicate after the test
+ verbose: bool - should be we wordy
+ Returns:
+ bool - True if test passes, False otherwise
+ """
+
+ if verbose:
+ print("Run test:", t)
+ objs = bpy.data.objects
+ if t.test_obj in objs:
+ otest = objs[t.test_obj]
+ else:
+ if verbose:
+ print('No test object', t.test_obj)
+ return False
+ bpy.ops.object.mode_set(mode='OBJECT')
+ bpy.ops.object.select_all(action='DESELECT')
+ bpy.context.scene.objects.active = otest
+ otest.select = True
+ bpy.ops.object.duplicate()
+ otestdup = bpy.context.active_object
+ smode = DoMeshSelect(t.select, verbose=verbose)
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.select_mode(type=smode)
+ f = getattr(bpy.ops.mesh, t.op)
+ if not f:
+ if verbose:
+ print('No mesh op', t.op)
+ if cleanup:
+ bpy.ops.object.delete()
+ return False
+ kw = t.ParseParams(verbose)
+ retval = f(**kw)
+ if retval != {'FINISHED'}:
+ if verbose:
+ print('unexpected operator return value', retval)
+ if cleanup:
+ bpy.ops.object.delete()
+ return False
+ bpy.ops.object.mode_set(mode='OBJECT')
+ if t.expected_obj in objs:
+ oexpected = objs[t.expected_obj]
+ else:
+ # If no expected object, test is run just for effect
+ if verbose:
+ print('No expected object', t.expected_obj)
+ return True
+ mtest = otestdup.data
+ mexpected = oexpected.data
+ cmpret = mtest.unit_test_compare(mexpected)
+ success = (cmpret == 'Same')
+ if success:
+ if verbose:
+ print('Success')
+ else:
+ if verbose:
+ print('Fail', cmpret)
+ if cleanup:
+ bpy.ops.object.delete()
+ otest.select = True
+ bpy.context.scene.objects.active = otest
+ return success
+
+
+def RunAllTests(tests, cleanup=True, verbose=False):
+ """Run all tests.
+
+ Args:
+ tests: list of TestSpec - tests to run
+ cleanup: bool - if True, don't leave result objects lying around
+ verbose: bool - if True, chatter about running tests and failures
+ Returns:
+ bool - True if all tests pass
+ """
+
+ tot = 0
+ failed = 0
+ for t in tests:
+ tot += 1
+ if not RunTest(t, cleanup=cleanup, verbose=verbose):
+ failed += 1
+ if verbose:
+ print('Ran', tot, 'tests,' if tot > 1 else 'test,', failed, 'failed')
+ if failed > 0:
+ print('Tests Failed')
+ return failed == 0
More information about the Bf-blender-cvs
mailing list