diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2017-02-14 20:15:09 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2017-02-14 20:21:46 +0300 |
commit | b9762fed932b7f508aec5650fd80679cf635d677 (patch) | |
tree | 69628258c4ed12e690dc4699c20179bb7d9de196 /tests/python/render_layer | |
parent | 6906a884eea23304cd6b9692bf50a18ab6330dc7 (diff) |
Layers: Unittesting - make them granular
Some tests may break Blender, which makes the entire unittest routine to fail.
They are now I isolate the tests into individual files
Kudos to Sybren Stüvel and Sergey Sharybin for the advice.
Note: at the moment test_link.py is failing (since a41bbfb7)
Diffstat (limited to 'tests/python/render_layer')
-rw-r--r-- | tests/python/render_layer/CMakeLists.txt | 106 | ||||
-rw-r--r-- | tests/python/render_layer/render_layer_common.py | 245 | ||||
-rw-r--r-- | tests/python/render_layer/test_active_collection.py | 76 | ||||
-rw-r--r-- | tests/python/render_layer/test_layer_linking.py | 116 | ||||
-rw-r--r-- | tests/python/render_layer/test_link.py | 55 | ||||
-rw-r--r-- | tests/python/render_layer/test_object_add.py | 125 | ||||
-rw-r--r-- | tests/python/render_layer/test_object_copy.py | 100 | ||||
-rw-r--r-- | tests/python/render_layer/test_object_delete.py | 104 | ||||
-rw-r--r-- | tests/python/render_layer/test_operator_context.py | 133 | ||||
-rw-r--r-- | tests/python/render_layer/test_scene_copy.py | 120 | ||||
-rw-r--r-- | tests/python/render_layer/test_scene_write_read.py | 150 | ||||
-rw-r--r-- | tests/python/render_layer/test_syncing.py | 120 |
12 files changed, 1450 insertions, 0 deletions
diff --git a/tests/python/render_layer/CMakeLists.txt b/tests/python/render_layer/CMakeLists.txt new file mode 100644 index 00000000000..04c8a4eea2a --- /dev/null +++ b/tests/python/render_layer/CMakeLists.txt @@ -0,0 +1,106 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +# --env-system-scripts allows to run without the install target. + +# Use '--write-blend=/tmp/test.blend' to view output + +# Some tests are interesting but take too long to run +# and don't give deterministic results +set(USE_EXPERIMENTAL_TESTS FALSE) + +set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/../lib/tests) +set(TEST_OUT_DIR ${CMAKE_BINARY_DIR}/tests) + +# ugh, any better way to do this on testing only? +execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR}) + +#~ if(NOT IS_DIRECTORY ${TEST_SRC_DIR}) +#~ message(FATAL_ERROR "CMake test directory not found!") +#~ endif() + +# 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) + else() + set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender.app/Contents/MacOS/blender) + endif() +else() + set(TEST_BLENDER_EXE ${EXECUTABLE_OUTPUT_PATH}/blender) +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 ${TEST_BLENDER_EXE} --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) + + +# ------------------------------------------------------------------------------ + +add_test(render_layer_scene_write_read ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_scene_write_read.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_scene_copy ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_scene_copy.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_syncing ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_syncing.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_layer_linking ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_layer_linking.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_active_collection ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_active_collection.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_object_delete ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_object_delete.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_link ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_link.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_operator_context ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_operator_context.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_object_add ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_object_add.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + +add_test(render_layer_object_copy ${TEST_BLENDER_EXE} + --python ${CMAKE_CURRENT_LIST_DIR}/test_object_copy.py -- + --testdir="${TEST_SRC_DIR}/layers" +) + diff --git a/tests/python/render_layer/render_layer_common.py b/tests/python/render_layer/render_layer_common.py new file mode 100644 index 00000000000..4a289936b2b --- /dev/null +++ b/tests/python/render_layer/render_layer_common.py @@ -0,0 +1,245 @@ +import unittest + +# ############################################################ +# Layer Collection Crawler +# ############################################################ + +def listbase_iter(data, struct, listbase): + element = data.get_pointer((struct, listbase, b'first')) + while element is not None: + yield element + element = element.get_pointer(b'next') + + +def linkdata_iter(collection, data): + element = collection.get_pointer((data, b'first')) + while element is not None: + yield element + element = element.get_pointer(b'next') + + +def get_layer_collection(layer_collection): + data = {} + flag = layer_collection.get(b'flag') + + data['is_visible'] = (flag & (1 << 0)) != 0; + data['is_selectable'] = (flag & (1 << 1)) != 0; + data['is_folded'] = (flag & (1 << 2)) != 0; + + scene_collection = layer_collection.get_pointer(b'scene_collection') + if scene_collection is None: + name = 'Fail!' + else: + name = scene_collection.get(b'name') + data['name'] = name + + objects = [] + for link in linkdata_iter(layer_collection, b'object_bases'): + ob_base = link.get_pointer(b'data') + ob = ob_base.get_pointer(b'object') + objects.append(ob.get((b'id', b'name'))[2:]) + data['objects'] = objects + + collections = {} + for nested_layer_collection in linkdata_iter(layer_collection, b'layer_collections'): + subname, subdata = get_layer_collection(nested_layer_collection) + collections[subname] = subdata + data['collections'] = collections + + return name, data + + +def get_layer(layer): + data = {} + 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') + + objects = [] + for link in linkdata_iter(layer, b'object_bases'): + ob = link.get_pointer(b'object') + objects.append(ob.get((b'id', b'name'))[2:]) + data['objects'] = objects + + collections = {} + for layer_collection in linkdata_iter(layer, b'layer_collections'): + subname, subdata = get_layer_collection(layer_collection) + collections[subname] = subdata + data['collections'] = collections + + return name, data + + +def get_layers(scene): + """Return all the render layers and their data""" + layers = {} + for layer in linkdata_iter(scene, b'render_layers'): + name, data = get_layer(layer) + layers[name] = data + return layers + + +def get_scene_collection_objects(collection, listbase): + objects = [] + for link in linkdata_iter(collection, listbase): + ob = link.get_pointer(b'data') + if ob is None: + name = 'Fail!' + else: + name = ob.get((b'id', b'name'))[2:] + objects.append(name) + return objects + + +def get_scene_collection(collection): + """""" + data = {} + name = collection.get(b'name') + + data['name'] = name + data['filter'] = collection.get(b'filter') + + data['objects'] = get_scene_collection_objects(collection, b'objects') + data['filter_objects'] = get_scene_collection_objects(collection, b'filter_objects') + + collections = {} + for nested_collection in linkdata_iter(collection, b'scene_collections'): + subname, subdata = get_scene_collection(nested_collection) + collections[subname] = subdata + data['collections'] = collections + + return name, data + + +def get_scene_collections(scene): + """Return all the scene collections ahd their data""" + master_collection = scene.get_pointer(b'collection') + return get_scene_collection(master_collection) + + +def query_scene(filepath, name, callbacks): + """Return the equivalent to bpy.context.scene""" + import blendfile + with blendfile.open_blend(filepath) as blend: + scenes = [block for block in blend.blocks if block.code == b'SC'] + for scene in scenes: + if scene.get((b'id', b'name'))[2:] == name: + output = [] + for callback in callbacks: + output.append(callback(scene)) + return output + + +# ############################################################ +# Utils +# ############################################################ + +def import_blendfile(): + import bpy + import os, sys + path = os.path.join( + bpy.utils.resource_path('LOCAL'), + 'scripts', + 'addons', + 'io_blend_utils', + 'blend', + ) + + if path not in sys.path: + sys.path.append(path) + + +def dump(data): + import json + return json.dumps( + data, + sort_keys=True, + indent=4, + separators=(',', ': '), + ) + + +# ############################################################ +# Tests +# ############################################################ + +PDB = False +DUMP_DIFF = True + +def compare_files(file_a, file_b): + import filecmp + + if not filecmp.cmp( + file_a, + file_b): + + if DUMP_DIFF: + import subprocess + subprocess.call(["diff", "-u", file_a, file_b]) + + if PDB: + import pdb + print("Files differ:", file_a, file_b) + pdb.set_trace() + + return False + + return True + + +class RenderLayerTesting(unittest.TestCase): + _test_simple = False + _extra_arguments = [] + + @classmethod + def setUpClass(cls): + """Runs once""" + cls.pretest_import_blendfile() + cls.pretest_parsing() + + @classmethod + def setUp(cls): + """Runs once per test""" + import bpy + bpy.ops.wm.read_factory_settings() + + def path_exists(self, filepath): + import os + self.assertTrue( + os.path.exists(filepath), + "Test file \"{0}\" not found".format(filepath)) + + @classmethod + def get_root(cls): + """ + return the folder with the test files + """ + arguments = {} + for argument in cls._extra_arguments: + name, value = argument.split('=') + cls.assertTrue(name and name.startswith("--"), "Invalid argument \"{0}\"".format(argument)) + cls.assertTrue(value, "Invalid argument \"{0}\"".format(argument)) + arguments[name[2:]] = value.strip('"') + + return arguments.get('testdir') + + @classmethod + def pretest_parsing(cls): + """ + Test if the arguments are properly set, and store ROOT + name has extra _ because we need this test to run first + """ + root = cls.get_root() + cls.assertTrue(root, "Testdir not set") + + @staticmethod + def pretest_import_blendfile(): + """ + Make sure blendfile imports with no problems + name has extra _ because we need this test to run first + """ + import_blendfile() + import blendfile + diff --git a/tests/python/render_layer/test_active_collection.py b/tests/python/render_layer/test_active_collection.py new file mode 100644 index 00000000000..5cd66538ff5 --- /dev/null +++ b/tests/python/render_layer/test_active_collection.py @@ -0,0 +1,76 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_active_collection.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def test_active_collection(self): + """ + See if active collection index is working + layer.collections.active_index works recursively + """ + import bpy + import os + + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + + scene = bpy.context.scene + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + layer = scene.render_layers.new('Fresh new Layer') + layer.collections.link(subzero) + + lookup = [ + 'Master Collection', + '1', + 'sub-zero', + 'scorpion', + '2', + '3', + '4', + '5', + 'sub-zero', + 'scorpion'] + + for i, name in enumerate(lookup): + layer.collections.active_index = i + self.assertEqual(name, layer.collections.active.name, + "Collection index mismatch: [{0}] : {1} != {2}".format( + i, name, layer.collections.active.name)) + + +# ############################################################ +# 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_layer_linking.py b/tests/python/render_layer/test_layer_linking.py new file mode 100644 index 00000000000..35af3d6a968 --- /dev/null +++ b/tests/python/render_layer/test_layer_linking.py @@ -0,0 +1,116 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_layer_linking.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_layer_linking(self, filepath_json, link_mode): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + + scene = bpy.context.scene + + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + + # test linking sync + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + + # test unlinking sync + layer = scene.render_layers.new('Fresh new Layer') + + if link_mode in {'COLLECTION_LINK', 'COLLECTION_UNLINK'}: + layer.collections.link(subzero) + + if link_mode == 'COLLECTION_UNLINK': + initial_collection = layer.collections['Master Collection'] + layer.collections.unlink(initial_collection) + + # save file + filepath_nested = os.path.join(dirpath, 'nested.blend') + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_nested) + + # get the generated json + datas = query_scene(filepath_nested, 'Main', (get_scene_collections, get_layers)) + self.assertTrue(datas, "Data is not valid") + + filepath_nested_json = os.path.join(dirpath, "nested.json") + with open(filepath_nested_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_nested_json, + filepath_json, + ), + "Scene dump files differ") + + def test_syncing_layer_new(self): + """ + See if the creation of new layers is going well + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_new_layer.json') + self.do_layer_linking(filepath_json, 'LAYER_NEW') + + def test_syncing_layer_collection_link(self): + """ + See if the creation of new layers is going well + And linking a new scene collection in the layer works + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_layer_collection_link.json') + self.do_layer_linking(filepath_json, 'COLLECTION_LINK') + + def test_syncing_layer_collection_unlink(self): + """ + See if the creation of new layers is going well + And unlinking the origin scene collection works + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_layer_collection_unlink.json') + self.do_layer_linking(filepath_json, 'COLLECTION_UNLINK') + + +# ############################################################ +# 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_link.py b/tests/python/render_layer/test_link.py new file mode 100644 index 00000000000..387d065dae0 --- /dev/null +++ b/tests/python/render_layer/test_link.py @@ -0,0 +1,55 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_link.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_link(self, master_collection): + import bpy + self.assertEqual(master_collection.name, "Master Collection") + self.assertEqual(master_collection, bpy.context.scene.master_collection) + master_collection.objects.link(bpy.data.objects.new('object', None)) + + def test_link_scene(self): + """ + See if we can link objects + """ + import bpy + master_collection = bpy.context.scene.master_collection + self.do_link(master_collection) + + def test_link_context(self): + """ + 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 + master_collection = bpy.context.scene_collection + self.do_link(master_collection) + + +# ############################################################ +# 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_object_add.py b/tests/python/render_layer/test_object_add.py new file mode 100644 index 00000000000..4c9402bd2fb --- /dev/null +++ b/tests/python/render_layer/test_object_add.py @@ -0,0 +1,125 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_scene_copy.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_object_add(self, filepath_json, add_mode): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + + scene = bpy.context.scene + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + layer = scene.render_layers.new('Fresh new Layer') + layer.collections.link(subzero) + + # change active collection + layer.collections.active_index = 3 + self.assertEqual(layer.collections.active.name, 'scorpion', "Run: test_syncing_object_add") + + # change active layer + override = bpy.context.copy() + override["render_layer"] = layer + override["scene_collection"] = layer.collections.active.collection + + # add new objects + if add_mode == 'EMPTY': + bpy.ops.object.add(override) # 'Empty' + + elif add_mode == 'CYLINDER': + bpy.ops.mesh.primitive_cylinder_add(override) # 'Cylinder' + + elif add_mode == 'TORUS': + bpy.ops.mesh.primitive_torus_add(override) # 'Torus' + + # save file + filepath_objects = os.path.join(dirpath, 'objects.blend') + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_objects) + + # get the generated json + datas = query_scene(filepath_objects, 'Main', (get_scene_collections, get_layers)) + self.assertTrue(datas, "Data is not valid") + + filepath_objects_json = os.path.join(dirpath, "objects.json") + with open(filepath_objects_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_objects_json, + filepath_json, + ), + "Scene dump files differ") + + def test_syncing_object_add_empty(self): + """ + See if new objects are added to the correct collection + bpy.ops.object.add() + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_object_add_empty.json') + self.do_object_add(filepath_json, 'EMPTY') + + def test_syncing_object_add_cylinder(self): + """ + See if new objects are added to the correct collection + bpy.ops.mesh.primitive_cylinder_add() + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_object_add_cylinder.json') + self.do_object_add(filepath_json, 'CYLINDER') + + def test_syncing_object_add_torus(self): + """ + See if new objects are added to the correct collection + bpy.ops.mesh.primitive_torus_add() + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_object_add_torus.json') + self.do_object_add(filepath_json, 'TORUS') + + +# ############################################################ +# 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_object_copy.py b/tests/python/render_layer/test_object_copy.py new file mode 100644 index 00000000000..419f8f418a5 --- /dev/null +++ b/tests/python/render_layer/test_object_copy.py @@ -0,0 +1,100 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_object_copy.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_object_copy(self, mode): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_json = os.path.join(ROOT, 'layers_object_copy_duplicate.json') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + + scene = bpy.context.scene + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + layer = scene.render_layers.new('Fresh new Layer') + layer.collections.link(subzero) + + scene.render_layers.active_index = len(scene.render_layers) - 1 + + if mode == 'DUPLICATE': + # assuming the latest layer is the active layer + bpy.ops.object.select_all(action='DESELECT') + three_c.select_set(action='SELECT') + bpy.ops.object.duplicate() + + elif mode == 'NAMED': + bpy.ops.object.add_named(name=three_c.name) + + # save file + filepath_objects = os.path.join(dirpath, 'objects.blend') + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_objects) + + # get the generated json + datas = query_scene(filepath_objects, 'Main', (get_scene_collections, get_layers)) + self.assertTrue(datas, "Data is not valid") + + filepath_objects_json = os.path.join(dirpath, "objects.json") + with open(filepath_objects_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_objects_json, + filepath_json, + ), + "Scene dump files differ") + + def test_copy_object(self): + """ + OBJECT_OT_duplicate + """ + self.do_object_copy('DUPLICATE') + + def test_copy_object_named(self): + """ + OBJECT_OT_add_named + """ + self.do_object_copy('NAMED') + + +# ############################################################ +# 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_object_delete.py b/tests/python/render_layer/test_object_delete.py new file mode 100644 index 00000000000..693357d7de0 --- /dev/null +++ b/tests/python/render_layer/test_object_delete.py @@ -0,0 +1,104 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_scene_copy.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_object_delete(self, del_mode): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_reference_json = os.path.join(ROOT, 'layers_object_delete.json') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_d = bpy.data.objects.get('T.3d') + + scene = bpy.context.scene + + # mangle the file a bit with some objects linked across collections + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + subzero.objects.link(three_d) + scorpion.objects.link(three_b) + scorpion.objects.link(three_d) + + # object to delete + ob = three_d + + # delete object + if del_mode == 'DATA': + bpy.data.objects.remove(ob, do_unlink=True) + + elif del_mode == 'OPERATOR': + bpy.ops.object.select_all(action='DESELECT') + ob.select_set(action='SELECT') + bpy.ops.object.delete() + + # save file + filepath_generated = os.path.join(dirpath, 'generated.blend') + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_generated) + + # get the generated json + datas = query_scene(filepath_generated, 'Main', (get_scene_collections, get_layers)) + self.assertTrue(datas, "Data is not valid") + + filepath_generated_json = os.path.join(dirpath, "generated.json") + with open(filepath_generated_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_generated_json, + filepath_reference_json, + ), + "Scene dump files differ") + + def test_object_delete_data(self): + """ + See if objects are removed correctly from all related collections + bpy.data.objects.remove() + """ + self.do_object_delete('DATA') + + def test_object_delete_operator(self): + """ + See if new objects are added to the correct collection + bpy.ops.object.del() + """ + self.do_object_delete('OPERATOR') + + +# ############################################################ +# 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_operator_context.py b/tests/python/render_layer/test_operator_context.py new file mode 100644 index 00000000000..82fd4be3c77 --- /dev/null +++ b/tests/python/render_layer/test_operator_context.py @@ -0,0 +1,133 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_scene_copy.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def test_operator_context(self): + """ + See if render layer context is properly set/get with operators overrides + when we set render_layer in context, the collection should change as well + """ + import bpy + import os + + class SampleOperator(bpy.types.Operator): + bl_idname = "testing.sample" + bl_label = "Sample Operator" + + render_layer = bpy.props.StringProperty( + default="Not Set", + options={'SKIP_SAVE'}, + ) + + scene_collection = bpy.props.StringProperty( + default="", + options={'SKIP_SAVE'}, + ) + + use_verbose = bpy.props.BoolProperty( + default=False, + options={'SKIP_SAVE'}, + ) + + def execute(self, context): + render_layer = context.render_layer + ret = {'FINISHED'} + + # this is simply playing safe + if render_layer.name != self.render_layer: + if self.use_verbose: + print('ERROR: Render Layer mismatch: "{0}" != "{1}"'.format( + render_layer.name, self.render_layer)) + ret = {'CANCELLED'} + + scene_collection_name = None + if self.scene_collection: + scene_collection_name = self.scene_collection + else: + scene_collection_name = render_layer.collections.active.name + + # while this is the real test + if context.scene_collection.name != scene_collection_name: + if self.use_verbose: + print('ERROR: Scene Collection mismatch: "{0}" != "{1}"'.format( + context.scene_collection.name, scene_collection_name)) + ret = {'CANCELLED'} + return ret + + bpy.utils.register_class(SampleOperator) + + # open sample file + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # change the file + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + scene = bpy.context.scene + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = subzero.collections.new('scorpion') + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + layer = scene.render_layers.new('Fresh new Layer') + layer.collections.unlink(layer.collections.active) + layer.collections.link(subzero) + layer.collections.active_index = 3 + self.assertEqual(layer.collections.active.name, 'scorpion') + + scene = bpy.context.scene + scene.render_layers.active_index = len(scene.render_layers) - 2 + self.assertEqual(scene.render_layers.active.name, "Render Layer") + + # old layer + self.assertEqual(bpy.ops.testing.sample(render_layer='Render Layer', use_verbose=True), {'FINISHED'}) + + # expected to fail + self.assertTrue(bpy.ops.testing.sample(render_layer=layer.name), {'CANCELLED'}) + + # set render layer and scene collection + override = bpy.context.copy() + override["render_layer"] = layer + override["scene_collection"] = subzero + self.assertEqual(bpy.ops.testing.sample(override, + render_layer=layer.name, + scene_collection=subzero.name, # 'sub-zero' + use_verbose=True), {'FINISHED'}) + + # set only render layer + override = bpy.context.copy() + override["render_layer"] = layer + + self.assertEqual(bpy.ops.testing.sample(override, + render_layer=layer.name, + scene_collection=layer.collections.active.name, # 'scorpion' + use_verbose=True), {'FINISHED'}) + + +# ############################################################ +# 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_copy.py b/tests/python/render_layer/test_scene_copy.py new file mode 100644 index 00000000000..bfa473a11d6 --- /dev/null +++ b/tests/python/render_layer/test_scene_copy.py @@ -0,0 +1,120 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_scene_copy.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_scene_copy(self, filepath_json_reference, copy_mode, data_callbacks): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + + (self.path_exists(f) for f in ( + filepath_layers, + filepath_json_reference, + )) + + filepath_saved = os.path.join(dirpath, '{0}.blend'.format(copy_mode)) + filepath_json = os.path.join(dirpath, "{0}.json".format(copy_mode)) + + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + bpy.ops.scene.new(type=copy_mode) + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_saved) + + datas = query_scene(filepath_saved, 'Main.001', data_callbacks) + self.assertTrue(datas, "Data is not valid") + + with open(filepath_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_json, + filepath_json_reference, + ), + "Scene copy \"{0}\" test failed".format(copy_mode.title())) + + def test_scene_collections_copy_full(self): + """ + See if scene copying 'FULL_COPY' is working for scene collections + """ + import os + ROOT = self.get_root() + + filepath_layers_json_copy = os.path.join(ROOT, 'layers_copy_full_simple.json') + self.do_scene_copy( + filepath_layers_json_copy, + 'FULL_COPY', + (get_scene_collections,)) + + def test_scene_collections_link(self): + """ + See if scene copying 'LINK_OBJECTS' is working for scene collections + """ + import os + ROOT = self.get_root() + + # note: nothing should change, so using `layers_simple.json` + filepath_layers_json_copy = os.path.join(ROOT, 'layers_simple.json') + self.do_scene_copy( + filepath_layers_json_copy, + 'LINK_OBJECTS', + (get_scene_collections,)) + + def test_scene_layers_copy(self): + """ + See if scene copying 'FULL_COPY' is working for scene layers + """ + import os + ROOT = self.get_root() + + filepath_layers_json_copy = os.path.join(ROOT, 'layers_copy_full.json') + self.do_scene_copy( + filepath_layers_json_copy, + 'FULL_COPY', + (get_scene_collections, get_layers)) + + def test_scene_layers_link(self): + """ + See if scene copying 'FULL_COPY' is working for scene layers + """ + import os + ROOT = self.get_root() + + filepath_layers_json_copy = os.path.join(ROOT, 'layers_copy_link.json') + self.do_scene_copy( + filepath_layers_json_copy, + 'LINK_OBJECTS', + (get_scene_collections, get_layers)) + + +# ############################################################ +# 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_write_read.py b/tests/python/render_layer/test_scene_write_read.py new file mode 100644 index 00000000000..020c81f7d37 --- /dev/null +++ b/tests/python/render_layer/test_scene_write_read.py @@ -0,0 +1,150 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_scene_write_read.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_scene_write_read(self, filepath_layers, filepath_layers_json, data_callbacks, do_read): + """ + See if write/read is working for scene collections and layers + """ + import bpy + import os + import tempfile + import filecmp + + with tempfile.TemporaryDirectory() as dirpath: + (self.path_exists(f) for f in (filepath_layers, filepath_layers_json)) + + filepath_doversion = os.path.join(dirpath, 'doversion.blend') + filepath_saved = os.path.join(dirpath, 'doversion_saved.blend') + filepath_read_json = os.path.join(dirpath, "read.json") + + # doversion + write test + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_doversion) + + datas = query_scene(filepath_doversion, 'Main', data_callbacks) + self.assertTrue(datas, "Data is not valid") + + filepath_doversion_json = os.path.join(dirpath, "doversion.json") + with open(filepath_doversion_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_doversion_json, + filepath_layers_json, + ), + "Run: test_scene_write_layers") + + if do_read: + # read test, simply open and save the file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_doversion) + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_saved) + + datas = query_scene(filepath_saved, 'Main', data_callbacks) + self.assertTrue(datas, "Data is not valid") + + with open(filepath_read_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_read_json, + filepath_layers_json, + ), + "Scene dump files differ") + + def test_scene_write_collections(self): + """ + See if the doversion and writing are working for scene collections + """ + import os + + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_layers_json = os.path.join(ROOT, 'layers_simple.json') + + self.do_scene_write_read( + filepath_layers, + filepath_layers_json, + (get_scene_collections,), + False) + + def test_scene_write_layers(self): + """ + See if the doversion and writing are working for collections and layers + """ + import os + + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_layers_json = os.path.join(ROOT, 'layers.json') + + self.do_scene_write_read( + filepath_layers, + filepath_layers_json, + (get_scene_collections, get_layers), + False) + + def test_scene_read_collections(self): + """ + See if read is working for scene collections + (run `test_scene_write_colections` first) + """ + import os + + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_layers_json = os.path.join(ROOT, 'layers_simple.json') + + self.do_scene_write_read( + filepath_layers, + filepath_layers_json, + (get_scene_collections,), + True) + + def test_scene_read_layers(self): + """ + See if read is working for scene layers + (run `test_scene_write_layers` first) + """ + import os + + ROOT = self.get_root() + filepath_layers = os.path.join(ROOT, 'layers.blend') + filepath_layers_json = os.path.join(ROOT, 'layers.json') + + self.do_scene_write_read( + filepath_layers, + filepath_layers_json, + (get_scene_collections, get_layers), + True) + + +# ############################################################ +# 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_syncing.py b/tests/python/render_layer/test_syncing.py new file mode 100644 index 00000000000..da367ac3818 --- /dev/null +++ b/tests/python/render_layer/test_syncing.py @@ -0,0 +1,120 @@ +# ./blender.bin --background -noaudio --python tests/python/render_layer/test_syncing.py -- --testdir="/data/lib/tests/" + +# ############################################################ +# Importing - Same For All Render Layer Tests +# ############################################################ + +import unittest + +import os, sys +sys.path.append(os.path.dirname(__file__)) + +from render_layer_common import * + + +# ############################################################ +# Testing +# ############################################################ + +class UnitTesting(RenderLayerTesting): + def do_syncing(self, filepath_json, unlink_mode): + import bpy + import os + import tempfile + import filecmp + + ROOT = self.get_root() + with tempfile.TemporaryDirectory() as dirpath: + filepath_layers = os.path.join(ROOT, 'layers.blend') + + # open file + bpy.ops.wm.open_mainfile('EXEC_DEFAULT', filepath=filepath_layers) + + # create sub-collections + three_b = bpy.data.objects.get('T.3b') + three_c = bpy.data.objects.get('T.3c') + three_d = bpy.data.objects.get('T.3d') + + scene = bpy.context.scene + + subzero = scene.master_collection.collections['1'].collections.new('sub-zero') + scorpion = scene.master_collection.collections['1'].collections.new('scorpion') + + # test linking sync + subzero.objects.link(three_b) + scorpion.objects.link(three_c) + + # test unlinking sync + if unlink_mode in {'OBJECT', 'COLLECTION'}: + scorpion.objects.link(three_d) + scorpion.objects.unlink(three_d) + + if unlink_mode == 'COLLECTION': + scorpion.objects.link(three_d) + scene.master_collection.collections['1'].collections.remove(subzero) + scene.master_collection.collections['1'].collections.remove(scorpion) + + # save file + filepath_nested = os.path.join(dirpath, 'nested.blend') + bpy.ops.wm.save_mainfile('EXEC_DEFAULT', filepath=filepath_nested) + + # get the generated json + datas = query_scene(filepath_nested, 'Main', (get_scene_collections, get_layers)) + self.assertTrue(datas, "Data is not valid") + + filepath_nested_json = os.path.join(dirpath, "nested.json") + with open(filepath_nested_json, "w") as f: + for data in datas: + f.write(dump(data)) + + self.assertTrue(compare_files( + filepath_nested_json, + filepath_json, + ), + "Scene dump files differ") + + def test_syncing_link(self): + """ + See if scene collections and layer collections are in sync + when we create new subcollections and link new objects + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_nested.json') + self.do_syncing(filepath_json, 'NONE') + + def test_syncing_unlink_object(self): + """ + See if scene collections and layer collections are in sync + when we create new subcollections, link new objects and unlink + some. + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers_nested.json') + self.do_syncing(filepath_json, 'OBJECT') + + def test_syncing_unlink_collection(self): + """ + See if scene collections and layer collections are in sync + when we create new subcollections, link new objects and unlink full collections + some. + """ + import os + ROOT = self.get_root() + filepath_json = os.path.join(ROOT, 'layers.json') + self.do_syncing(filepath_json, 'COLLECTION') + + +# ############################################################ +# 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() |