Such IDs are tagged with the new `LIB_TAG_RUNTIME`. They are essentially like any other regular ID, except that they do not get written in .blend files. They also do not make their linked data usages directly linked. They do be written in undo steps however. This tag should be ignored in any non-Main IDs (e.g. evaluated data, `NO_MAIN`, etc.). This commit also adds a new RNA ID property, `is_runtime`. This is not a direct mapping to the DNA tag, as a non-main ID will also return True, and the property is only editable for Main IDs. Some basic testing for expected behavior of that new tag was also added to `blendfile_io` py unittest. Required for brush asset project, see T101908. Reviewed By: brecht Differential Revision: https://developer.blender.org/D16675
168 lines
5.7 KiB
Python
168 lines
5.7 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# ./blender.bin --background -noaudio --python tests/python/bl_blendfile_io.py
|
|
import bpy
|
|
import os
|
|
import sys
|
|
|
|
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
|
from bl_blendfile_utils import TestHelper
|
|
|
|
|
|
class TestBlendFileSaveLoadBasic(TestHelper):
|
|
|
|
def __init__(self, args):
|
|
self.args = args
|
|
|
|
def test_save_load(self):
|
|
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
|
|
|
|
bpy.data.meshes.new("OrphanedMesh")
|
|
|
|
output_dir = self.args.output_dir
|
|
self.ensure_path(output_dir)
|
|
|
|
# Take care to keep the name unique so multiple test jobs can run at once.
|
|
output_path = os.path.join(output_dir, "blendfile_io.blend")
|
|
|
|
orig_data = self.blender_data_to_tuple(bpy.data, "orig_data 1")
|
|
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
|
|
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
|
|
|
|
read_data = self.blender_data_to_tuple(bpy.data, "read_data 1")
|
|
|
|
# We have orphaned data, which should be removed by file reading, so there should not be equality here.
|
|
assert orig_data != read_data
|
|
|
|
bpy.data.orphans_purge()
|
|
|
|
orig_data = self.blender_data_to_tuple(bpy.data, "orig_data 2")
|
|
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_path, check_existing=False, compress=False)
|
|
bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False)
|
|
|
|
read_data = self.blender_data_to_tuple(bpy.data, "read_data 2")
|
|
|
|
assert orig_data == read_data
|
|
|
|
|
|
# NOTE: Technically this should rather be in `bl_id_management.py` test, but that file uses `unittest` module,
|
|
# which makes mixing it with tests system used here and passing extra parameters complicated.
|
|
# Since the main effect of 'RUNTIME' ID tag is on file save, it can as well be here for now.
|
|
class TestIdRuntimeTag(TestHelper):
|
|
|
|
def __init__(self, args):
|
|
self.args = args
|
|
|
|
def unique_blendfile_name(self, base_name):
|
|
return base_name + self.__class__.__name__ + ".blend"
|
|
|
|
def test_basics(self):
|
|
output_dir = self.args.output_dir
|
|
self.ensure_path(output_dir)
|
|
bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
|
|
|
|
obj = bpy.data.objects['Cube']
|
|
assert obj.is_runtime_data == False
|
|
assert bpy.context.view_layer.depsgraph.ids['Cube'].is_runtime_data == True
|
|
|
|
output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile"))
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
|
|
|
|
bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
|
|
obj = bpy.data.objects['Cube']
|
|
assert obj.is_runtime_data == False
|
|
|
|
obj.is_runtime_data = True
|
|
assert obj.is_runtime_data == True
|
|
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
|
|
bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
|
|
|
|
assert 'Cube' not in bpy.data.objects
|
|
mesh = bpy.data.meshes['Cube']
|
|
assert mesh.is_runtime_data == False
|
|
assert mesh.users == 0
|
|
|
|
def test_linking(self):
|
|
output_dir = self.args.output_dir
|
|
self.ensure_path(output_dir)
|
|
bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
|
|
|
|
material = bpy.data.materials.new("LibMaterial")
|
|
material.use_fake_user = True
|
|
|
|
output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_runtimetag_basic"))
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False)
|
|
|
|
bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
|
|
|
|
obj = bpy.data.objects['Cube']
|
|
assert obj.is_runtime_data == False
|
|
obj.is_runtime_data = True
|
|
|
|
link_dir = os.path.join(output_lib_path, "Material")
|
|
bpy.ops.wm.link(directory=link_dir, filename="LibMaterial")
|
|
|
|
linked_material = bpy.data.materials['LibMaterial']
|
|
assert linked_material.is_library_indirect == False
|
|
|
|
obj.material_slots[0].link = 'OBJECT'
|
|
obj.material_slots[0].material = linked_material
|
|
|
|
output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile"))
|
|
bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
|
|
|
|
# Only usage of this linked material is a runtime ID (object),
|
|
# so writing .blend file will have properly reset its tag to indirectly linked data.
|
|
assert linked_material.is_library_indirect == True
|
|
|
|
bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
|
|
|
|
assert 'Cube' not in bpy.data.objects
|
|
assert 'LibMaterial' not in bpy.data.materials
|
|
mesh = bpy.data.meshes['Cube']
|
|
assert mesh.is_runtime_data == False
|
|
assert mesh.users == 0
|
|
|
|
|
|
TESTS = (
|
|
TestBlendFileSaveLoadBasic,
|
|
|
|
TestIdRuntimeTag,
|
|
)
|
|
|
|
|
|
def argparse_create():
|
|
import argparse
|
|
|
|
# When --help or no args are given, print this help
|
|
description = "Test basic IO of blend file."
|
|
parser = argparse.ArgumentParser(description=description)
|
|
parser.add_argument(
|
|
"--output-dir",
|
|
dest="output_dir",
|
|
default=".",
|
|
help="Where to output temp saved blendfiles",
|
|
required=False,
|
|
)
|
|
|
|
return parser
|
|
|
|
|
|
def main():
|
|
args = argparse_create().parse_args()
|
|
|
|
# Don't write thumbnails into the home directory.
|
|
bpy.context.preferences.filepaths.file_preview_type = 'NONE'
|
|
|
|
for Test in TESTS:
|
|
Test(args).run_all_tests()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [])
|
|
main()
|