diff options
author | Julian Eisel <julian@blender.org> | 2022-07-20 18:25:31 +0300 |
---|---|---|
committer | Julian Eisel <julian@blender.org> | 2022-07-20 18:25:31 +0300 |
commit | 9dbcefb10e53cc809eb2e99333376b2a881c0863 (patch) | |
tree | 9ea058c2877c472aba82650e24e5927e3f03eded /tests | |
parent | e1ced645fa208b3b77e07c99cb289cf7fa659ad3 (diff) | |
parent | 85f0b2ef5d5bfb67c245da0a52aeec44e63445fa (diff) |
Merge branch 'asset-browser-grid-view' into file-browser-grid-view
Diffstat (limited to 'tests')
42 files changed, 520 insertions, 103 deletions
diff --git a/tests/performance/tests/eevee.py b/tests/performance/tests/eevee.py new file mode 100644 index 00000000000..df7cac695da --- /dev/null +++ b/tests/performance/tests/eevee.py @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: Apache-2.0 + +import os +import enum +import time + + +class RecordStage(enum.Enum): + INIT = 0, + WAIT_SHADERS = 1, + WARMUP = 2, + RECORD = 3, + FINISHED = 4 + + +WARMUP_SECONDS = 3 +WARMUP_FRAMES = 10 +SHADER_FALLBACK_SECONDS = 60 +RECORD_PLAYBACK_ITER = 3 +LOG_KEY = "ANIMATION_PERFORMANCE: " + + +def _run(args): + import bpy + + global record_stage + record_stage = RecordStage.INIT + + bpy.app.handlers.frame_change_post.append(frame_change_handler) + bpy.ops.screen.animation_play() + + +def frame_change_handler(scene): + import bpy + + global record_stage + global start_time + global start_record_time + global start_warmup_time + global warmup_frame + global stop_record_time + global playback_iteration + + if record_stage == RecordStage.INIT: + screen = bpy.context.window_manager.windows[0].screen + bpy.context.scene.sync_mode = 'NONE' + + for area in screen.areas: + if area.type == 'VIEW_3D': + space = area.spaces[0] + space.shading.type = 'RENDERED' + space.overlay.show_overlays = False + + start_time = time.perf_counter() + record_stage = RecordStage.WAIT_SHADERS + + elif record_stage == RecordStage.WAIT_SHADERS: + shaders_compiled = False + if hasattr(bpy.app, 'is_job_running'): + shaders_compiled = not bpy.app.is_job_running("SHADER_COMPILATION") + else: + # Fallback when is_job_running doesn't exists by waiting for a time. + shaders_compiled = time.perf_counter() - start_time > SHADER_FALLBACK_SECONDS + + if shaders_compiled: + start_warmup_time = time.perf_counter() + warmup_frame = 0 + record_stage = RecordStage.WARMUP + + elif record_stage == RecordStage.WARMUP: + warmup_frame += 1 + if time.perf_counter() - start_warmup_time > WARMUP_SECONDS and warmup_frame > WARMUP_FRAMES: + start_record_time = time.perf_counter() + playback_iteration = 0 + scene = bpy.context.scene + scene.frame_set(scene.frame_start) + record_stage = RecordStage.RECORD + + elif record_stage == RecordStage.RECORD: + current_time = time.perf_counter() + scene = bpy.context.scene + if scene.frame_current == scene.frame_end: + playback_iteration += 1 + + if playback_iteration >= RECORD_PLAYBACK_ITER: + stop_record_time = current_time + record_stage = RecordStage.FINISHED + + elif record_stage == RecordStage.FINISHED: + bpy.ops.screen.animation_cancel() + num_frames = RECORD_PLAYBACK_ITER * ((scene.frame_end - scene.frame_start) + 1) + elapse_seconds = stop_record_time - start_record_time + avg_frame_time = elapse_seconds / num_frames + fps = 1.0 / avg_frame_time + print(f"{LOG_KEY}{{'time': {avg_frame_time}, 'fps': {fps} }}") + bpy.app.handlers.frame_change_post.remove(frame_change_handler) + bpy.ops.wm.quit_blender() + + +if __name__ == '__main__': + _run(None) + +else: + import api + + class EeveeTest(api.Test): + def __init__(self, filepath): + self.filepath = filepath + + def name(self): + return self.filepath.stem + + def category(self): + return "eevee" + + def run(self, env, device_id): + args = {} + _, log = env.run_in_blender(_run, args, [self.filepath], foreground=True) + for line in log: + if line.startswith(LOG_KEY): + result_str = line[len(LOG_KEY):] + result = eval(result_str) + return result + + raise Exception("No playback performance result found in log.") + + def generate(env): + filepaths = env.find_blend_files('eevee/*') + return [EeveeTest(filepath) for filepath in filepaths] diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 38c3fc4389a..d95f2cd2644 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -102,6 +102,11 @@ add_blender_test( ) add_blender_test( + script_pyapi_bpy_driver_secure_eval + --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_driver_secure_eval.py +) + +add_blender_test( script_pyapi_idprop --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py ) diff --git a/tests/python/alembic_export_tests.py b/tests/python/alembic_export_tests.py index f6c05d955c9..95ae3ee9feb 100644 --- a/tests/python/alembic_export_tests.py +++ b/tests/python/alembic_export_tests.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - """ Alembic Export Tests diff --git a/tests/python/batch_import.py b/tests/python/batch_import.py index a6b44bc7478..811b070b0ca 100644 --- a/tests/python/batch_import.py +++ b/tests/python/batch_import.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - """ Example Usage: diff --git a/tests/python/bl_alembic_io_test.py b/tests/python/bl_alembic_io_test.py index f3480380911..4cfda239bd1 100644 --- a/tests/python/bl_alembic_io_test.py +++ b/tests/python/bl_alembic_io_test.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - """ ./blender.bin --background -noaudio --factory-startup --python tests/python/bl_alembic_io_test.py -- --testdir /path/to/lib/tests/alembic """ diff --git a/tests/python/bl_animation_fcurves.py b/tests/python/bl_animation_fcurves.py index 449f17ebfec..931db3f2d22 100644 --- a/tests/python/bl_animation_fcurves.py +++ b/tests/python/bl_animation_fcurves.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - """ blender -b -noaudio --factory-startup --python tests/python/bl_animation_fcurves.py -- --testdir /path/to/lib/tests/animation """ diff --git a/tests/python/bl_blendfile_library_overrides.py b/tests/python/bl_blendfile_library_overrides.py index 1acc1e4d862..3ba99bd61e4 100644 --- a/tests/python/bl_blendfile_library_overrides.py +++ b/tests/python/bl_blendfile_library_overrides.py @@ -181,9 +181,125 @@ class TestLibraryTemplate(TestHelper, unittest.TestCase): assert(operation.operation == 'NOOP') +class TestLibraryOverridesResync(TestHelper, unittest.TestCase): + DATA_NAME_CONTAINER = "LibCollection" + DATA_NAME_RIGGED = "LibRigged" + DATA_NAME_RIG = "LibRig" + DATA_NAME_CONTROLLER_1 = "LibController1" + DATA_NAME_CONTROLLER_2 = "LibController2" + + def __init__(self, args): + self.args = args + + output_dir = pathlib.Path(self.args.output_dir) + self.ensure_path(str(output_dir)) + self.output_path = output_dir / "blendlib_overrides.blend" + self.test_output_path = output_dir / "blendlib_overrides_test.blend" + + bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True) + + collection_container = bpy.data.collections.new(TestLibraryOverridesResync.DATA_NAME_CONTAINER) + bpy.context.collection.children.link(collection_container) + + mesh = bpy.data.meshes.new(TestLibraryOverridesResync.DATA_NAME_RIGGED) + obj_child = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_RIGGED, object_data=mesh) + collection_container.objects.link(obj_child) + armature = bpy.data.armatures.new(TestLibraryOverridesResync.DATA_NAME_RIG) + obj_armature = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_RIG, object_data=armature) + obj_child.parent = obj_armature + collection_container.objects.link(obj_armature) + + obj_child_modifier = obj_child.modifiers.new("", 'ARMATURE') + obj_child_modifier.object = obj_armature + + obj_ctrl1 = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_1, object_data=None) + collection_container.objects.link(obj_ctrl1) + + obj_armature_constraint = obj_armature.constraints.new('COPY_LOCATION') + obj_armature_constraint.target = obj_ctrl1 + + collection_sub = bpy.data.collections.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2) + collection_container.children.link(collection_sub) + obj_ctrl2 = bpy.data.objects.new(TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2, object_data=None) + collection_sub.objects.link(obj_ctrl2) + + bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False) + + def test_link_and_override_resync(self): + bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True) + bpy.data.orphans_purge() + + link_dir = self.output_path / "Collection" + bpy.ops.wm.link( + directory=str(link_dir), + filename=TestLibraryOverridesResync.DATA_NAME_CONTAINER, + instance_collections=False, + ) + + linked_collection_container = bpy.data.collections[TestLibraryOverridesResync.DATA_NAME_CONTAINER] + assert(linked_collection_container.library is not None) + assert(linked_collection_container.override_library is None) + assert(len(bpy.data.collections) == 2) + assert(all(id_.library is not None for id_ in bpy.data.collections)) + assert(len(bpy.data.objects) == 4) + assert(all(id_.library is not None for id_ in bpy.data.objects)) + assert(len(bpy.data.meshes) == 1) + assert(all(id_.library is not None for id_ in bpy.data.meshes)) + assert(len(bpy.data.armatures) == 1) + assert(all(id_.library is not None for id_ in bpy.data.armatures)) + + override_collection_container = linked_collection_container.override_hierarchy_create( + bpy.context.scene, + bpy.context.view_layer, + ) + assert(override_collection_container.library is None) + assert(override_collection_container.override_library is not None) + # Objects and collections are duplicated as overrides, but meshes and armatures remain only linked data. + assert(len(bpy.data.collections) == 4) + assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.collections[:2])) + assert(len(bpy.data.objects) == 8) + assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.objects[:4])) + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.armatures) == 1) + + bpy.ops.wm.save_as_mainfile(filepath=str(self.test_output_path), check_existing=False, compress=False) + + # Re-open the lib file, and change its ID relationships. + bpy.ops.wm.open_mainfile(filepath=str(self.output_path)) + + obj_armature = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_RIG] + obj_armature_constraint = obj_armature.constraints[0] + obj_ctrl2 = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2] + obj_armature_constraint.target = obj_ctrl2 + + bpy.ops.wm.save_as_mainfile(filepath=str(self.output_path), check_existing=False, compress=False) + + # Re-open the main file, and check that automatic resync did its work correctly, remapping the target of the + # armature constraint to controller 2, without creating unexpected garbage IDs along the line. + bpy.ops.wm.open_mainfile(filepath=str(self.test_output_path)) + + override_collection_container = bpy.data.collections[TestLibraryOverridesResync.DATA_NAME_CONTAINER] + assert(override_collection_container.library is None) + assert(override_collection_container.override_library is not None) + # Objects and collections are duplicated as overrides, but meshes and armatures remain only linked data. + assert(len(bpy.data.collections) == 4) + assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.collections[:2])) + assert(len(bpy.data.objects) == 8) + assert(all((id_.library is None and id_.override_library is not None) for id_ in bpy.data.objects[:4])) + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.armatures) == 1) + + obj_armature = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_RIG] + obj_ctrl2 = bpy.data.objects[TestLibraryOverridesResync.DATA_NAME_CONTROLLER_2] + assert(obj_armature.library is None and obj_armature.override_library is not None) + assert(obj_ctrl2.library is None and obj_ctrl2.override_library is not None) + assert(obj_armature.constraints[0].target == obj_ctrl2) + + TESTS = ( TestLibraryOverrides, TestLibraryTemplate, + TestLibraryOverridesResync, ) diff --git a/tests/python/bl_bundled_modules.py b/tests/python/bl_bundled_modules.py index 4a055960f83..7728a2deb47 100644 --- a/tests/python/bl_bundled_modules.py +++ b/tests/python/bl_bundled_modules.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Test that modules we ship with our Python installation are available import bz2 diff --git a/tests/python/bl_keymap_completeness.py b/tests/python/bl_keymap_completeness.py index ee24a531f53..97335a94c01 100644 --- a/tests/python/bl_keymap_completeness.py +++ b/tests/python/bl_keymap_completeness.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # simple script to test 'bl_keymap_utils.keymap_hierarchy' contains correct values. # Needed for 'bl_keymap_utils.keymap_hierarchy' which inspects tools. diff --git a/tests/python/bl_keymap_validate.py b/tests/python/bl_keymap_validate.py index b87eed0c0df..83d41c8a9f6 100644 --- a/tests/python/bl_keymap_validate.py +++ b/tests/python/bl_keymap_validate.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # ./blender.bin --background -noaudio --factory-startup --python tests/python/bl_keymap_validate.py # diff --git a/tests/python/bl_load_addons.py b/tests/python/bl_load_addons.py index 8fee31f7a2b..b94c56541af 100644 --- a/tests/python/bl_load_addons.py +++ b/tests/python/bl_load_addons.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # simple script to enable all addons, and disable """ diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py index 4d4196d1f64..7ad5895ce86 100644 --- a/tests/python/bl_load_py_modules.py +++ b/tests/python/bl_load_py_modules.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # simple script to enable all addons, and disable """ diff --git a/tests/python/bl_mesh_modifiers.py b/tests/python/bl_mesh_modifiers.py index 3b653402083..640cf1c30f2 100644 --- a/tests/python/bl_mesh_modifiers.py +++ b/tests/python/bl_mesh_modifiers.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Currently this script only generates images from different modifier # combinations and does not validate they work correctly, # this is because we don't get 1:1 match with bmesh. diff --git a/tests/python/bl_mesh_validate.py b/tests/python/bl_mesh_validate.py index 9a9384ce777..9e093608406 100644 --- a/tests/python/bl_mesh_validate.py +++ b/tests/python/bl_mesh_validate.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Simple script to check mash validate code. # XXX Should be extended with many more "wrong cases"! diff --git a/tests/python/bl_pyapi_bpy_driver_secure_eval.py b/tests/python/bl_pyapi_bpy_driver_secure_eval.py new file mode 100644 index 00000000000..953dbcd5381 --- /dev/null +++ b/tests/python/bl_pyapi_bpy_driver_secure_eval.py @@ -0,0 +1,220 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_bpy_driver_secure_eval.py -- --verbose +import bpy +import unittest +import builtins +from types import ModuleType + + +# ----------------------------------------------------------------------------- +# Mock Environment + + +expect_unreachable_msg = "This function should _NEVER_ run!" +# Internal check, to ensure this actually runs as expected. +expect_unreachable_count = 0 + + +def expect_os_unreachable(): + global expect_unreachable_count + expect_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +__import__("os").expect_os_unreachable = expect_os_unreachable + + +expect_open_unreachable_count = 0 + + +def open_expect_unreachable(*args, **kwargs): + global expect_open_unreachable_count + expect_open_unreachable_count += 1 + raise Exception(expect_unreachable_msg) + + +mock_builtins = {**builtins.__dict__, **{"open": open_expect_unreachable}} + + +# ----------------------------------------------------------------------------- +# Utility Functions + + +def is_expression_secure(expr_str, verbose): + """ + Return (ok, code) where ok is true if expr_str is considered secure. + """ + # Internal function only for testing (not part of the public API). + from _bpy import _driver_secure_code_test + expr_code = compile(expr_str, "<is_expression_secure>", 'eval') + ok = _driver_secure_code_test(expr_code, verbose=verbose) + return ok, expr_code + + +# ----------------------------------------------------------------------------- +# Tests (Accept) + + +class _TestExprMixIn: + """ + Sub-classes must define: + - expressions_expect_secure: boolean, the expected secure state. + - expressions: A sequence of expressions that must evaluate in the driver name-space. + + Optionally: + - expressions_expect_unreachable: + A boolean, when true, it's expected each expression should call + ``expect_os_unreachable`` or ``expect_open_unreachable``. + """ + + # Sub-class may override. + expressions_expect_unreachable = False + + def assertSecure(self, expect_secure, expr_str): + is_secure, expr_code = is_expression_secure( + expr_str, + # Only verbose when secure as this is will result in an failure, + # in that case it's useful to know which op-codes caused the test to unexpectedly fail. + verbose=expect_secure, + ) + if is_secure != expect_secure: + raise self.failureException( + "Expression \"%s\" was expected to be %s" % + (expr_str, "secure" if expect_secure else "insecure")) + # NOTE: executing is not essential, it's just better to ensure the expressions make sense. + try: + exec( + expr_code, + {"__builtins__": mock_builtins}, + {**bpy.app.driver_namespace, **{"__builtins__": mock_builtins}}, + ) + # exec(expr_code, {}, bpy.app.driver_namespace) + ex = None + except BaseException as ex_test: + ex = ex_test + + if self.expressions_expect_unreachable: + if ex and ex.args == (expect_unreachable_msg,): + ex = None + elif not ex: + raise self.failureException("Expression \"%s\" failed to run `os.expect_os_unreachable`" % (expr_str,)) + else: + # An unknown exception was raised, use the exception below. + pass + + if ex: + raise self.failureException("Expression \"%s\" failed to evaluate with error: %r" % (expr_str, ex)) + + def test_expr(self): + expect_secure = self.expressions_expect_secure + for expr_str in self.expressions: + self.assertSecure(expect_secure, expr_str) + + +class TestExprMixIn_Accept(_TestExprMixIn): + expressions_expect_secure = True + + +class TestExprMixIn_Reject(_TestExprMixIn): + expressions_expect_secure = False + + +class TestAcceptLiteralNumbers(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("1", "1_1", "1.1", "1j", "0x1", "0o1", "0b1") + + +class TestAcceptLiteralStrings(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("''", "'_'", "r''", "r'_'", "'''_'''") + + +class TestAcceptSequencesEmpty(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("()", "[]", "{}", "[[]]", "(())") + + +class TestAcceptSequencesSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("('', '')", "['', '_']", "{'', '_'}", "{'': '_'}") + + +class TestAcceptSequencesExpand(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("(*('', '_'),)", "[*(), *[]]", "{*{1, 2}}") + + +class TestAcceptSequencesComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("[1, 2, 3][-1:0:-1][0]", "1 in (1, 2)", "False if 1 in {1, 2} else True") + + +class TestAcceptMathOperators(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("4 / 4", "4 * 4", "4 // 4", "2 ** 2", "4 ^ -1", "4 & 1", "4 % 1") + + +class TestAcceptMathFunctionsSimple(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("sin(pi)", "degrees(pi / 2)", "clamp(4, 0, 1)") + + +class TestAcceptMathFunctionsComplex(unittest.TestCase, TestExprMixIn_Accept): + expressions = ("-(sin(pi) ** 2) / 2", "floor(22 / 7)", "ceil(pi + 1)") + + +# ----------------------------------------------------------------------------- +# Tests (Reject) + +class TestRejectLiteralFStrings(unittest.TestCase, TestExprMixIn_Reject): + # F-String's are not supported as `BUILD_STRING` op-code is disabled, + # while it may be safe to enable that needs to be double-checked. + # Further it doesn't seem useful for typical math expressions used in drivers. + expressions = ("f''", "f'{1}'", "f'{\"_\"}'") + + +class TestRejectModuleAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_os_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "__import__('os').expect_os_unreachable()", + "exec(\"__import__('os').expect_os_unreachable()\")", + "(globals().update(__import__('os').__dict__), expect_os_unreachable())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_unreachable_count + + def tearDown(self): + count_actual = expect_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'os.expect_os_unreachable' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +class TestRejectOpenAccess(unittest.TestCase, TestExprMixIn_Reject): + # Each of these commands _must_ run `expect_open_unreachable`, + # and must also be rejected as insecure - otherwise we have problems. + expressions_expect_unreachable = True + expressions = ( + "open('file.txt', 'r')", + "exec(\"open('file.txt', 'r')\")", + "(globals().update({'fake_open': __builtins__['open']}), fake_open())", + ) + + # Ensure the functions are actually called. + def setUp(self): + self._count = expect_open_unreachable_count + + def tearDown(self): + count_actual = expect_open_unreachable_count - self._count + count_expect = len(self.expressions) + if count_actual != count_expect: + raise Exception( + "Expected 'open' to be called %d times but was called %d times" % + (count_expect, count_actual), + ) + + +if __name__ == '__main__': + import sys + sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) + unittest.main() diff --git a/tests/python/bl_rigging_symmetrize.py b/tests/python/bl_rigging_symmetrize.py index 6531b4df85f..10ba99ac6e9 100644 --- a/tests/python/bl_rigging_symmetrize.py +++ b/tests/python/bl_rigging_symmetrize.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - """ blender -b -noaudio --factory-startup --python tests/python/bl_rigging_symmetrize.py -- --testdir /path/to/lib/tests/animation """ @@ -42,7 +40,7 @@ def check_loc_rot_scale(self, bone, exp_bone): def check_parent(self, bone, exp_bone): self.assertEqual(type(bone.parent), type(exp_bone.parent), - "Missmatching types in pose.bones[%s].parent" % (bone.name)) + "Mismatching types in pose.bones[%s].parent" % (bone.name)) self.assertTrue(bone.parent is None or bone.parent.name == exp_bone.parent.name, "Bone parent does not match on bone %s" % (bone.name)) @@ -58,17 +56,17 @@ def check_bendy_bones(self, bone, exp_bone): exp_value = getattr(exp_bone, var) self.assertEqual(type(value), type(exp_value), - "Missmatching types in pose.bones[%s].%s" % (bone.name, var)) + "Mismatching types in pose.bones[%s].%s" % (bone.name, var)) if isinstance(value, str): self.assertEqual(value, exp_value, - "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + "Mismatching value in pose.bones[%s].%s" % (bone.name, var)) elif hasattr(value, "name"): self.assertEqual(value.name, exp_value.name, - "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + "Mismatching value in pose.bones[%s].%s" % (bone.name, var)) else: self.assertAlmostEqual(value, exp_value, - "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + "Mismatching value in pose.bones[%s].%s" % (bone.name, var)) def check_ik(self, bone, exp_bone): @@ -83,7 +81,7 @@ def check_ik(self, bone, exp_bone): value = getattr(bone, var) exp_value = getattr(exp_bone, var) self.assertAlmostEqual(value, exp_value, - "Missmatching value in pose.bones[%s].%s" % (bone.name, var)) + "Mismatching value in pose.bones[%s].%s" % (bone.name, var)) def check_constraints(self, input_arm, expected_arm, bone, exp_bone): @@ -91,7 +89,7 @@ def check_constraints(self, input_arm, expected_arm, bone, exp_bone): expo_const_len = len(exp_bone.constraints) self.assertEqual(const_len, expo_const_len, - "Constraints missmatch on bone %s" % (bone.name)) + "Constraints mismatch on bone %s" % (bone.name)) for exp_constraint in exp_bone.constraints: const_name = exp_constraint.name @@ -113,28 +111,28 @@ def check_constraints(self, input_arm, expected_arm, bone, exp_bone): exp_value = getattr(exp_constraint, var) self.assertEqual(type(value), type(exp_value), - "Missmatching constraint value types in pose.bones[%s].constraints[%s].%s" % ( + "Mismatching constraint value types in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) if isinstance(value, str): self.assertEqual(value, exp_value, - "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + "Mismatching constraint value in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) elif hasattr(value, "name"): - # Some constraints targets the armature itself, so the armature name should missmatch. + # Some constraints targets the armature itself, so the armature name should mismatch. if value.name == input_arm.name and exp_value.name == expected_arm.name: continue self.assertEqual(value.name, exp_value.name, - "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + "Mismatching constraint value in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) elif isinstance(value, bool): self.assertEqual(value, exp_value, - "Missmatching constraint boolean in pose.bones[%s].constraints[%s].%s" % ( + "Mismatching constraint boolean in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var)) else: - msg = "Missmatching constraint value in pose.bones[%s].constraints[%s].%s" % ( + msg = "Mismatching constraint value in pose.bones[%s].constraints[%s].%s" % ( bone.name, const_name, var) self.assertAlmostEqual(value, exp_value, places=6, msg=msg) diff --git a/tests/python/bl_rna_manual_reference.py b/tests/python/bl_rna_manual_reference.py index 70f218f0bb2..257c8b7601a 100644 --- a/tests/python/bl_rna_manual_reference.py +++ b/tests/python/bl_rna_manual_reference.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Use for validating our manual interlinking. # ./blender.bin --background -noaudio --python tests/python/bl_rna_manual_reference.py # diff --git a/tests/python/bl_rst_completeness.py b/tests/python/bl_rst_completeness.py index 59e532c433b..4846e65b78f 100644 --- a/tests/python/bl_rst_completeness.py +++ b/tests/python/bl_rst_completeness.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # run this script in the game engine. # or on the command line with... # ./blender.bin --background -noaudio --python tests/python/bl_rst_completeness.py diff --git a/tests/python/bl_run_operators.py b/tests/python/bl_run_operators.py index 7e73ec163a4..a2478bd7547 100644 --- a/tests/python/bl_run_operators.py +++ b/tests/python/bl_run_operators.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # semi-useful script, runs all operators in a number of different # contexts, cheap way to find misc small bugs but is in no way a complete test. # diff --git a/tests/python/bl_run_operators_event_simulate.py b/tests/python/bl_run_operators_event_simulate.py index 56f96847d0b..d218e6b1bc0 100644 --- a/tests/python/bl_run_operators_event_simulate.py +++ b/tests/python/bl_run_operators_event_simulate.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - r""" Overview ======== diff --git a/tests/python/bl_test.py b/tests/python/bl_test.py index 7e79639a226..b71ebd2a7a7 100644 --- a/tests/python/bl_test.py +++ b/tests/python/bl_test.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import sys import os diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py index 1ba9b4f1edf..95b2328b2aa 100644 --- a/tests/python/bl_usd_import_test.py +++ b/tests/python/bl_usd_import_test.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import pathlib import sys import unittest diff --git a/tests/python/boolean_operator.py b/tests/python/boolean_operator.py index fed0b2bddfd..8b93226ab93 100644 --- a/tests/python/boolean_operator.py +++ b/tests/python/boolean_operator.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # To run all tests, use # BLENDER_VERBOSE=1 blender path/to/bool_regression.blend --python path/to/boolean_operator.py -- --run-all-tests # To run one test, use diff --git a/tests/python/deform_modifiers.py b/tests/python/deform_modifiers.py index 40cd9d4839c..e5be133a3ef 100644 --- a/tests/python/deform_modifiers.py +++ b/tests/python/deform_modifiers.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # To run the test type: blender -b /path/to/the/blend/file --python path/to/this/py/file -- --run-all-tests -- --verbose # Type the above line in cmd/terminal, for example, look below # blender -b c:\blender-lib\deform_modifiers.blend --python c:\deform_modifiers.py -- --run-all-tests -- --verbose diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index 8c6f08ae76e..68895291044 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -7,6 +7,7 @@ import shlex import shutil import subprocess import sys +from pathlib import Path def setup(): @@ -138,6 +139,11 @@ def main(): report.set_pixelated(True) report.set_reference_dir("eevee_renders") report.set_compare_engine('cycles', 'CPU') + + test_dir_name = Path(test_dir).name + if test_dir_name.startswith('image'): + report.set_fail_threshold(0.051) + ok = report.run(test_dir, blender, get_arguments, batch=True) sys.exit(not ok) diff --git a/tests/python/ffmpeg_tests.py b/tests/python/ffmpeg_tests.py index abbe38193b5..b40b8030f7e 100644 --- a/tests/python/ffmpeg_tests.py +++ b/tests/python/ffmpeg_tests.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import argparse import pathlib import sys diff --git a/tests/python/geo_node_test.py b/tests/python/geo_node_test.py index 9d7c634db76..0842dd001da 100644 --- a/tests/python/geo_node_test.py +++ b/tests/python/geo_node_test.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/modifiers.py b/tests/python/modifiers.py index 8f8b5c6498c..11d696c3bed 100644 --- a/tests/python/modifiers.py +++ b/tests/python/modifiers.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import math import os import sys @@ -215,7 +213,6 @@ def main(): SpecMeshTest("MergedNoneWeld", "testMergedNoneWeld", "expectedMergedNoneWeld", [ModifierSpec("weld", 'WELD', {"merge_threshold": 0.019})]), - ############################################# # One 'Deform' modifier on primitive meshes ############################################# @@ -327,6 +324,22 @@ def main(): ] + boolean_basename = "CubeBooleanDiffBMeshObject" + tests.append(SpecMeshTest("BooleandDiffBMeshObject", "test" + boolean_basename, "expected" + boolean_basename, + [ModifierSpec("boolean", 'BOOLEAN', + {"solver": 'FAST', "operation": 'DIFFERENCE', "operand_type": 'OBJECT', + "object": bpy.data.objects["test" + boolean_basename + "Operand"]})])) + boolean_basename = "CubeBooleanDiffBMeshCollection" + tests.append(SpecMeshTest("BooleandDiffBMeshCollection", + "test" + boolean_basename, + "expected" + boolean_basename, + [ModifierSpec("boolean", + 'BOOLEAN', + {"solver": 'FAST', + "operation": 'DIFFERENCE', + "operand_type": 'COLLECTION', + "collection": bpy.data.collections["test" + boolean_basename + "Operands"]})])) + modifiers_test = RunTest(tests) command = list(sys.argv) diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index 873ab779d65..5b01bfeee94 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # A framework to run regression tests on mesh modifiers and operators based on howardt's mesh_ops_test.py # # General idea: diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index cae81af3144..15441918800 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -253,8 +253,11 @@ class Report: failed = len(failed_tests) > 0 if failed: message = """<div class="alert alert-danger" role="alert">""" - message += """Run this command to update reference images for failed tests, or create images for new tests:<br>""" - message += """<tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt>""" % self.title.lower() + message += """<p>Run this command to regenerate reference (ground truth) images:</p>""" + message += """<p><tt>BLENDER_TEST_UPDATE=1 ctest -R %s</tt></p>""" % self.title.lower() + message += """<p>This then happens for new and failing tests; reference images of """ \ + """passing test cases will not be updated. Be sure to commit the new reference """ \ + """images to the SVN repository afterwards.</p>""" message += """</div>""" else: message = "" @@ -294,6 +297,7 @@ class Report: background-position:0 0, 25px 0, 25px -25px, 0px 25px; }} table td:first-child {{ width: 256px; }} + p {{ margin-bottom: 0.5rem; }} </style> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> </head> diff --git a/tests/python/modules/test_utils.py b/tests/python/modules/test_utils.py index d5cd743cde9..6aba3a75263 100755 --- a/tests/python/modules/test_utils.py +++ b/tests/python/modules/test_utils.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import functools import shutil import pathlib diff --git a/tests/python/operators.py b/tests/python/operators.py index 6ccc96dba5d..548a2b50b05 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import bpy import os import sys diff --git a/tests/python/pep8.py b/tests/python/pep8.py index 9a2871c9ed5..2583bec8256 100644 --- a/tests/python/pep8.py +++ b/tests/python/pep8.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8-80 compliant> - import os import subprocess import shutil @@ -22,7 +20,6 @@ import shutil # how many lines to read into the file, pep8 comment # should be directly after the license header, ~20 in most cases -PEP8_SEEK_COMMENT = 40 SKIP_PREFIX = "./tools", "./config", "./extern" SKIP_ADDONS = True FORCE_PEP8_ALL = False @@ -39,22 +36,8 @@ def is_pep8(path): print(path) if open(path, 'rb').read(3) == b'\xef\xbb\xbf': print("\nfile contains BOM, remove first 3 bytes: %r\n" % path) - - # templates don't have a header but should be pep8 - for d in ("presets", "templates_py", "examples"): - if ("%s%s%s" % (os.sep, d, os.sep)) in path: - return 1 - - f = open(path, 'r', encoding="utf8") - for _ in range(PEP8_SEEK_COMMENT): - line = f.readline() - if line.startswith("# <pep8"): - if line.startswith("# <pep8 compliant>"): - return 1 - elif line.startswith("# <pep8-80 compliant>"): - return 2 - f.close() - return 0 + # Currently all scripts assumed to be pep8. + return 1 def check_files_flake8(files): diff --git a/tests/python/physics_cloth.py b/tests/python/physics_cloth.py index e453b4dd68b..fbd33392371 100644 --- a/tests/python/physics_cloth.py +++ b/tests/python/physics_cloth.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/physics_dynamic_paint.py b/tests/python/physics_dynamic_paint.py index 57b96ccffba..132c7b8c46d 100644 --- a/tests/python/physics_dynamic_paint.py +++ b/tests/python/physics_dynamic_paint.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/physics_ocean.py b/tests/python/physics_ocean.py index 20d563f782b..54cf8d65a9d 100644 --- a/tests/python/physics_ocean.py +++ b/tests/python/physics_ocean.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/physics_particle_instance.py b/tests/python/physics_particle_instance.py index 353c0d868c8..3cd52fd9fa0 100644 --- a/tests/python/physics_particle_instance.py +++ b/tests/python/physics_particle_instance.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/physics_particle_system.py b/tests/python/physics_particle_system.py index 37e3df781b0..51afae68a7b 100644 --- a/tests/python/physics_particle_system.py +++ b/tests/python/physics_particle_system.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys import bpy diff --git a/tests/python/physics_softbody.py b/tests/python/physics_softbody.py index 00d2a637cf7..ebb9fbb724f 100644 --- a/tests/python/physics_softbody.py +++ b/tests/python/physics_softbody.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - import os import sys diff --git a/tests/python/rna_info_dump.py b/tests/python/rna_info_dump.py index afedf670f2f..af00ef54de9 100644 --- a/tests/python/rna_info_dump.py +++ b/tests/python/rna_info_dump.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Used for generating API diffs between releases # ./blender.bin --background -noaudio --python tests/python/rna_info_dump.py diff --git a/tests/python/rst_to_doctree_mini.py b/tests/python/rst_to_doctree_mini.py index 3466c915aa0..43116922fe5 100644 --- a/tests/python/rst_to_doctree_mini.py +++ b/tests/python/rst_to_doctree_mini.py @@ -1,7 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later -# <pep8 compliant> - # Module with function to extract a doctree from an reStructuredText file. # Named 'Mini' because we only parse the minimum data needed to check # Python classes, methods and attributes match up to those in existing modules. diff --git a/tests/python/workbench_render_tests.py b/tests/python/workbench_render_tests.py index 3ceb0fb3226..e182b2a41e2 100644 --- a/tests/python/workbench_render_tests.py +++ b/tests/python/workbench_render_tests.py @@ -3,10 +3,12 @@ import argparse import os +import platform import shlex import shutil import subprocess import sys +from pathlib import Path def setup(): @@ -73,6 +75,11 @@ def main(): report.set_pixelated(True) report.set_reference_dir("workbench_renders") report.set_compare_engine('eevee') + + test_dir_name = Path(test_dir).name + if test_dir_name.startswith('hair') and platform.system() == "Darwin": + report.set_fail_threshold(0.050) + ok = report.run(test_dir, blender, get_arguments, batch=True) sys.exit(not ok) |