Merge branch 'blender-v2.83-release'

This commit is contained in:
Brecht Van Lommel 2020-04-28 13:02:06 +02:00
commit 8da80e7771
21 changed files with 219 additions and 120 deletions

@ -87,8 +87,10 @@ def context_path_validate(context, data_path):
# One of the items in the rna path is None, just ignore this # One of the items in the rna path is None, just ignore this
value = Ellipsis value = Ellipsis
else: else:
# We have a real error in the rna path, don't ignore that # Print invalid path, but don't show error to the users and fully
raise # break the UI if the operator is bound to an event like left click.
print("context_path_validate error: context.%s not found (invalid keymap entry?)" % data_path)
value = Ellipsis
return value return value

@ -39,7 +39,8 @@ unset(_default_test_python_exe)
# set(TEST_BLENDER_EXE valgrind --track-origins=yes --error-limit=no ${TEST_BLENDER_EXE}) # set(TEST_BLENDER_EXE valgrind --track-origins=yes --error-limit=no ${TEST_BLENDER_EXE})
# Standard Blender arguments for running tests. # Standard Blender arguments for running tests.
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup) # Specify exit code so that if a Python script error happens, the test fails.
set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --python-exit-code 1)
# Python CTests # Python CTests
if(WITH_BLENDER AND WITH_PYTHON) if(WITH_BLENDER AND WITH_PYTHON)

