diff options
Diffstat (limited to 'tests/python')
26 files changed, 703 insertions, 185 deletions
diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 20fa8257a32..a3460823e69 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -39,78 +39,101 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR}) # all calls to blender use this if(APPLE) if(${CMAKE_GENERATOR} MATCHES "Xcode") - set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/Debug/blender.app/Contents/MacOS/blender) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup) else() - set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender.app/Contents/MacOS/blender) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) endif() else() - set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) endif() # for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no -set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE}) -set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) -set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} ${TEST_BLENDER_EXE_PARAMS} ) +# set(TEST_BLENDER_EXE_BARE ${TEST_BLENDER_EXE}) +# set(TEST_BLENDER_EXE ${TEST_BLENDER_EXE} ${TEST_BLENDER_EXE_PARAMS} ) # ------------------------------------------------------------------------------ # GENERAL PYTHON CORRECTNESS TESTS -add_test(script_load_keymap ${TEST_BLENDER_EXE} +add_test( + NAME script_load_keymap + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_keymap_completeness.py ) -add_test(script_load_addons ${TEST_BLENDER_EXE} +add_test( + NAME script_load_addons + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_load_addons.py ) -add_test(script_load_modules ${TEST_BLENDER_EXE} +add_test( + NAME script_load_modules + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_load_py_modules.py ) # test running operators doesn't segfault under various conditions if(USE_EXPERIMENTAL_TESTS) - add_test(script_run_operators ${TEST_BLENDER_EXE} + add_test( + NAME script_run_operators + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_run_operators.py ) endif() # ------------------------------------------------------------------------------ # PY API TESTS -add_test(script_pyapi_bpy_path ${TEST_BLENDER_EXE} +add_test( + NAME script_pyapi_bpy_path + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_path.py ) -add_test(script_pyapi_bpy_utils_units ${TEST_BLENDER_EXE} +add_test( + NAME script_pyapi_bpy_utils_units + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_bpy_utils_units.py ) -add_test(script_pyapi_mathutils ${TEST_BLENDER_EXE} +add_test( + NAME script_pyapi_mathutils + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_mathutils.py ) -add_test(script_pyapi_idprop ${TEST_BLENDER_EXE} +add_test( + NAME script_pyapi_idprop + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop.py ) -add_test(script_pyapi_idprop_datablock ${TEST_BLENDER_EXE} +add_test( + NAME script_pyapi_idprop_datablock + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_pyapi_idprop_datablock.py ) # ------------------------------------------------------------------------------ # MODELING TESTS -add_test(bevel ${TEST_BLENDER_EXE} +add_test( + NAME bevel + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/modeling/bevel_regression.blend --python-text run_tests ) -add_test(split_faces ${TEST_BLENDER_EXE} +add_test( + NAME split_faces + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/modeling/split_faces_test.blend --python-text run_tests ) # ------------------------------------------------------------------------------ # MODIFIERS TESTS -add_test(modifier_array ${TEST_BLENDER_EXE} +add_test( + NAME modifier_array + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/modifier_stack/array_test.blend --python-text run_tests ) @@ -121,21 +144,27 @@ add_test(modifier_array ${TEST_BLENDER_EXE} # OBJ Import tests # disabled until updated & working if(FALSE) -add_test(import_obj_cube ${TEST_BLENDER_EXE} +add_test( + NAME import_obj_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/cube.obj'\) --md5=39cce4bacac2d1b18fc470380279bc15 --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_obj_cube.blend ) -add_test(import_obj_nurbs_cyclic ${TEST_BLENDER_EXE} +add_test( + NAME import_obj_nurbs_cyclic + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/nurbs_cyclic.obj'\) --md5=ad3c307e5883224a0492378cd32691ab --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_obj_nurbs_cyclic.blend ) -add_test(import_obj_makehuman ${TEST_BLENDER_EXE} +add_test( + NAME import_obj_makehuman + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.obj\(filepath='${TEST_SRC_DIR}/io_tests/obj/makehuman.obj'\) --md5=c9f78b185e58358daa4ecaecfa75464e --md5_method=SCENE @@ -144,7 +173,9 @@ add_test(import_obj_makehuman ${TEST_BLENDER_EXE} endif() # OBJ Export tests -add_test(export_obj_cube ${TEST_BLENDER_EXE} +add_test( + NAME export_obj_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_cube.obj',use_selection=False\) @@ -153,7 +184,9 @@ add_test(export_obj_cube ${TEST_BLENDER_EXE} --md5=e80660437ad9bfe082849641c361a233 --md5_method=FILE ) -add_test(export_obj_nurbs ${TEST_BLENDER_EXE} +add_test( + NAME export_obj_nurbs + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_nurbs.obj',use_selection=False,use_nurbs=True\) @@ -164,7 +197,9 @@ add_test(export_obj_nurbs ${TEST_BLENDER_EXE} # disabled until updated & working if(FALSE) -add_test(export_obj_all_objects ${TEST_BLENDER_EXE} +add_test( + NAME export_obj_all_objects + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/export_obj_all_objects.obj',use_selection=False,use_nurbs=True\) @@ -177,21 +212,27 @@ endif() # PLY Import tests -add_test(import_ply_cube ${TEST_BLENDER_EXE} +add_test( + NAME import_ply_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/cube_ascii.ply'\) --md5=527134343c27fc0ea73115b85fbfd3ac --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_ply_cube.blend ) -add_test(import_ply_bunny ${TEST_BLENDER_EXE} +add_test( + NAME import_ply_bunny + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/bunny2.ply'\) --md5=6ea5b8533400a17accf928b8fd024eaa --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_ply_bunny.blend ) -add_test(import_ply_small_holes ${TEST_BLENDER_EXE} +add_test( + NAME import_ply_small_holes + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.ply\(filepath='${TEST_SRC_DIR}/io_tests/ply/many_small_holes.ply'\) --md5=c3093e26ecae5b6d59fbbcf2a0d0b39f --md5_method=SCENE @@ -201,7 +242,9 @@ add_test(import_ply_small_holes ${TEST_BLENDER_EXE} # PLY Export # disabled until updated & working if(FALSE) -add_test(export_ply_cube_all_data ${TEST_BLENDER_EXE} +add_test( + NAME export_ply_cube_all_data + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/cube_all_data.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_cube_all_data.ply'\) @@ -209,7 +252,9 @@ add_test(export_ply_cube_all_data ${TEST_BLENDER_EXE} --md5=6adc3748ceae8298496f99d0e7e76c15 --md5_method=FILE ) -add_test(export_ply_suzanne_all_data ${TEST_BLENDER_EXE} +add_test( + NAME export_ply_suzanne_all_data + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/suzanne_all_data.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_suzanne_all_data.ply'\) @@ -218,7 +263,9 @@ add_test(export_ply_suzanne_all_data ${TEST_BLENDER_EXE} ) endif() -add_test(export_ply_vertices ${TEST_BLENDER_EXE} # lame, add a better one +add_test( + NAME export_ply_vertices # lame, add a better one + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/vertices.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.ply\(filepath='${TEST_OUT_DIR}/export_ply_vertices.ply'\) @@ -230,21 +277,27 @@ add_test(export_ply_vertices ${TEST_BLENDER_EXE} # lame, add a better one # STL Import tests # disabled until updated & working if(FALSE) -add_test(import_stl_cube ${TEST_BLENDER_EXE} +add_test( + NAME import_stl_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/cube.stl'\) --md5=8ceb5bb7e1cb5f4342fa1669988c66b4 --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_stl_cube.blend ) -add_test(import_stl_conrod ${TEST_BLENDER_EXE} +add_test( + NAME import_stl_conrod + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/conrod.stl'\) --md5=690a4b8eb9002dcd8631c5a575ea7348 --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_stl_conrod.blend ) -add_test(import_stl_knot_max_simplified ${TEST_BLENDER_EXE} +add_test( + NAME import_stl_knot_max_simplified + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_mesh.stl\(filepath='${TEST_SRC_DIR}/io_tests/stl/knot_max_simplified.stl'\) --md5=baf82803f45a84ec4ddbad9cef57dd3e --md5_method=SCENE @@ -255,7 +308,9 @@ endif() # STL Export # disabled until updated & working if(FALSE) -add_test(export_stl_cube_all_data ${TEST_BLENDER_EXE} +add_test( + NAME export_stl_cube_all_data + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/cube_all_data.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_cube_all_data.stl'\) @@ -263,7 +318,9 @@ add_test(export_stl_cube_all_data ${TEST_BLENDER_EXE} --md5=64cb97c0cabb015e1c3f76369835075a --md5_method=FILE ) -add_test(export_stl_suzanne_all_data ${TEST_BLENDER_EXE} +add_test( + NAME export_stl_suzanne_all_data + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/suzanne_all_data.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_suzanne_all_data.stl'\) @@ -271,7 +328,9 @@ add_test(export_stl_suzanne_all_data ${TEST_BLENDER_EXE} --md5=e9b23c97c139ad64961c635105bb9192 --md5_method=FILE ) -add_test(export_stl_vertices ${TEST_BLENDER_EXE} # lame, add a better one +add_test( + NAME export_stl_vertices # lame, add a better one + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/vertices.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_mesh.stl\(filepath='${TEST_OUT_DIR}/export_stl_vertices.stl'\) @@ -284,21 +343,27 @@ endif() # X3D Import # disabled until updated & working if(FALSE) -add_test(import_x3d_cube ${TEST_BLENDER_EXE} +add_test( + NAME import_x3d_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/color_cube.x3d'\) --md5=3fae9be004199c145941cd3f9f80ad7b --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_x3d_cube.blend ) -add_test(import_x3d_teapot ${TEST_BLENDER_EXE} +add_test( + NAME import_x3d_teapot + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/teapot.x3d'\) --md5=8ee196c71947dce4199d55698501691e --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_x3d_teapot.blend ) -add_test(import_x3d_suzanne_material ${TEST_BLENDER_EXE} +add_test( + NAME import_x3d_suzanne_material + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.x3d\(filepath='${TEST_SRC_DIR}/io_tests/x3d/suzanne_material.x3d'\) --md5=3edea1353257d8b5a5f071942f417be6 --md5_method=SCENE @@ -306,7 +371,9 @@ add_test(import_x3d_suzanne_material ${TEST_BLENDER_EXE} ) # X3D Export -add_test(export_x3d_cube ${TEST_BLENDER_EXE} +add_test( + NAME export_x3d_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_cube.x3d',use_selection=False\) @@ -314,7 +381,9 @@ add_test(export_x3d_cube ${TEST_BLENDER_EXE} --md5=05312d278fe41da33560fdfb9bdb268f --md5_method=FILE ) -add_test(export_x3d_nurbs ${TEST_BLENDER_EXE} +add_test( + NAME export_x3d_nurbs + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_nurbs.x3d',use_selection=False\) @@ -322,7 +391,9 @@ add_test(export_x3d_nurbs ${TEST_BLENDER_EXE} --md5=4286d4a2aa507ef78b22ddcbdcc88481 --md5_method=FILE ) -add_test(export_x3d_all_objects ${TEST_BLENDER_EXE} +add_test( + NAME export_x3d_all_objects + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.x3d\(filepath='${TEST_OUT_DIR}/export_x3d_all_objects.x3d',use_selection=False\) @@ -336,21 +407,27 @@ endif() # 3DS Import # disabled until updated & working if(FALSE) -add_test(import_3ds_cube ${TEST_BLENDER_EXE} +add_test( + NAME import_3ds_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/cube.3ds'\) --md5=cb5a45c35a343c3f5beca2a918472951 --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_3ds_cube.blend ) -add_test(import_3ds_hierarchy_lara ${TEST_BLENDER_EXE} +add_test( + NAME import_3ds_hierarchy_lara + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_lara.3ds'\) --md5=766c873d9fdb5f190e43796cfbae63b6 --md5_method=SCENE --write-blend=${TEST_OUT_DIR}/import_3ds_hierarchy_lara.blend ) -add_test(import_3ds_hierarchy_greek_trireme ${TEST_BLENDER_EXE} +add_test( + NAME import_3ds_hierarchy_greek_trireme + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.import_scene.autodesk_3ds\(filepath='${TEST_SRC_DIR}/io_tests/3ds/hierarchy_greek_trireme.3ds'\) --md5=b62ee30101e8999cb91ef4f8a8760056 --md5_method=SCENE @@ -361,7 +438,9 @@ endif() # 3DS Export # disabled until updated & working if(FALSE) -add_test(export_3ds_cube ${TEST_BLENDER_EXE} +add_test( + NAME export_3ds_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_cube.3ds',use_selection=False\) @@ -369,7 +448,9 @@ add_test(export_3ds_cube ${TEST_BLENDER_EXE} --md5=a31f5071b6c6dc7445b9099cdc7f63b3 --md5_method=FILE ) -add_test(export_3ds_nurbs ${TEST_BLENDER_EXE} +add_test( + NAME export_3ds_nurbs + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_nurbs.3ds',use_selection=False\) @@ -377,7 +458,9 @@ add_test(export_3ds_nurbs ${TEST_BLENDER_EXE} --md5=5bdd21be3c80d814fbc83cb25edb08c2 --md5_method=FILE ) -add_test(export_3ds_all_objects ${TEST_BLENDER_EXE} +add_test( + NAME export_3ds_all_objects + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.autodesk_3ds\(filepath='${TEST_OUT_DIR}/export_3ds_all_objects.3ds',use_selection=False\) @@ -392,7 +475,9 @@ endif() # 'use_metadata=False' for reliable md5's # disabled until updated & working if(FALSE) -add_test(export_fbx_cube ${TEST_BLENDER_EXE} +add_test( + NAME export_fbx_cube + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/all_quads.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_cube.fbx',use_selection=False,use_metadata=False\) @@ -400,7 +485,9 @@ add_test(export_fbx_cube ${TEST_BLENDER_EXE} --md5=59a35577462f95f9a0b4e6035226ce9b --md5_method=FILE ) -add_test(export_fbx_nurbs ${TEST_BLENDER_EXE} +add_test( + NAME export_fbx_nurbs + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_geometry/nurbs.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_nurbs.fbx',use_selection=False,use_metadata=False\) @@ -408,7 +495,9 @@ add_test(export_fbx_nurbs ${TEST_BLENDER_EXE} --md5=d31875f18f613fa0c3b16e978f87f6f8 --md5_method=FILE ) -add_test(export_fbx_all_objects ${TEST_BLENDER_EXE} +add_test( + NAME export_fbx_all_objects + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} ${TEST_SRC_DIR}/io_tests/blend_scene/all_objects.blend --python ${CMAKE_CURRENT_LIST_DIR}/bl_test.py -- --run={'FINISHED'}&bpy.ops.export_scene.fbx\(filepath='${TEST_OUT_DIR}/export_fbx_all_objects.fbx',use_selection=False,use_metadata=False\) @@ -421,31 +510,44 @@ if(WITH_CYCLES) if(OPENIMAGEIO_IDIFF AND EXISTS "${TEST_SRC_DIR}/cycles/ctests/shader") macro(add_cycles_render_test subject) if(MSVC) - add_test(NAME cycles_${subject}_test + add_test( + NAME cycles_${subject}_test COMMAND "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>" ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py -blender "$<TARGET_FILE:blender>" -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}" -idiff "${OPENIMAGEIO_IDIFF}" + -outdir "${TEST_OUT_DIR}/cycles" ) else() - add_test(cycles_${subject}_test - ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py - -blender "${TEST_BLENDER_EXE_BARE}" + add_test( + NAME cycles_${subject}_test + COMMAND ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py + -blender "$<TARGET_FILE:blender>" -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}" -idiff "${OPENIMAGEIO_IDIFF}" + -outdir "${TEST_OUT_DIR}/cycles" ) endif() endmacro() if(WITH_OPENGL_TESTS) add_cycles_render_test(opengl) endif() - add_cycles_render_test(image) + add_cycles_render_test(bake) + add_cycles_render_test(denoise) + add_cycles_render_test(displacement) + add_cycles_render_test(image_data_types) + add_cycles_render_test(image_mapping) + add_cycles_render_test(image_texture_limit) + add_cycles_render_test(light) add_cycles_render_test(mblur) add_cycles_render_test(reports) add_cycles_render_test(render) add_cycles_render_test(shader) + add_cycles_render_test(shader_tangent) + add_cycles_render_test(shadow_catcher) + add_cycles_render_test(volume) else() MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist") endif() @@ -460,7 +562,9 @@ if(WITH_ALEMBIC) get_filename_component(ALEMBIC_ROOT_DIR ${real_include_dir} DIRECTORY) if(MSVC) - add_test(NAME alembic_tests + # FIXME, de-duplicate. + add_test( + NAME alembic_tests COMMAND "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>" ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py @@ -468,30 +572,24 @@ if(WITH_ALEMBIC) --testdir "${TEST_SRC_DIR}/alembic" --alembic-root "${ALEMBIC_ROOT_DIR}" ) - add_test(NAME script_alembic_import - COMMAND - "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} - --python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py - -- - --testdir "${TEST_SRC_DIR}/alembic" - --with-legacy-depsgraph=${WITH_LEGACY_DEPSGRAPH} - ) - else() - add_test(alembic_tests - ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py - --blender "${TEST_BLENDER_EXE_BARE}" + add_test( + NAME alembic_tests + COMMAND ${CMAKE_CURRENT_LIST_DIR}/alembic_tests.py + --blender "$<TARGET_FILE:blender>" --testdir "${TEST_SRC_DIR}/alembic" --alembic-root "${ALEMBIC_ROOT_DIR}" ) - add_test(script_alembic_import ${TEST_BLENDER_EXE} + endif() + + add_test( + NAME script_alembic_import + COMMAND "$<TARGET_FILE:blender>" ${TEST_BLENDER_EXE_PARAMS} --python ${CMAKE_CURRENT_LIST_DIR}/bl_alembic_import_test.py -- --testdir "${TEST_SRC_DIR}/alembic" --with-legacy-depsgraph=${WITH_LEGACY_DEPSGRAPH} - ) - - endif() + ) endif() add_subdirectory(render_layer) diff --git a/tests/python/batch_import.py b/tests/python/batch_import.py index bbe3a70327f..a6e2469349b 100644 --- a/tests/python/batch_import.py +++ b/tests/python/batch_import.py @@ -72,10 +72,8 @@ def batch_import( def file_generator(path): for dirpath, dirnames, filenames in os.walk(path): - - # skip '.svn' - if dirpath.startswith("."): - continue + # skip '.git' + dirnames[:] = [d for d in dirnames if not d.startswith(".")] for filename in filenames: if pattern_match(filename): diff --git a/tests/python/bl_alembic_import_test.py b/tests/python/bl_alembic_import_test.py index 2f3daaefd44..e64da2fe540 100644 --- a/tests/python/bl_alembic_import_test.py +++ b/tests/python/bl_alembic_import_test.py @@ -75,6 +75,39 @@ class SimpleImportTest(AbstractAlembicTest): self.assertEqual(objects['Cube_003'], objects['Cube_005'].parent) self.assertEqual(objects['Cube_003'], objects['Cube_006'].parent) + def test_inherit_or_not(self): + res = bpy.ops.wm.alembic_import( + filepath=str(self.testdir / "T52022-inheritance.abc"), + as_background_job=False) + self.assertEqual({'FINISHED'}, res) + + # The objects should be linked to scene_collection in Blender 2.8, + # and to scene in Blender 2.7x. + objects = bpy.context.scene_collection.objects + + # ABC parent is top-level object, which translates to nothing in Blender + self.assertIsNone(objects['locator1'].parent) + + # ABC parent is locator1, but locator2 has "inherits Xforms" = false, which + # translates to "no parent" in Blender. + self.assertIsNone(objects['locator2'].parent) + + # Shouldn't have inherited the ABC parent's transform. + x, y, z = objects['locator2'].matrix_world.to_translation() + self.assertAlmostEqual(0, x) + self.assertAlmostEqual(0, y) + self.assertAlmostEqual(2, z) + + # ABC parent is inherited and translates to normal parent in Blender. + self.assertEqual(objects['locator2'], objects['locatorShape2'].parent) + + # Should have inherited its ABC parent's transform. + x, y, z = objects['locatorShape2'].matrix_world.to_translation() + self.assertAlmostEqual(0, x) + self.assertAlmostEqual(0, y) + self.assertAlmostEqual(2, z) + + def test_select_after_import(self): # Add a sphere, so that there is something in the scene, selected, and active, # before we do the Alembic import. @@ -146,22 +179,26 @@ class SimpleImportTest(AbstractAlembicTest): res = bpy.ops.wm.alembic_import(filepath=str(abc), as_background_job=False) self.assertEqual({'FINISHED'}, res) - cube = bpy.context.active_object + plane = bpy.context.active_object # Check that the file loaded ok. bpy.context.scene.frame_set(6) - self.assertAlmostEqual(-1, cube.data.vertices[0].co.x) - self.assertAlmostEqual(-1, cube.data.vertices[0].co.y) - self.assertAlmostEqual(0.5905638933181763, cube.data.vertices[0].co.z) + scene = bpy.context.scene + layer = scene.render_layers[scene.active_layer] + mesh = plane.to_mesh(scene, layer, True, 'RENDER') + self.assertAlmostEqual(-1, mesh.vertices[0].co.x) + self.assertAlmostEqual(-1, mesh.vertices[0].co.y) + self.assertAlmostEqual(0.5905638933181763, mesh.vertices[0].co.z) # Change path from absolute to relative. This should not break the animation. - bpy.context.scene.frame_set(1) + scene.frame_set(1) bpy.data.cache_files[fname].filepath = relpath - bpy.context.scene.frame_set(6) + scene.frame_set(6) - self.assertAlmostEqual(1, cube.data.vertices[3].co.x) - self.assertAlmostEqual(1, cube.data.vertices[3].co.y) - self.assertAlmostEqual(0.5905638933181763, cube.data.vertices[3].co.z) + mesh = plane.to_mesh(scene, layer, True, 'RENDER') + self.assertAlmostEqual(1, mesh.vertices[3].co.x) + self.assertAlmostEqual(1, mesh.vertices[3].co.y) + self.assertAlmostEqual(0.5905638933181763, mesh.vertices[3].co.z) def test_import_long_names(self): # This file contains very long names. The longest name is 4047 chars. diff --git a/tests/python/bl_load_py_modules.py b/tests/python/bl_load_py_modules.py index 4935491a6be..39e7bd33d44 100644 --- a/tests/python/bl_load_py_modules.py +++ b/tests/python/bl_load_py_modules.py @@ -38,7 +38,7 @@ BLACKLIST = { 'io_import_dxf', # Because of cydxfentity.so dependency # The unpacked wheel is only loaded when actually used, not directly on import: - "io_blend_utils/blender_bam-unpacked.whl", + os.path.join("io_blend_utils", "blender_bam-unpacked.whl"), } # Some modules need to add to the `sys.path`. @@ -93,9 +93,8 @@ def addon_modules_sorted(): def source_list(path, filename_check=None): from os.path import join for dirpath, dirnames, filenames in os.walk(path): - # skip '.svn' - if dirpath.startswith("."): - continue + # skip '.git' + dirnames[:] = [d for d in dirnames if not d.startswith(".")] for filename in filenames: filepath = join(dirpath, filename) @@ -123,6 +122,8 @@ def load_addons(): def load_modules(): + VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None + modules = [] module_paths = [] @@ -162,6 +163,14 @@ def load_modules(): del module_names # + # test we tested all files except for presets and templates + ignore_paths = [ + os.sep + "presets" + os.sep, + os.sep + "templates" + os.sep, + ] + ([(os.sep + f + os.sep) for f in BLACKLIST] + + [(os.sep + f + ".py") for f in BLACKLIST]) + + # # now submodules for m in modules: filepath = m.__file__ @@ -178,15 +187,35 @@ def load_modules(): for f in MODULE_SYS_PATHS.get(mod_name_full, ()) ]) - __import__(mod_name_full) - mod_imp = sys.modules[mod_name_full] - - sys.path[:] = sys_path_back - - # check we load what we ask for. - assert(os.path.samefile(mod_imp.__file__, submod_full)) - - modules.append(mod_imp) + try: + __import__(mod_name_full) + mod_imp = sys.modules[mod_name_full] + + sys.path[:] = sys_path_back + + # check we load what we ask for. + assert(os.path.samefile(mod_imp.__file__, submod_full)) + + modules.append(mod_imp) + except Exception as e: + import traceback + # Module might fail to import, but we don't want whole test to fail here. + # Reasoning: + # - This module might be in ignored list (for example, preset or template), + # so failing here will cause false-positive test failure. + # - If this is module which should not be ignored, it is not added to list + # of successfully loaded modules, meaning the test will catch this + # import failure. + # - We want to catch all failures of this script instead of stopping on + # a first big failure. + do_print = True + if not VERBOSE: + for ignore in ignore_paths: + if ignore in submod_full: + do_print = False + break + if do_print: + traceback.print_exc() # # check which filepaths we didn't load @@ -205,14 +234,6 @@ def load_modules(): for f in loaded_files: source_files.remove(f) - # - # test we tested all files except for presets and templates - ignore_paths = [ - os.sep + "presets" + os.sep, - os.sep + "templates" + os.sep, - ] + ([(os.sep + f + os.sep) for f in BLACKLIST] + - [(os.sep + f + ".py") for f in BLACKLIST]) - for f in source_files: for ignore in ignore_paths: if ignore in f: diff --git a/tests/python/bl_pyapi_bpy_utils_units.py b/tests/python/bl_pyapi_bpy_utils_units.py index f40dab4b5eb..251419cb9ef 100644 --- a/tests/python/bl_pyapi_bpy_utils_units.py +++ b/tests/python/bl_pyapi_bpy_utils_units.py @@ -32,18 +32,17 @@ class UnitsTesting(unittest.TestCase): OUTPUT_TESTS = ( # system, type, prec, sep, compat, value, output ##### LENGTH + # Note: precision handling is a bit complicated when using multi-units... ('IMPERIAL', 'LENGTH', 3, False, False, 0.3048, "1'"), ('IMPERIAL', 'LENGTH', 3, False, True, 0.3048, "1ft"), - ('IMPERIAL', 'LENGTH', 3, True, False, 0.3048 * 2 + 0.0254 * 5.5, "2' 5.5\""), - # Those next two fail, here again because precision ignores order magnitude :/ - #('IMPERIAL', 'LENGTH', 3, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.004mi!!! - #('IMPERIAL', 'LENGTH', 6, False, False, 1609.344 * 1e6, "1000000mi"), # == 1000000.003641mi!!! - ('METRIC', 'LENGTH', 3, True, False, 1000 * 2 + 0.001 * 15, "2km 1.5cm"), - ('METRIC', 'LENGTH', 3, True, False, 1234.56789, "1km 234.6m"), - # Note: precision seems basically unused when using multi units! - ('METRIC', 'LENGTH', 9, True, False, 1234.56789, "1km 234.6m"), - ('METRIC', 'LENGTH', 9, False, False, 1234.56789, "1.23456789km"), - ('METRIC', 'LENGTH', 9, True, False, 1000.000123456789, "1km 0.1mm"), + ('IMPERIAL', 'LENGTH', 4, True, False, 0.3048 * 2 + 0.0254 * 5.5, "2' 5.5\""), + ('IMPERIAL', 'LENGTH', 3, False, False, 1609.344 * 1e6, "1000000mi"), + ('IMPERIAL', 'LENGTH', 6, False, False, 1609.344 * 1e6, "1000000mi"), + ('METRIC', 'LENGTH', 3, True, False, 1000 * 2 + 0.001 * 15, "2km 2cm"), + ('METRIC', 'LENGTH', 5, True, False, 1234.56789, "1km 234.6m"), + ('METRIC', 'LENGTH', 6, True, False, 1234.56789, "1km 234.57m"), + ('METRIC', 'LENGTH', 9, False, False, 1234.56789, "1.234568km"), + ('METRIC', 'LENGTH', 9, True, False, 1000.000123456789, "1km 0.123mm"), ) def test_units_inputs(self): diff --git a/tests/python/bl_run_operators.py b/tests/python/bl_run_operators.py index 7d5f4127378..7b6b97e5ad1 100644 --- a/tests/python/bl_run_operators.py +++ b/tests/python/bl_run_operators.py @@ -100,10 +100,8 @@ def blend_list(mainpath): def file_list(path, filename_check=None): for dirpath, dirnames, filenames in os.walk(path): - - # skip '.svn' - if dirpath.startswith("."): - continue + # skip '.git' + dirnames[:] = [d for d in dirnames if not d.startswith(".")] for filename in filenames: filepath = join(dirpath, filename) diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index a030cc5e0de..2122150467c 100755 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -2,7 +2,10 @@ # Apache License, Version 2.0 import argparse +import glob import os +import pathlib +import shlex import shutil import subprocess import sys @@ -24,7 +27,7 @@ class COLORS_DUMMY: COLORS = COLORS_DUMMY -def printMessage(type, status, message): +def print_message(message, type=None, status=''): if type == 'SUCCESS': print(COLORS.GREEN, end="") elif type == 'FAILURE': @@ -50,38 +53,60 @@ def render_file(filepath): dirname = os.path.dirname(filepath) basedir = os.path.dirname(dirname) subject = os.path.basename(dirname) + + custom_args = os.getenv('CYCLESTEST_ARGS') + custom_args = shlex.split(custom_args) if custom_args else [] + + # OSL and GPU examples + # custom_args += ["--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True"] + # custom_args += ["--python-expr", "import bpy; bpy.context.scene.cycles.device = 'GPU'"] + if subject == 'opengl': - command = ( + command = [ BLENDER, "--window-geometry", "0", "0", "1", "1", "-noaudio", "--factory-startup", "--enable-autoexec", filepath, - "-E", "CYCLES", - # Run with OSL enabled - # "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True", + "-E", "CYCLES"] + command += custom_args + command += [ "-o", TEMP_FILE_MASK, "-F", "PNG", '--python', os.path.join(basedir, "util", - "render_opengl.py") - ) + "render_opengl.py")] + elif subject == 'bake': + command = [ + BLENDER, + "-b", + "-noaudio", + "--factory-startup", + "--enable-autoexec", + filepath, + "-E", "CYCLES"] + command += custom_args + command += [ + "-o", TEMP_FILE_MASK, + "-F", "PNG", + '--python', os.path.join(basedir, + "util", + "render_bake.py")] else: - command = ( + command = [ BLENDER, "--background", "-noaudio", "--factory-startup", "--enable-autoexec", filepath, - "-E", "CYCLES", - # Run with OSL enabled - # "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True", + "-E", "CYCLES"] + command += custom_args + command += [ "-o", TEMP_FILE_MASK, "-F", "PNG", - "-f", "1", - ) + "-f", "1"] try: output = subprocess.check_output(command) if VERBOSE: @@ -109,68 +134,241 @@ def test_get_name(filepath): filename = os.path.basename(filepath) return os.path.splitext(filename)[0] - -def verify_output(filepath): +def test_get_images(filepath): testname = test_get_name(filepath) dirpath = os.path.dirname(filepath) - reference_dirpath = os.path.join(dirpath, "reference_renders") - reference_image = os.path.join(reference_dirpath, testname + ".png") - failed_image = os.path.join(reference_dirpath, testname + ".fail.png") - if not os.path.exists(reference_image): - return False + + old_dirpath = os.path.join(dirpath, "reference_renders") + old_img = os.path.join(old_dirpath, testname + ".png") + + ref_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath), "ref") + ref_img = os.path.join(ref_dirpath, testname + ".png") + if not os.path.exists(ref_dirpath): + os.makedirs(ref_dirpath) + if os.path.exists(old_img): + shutil.copy(old_img, ref_img) + + new_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath)) + if not os.path.exists(new_dirpath): + os.makedirs(new_dirpath) + new_img = os.path.join(new_dirpath, testname + ".png") + + diff_dirpath = os.path.join(OUTDIR, os.path.basename(dirpath), "diff") + if not os.path.exists(diff_dirpath): + os.makedirs(diff_dirpath) + diff_img = os.path.join(diff_dirpath, testname + ".diff.png") + + return old_img, ref_img, new_img, diff_img + + +class Report: + def __init__(self, testname): + self.failed_tests = "" + self.passed_tests = "" + self.testname = testname + + def output(self): + # write intermediate data for single test + outdir = os.path.join(OUTDIR, self.testname) + if not os.path.exists(outdir): + os.makedirs(outdir) + + filepath = os.path.join(outdir, "failed.data") + pathlib.Path(filepath).write_text(self.failed_tests) + + filepath = os.path.join(outdir, "passed.data") + pathlib.Path(filepath).write_text(self.passed_tests) + + # gather intermediate data for all tests + failed_data = sorted(glob.glob(os.path.join(OUTDIR, "*/failed.data"))) + passed_data = sorted(glob.glob(os.path.join(OUTDIR, "*/passed.data"))) + + failed_tests = "" + passed_tests = "" + + for filename in failed_data: + filepath = os.path.join(OUTDIR, filename) + failed_tests += pathlib.Path(filepath).read_text() + for filename in passed_data: + filepath = os.path.join(OUTDIR, filename) + passed_tests += pathlib.Path(filepath).read_text() + + # write html for all tests + self.html = """ +<html> +<head> + <title>Cycles Test Report</title> + <style> + img {{ image-rendering: pixelated; width: 256px; background-color: #000; }} + img.render {{ + background-color: #fff; + background-image: + -moz-linear-gradient(45deg, #eee 25%, transparent 25%), + -moz-linear-gradient(-45deg, #eee 25%, transparent 25%), + -moz-linear-gradient(45deg, transparent 75%, #eee 75%), + -moz-linear-gradient(-45deg, transparent 75%, #eee 75%); + background-image: + -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, #eee), color-stop(.25, transparent)), + -webkit-gradient(linear, 0 0, 100% 100%, color-stop(.25, #eee), color-stop(.25, transparent)), + -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.75, transparent), color-stop(.75, #eee)), + -webkit-gradient(linear, 0 0, 100% 100%, color-stop(.75, transparent), color-stop(.75, #eee)); + + -moz-background-size:50px 50px; + background-size:50px 50px; + -webkit-background-size:50px 51px; /* override value for shitty webkit */ + + background-position:0 0, 25px 0, 25px -25px, 0px 25px; + }} + table td:first-child {{ width: 256px; }} + </style> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"> +</head> +<body> + <div class="container"> + <br/> + <h1>Cycles Test Report</h1> + <br/> + <table class="table table-striped"> + <thead class="thead-default"> + <tr><th>Name</th><th>New</th><th>Reference</th><th>Diff</th> + </thead> + {}{} + </table> + <br/> + </div> +</body> +</html> + """ . format(failed_tests, passed_tests) + + filepath = os.path.join(OUTDIR, "report.html") + pathlib.Path(filepath).write_text(self.html) + + print_message("Report saved to: " + pathlib.Path(filepath).as_uri()) + + def relative_url(self, filepath): + relpath = os.path.relpath(filepath, OUTDIR) + return pathlib.Path(relpath).as_posix() + + def add_test(self, filepath, error): + name = test_get_name(filepath) + name = name.replace('_', ' ') + + old_img, ref_img, new_img, diff_img = test_get_images(filepath) + + status = error if error else "" + style = """ style="background-color: #f99;" """ if error else "" + + new_url = self.relative_url(new_img) + ref_url = self.relative_url(ref_img) + diff_url = self.relative_url(diff_img) + + test_html = """ + <tr{}> + <td><b>{}</b><br/>{}<br/>{}</td> + <td><img src="{}" onmouseover="this.src='{}';" onmouseout="this.src='{}';" class="render"></td> + <td><img src="{}" onmouseover="this.src='{}';" onmouseout="this.src='{}';" class="render"></td> + <td><img src="{}"></td> + </tr>""" . format(style, name, self.testname, status, + new_url, ref_url, new_url, + ref_url, new_url, ref_url, + diff_url) + + if error: + self.failed_tests += test_html + else: + self.passed_tests += test_html + + +def verify_output(report, filepath): + old_img, ref_img, new_img, diff_img = test_get_images(filepath) + + # copy new image + if os.path.exists(new_img): + os.remove(new_img) + if os.path.exists(TEMP_FILE): + shutil.copy(TEMP_FILE, new_img) + + update = os.getenv('CYCLESTEST_UPDATE') + + if os.path.exists(ref_img): + # diff test with threshold + command = ( + IDIFF, + "-fail", "0.016", + "-failpercent", "1", + ref_img, + TEMP_FILE, + ) + try: + subprocess.check_output(command) + failed = False + except subprocess.CalledProcessError as e: + if VERBOSE: + print_message(e.output.decode("utf-8")) + failed = e.returncode != 1 + else: + if not update: + return False + + failed = True + + if failed and update: + # update reference + shutil.copy(new_img, ref_img) + shutil.copy(new_img, old_img) + failed = False + + # generate diff image command = ( IDIFF, - "-fail", "0.015", - "-failpercent", "1", - reference_image, - TEMP_FILE, + "-o", diff_img, + "-abs", "-scale", "16", + ref_img, + TEMP_FILE ) + try: subprocess.check_output(command) - failed = False except subprocess.CalledProcessError as e: if VERBOSE: - print(e.output.decode("utf-8")) - failed = e.returncode != 1 - if failed: - shutil.copy(TEMP_FILE, failed_image) - elif os.path.exists(failed_image): - os.remove(failed_image) + print_message(e.output.decode("utf-8")) + return not failed -def run_test(filepath): +def run_test(report, filepath): testname = test_get_name(filepath) spacer = "." * (32 - len(testname)) - printMessage('SUCCESS', 'RUN', testname) + print_message(testname, 'SUCCESS', 'RUN') time_start = time.time() error = render_file(filepath) status = "FAIL" if not error: - if not verify_output(filepath): + if not verify_output(report, filepath): error = "VERIFY" time_end = time.time() elapsed_ms = int((time_end - time_start) * 1000) if not error: - printMessage('SUCCESS', 'OK', "{} ({} ms)" . - format(testname, elapsed_ms)) + print_message("{} ({} ms)" . format(testname, elapsed_ms), + 'SUCCESS', 'OK') else: if error == "NO_CYCLES": - print("Can't perform tests because Cycles failed to load!") - return False + print_message("Can't perform tests because Cycles failed to load!") + return error elif error == "NO_START": - print('Can not perform tests because blender fails to start.', + print_message('Can not perform tests because blender fails to start.', 'Make sure INSTALL target was run.') - return False + return error elif error == 'VERIFY': - print("Rendered result is different from reference image") + print_message("Rendered result is different from reference image") else: - print("Unknown error %r" % error) - printMessage('FAILURE', 'FAILED', "{} ({} ms)" . - format(testname, elapsed_ms)) + print_message("Unknown error %r" % error) + print_message("{} ({} ms)" . format(testname, elapsed_ms), + 'FAILURE', 'FAILED') return error + def blend_list(path): for dirpath, dirnames, filenames in os.walk(path): for filename in filenames: @@ -178,17 +376,18 @@ def blend_list(path): filepath = os.path.join(dirpath, filename) yield filepath - def run_all_tests(dirpath): passed_tests = [] failed_tests = [] all_files = list(blend_list(dirpath)) all_files.sort() - printMessage('SUCCESS', "==========", - "Running {} tests from 1 test case." . format(len(all_files))) + report = Report(os.path.basename(dirpath)) + print_message("Running {} tests from 1 test case." . + format(len(all_files)), + 'SUCCESS', "==========") time_start = time.time() for filepath in all_files: - error = run_test(filepath) + error = run_test(report, filepath) testname = test_get_name(filepath) if error: if error == "NO_CYCLES": @@ -198,28 +397,33 @@ def run_all_tests(dirpath): failed_tests.append(testname) else: passed_tests.append(testname) + report.add_test(filepath, error) time_end = time.time() elapsed_ms = int((time_end - time_start) * 1000) - print("") - printMessage('SUCCESS', "==========", - "{} tests from 1 test case ran. ({} ms total)" . - format(len(all_files), elapsed_ms)) - printMessage('SUCCESS', 'PASSED', "{} tests." . - format(len(passed_tests))) + print_message("") + print_message("{} tests from 1 test case ran. ({} ms total)" . + format(len(all_files), elapsed_ms), + 'SUCCESS', "==========") + print_message("{} tests." . + format(len(passed_tests)), + 'SUCCESS', 'PASSED') if failed_tests: - printMessage('FAILURE', 'FAILED', "{} tests, listed below:" . - format(len(failed_tests))) + print_message("{} tests, listed below:" . + format(len(failed_tests)), + 'FAILURE', 'FAILED') failed_tests.sort() for test in failed_tests: - printMessage('FAILURE', "FAILED", "{}" . format(test)) - return False - return True + print_message("{}" . format(test), 'FAILURE', "FAILED") + + report.output() + return not bool(failed_tests) def create_argparse(): parser = argparse.ArgumentParser() parser.add_argument("-blender", nargs="+") parser.add_argument("-testdir", nargs=1) + parser.add_argument("-outdir", nargs=1) parser.add_argument("-idiff", nargs=1) return parser @@ -229,7 +433,7 @@ def main(): args = parser.parse_args() global COLORS - global BLENDER, ROOT, IDIFF + global BLENDER, TESTDIR, IDIFF, OUTDIR global TEMP_FILE, TEMP_FILE_MASK, TEST_SCRIPT global VERBOSE @@ -237,8 +441,12 @@ def main(): COLORS = COLORS_ANSI BLENDER = args.blender[0] - ROOT = args.testdir[0] + TESTDIR = args.testdir[0] IDIFF = args.idiff[0] + OUTDIR = args.outdir[0] + + if not os.path.exists(OUTDIR): + os.makedirs(OUTDIR) TEMP = tempfile.mkdtemp() TEMP_FILE_MASK = os.path.join(TEMP, "test") @@ -248,7 +456,7 @@ def main(): VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None - ok = run_all_tests(ROOT) + ok = run_all_tests(TESTDIR) # Cleanup temp files and folders if os.path.exists(TEMP_FILE): diff --git a/tests/python/render_layer/CMakeLists.txt b/tests/python/render_layer/CMakeLists.txt index 526b169bc3d..5ff985073e3 100644 --- a/tests/python/render_layer/CMakeLists.txt +++ b/tests/python/render_layer/CMakeLists.txt @@ -170,4 +170,6 @@ RENDER_LAYER_TEST(scene_copy_b) RENDER_LAYER_TEST(scene_copy_c) RENDER_LAYER_TEST(scene_copy_d) RENDER_LAYER_TEST(scene_copy_e) +RENDER_LAYER_TEST(scene_copy_f) +RENDER_LAYER_TEST(scene_delete) RENDER_LAYER_TEST(scene_write_read) diff --git a/tests/python/render_layer/render_layer_common.py b/tests/python/render_layer/render_layer_common.py index f6e16459ed3..adc32f062ea 100644 --- a/tests/python/render_layer/render_layer_common.py +++ b/tests/python/render_layer/render_layer_common.py @@ -55,9 +55,15 @@ def get_layer(layer): name = layer.get(b'name') data['name'] = name - data['active_object'] = layer.get((b'basact', b'object', b'id', b'name'))[2:] data['engine'] = layer.get(b'engine') + active_base = layer.get_pointer(b'basact') + if active_base: + ob = active_base.get_pointer(b'object') + data['active_object'] = ob.get((b'id', b'name'))[2:] + else: + data['active_object'] = "" + objects = [] for link in linkdata_iter(layer, b'object_bases'): ob = link.get_pointer(b'object') @@ -747,6 +753,7 @@ class Clay: layer = self._scene.render_layers.new('Evaluation Test') layer.collections.unlink(layer.collections[0]) + self._scene.render_layers.active = layer bpy.context.workspace.render_layer = layer # remove all other layers diff --git a/tests/python/render_layer/test_evaluation_selectability_a.py b/tests/python/render_layer/test_evaluation_selectability_a.py index 6faebac79aa..393f406eefc 100644 --- a/tests/python/render_layer/test_evaluation_selectability_a.py +++ b/tests/python/render_layer/test_evaluation_selectability_a.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Selectability Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_selectability_b.py b/tests/python/render_layer/test_evaluation_selectability_b.py index f2a11865d09..ea05710ebcf 100644 --- a/tests/python/render_layer/test_evaluation_selectability_b.py +++ b/tests/python/render_layer/test_evaluation_selectability_b.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Selectability Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_selectability_c.py b/tests/python/render_layer/test_evaluation_selectability_c.py index 5ec6d6e7d9c..8487ee1a8af 100644 --- a/tests/python/render_layer/test_evaluation_selectability_c.py +++ b/tests/python/render_layer/test_evaluation_selectability_c.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Selectability Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_selectability_d.py b/tests/python/render_layer/test_evaluation_selectability_d.py index 3ce9c07c907..b2a924dd6bc 100644 --- a/tests/python/render_layer/test_evaluation_selectability_d.py +++ b/tests/python/render_layer/test_evaluation_selectability_d.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Selectability Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_selectability_e.py b/tests/python/render_layer/test_evaluation_selectability_e.py index df308845292..70ede58cb9e 100644 --- a/tests/python/render_layer/test_evaluation_selectability_e.py +++ b/tests/python/render_layer/test_evaluation_selectability_e.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Selectability Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_a.py b/tests/python/render_layer/test_evaluation_visibility_a.py index a3a6bb261f5..b9f57539330 100644 --- a/tests/python/render_layer/test_evaluation_visibility_a.py +++ b/tests/python/render_layer/test_evaluation_visibility_a.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_b.py b/tests/python/render_layer/test_evaluation_visibility_b.py index 4f81cd540e2..712cc9bd277 100644 --- a/tests/python/render_layer/test_evaluation_visibility_b.py +++ b/tests/python/render_layer/test_evaluation_visibility_b.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_c.py b/tests/python/render_layer/test_evaluation_visibility_c.py index 6b88f6e3c3a..5e378fb07e3 100644 --- a/tests/python/render_layer/test_evaluation_visibility_c.py +++ b/tests/python/render_layer/test_evaluation_visibility_c.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_d.py b/tests/python/render_layer/test_evaluation_visibility_d.py index b19166bd750..e4f0957bd26 100644 --- a/tests/python/render_layer/test_evaluation_visibility_d.py +++ b/tests/python/render_layer/test_evaluation_visibility_d.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_e.py b/tests/python/render_layer/test_evaluation_visibility_e.py index f9f11cb8f23..33c6d7be111 100644 --- a/tests/python/render_layer/test_evaluation_visibility_e.py +++ b/tests/python/render_layer/test_evaluation_visibility_e.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_evaluation_visibility_f.py b/tests/python/render_layer/test_evaluation_visibility_f.py index 497087cb9ca..66948b343cb 100644 --- a/tests/python/render_layer/test_evaluation_visibility_f.py +++ b/tests/python/render_layer/test_evaluation_visibility_f.py @@ -26,6 +26,7 @@ class UnitTesting(RenderLayerTesting): layer = scene.render_layers.new('Visibility Test') layer.collections.unlink(layer.collections[0]) + scene.render_layers.active = layer workspace.render_layer = layer scene_collection_mom = scene.master_collection.collections.new("Mom") diff --git a/tests/python/render_layer/test_object_copy.py b/tests/python/render_layer/test_object_copy.py index 69e05d46140..1e6e9e5b889 100644 --- a/tests/python/render_layer/test_object_copy.py +++ b/tests/python/render_layer/test_object_copy.py @@ -42,6 +42,7 @@ class UnitTesting(RenderLayerTesting): layer.collections.link(subzero) scene.render_layers.active_index = len(scene.render_layers) - 1 + bpy.context.workspace.render_layer = bpy.context.scene.render_layers['Fresh new Layer'] if mode == 'DUPLICATE': # assuming the latest layer is the active layer diff --git a/tests/python/render_layer/test_object_link_b.py b/tests/python/render_layer/test_object_link_b.py index e0228594be7..b18eb844fc4 100644 --- a/tests/python/render_layer/test_object_link_b.py +++ b/tests/python/render_layer/test_object_link_b.py @@ -19,6 +19,7 @@ class UnitTesting(RenderLayerTesting): See if we can link objects via bpy.context.scene_collection """ import bpy + bpy.context.scene.render_layers.active_index = len(bpy.context.scene.render_layers) - 1 bpy.context.workspace.render_layer = bpy.context.scene.render_layers['Viewport'] master_collection = bpy.context.scene_collection self.do_object_link(master_collection) diff --git a/tests/python/render_layer/test_operator_context.py b/tests/python/render_layer/test_operator_context.py index 05a8957fa20..5055d957551 100644 --- a/tests/python/render_layer/test_operator_context.py +++ b/tests/python/render_layer/test_operator_context.py @@ -88,6 +88,10 @@ class UnitTesting(RenderLayerTesting): layer.collections.active_index = 3 self.assertEqual(layer.collections.active.name, 'scorpion') + # Change active scene layer (do it for workspace too just to don't get mangled in workspace bugs) + scene = bpy.context.scene + scene.render_layers.active_index = len(scene.render_layers) - 2 + self.assertEqual(scene.render_layers.active.name, "Viewport") bpy.context.workspace.render_layer = bpy.context.scene.render_layers['Viewport'] # old layer diff --git a/tests/python/render_layer/test_scene_copy_d.py b/tests/python/render_layer/test_scene_copy_d.py index 54988f49036..f398650eade 100644 --- a/tests/python/render_layer/test_scene_copy_d.py +++ b/tests/python/render_layer/test_scene_copy_d.py @@ -16,7 +16,7 @@ from render_layer_common import * class UnitTesting(RenderLayerTesting): def test_scene_layers_link(self): """ - See if scene copying 'FULL_COPY' is working for scene layers + See if scene copying 'LINK_OBJECTS' is working for scene layers """ import os ROOT = self.get_root() diff --git a/tests/python/render_layer/test_scene_copy_f.py b/tests/python/render_layer/test_scene_copy_f.py new file mode 100644 index 00000000000..fd4298675f2 --- /dev/null +++ b/tests/python/render_layer/test_scene_copy_f.py @@ -0,0 +1,94 @@ +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest +import os +import sys + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def test_shared_layer_collections_copy_full(self): + """ + See if scene copying 'FULL_COPY' is keeping collections visibility + and selectability. + """ + import os + import bpy + + scene = bpy.context.scene + + hide_lookup = [0, 1, 1, 0] + hide_lookup_sub = [1, 0, 1] + + hide_select_lookup = [0, 0, 1, 1] + hide_select_lookup_sub = [1, 0, 1, 0] + new_collections = [] + + # clean everything + for layer in scene.render_layers: + while layer.collections: + layer.collections.unlink(layer.collections[0]) + + # create new collections + for i in range(4): + collection = scene.master_collection.collections.new(str(i)) + new_collections.append(collection) + + for j in range(3): + sub_collection = collection.collections.new("{0}:{1}".format(i, j)) + + # link to the original scene + for layer in scene.render_layers: + for i, collection in enumerate(new_collections): + layer.collections.link(collection) + self.assertEqual(layer.collections[-1], layer.collections[i]) + + layer.collections[i].hide = hide_lookup[i] + layer.collections[i].hide_select = hide_select_lookup[i] + + for j, sub_collection in enumerate(layer.collections[i].collections): + sub_collection.hide = hide_lookup_sub[j] + sub_collection.hide_select = hide_select_lookup_sub[j] + + # copy scene + bpy.ops.scene.new(type='FULL_COPY') + new_scene = bpy.context.scene + self.assertNotEqual(scene, new_scene) + + # update depsgrah + scene.update() # update depsgraph + + # compare scenes + for h, layer in enumerate(scene.render_layers): + new_layer = new_scene.render_layers[h] + + for i, collection in enumerate(layer.collections): + new_collection = new_layer.collections[i] + self.assertEqual(collection.hide, new_collection.hide) + self.assertEqual(collection.hide_select, new_collection.hide_select) + + for j, sub_collection in enumerate(layer.collections[i].collections): + new_sub_collection = new_collection.collections[j] + self.assertEqual(sub_collection.hide, new_sub_collection.hide) + self.assertEqual(sub_collection.hide_select, new_sub_collection.hide_select) + + +# ############################################################ +# Main - Same For All Render Layer Tests +# ############################################################ + +if __name__ == '__main__': + import sys + + extra_arguments = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] + sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 2:] if "--" in sys.argv else []) + + UnitTesting._extra_arguments = extra_arguments + unittest.main() diff --git a/tests/python/render_layer/test_scene_delete.py b/tests/python/render_layer/test_scene_delete.py new file mode 100644 index 00000000000..cd59a446c9a --- /dev/null +++ b/tests/python/render_layer/test_scene_delete.py @@ -0,0 +1,39 @@ +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest +import os +import sys + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def test_scene_delete(self): + """ + See if a scene can be properly deleted + """ + import bpy + + scene = bpy.context.scene + bpy.data.scenes.new('New') + bpy.data.scenes.remove(scene) + + +# ############################################################ +# Main - Same For All Render Layer Tests +# ############################################################ + +if __name__ == '__main__': + import sys + + extra_arguments = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] + sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 2:] if "--" in sys.argv else []) + + UnitTesting._extra_arguments = extra_arguments + unittest.main() |