From e20a284cb944a5297b66f53328400992eebf6282 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 25 Aug 2023 11:01:46 +0200 Subject: [PATCH] Fix #111488: Copying object crashes Blender after recent refactor. Caused by 8bb5916183. Trivial mistake, would have been caught by a basic test, so also added said test for the 'write partial' feature. --- source/blender/blenloader/BLO_readfile.h | 2 +- source/blender/blenloader/intern/readfile.cc | 4 +- tests/python/bl_blendfile_io.py | 47 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index be08dcd668e..1df91f43cdd 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -497,7 +497,7 @@ void BLO_main_expander(BLOExpandDoitCallback expand_doit_func); * Loop over all ID data in Main to mark relations. * Set (id->tag & LIB_TAG_NEED_EXPAND) to mark expanding. Flags get cleared after expanding. * - * \param fdhandle: usually file-data, or own handle. + * \param fdhandle: usually file-data, or own handle. May be nullptr. * \param mainvar: the Main database to expand. */ void BLO_expand_main(void *fdhandle, struct Main *mainvar); diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 7243175ddd6..8a5615e3376 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -4148,7 +4148,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) * Expanding should never modify ID pointers themselves. * Handling of DNA deprecated data should never be needed in undo case. */ const int flag = IDWALK_READONLY | IDWALK_NO_ORIG_POINTERS_ACCESS | - ((fd->flags & FD_FLAGS_IS_MEMFILE) ? 0 : IDWALK_DO_DEPRECATED_POINTERS); + ((!fd || (fd->flags & FD_FLAGS_IS_MEMFILE)) ? + 0 : + IDWALK_DO_DEPRECATED_POINTERS); BKE_library_foreach_ID_link(nullptr, id_iter, expand_cb, &expander, flag); do_it = true; diff --git a/tests/python/bl_blendfile_io.py b/tests/python/bl_blendfile_io.py index 418a49a069f..32de9a4da14 100644 --- a/tests/python/bl_blendfile_io.py +++ b/tests/python/bl_blendfile_io.py @@ -49,6 +49,52 @@ class TestBlendFileSaveLoadBasic(TestHelper): assert orig_data == read_data +class TestBlendFileSavePartial(TestHelper): + OBJECT_MESH_NAME = "ObjectMesh" + OBJECT_MATERIAL_NAME = "ObjectMaterial" + OBJECT_NAME = "Object" + UNUSED_MESH_NAME = "UnusedMesh" + + def __init__(self, args): + self.args = args + + def test_save_load(self): + bpy.ops.wm.read_homefile(use_empty=True, use_factory_startup=True) + + ob_mesh = bpy.data.meshes.new(self.OBJECT_MESH_NAME) + ob_material = bpy.data.materials.new(self.OBJECT_MATERIAL_NAME) + ob_mesh.materials.append(ob_material) + ob = bpy.data.objects.new(self.OBJECT_NAME, object_data=ob_mesh) + bpy.context.collection.objects.link(ob) + + unused_mesh = bpy.data.meshes.new(self.UNUSED_MESH_NAME) + unused_mesh.materials.append(ob_material) + + assert ob_mesh.users == 1 + assert ob_material.users == 2 + assert ob.users == 1 + assert unused_mesh.users == 0 + + 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_partial.blend") + + bpy.data.libraries.write(filepath=output_path, datablocks={ob, unused_mesh}, fake_user=False) + bpy.ops.wm.open_mainfile(filepath=output_path, load_ui=False) + + assert self.OBJECT_MESH_NAME in bpy.data.meshes + assert self.OBJECT_MATERIAL_NAME in bpy.data.materials + assert self.OBJECT_NAME in bpy.data.objects + assert self.UNUSED_MESH_NAME in bpy.data.meshes + + assert bpy.data.meshes[self.OBJECT_MESH_NAME].users == 1 + assert bpy.data.materials[self.OBJECT_MATERIAL_NAME].users == 2 + assert bpy.data.objects[self.OBJECT_NAME].users == 0 + assert bpy.data.meshes[self.UNUSED_MESH_NAME].users == 0 + + # 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. @@ -145,6 +191,7 @@ class TestIdRuntimeTag(TestHelper): TESTS = ( TestBlendFileSaveLoadBasic, + TestBlendFileSavePartial, TestIdRuntimeTag, )