@ -184,6 +184,22 @@ add_blender_test(
--run-all-tests --run-all-tests
) )
add_blender_test(
physics_cloth
${TEST_SRC_DIR}/physics/cloth_test.blend
--python ${TEST_PYTHON_DIR}/physics_cloth.py
--
--run-all-tests
)
add_blender_test(
physics_softbody
${TEST_SRC_DIR}/physics/softbody_test.blend
--python ${TEST_PYTHON_DIR}/physics_softbody.py
--
--run-all-tests
)
add_blender_test( add_blender_test(
constraints constraints
--python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py --python ${CMAKE_CURRENT_LIST_DIR}/bl_constraints.py

@ -176,9 +176,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -375,12 +375,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
import traceback main()
# So a python error exits Blender itself too
try:
main()
except SystemExit:
raise
except:
traceback.print_exc()
sys.exit(1)

@ -77,9 +77,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -75,9 +75,4 @@ def main():
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -21,7 +21,7 @@
# Test that modules we ship with our Python installation are available # Test that modules we ship with our Python installation are available
import bz2 import bz2
import cffi import ctypes
import lzma import lzma
import numpy import numpy
import sqlite3 import sqlite3

@ -255,12 +255,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
import traceback main()
# So a python error exits Blender itself too
try:
main()
except SystemExit:
raise
except:
traceback.print_exc()
sys.exit(1)

@ -144,11 +144,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main()
# So a python error exits(1)
try:
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -234,10 +234,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
# So a python error exits(1) main()
try:
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -842,17 +842,7 @@ if __name__ == "__main__":
print("Load Handler:", bpy.data.filepath) print("Load Handler:", bpy.data.filepath)
if load_handler.first is False: if load_handler.first is False:
bpy.app.handlers.scene_update_post.remove(load_handler) bpy.app.handlers.scene_update_post.remove(load_handler)
try: main()
main()
import sys
sys.exit(0)
except:
import traceback
traceback.print_exc()
# import sys
# sys.exit(1) # comment to debug
else: else:
load_handler.first = False load_handler.first = False

@ -152,10 +152,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
# So a python error exits(1) main()
try:
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -20,7 +20,6 @@ import bpy
import sys import sys
import os import os
import tempfile import tempfile
import traceback
import inspect import inspect
from bpy.types import UIList from bpy.types import UIList
@ -331,11 +330,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.stderr.flush()
os._exit(1)

@ -136,12 +136,7 @@ def main():
print(" Running: '%s'" % run) print(" Running: '%s'" % run)
print(" MD5: '%s'!" % md5) print(" MD5: '%s'!" % md5)
try: result = eval(run)
result = eval(run)
except:
import traceback
traceback.print_exc()
sys.exit(1)
if write_blend is not None: if write_blend is not None:
print(" Writing Blend: %s" % write_blend) print(" Writing Blend: %s" % write_blend)
@ -188,10 +183,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
# So a python error exits(1) main()
try:
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -60,9 +60,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -255,9 +255,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -73,6 +73,28 @@ class ModifierSpec:
" with parameters: " + str(self.modifier_parameters) " with parameters: " + str(self.modifier_parameters)
class PhysicsSpec:
"""
Holds one Physics modifier and its parameters.
"""
def __init__(self, modifier_name: str, modifier_type: str, modifier_parameters: dict, frame_end: int):
"""
Constructs a physics spec.
:param modifier_name: str - name of object modifier, e.g. "Cloth"
:param modifier_type: str - type of object modifier, e.g. "CLOTH"
:param modifier_parameters: dict - {name : val} dictionary giving modifier parameters, e.g. {"quality" : 4}
:param frame_end:int - the last frame of the simulation at which it is baked
"""
self.modifier_name = modifier_name
self.modifier_type = modifier_type
self.modifier_parameters = modifier_parameters
self.frame_end = frame_end
def __str__(self):
return "Physics Modifier: " + self.modifier_name + " of type " + self.modifier_type + \
" with parameters: " + str(self.modifier_parameters) + " with frame end: " + str(self.frame_end)
class OperatorSpec: class OperatorSpec:
""" """
Holds one operator and its parameters. Holds one operator and its parameters.
@ -105,7 +127,7 @@ class MeshTest:
the public method run_test(). the public method run_test().
""" """
def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False): def __init__(self, test_object_name: str, expected_object_name: str, operations_stack=None, apply_modifiers=False, threshold=None):
""" """
Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name Constructs a MeshTest object. Raises a KeyError if objects with names expected_object_name
or test_object_name don't exist. or test_object_name don't exist.
@ -125,6 +147,7 @@ class MeshTest:
type(operation))) type(operation)))
self.operations_stack = operations_stack self.operations_stack = operations_stack
self.apply_modifier = apply_modifiers self.apply_modifier = apply_modifiers
self.threshold = threshold
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
self.update = os.getenv('BLENDER_TEST_UPDATE') is not None self.update = os.getenv('BLENDER_TEST_UPDATE') is not None
@ -235,6 +258,49 @@ class MeshTest:
if self.apply_modifier: if self.apply_modifier:
bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name) bpy.ops.object.modifier_apply(modifier=modifier_spec.modifier_name)
def _bake_current_simulation(self, obj, test_mod_type, test_mod_name, frame_end):
for scene in bpy.data.scenes:
for modifier in obj.modifiers:
if modifier.type == test_mod_type:
obj.modifiers[test_mod_name].point_cache.frame_end = frame_end
override = {'scene': scene, 'active_object': obj, 'point_cache': modifier.point_cache}
bpy.ops.ptcache.bake(override, bake=True)
break
def _apply_physics_settings(self, test_object, physics_spec: PhysicsSpec):
"""
Apply Physics settings to test objects.
"""
scene = bpy.context.scene
scene.frame_set(1)
modifier = test_object.modifiers.new(physics_spec.modifier_name,
physics_spec.modifier_type)
physics_setting = modifier.settings
if self.verbose:
print("Created modifier '{}' of type '{}'.".
format(physics_spec.modifier_name, physics_spec.modifier_type))
for param_name in physics_spec.modifier_parameters:
try:
setattr(physics_setting, param_name, physics_spec.modifier_parameters[param_name])
if self.verbose:
print("\t set parameter '{}' with value '{}'".
format(param_name, physics_spec.modifier_parameters[param_name]))
except AttributeError:
# Clean up first
bpy.ops.object.delete()
raise AttributeError("Modifier '{}' has no parameter named '{}'".
format(physics_spec.modifier_type, param_name))
scene.frame_set(physics_spec.frame_end + 1)
self._bake_current_simulation(test_object, physics_spec.modifier_type, physics_spec.modifier_name, physics_spec.frame_end)
if self.apply_modifier:
bpy.ops.object.modifier_apply(modifier=physics_spec.modifier_name)
def _apply_operator(self, test_object, operator: OperatorSpec): def _apply_operator(self, test_object, operator: OperatorSpec):
""" """
Apply operator on test object. Apply operator on test object.
@ -302,9 +368,12 @@ class MeshTest:
elif isinstance(operation, OperatorSpec): elif isinstance(operation, OperatorSpec):
self._apply_operator(evaluated_test_object, operation) self._apply_operator(evaluated_test_object, operation)
elif isinstance(operation, PhysicsSpec):
self._apply_physics_settings(evaluated_test_object, operation)
else: else:
raise ValueError("Expected operation of type {} or {}. Got {}". raise ValueError("Expected operation of type {} or {} or {}. Got {}".
format(type(ModifierSpec), type(OperatorSpec), format(type(ModifierSpec), type(OperatorSpec), type(PhysicsSpec),
type(operation))) type(operation)))
# Compare resulting mesh with expected one. # Compare resulting mesh with expected one.
@ -312,7 +381,10 @@ class MeshTest:
print("Comparing expected mesh with resulting mesh...") print("Comparing expected mesh with resulting mesh...")
evaluated_test_mesh = evaluated_test_object.data evaluated_test_mesh = evaluated_test_object.data
expected_mesh = self.expected_object.data expected_mesh = self.expected_object.data
compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh) if self.threshold:
compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh, threshold=self.threshold)
else:
compare_result = evaluated_test_mesh.unit_test_compare(mesh=expected_mesh)
compare_success = (compare_result == 'Same') compare_success = (compare_result == 'Same')
# Also check if invalid geometry (which is never expected) had to be corrected... # Also check if invalid geometry (which is never expected) had to be corrected...
@ -434,7 +506,7 @@ class ModifierTest:
>>> modifiers_test.run_all_tests() >>> modifiers_test.run_all_tests()
""" """
def __init__(self, modifier_tests: list, apply_modifiers=False): def __init__(self, modifier_tests: list, apply_modifiers=False, threshold=None):
""" """
Construct a modifier test. Construct a modifier test.
:param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following :param modifier_tests: list - list of modifier test cases. Each element in the list must contain the following
@ -445,6 +517,7 @@ class ModifierTest:
""" """
self.modifier_tests = modifier_tests self.modifier_tests = modifier_tests
self.apply_modifiers = apply_modifiers self.apply_modifiers = apply_modifiers
self.threshold = threshold
self.verbose = os.environ.get("BLENDER_VERBOSE") is not None self.verbose = os.environ.get("BLENDER_VERBOSE") is not None
self._failed_tests_list = [] self._failed_tests_list = []
@ -461,7 +534,7 @@ class ModifierTest:
expected_object_name = case[1] expected_object_name = case[1]
spec_list = case[2] spec_list = case[2]
test = MeshTest(test_object_name, expected_object_name) test = MeshTest(test_object_name, expected_object_name, threshold=self.threshold)
if self.apply_modifiers: if self.apply_modifiers:
test.apply_modifier = True test.apply_modifier = True

