[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