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)
This commit is contained in:
245
tests/python/render_layer/render_layer_common.py
Normal file
245
tests/python/render_layer/render_layer_common.py
Normal file
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user