@ -163,10 +163,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
try: main()
main()
except:
import traceback
traceback.print_exc()
sys.exit(1)

@ -0,0 +1,51 @@
# ##### 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>
import os
import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from modules.mesh_test import ModifierTest, PhysicsSpec
def main():
test = [
["testCloth", "expectedCloth",
[PhysicsSpec('Cloth', 'CLOTH', {'quality': 5}, 35)]],
]
cloth_test = ModifierTest(test, threshold=1e-3)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
cloth_test.apply_modifiers = True
cloth_test.run_all_tests()
break
elif cmd == "--run-test":
cloth_test.apply_modifiers = False
index = int(command[i + 1])
cloth_test.run_test(index)
break
if __name__ == "__main__":
main()

@ -0,0 +1,51 @@
# ##### 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>
import os
import sys
import bpy
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
from modules.mesh_test import ModifierTest, PhysicsSpec
def main():
test = [
["testSoftBody", "expectedSoftBody",
[PhysicsSpec('Softbody', 'SOFT_BODY', {'use_goal': False, 'bend': 8, 'pull': 0.8, 'push': 0.8}, 45)]],
]
softBody_test = ModifierTest(test)
command = list(sys.argv)
for i, cmd in enumerate(command):
if cmd == "--run-all-tests":
softBody_test.apply_modifiers = True
softBody_test.run_all_tests()
break
elif cmd == "--run-test":
softBody_test.apply_modifiers = False
index = int(command[i + 1])
softBody_test.run_test(index)
break
if __name__ == "__main__":
main()