2023-06-15 05:09:04 +02:00
|
|
|
# SPDX-FileCopyrightText: 2020-2023 Blender Foundation
|
|
|
|
#
|
2022-02-10 23:07:11 +01:00
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
# ./blender.bin --background -noaudio --python tests/python/bl_blendfile_io.py
|
|
|
|
import bpy
|
|
|
|
import os
|
2020-02-14 11:02:22 +01:00
|
|
|
import sys
|
2020-02-13 17:48:00 +01:00
|
|
|
|
2020-02-14 11:02:22 +01:00
|
|
|
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
|
|
|
from bl_blendfile_utils import TestHelper
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
class TestBlendFileSaveLoadBasic(TestHelper):
|
|
|
|
|
|
|
|
def __init__(self, args):
|
|
|
|
self.args = args
|
|
|
|
|
|
|
|
def test_save_load(self):
|
2021-03-04 07:43:12 +01:00
|
|
|
bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True)
|
|
|
|
|
2020-02-13 17:48:00 +01:00
|
|
|
bpy.data.meshes.new("OrphanedMesh")
|
|
|
|
|
|
|
|
output_dir = self.args.output_dir
|
|
|
|
self.ensure_path(output_dir)
|
2020-12-10 07:16:18 +01:00
|
|
|
|
|
|
|
# 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")
|
2020-02-13 17:48:00 +01:00
|
|
|
|
2020-02-14 11:02:22 +01:00
|
|
|
orig_data = self.blender_data_to_tuple(bpy.data, "orig_data 1")
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2020-02-14 11:02:22 +01:00
|
|
|
read_data = self.blender_data_to_tuple(bpy.data, "read_data 1")
|
|
|
|
|
2020-02-13 17:48:00 +01:00
|
|
|
# We have orphaned data, which should be removed by file reading, so there should not be equality here.
|
2022-09-14 08:18:59 +02:00
|
|
|
assert orig_data != read_data
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
bpy.data.orphans_purge()
|
2020-02-14 11:02:22 +01:00
|
|
|
|
|
|
|
orig_data = self.blender_data_to_tuple(bpy.data, "orig_data 2")
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2020-02-14 11:02:22 +01:00
|
|
|
read_data = self.blender_data_to_tuple(bpy.data, "read_data 2")
|
|
|
|
|
2022-09-14 08:18:59 +02:00
|
|
|
assert orig_data == read_data
|
2020-02-13 17:48:00 +01:00
|
|
|
|
|
|
|
|
2022-12-16 02:13:40 +01:00
|
|
|
# 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']
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert obj.is_runtime_data is False
|
2022-12-29 08:49:08 +01:00
|
|
|
assert bpy.context.view_layer.depsgraph.ids['Cube'].is_runtime_data
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
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']
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert obj.is_runtime_data is False
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
obj.is_runtime_data = True
|
2022-12-29 08:49:08 +01:00
|
|
|
assert obj.is_runtime_data
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
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']
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert mesh.is_runtime_data is False
|
2022-12-16 02:13:40 +01:00
|
|
|
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")
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
# Use a dummy mesh as user of the material, such that the material is saved
|
|
|
|
# without having to use fake user on it.
|
|
|
|
mesh = bpy.data.meshes.new("LibMesh")
|
|
|
|
mesh.materials.append(material)
|
|
|
|
mesh.use_fake_user = True
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
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']
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert obj.is_runtime_data is False
|
2022-12-16 02:13:40 +01:00
|
|
|
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']
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert linked_material.is_library_indirect is False
|
2022-12-16 02:13:40 +01:00
|
|
|
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
link_dir = os.path.join(output_lib_path, "Mesh")
|
|
|
|
bpy.ops.wm.link(directory=link_dir, filename="LibMesh")
|
|
|
|
|
|
|
|
linked_mesh = bpy.data.meshes['LibMesh']
|
|
|
|
assert linked_mesh.is_library_indirect is False
|
|
|
|
|
|
|
|
obj.data = linked_mesh
|
2022-12-16 02:13:40 +01:00
|
|
|
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.
|
2022-12-29 08:49:08 +01:00
|
|
|
assert linked_material.is_library_indirect
|
2022-12-16 02:13:40 +01:00
|
|
|
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
# Only usage of this linked mesh is a runtime ID (object), but it is flagged as 'fake user' in its library,
|
|
|
|
# so writing .blend file will have kept its tag to directly linked data.
|
|
|
|
assert not linked_mesh.is_library_indirect
|
|
|
|
|
2022-12-16 02:13:40 +01:00
|
|
|
bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
|
|
|
|
|
|
|
|
assert 'Cube' not in bpy.data.objects
|
Address #105786: How to handle linked data with 'fake user' set.
133dde41bb changed how 'fake user' flag is handled with linked data.
Previous behavior was a bug/inconsistency, in that the 'directly linked'
tag would be 'over-set' and never cleared, forcing saving references to
a lot of unused linked data.
Note that ideally, 'Fake user' flag should be ignored, and the only way
to decide whether to keep or not a linked ID should be whether it's
actually used by some local data.
However, #103867 and #105687 show that this is causing issues in some cases,
where users wrongly relied on the linked data's pre-defined 'Fake user' flag
to keep their linked data in their production files, even if said data had no
real user.
While not ideal, for now we should consider 'fake user' flag for linked data
as a real usage case. A better handling of this edge-case is related to
wider designs aboud handling of 'non used' data on file save, whether
linked IDs should keep track of being explicitly or implicitly linked by
the user, etc.
2023-03-20 15:01:53 +01:00
|
|
|
assert 'LibMaterial' in bpy.data.materials # Pulled-in by the linked mesh.
|
|
|
|
linked_mesh = bpy.data.meshes['LibMesh']
|
|
|
|
assert linked_mesh.use_fake_user is True
|
|
|
|
assert linked_mesh.is_library_indirect is False
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
|
2020-02-13 17:48:00 +01:00
|
|
|
TESTS = (
|
|
|
|
TestBlendFileSaveLoadBasic,
|
2022-12-16 02:13:40 +01:00
|
|
|
|
|
|
|
TestIdRuntimeTag,
|
2020-02-13 17:48:00 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2021-03-04 07:43:12 +01:00
|
|
|
# Don't write thumbnails into the home directory.
|
2021-09-06 10:56:02 +02:00
|
|
|
bpy.context.preferences.filepaths.file_preview_type = 'NONE'
|
2021-03-04 07:43:12 +01:00
|
|
|
|
2020-02-13 17:48:00 +01:00
|
|
|
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 [])
|
2020-04-27 17:43:47 +02:00
|
|
|
main()
|