def test_all_soa_grease_pencil(self): import array bpy.ops.object.gpencil_add(type="STROKE") proxy = BpyDataProxy() proxy.load(test_properties) gp_layers = proxy.data("grease_pencils").search_one("Stroke").data("layers") gp_points = gp_layers.data("Lines").data("frames").data(0).data("strokes").data(0).data("points")._data expected = ( ("co", array.array, "f"), ("pressure", array.array, "f"), ("strength", array.array, "f"), ("uv_factor", array.array, "f"), ("uv_rotation", array.array, "f"), ("select", list, bool), ) for name, type_, element_type in expected: self.assertIn("co", gp_points) item = gp_points[name] self.assertIsInstance(item, SoaElement) self.assertIsInstance(item._data, type_) if type_ is array.array: self.assertEqual(item._data.typecode, element_type) else: self.assertIsInstance(item._data[0], element_type) self.assertEqual(len(gp_points["pressure"]._data), len(gp_points["strength"]._data)) self.assertEqual(3 * len(gp_points["pressure"]._data), len(gp_points["co"]._data))
def test_blenddata_filtered(self): blend_data = self.proxy._data scene = blend_data["scenes"].search_one("Scene_0")._data self.assertTrue("eevee" in scene) filter_stack = copy.copy(test_filter) filter_stack.append({T.Scene: [TypeFilterOut(T.SceneEEVEE)]}) proxy = BpyDataProxy() proxy.load(SynchronizedProperties(filter_stack, property_order)) blend_data_ = proxy._data scene_ = blend_data_["scenes"].search_one("Scene_0")._data self.assertFalse("eevee" in scene_)
class Aos(DifferentialCompute): # test_diff_compute.Aos # @unittest.skip("AttributeError: 'CollectionObjects' object has no attribute 'fixed_type'") def test_modify_value(self): # modify a vertex coordinate in a mesh # test_diff_compute.Aos.test_modify_value mesh = bpy.data.meshes.new("Mesh") mesh.vertices.add(4) for i in [0, 1, 2, 3]: v = 10 * i mesh.vertices[i].co = [v, v + 1, v + 2] self.proxy = BpyDataProxy() self.proxy.load(test_properties) mesh_proxy = self.proxy.data("meshes").search_one("Mesh") plane_mesh = bpy.data.meshes["Mesh"] expected_vertex = (-1.0, -2.0, -3.0) plane_mesh.vertices[0].co = expected_vertex expected_vertices = [list(vertex.co) for vertex in mesh.vertices] self.generate_all_uuids() mesh_delta = mesh_proxy.diff(plane_mesh, plane_mesh.name, None, self.proxy.context()) self.assertIsInstance(mesh_delta, DeltaUpdate) mesh_update = mesh_delta.value self.assertIsInstance(mesh_update, DatablockProxy) self.assertTrue(mesh_update.is_standalone_datablock) vertices_delta = mesh_update.data("vertices", resolve_delta=False) self.assertIsInstance(vertices_delta, DeltaUpdate) vertices_update = vertices_delta.value self.assertIsInstance(vertices_update, AosProxy) self.assertTrue(vertices_update) co_delta = vertices_update.data("co", resolve_delta=False) self.assertIsInstance(co_delta, DeltaUpdate) co_update = co_delta.value self.assertIsInstance(co_update, SoaElement) array_ = co_update._array self.assertEqual(len(array_), 4 * 3) vertices = [[ x, y, z ] for x, y, z in zip(array_[0::3], array_[1::3], array_[2::3])] self.assertEqual(vertices, expected_vertices)
class Datablock(DifferentialApply): def test_builtin(self): # a python builtin in a dataclock # Scene.audio_volume # test_diff_apply.Datablock.test_builtin self.scene.audio_volume = 0.5 delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # the diff has audio_volume, updated to 0.5 # rollback to anything else self.scene.audio_volume = 0.0 # apply the diff scene = bpy.data.scenes[self.scene.name] self.scene_proxy.apply(scene, bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.audio_volume, 0.5) def test_struct_builtin(self): # a python builtin a a struct inside a datablock # Scene.eevee.use_bloom # test_diff_apply.Datablock.test_struct_builtin self.scene.eevee.use_bloom = False self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene.eevee.use_bloom = True delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # diff is -> True # reset self.scene.eevee.use_bloom = False # apply the diff scene = bpy.data.scenes[self.scene.name] self.scene_proxy.apply(scene, bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.eevee.use_bloom, True)
class TestCodec(unittest.TestCase): def setUp(self): bpy.ops.wm.open_mainfile(filepath=test_blend_file) self.proxy = BpyDataProxy() register_bl_equals(self, test_properties) def test_camera(self): # test_codec.TestCodec.test_camera # prepare camera transmit_name = "transmit_camera" cam_sent = D.cameras["Camera_0"] cam_sent.dof.focus_object = D.objects["Cube"] # load into proxy self.proxy.load(test_properties) # patch the name so that it does not get mixed up as we restore later in the same scene cam_proxy_sent = self.proxy.data("cameras").search_one("Camera_0") cam_proxy_sent._data["name"] = transmit_name self.assertIsInstance(cam_proxy_sent, DatablockProxy) # encode codec = Codec() message = codec.encode(cam_proxy_sent) # # transmit # # decode into proxy cam_proxy_received = codec.decode(message) focus_object_proxy = cam_proxy_received.data("dof").data( "focus_object") self.assertIsInstance(focus_object_proxy, DatablockRefProxy) self.assertEqual(focus_object_proxy._datablock_uuid, cam_sent.dof.focus_object.mixer_uuid) # save into blender cam_proxy_received._datablock_uuid = "__" + cam_proxy_received._datablock_uuid cam_received, _ = cam_proxy_received.create_standalone_datablock( self.proxy.context()) self.assertEqual(cam_sent, cam_received) pass
class DifferentialCompute(unittest.TestCase): def setUp(self): this_folder = Path(__file__).parent test_blend_file = str(this_folder / "empty.blend") file = test_blend_file bpy.ops.wm.open_mainfile(filepath=file) self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] self.scenes_property = bpy.data.bl_rna.properties["scenes"] def generate_all_uuids(self): # as a side effect, BpyBlendDiff generates the uuids _ = BpyBlendDiff() _.diff(self.proxy, test_properties)
def execute(self, context): # Cannot import at module level, since it requires access to bpy.data which is not # accessible during module load from mixer.blender_data.bpy_data_proxy import BpyDataProxy from mixer.blender_data.filter import test_properties import cProfile import io import pstats from pstats import SortKey global proxy profile_cumulative = get_props().profile_cumulative profile_callers = get_props().profile_callers profile = profile_callers or profile_cumulative proxy = BpyDataProxy() if profile: pr = cProfile.Profile() pr.enable() t1 = time.time() proxy.load(test_properties) t2 = time.time() if profile: pr.disable() s = io.StringIO() sortby = SortKey.CUMULATIVE ps = pstats.Stats(pr, stream=s).sort_stats(sortby) if profile_cumulative: ps.print_stats() if profile_callers: ps.print_callers() print(s.getvalue()) logger.warning(f"Elapse: {t2 - t1} s.") non_empty = proxy.get_non_empty_collections() logger.info( f"Number of non empty collections in proxy: {len(non_empty)}") # Put breakpoint here and examinate non_empty dictionnary return {"FINISHED"}
def test_all_soa_grease_pencil(self): # test_misc.TestAosSoa.test_all_soa_grease_pencil import array bpy.ops.object.gpencil_add(type="STROKE") proxy = BpyDataProxy() proxy.load(test_properties) gp = proxy.data("grease_pencils").search_one("Stroke") gp_layer_lines = gp.data("layers").data(1) gp_points = gp_layer_lines.data("frames").data(0).data("strokes").data(0).data("points")._data expected = ( ("co", array.array, "f"), ("pressure", array.array, "f"), ("strength", array.array, "f"), ("uv_factor", array.array, "f"), ("uv_rotation", array.array, "f"), ) for name, type_, element_type in expected: self.assertIn("co", gp_points) item = gp_points[name] self.assertIsInstance(item, SoaElement) self.assertIsInstance(item._array, type_) if type_ is array.array: self.assertEqual(item._array.typecode, element_type) else: self.assertIsInstance(item._array[0], element_type) self.assertEqual(len(gp_points["pressure"]._array), len(gp_points["strength"]._array)) self.assertEqual(3 * len(gp_points["pressure"]._array), len(gp_points["co"]._array)) # Test path resolution for soa buffers stroke = bpy.data.grease_pencils["Stroke"] path = next(iter(gp._soas)) points_attribute, points_gp_points = gp.find_by_path(stroke, path) self.assertEqual(points_attribute, stroke.layers["Lines"].frames[0].strokes[0].points) self.assertIs(points_gp_points._data, gp_points)
class Aos(DifferentialApply): # test_diff_compute.Aos # @unittest.skip("AttributeError: 'CollectionObjects' object has no attribute 'fixed_type'") def test_modify_value(self): # modify a vertex coordinate in a mesh # test_diff_apply.Aos.test_modify_value mesh = bpy.data.meshes.new("Mesh") mesh.vertices.add(4) for i in [0, 1, 2, 3]: v = 10 * i mesh.vertices[i].co = [v, v + 1, v + 2] expected_vertices = [list(vertex.co) for vertex in mesh.vertices] self.proxy = BpyDataProxy() self.proxy.load(test_properties) mesh_proxy = self.proxy.data("meshes").search_one("Mesh") mesh = bpy.data.meshes["Mesh"] modified_vertex = (-1.0, -2.0, -3.0) mesh.vertices[0].co = modified_vertex self.generate_all_uuids() mesh_delta = mesh_proxy.diff(mesh, mesh.name, None, self.proxy.context()) # reset mesh state mesh.vertices[0].co = (0.0, 1.0, 2.0) mesh_proxy.apply(bpy.data.meshes, mesh.name, mesh_delta, self.proxy.context()) vertices = [list(vertex.co) for vertex in mesh.vertices] self.assertEqual(vertices, expected_vertices)
class TestWriteAttribute(unittest.TestCase): def setUp(self): bpy.ops.wm.open_mainfile(filepath=test_blend_file) # otherwise the loaded scene way have curves despite use_curve_mapping==False and # the new one will not have curves and will not receive them as they are not send # use_curve_mapping == False D.scenes["Scene_0"].view_settings.use_curve_mapping = True self.proxy = BpyDataProxy() self.proxy.load(synchronized_properties) register_bl_equals(self, synchronized_properties) def test_write_simple_types(self): scene = D.scenes[0] object_ = D.objects[0] # matrix = [10.0, 20.0, 30.0, 40.0, 11.0, 21.0, 31.0, 41.0, 12.0, 22.0, 32.0, 42.0, 14.0, 24.0, 34.0, 44] matrix2 = [[10.0, 20.0, 30.0, 40], [11.0, 21.0, 31.0, 41], [12.0, 22.0, 32.0, 42], [14.0, 24.0, 34.0, 44]] values = [ # (scene, "name", "Plop"), (scene, "frame_current", 99), (scene, "use_gravity", False), (scene, "gravity", [-1, -2, -3]), (scene, "gravity", Vector([-10, -20, -30])), (scene, "sync_mode", "FRAME_DROP"), # (object_, "matrix_world", matrix), (object_, "matrix_world", Matrix(matrix2)), ] for bl_instance, name, value in values: write_attribute(bl_instance, name, value, self.proxy.context()) stored_value = getattr(bl_instance, name) stored_type = type(stored_value) self.assertEqual(stored_type(value), stored_value) def test_write_bpy_struct_scene_eevee(self): scene = D.scenes[0] eevee_proxy = self.proxy._data["scenes"].search_one( "Scene_0")._data["eevee"] eevee_proxy._data["gi_cubemap_resolution"] = "64" eevee_proxy.save(scene, "eevee", self.proxy.context()) self.assertEqual("64", scene.eevee.gi_cubemap_resolution) def test_write_bpy_property_group_scene_cycles(self): # Not very useful it derives from struct scene = D.scenes[0] cycles_proxy = self.proxy._data["scenes"].search_one( "Scene_0")._data["cycles"] cycles_proxy._data["shading_system"] = True cycles_proxy.save(scene, "cycles", self.proxy.context()) self.assertEqual(True, scene.cycles.shading_system) @unittest.skip("Mesh currently restricted to Mesh.name") def test_write_array_of_struct_with_vec(self): # self.addTypeEqualityFunc(D.bpy_struct, bl_equalityfunc) cube = D.meshes["Cube"] vertices_proxy = self.proxy._data["meshes"].search_one( "Cube")._data["vertices"] # loaded as SOA into array.array co_proxy = vertices_proxy._data["co"]._data # first vertex co_proxy[0] *= 2 co_proxy[1] *= 2 co_proxy[2] *= 2 vertices_proxy.save(cube, "vertices", self.proxy.context()) self.assertListEqual(list(cube.vertices[0].co[0:3]), co_proxy[0:3].tolist()) # explicit test per data type , including addition in collections def test_write_datablock_light(self): # Write a whole scene datablock light_name = "Light" light = D.lights[light_name] light_type = light.type light_proxy = self.proxy.data("lights").search_one(light_name) light.name = "light_bak" light_bak = D.lights["light_bak"] light = D.lights.new(light_name, light_type) light_proxy.save(D.lights, light_name, self.proxy.context()) self.assertEqual(D.lights[light_name], light_bak) def test_write_datablock_world(self): # Write a whole scene datablock world_name = "World" world = D.worlds[world_name] world_proxy = self.proxy.data("worlds").search_one(world_name) world.name = "world_bak" world_bak = D.worlds["world_bak"] world = D.worlds.new(world_name) world_proxy.save(D.worlds, world_name, self.proxy.context()) self.assertEqual(D.worlds[world_name], world_bak) def test_write_array_curvemap(self): bpy.ops.wm.open_mainfile(filepath=test_blend_file) light_name = "Light" light = D.lights["Light"] points = [(0.111, 0.222), (0.333, 0.444)] curve0 = light.falloff_curve.curves[0] for i, point in enumerate(points): curve0.points[i].location = point self.proxy = BpyDataProxy() self.proxy.load(synchronized_properties) light.name = "light_bak" light_bak = D.lights["light_bak"] light = None light_proxy = self.proxy.data("lights").search_one(light_name) light_proxy.save(D.lights, light_name, self.proxy.context()) light = D.lights[light_name] curve = light.falloff_curve.curves[0] for i, point in enumerate(points): for clone, expected in zip(curve.points[i].location, point): self.assertAlmostEqual(clone, expected) self.assertEqual(D.lights[light_name], light_bak) def test_array_curvemap_shrink(self): bpy.ops.wm.open_mainfile(filepath=test_blend_file) light_name = "Light" light = D.lights["Light"] src_points = [(0.666, 0.777), (0.888, 0.999)] curve0 = light.falloff_curve.curves[0] for i, point in enumerate(src_points): curve0.points[i].location = point self.proxy = BpyDataProxy() self.proxy.load(synchronized_properties) light.name = "light_bak" light = None light_proxy = self.proxy.data("lights").search_one(light_name) light_proxy.save(D.lights, light_name, self.proxy.context()) light = D.lights[light_name] dst_curve = light.falloff_curve.curves[0] self.assertEqual(len(src_points), len(dst_curve.points)) # extend the dst curvemap to 3 points dst_points = [(0.111, 0.222), (0.333, 0.444), (0.555, 0.666)] curve0 = light.falloff_curve.curves[0] curve0.points.new(*dst_points[2]) for i, point in enumerate(dst_points): curve0.points[i].location = point self.assertEqual(len(dst_points), len(dst_curve.points)) # restore again, save needs to shrink light_proxy.save(D.lights, light_name, self.proxy.context()) light = D.lights[light_name] dst_curve = light.falloff_curve.curves[0] self.assertEqual(len(src_points), len(dst_curve.points)) for i, point in enumerate(src_points): for dst, expected in zip(dst_curve.points[i].location, point): self.assertAlmostEqual(dst, expected) def test_array_curvemap_extend(self): bpy.ops.wm.open_mainfile(filepath=test_blend_file) light_name = "Light" light = D.lights["Light"] light_type = light.type # extend the source curvemap to 3 points src_points = [(0.111, 0.222), (0.333, 0.444), (0.555, 0.666)] curve0 = light.falloff_curve.curves[0] curve0.points.new(*src_points[2]) for i, point in enumerate(src_points): curve0.points[i].location = point self.proxy = BpyDataProxy() self.proxy.load(synchronized_properties) light.name = "light_bak" light = D.lights.new(light_name, light_type) # the dst curvemap has 2 points by default # save() needs to extend light_proxy = self.proxy.data("lights").search_one(light_name) light_proxy.save(D.lights, light_name, self.proxy.context()) dst_curve = light.falloff_curve.curves[0] self.assertEqual(len(src_points), len(dst_curve.points)) for i, point in enumerate(src_points): for dst, expected in zip(dst_curve.points[i].location, point): self.assertAlmostEqual(dst, expected) def test_write_datablock_scene(self): # Write a whole scene datablock scene_name = "Scene_0" scene = D.scenes[scene_name] scene_proxy = self.proxy.data("scenes").search_one(scene_name) self.assertIsInstance(scene_proxy, DatablockProxy) scene.name = "scene_bak" scene_bak = D.scenes["scene_bak"] scene_proxy.save(D.scenes, scene_name, self.proxy.context()) self.assertEqual(D.scenes[scene_name], scene_bak) def test_write_datablock_reference_scene_world(self): # just write the Scene.world attribute scene_name = "Scene_0" scene = D.scenes[scene_name] expected_world = scene.world assert expected_world is not None world_ref_proxy = self.proxy.data("scenes").search_one( scene_name).data("world") self.assertIsInstance(world_ref_proxy, DatablockRefProxy) scene.world = None assert scene.world != expected_world world_ref_proxy.save(scene, "world", self.proxy.context()) self.assertEqual(scene.world, expected_world) def test_write_datablock_with_reference_camera_dof_target(self): # Write the whole camera datablock, including its reference to dof target camera_name = "Camera_0" camera = D.cameras[camera_name] # setup the scene and reload focus_object = D.objects["Cube"] camera.dof.focus_object = focus_object self.proxy = BpyDataProxy() self.proxy.load(synchronized_properties) camera.name = "camera_bak" camera_proxy = self.proxy.data("cameras").search_one(camera_name) camera_proxy.save(D.cameras, camera_name, self.proxy.context()) self.assertEqual(D.cameras[camera_name].dof.focus_object, focus_object)
class StructDatablockRef(DifferentialCompute): # datablock reference in a struct # Scene.world def test_add(self): # set reference from NOne to a valid datablock # test_diff_compute.StructDatablockRef.test_add self.scene.world = None self.proxy = BpyDataProxy() self.proxy.load(test_properties) world = bpy.data.worlds.new("W") self.scene.world = world self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world", resolve_delta=False) self.assertIsInstance(world_delta, DeltaUpdate) world_update = world_delta.value self.assertIsInstance(world_update, DatablockRefProxy) self.assertEqual(world_update._datablock_uuid, world.mixer_uuid) def test_update(self): # set reference from None to a valid datablock # test_diff_compute.StructDatablockRef.test_update world1 = bpy.data.worlds.new("W1") world2 = bpy.data.worlds.new("W2") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene.world = world2 self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world", resolve_delta=False) self.assertIsInstance(world_delta, DeltaUpdate) world_update = world_delta.value self.assertIsInstance(world_update, DatablockRefProxy) self.assertEqual(world_update._datablock_uuid, world2.mixer_uuid) @unittest.skip("Need BpyIDRefNoneProxy") def test_remove(self): # set reference from a valid datablock to None # test_diff_compute.StructDatablockRef.test_remove world1 = bpy.data.worlds.new("W1") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene.world = None self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # TODO fails. should a null ref be implemented as a DatablockRefProxy # with a null ref (uuid is None) # or what else self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world") self.assertIsInstance(world_delta, DeltaDeletion)
class Collection(DifferentialCompute): # test_diff_compute.Collection # @unittest.skip("AttributeError: 'CollectionObjects' object has no attribute 'fixed_type'") def test_datablock_collection(self): # Scene.collection.objects # A collection of references to standalone datablocks # test_diff_compute.Collection.test_datablock_collection for i in range(2): name = f"Unchanged{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) for i in range(2): name = f"Deleted{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] for i in range(2): name = f"Added{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) for i in range(2): bpy.data.objects.remove(bpy.data.objects[f"Deleted{i}"]) self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) scene_update = scene_delta.value self.assertIsInstance(scene_update, DatablockProxy) self.assertTrue(scene_update.is_standalone_datablock) collection_delta = scene_update.data("collection", resolve_delta=False) self.assertIsInstance(scene_delta, DeltaUpdate) collection_update = collection_delta.value self.assertIsInstance(collection_update, DatablockProxy) self.assertTrue(collection_update.is_embedded_data) objects_delta = collection_update.data("objects", resolve_delta=False) self.assertIsInstance(objects_delta, DeltaUpdate) objects_update = objects_delta.value self.assertIsInstance(objects_update, DatablockRefCollectionProxy) deltas = { delta.value._initial_name: delta for delta in objects_update._data.values() } proxies = {name: delta.value for name, delta in deltas.items()} for name in ("Added0", "Added1"): self.assertIsInstance(deltas[name], DeltaAddition) self.assertIsInstance(proxies[name], DatablockRefProxy) for name in ("Deleted0", "Deleted1"): self.assertIsInstance(deltas[name], DeltaDeletion) self.assertIsInstance(proxies[name], DatablockRefProxy) def test_bpy_collection(self): # bpy.data.collections[x].objects # A collection of references to standalone datablocks # test_diff_compute.Collection.test_bpy_collection collection = bpy.data.collections.new("Collection") for i in range(2): empty = bpy.data.objects.new(f"Unchanged{i}", None) collection.objects.link(empty) for i in range(2): empty = bpy.data.objects.new(f"Unlinked{i}", None) collection.objects.link(empty) self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.collection_proxy = self.proxy.data("collections").search_one( "Collection") self.collection = bpy.data.collections["Collection"] for i in range(2): empty = bpy.data.objects.new(f"Added{i}", None) collection.objects.link(empty) for i in range(2): collection.objects.unlink(bpy.data.objects[f"Unlinked{i}"]) self.generate_all_uuids() collections_property = bpy.data.bl_rna.properties["scenes"] collection_delta = self.collection_proxy.diff(self.collection, self.collection.name, collections_property, self.proxy.context()) self.assertIsInstance(collection_delta, DeltaUpdate) collection_update = collection_delta.value self.assertIsInstance(collection_update, DatablockProxy) self.assertTrue(collection_update.is_standalone_datablock) objects_delta = collection_update.data("objects", resolve_delta=False) self.assertIsInstance(objects_delta, DeltaUpdate) objects_update = objects_delta.value self.assertIsInstance(objects_update, DatablockRefCollectionProxy) # test_diff_compute.Collection.test_bpy_collection deltas = { delta.value._initial_name: delta for delta in objects_update._data.values() } proxies = {name: delta.value for name, delta in deltas.items()} for name in ("Added0", "Added1"): self.assertIsInstance(deltas[name], DeltaAddition) self.assertIsInstance(proxies[name], DatablockRefProxy) for name in ("Unlinked0", "Unlinked1"): self.assertIsInstance(deltas[name], DeltaDeletion) self.assertIsInstance(proxies[name], DatablockRefProxy) def test_key_str(self): # Scene.render.views # A bpy_prop_collection with string keys # test_diff_compute.Collection.test_key_str self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] view = self.scene.render.views["right"] self.scene.render.views.remove(view) view = self.scene.render.views.new("New") view = self.scene.render.views["left"] view.file_suffix = "new_suffix" self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) scene_update = scene_delta.value self.assertIsInstance(scene_update, DatablockProxy) self.assertTrue(scene_update.is_standalone_datablock) render_delta = scene_update.data("render", resolve_delta=False) self.assertIsInstance(render_delta, DeltaUpdate) render_update = render_delta.value self.assertIsInstance(render_update, StructProxy) views_delta = render_update.data("views", resolve_delta=False) self.assertIsInstance(views_delta, DeltaUpdate) views_update = views_delta.value self.assertIsInstance(views_update, StructCollectionProxy) # for why "A" and "D" see BpyProStructCollectionProxy.diff() self.assertIn("ANew", views_update) view_delta = views_update.data("ANew", resolve_delta=False) self.assertIsInstance(view_delta, DeltaAddition) view_update = view_delta.value self.assertIsInstance(view_update, StructProxy) self.assertIn("Dright", views_update) view_delta = views_update.data("Dright", resolve_delta=False) self.assertIsInstance(view_delta, DeltaDeletion) self.assertIn("left", views_update) view_delta = views_update.data("left", resolve_delta=False) self.assertIsInstance(view_delta, DeltaUpdate) view_update = view_delta.value self.assertIsInstance(view_update, StructProxy) property_delta = view_update.data("file_suffix", resolve_delta=False) self.assertIsInstance(view_delta, DeltaUpdate) self.assertEqual(property_delta.value, "new_suffix") def test_key_int(self): # Scene.view_settings.curve_mapping.curves # A bpy_prop_collection with string keys # test_diff_compute.Collection.test_key_int self.scene.view_settings.use_curve_mapping = True points_remove = self.scene.view_settings.curve_mapping.curves[0].points points_remove.new(0.5, 0.5) points_add = self.scene.view_settings.curve_mapping.curves[1].points self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] points_remove.remove(points_remove[1]) points_add.new(2.0, 2.0) self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) points_remove_proxy = (scene_delta.value.data("view_settings").data( "curve_mapping").data("curves").data(0).data("points")) self.assertIsInstance(points_remove_proxy, StructCollectionProxy) # points are ordered by location. removing the second one produces an update # at index 1 and a delete at index 2 point1 = points_remove_proxy.data(1) point1_update = point1.data("location", resolve_delta=False) self.assertIsInstance(point1_update, DeltaUpdate) location1 = point1_update.value self.assertAlmostEqual(location1[0], 1.0) self.assertAlmostEqual(location1[1], 1.0) point2 = points_remove_proxy.data(2, resolve_delta=False) self.assertIsInstance(point2, DeltaDeletion) points_add_proxy = (scene_delta.value.data("view_settings").data( "curve_mapping").data("curves").data(1).data("points")) self.assertIsInstance(points_add_proxy, StructCollectionProxy) self.assertIsInstance(points_add_proxy.data(2, resolve_delta=False), DeltaAddition) # points are ordered by location. removing the second one produces an update # at index 1 and a delete at index 2 point = points_add_proxy.data(2) location = point.data("location") self.assertAlmostEqual(location[0], 2.0) self.assertAlmostEqual(location[1], 2.0)
class Collection(DifferentialCompute): # test_diff_compute.Collection # @unittest.skip("AttributeError: 'CollectionObjects' object has no attribute 'fixed_type'") def test_datablock_collection(self): # Scene.collection.objects # A collection of references to standalone datablocks # test_diff_compute.Collection.test_datablock_collection for i in range(2): name = f"Unchanged{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) for i in range(2): name = f"Deleted{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) self.proxy = BpyDataProxy() self.proxy.load(test_properties) for i in range(2): name = f"Added{i}" empty = bpy.data.objects.new(name, None) self.scene.collection.objects.link(empty) for i in range(2): bpy.data.objects.remove(bpy.data.objects[f"Deleted{i}"]) self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) scene_update = scene_delta.value self.assertIsInstance(scene_update, DatablockProxy) collection_delta = scene_update.data("collection", resolve_delta=False) self.assertIsInstance(scene_delta, DeltaUpdate) collection_update = collection_delta.value self.assertIsInstance(collection_update, StructProxy) objects_delta = collection_update.data("objects", resolve_delta=False) self.assertIsInstance(objects_delta, DeltaUpdate) objects_update = objects_delta.value self.assertIsInstance(objects_update, DatablockRefCollectionProxy) deltas = { delta.value._initial_name: delta for delta in objects_update._data.values() } proxies = {name: delta.value for name, delta in deltas.items()} for name in ("Added0", "Added1"): self.assertIsInstance(deltas[name], DeltaAddition) self.assertIsInstance(proxies[name], DatablockRefProxy) for name in ("Deleted0", "Deleted1"): self.assertIsInstance(deltas[name], DeltaDeletion) self.assertIsInstance(proxies[name], DatablockRefProxy) def test_bpy_collection(self): # bpy.data.collections[x].objects # A collection of references to standalone datablocks # test_diff_compute.Collection.test_bpy_collection collection = bpy.data.collections.new("Collection") for i in range(2): empty = bpy.data.objects.new(f"Unchanged{i}", None) collection.objects.link(empty) for i in range(2): empty = bpy.data.objects.new(f"Unlinked{i}", None) collection.objects.link(empty) self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.collection_proxy = self.proxy.data("collections").search_one( "Collection") self.collection = bpy.data.collections["Collection"] for i in range(2): empty = bpy.data.objects.new(f"Added{i}", None) collection.objects.link(empty) for i in range(2): collection.objects.unlink(bpy.data.objects[f"Unlinked{i}"]) self.generate_all_uuids() collections_property = bpy.data.bl_rna.properties["scenes"] collection_delta = self.collection_proxy.diff(self.collection, self.collection.name, collections_property, self.proxy.context()) self.assertIsInstance(collection_delta, DeltaUpdate) collection_update = collection_delta.value self.assertIsInstance(collection_update, DatablockProxy) objects_delta = collection_update.data("objects", resolve_delta=False) self.assertIsInstance(objects_delta, DeltaUpdate) objects_update = objects_delta.value self.assertIsInstance(objects_update, DatablockRefCollectionProxy) # test_diff_compute.Collection.test_bpy_collection deltas = { delta.value._initial_name: delta for delta in objects_update._data.values() } proxies = {name: delta.value for name, delta in deltas.items()} for name in ("Added0", "Added1"): self.assertIsInstance(deltas[name], DeltaAddition) self.assertIsInstance(proxies[name], DatablockRefProxy) for name in ("Unlinked0", "Unlinked1"): self.assertIsInstance(deltas[name], DeltaDeletion) self.assertIsInstance(proxies[name], DatablockRefProxy)
class TestLoadProxy(unittest.TestCase): # test_misc.TestLoadProxy def setUp(self): file = test_blend_file # file = r"D:\work\data\test_files\BlenderSS 2_82.blend" bpy.ops.wm.open_mainfile(filepath=file) self.proxy = BpyDataProxy() self.proxy.load(test_properties) def check(self, item, expected_elements): self.assertSetEqual(set(item._data.keys()), set(expected_elements)) def expected_uuids(self, collection: bpy.types.Collection, names: Iterable[str]) -> Set[str]: return {collection[name].mixer_uuid for name in names} # @unittest.skip("") def test_blenddata(self): # test_misc.TestLoadProxy.test_blenddata blend_data = self.proxy._data expected_data = {"scenes", "collections", "objects", "materials", "lights"} self.assertTrue(all([e in blend_data.keys() for e in expected_data])) expected_uuids = self.expected_uuids(bpy.data.scenes, ["Scene_0", "Scene_1"]) self.check(self.proxy._data["scenes"], expected_uuids) expected_uuids = self.expected_uuids(bpy.data.cameras, ["Camera_0", "Camera_1"]) self.check(self.proxy._data["cameras"], expected_uuids) expected_uuids = self.expected_uuids( bpy.data.objects, ["Camera_obj_0", "Camera_obj_1", "Cone", "Cube", "Light"] ) self.check(self.proxy._data["objects"], expected_uuids) expected_uuids = self.expected_uuids( bpy.data.collections, ["Collection_0_0", "Collection_0_1", "Collection_0_0_0", "Collection_1_0"] ) self.check(self.proxy._data["collections"], expected_uuids) def test_blenddata_filtered(self): blend_data = self.proxy._data scene = blend_data["scenes"].search_one("Scene_0")._data self.assertTrue("eevee" in scene) filter_stack = copy.copy(test_filter) filter_stack.append({T.Scene: [TypeFilterOut(T.SceneEEVEE)]}) proxy = BpyDataProxy() proxy.load(SynchronizedProperties(filter_stack, property_order)) blend_data_ = proxy._data scene_ = blend_data_["scenes"].search_one("Scene_0")._data self.assertFalse("eevee" in scene_) def test_collections(self): # test_misc.TestLoadProxy.test_collections collections = self.proxy._data["collections"] coll_0_0 = collections.search_one("Collection_0_0")._data coll_0_0_children = coll_0_0["children"] expected_uuids = self.expected_uuids(bpy.data.collections, ["Collection_0_0_0"]) self.check(coll_0_0_children, expected_uuids) for c in coll_0_0_children._data.values(): self.assertIsInstance(c, DatablockRefProxy) coll_0_0_objects = coll_0_0["objects"] expected_uuids = self.expected_uuids(bpy.data.objects, ["Camera_obj_0", "Camera_obj_1", "Cube", "Light"]) self.check(coll_0_0_objects, expected_uuids) for o in coll_0_0_objects._data.values(): self.assertIsInstance(o, DatablockRefProxy) def test_camera_focus_object_idref(self): # test_misc.TestLoadProxy.test_camera_focus_object_idref cam = D.cameras["Camera_0"] cam.dof.focus_object = D.objects["Cube"] self.proxy = BpyDataProxy() self.proxy.load(test_properties) # load into proxy cam_proxy = self.proxy.data("cameras").search_one("Camera_0") focus_object_proxy = cam_proxy.data("dof").data("focus_object") self.assertIsInstance(focus_object_proxy, DatablockRefProxy) self.assertEqual(focus_object_proxy._datablock_uuid, D.objects["Cube"].mixer_uuid) def test_camera_focus_object_none(self): # test_misc.TestLoadProxy.test_camera_focus_object_none self.proxy = BpyDataProxy() self.proxy.load(test_properties) # load into proxy cam_proxy = self.proxy.data("cameras").search_one("Camera_0") focus_object_proxy = cam_proxy.data("dof").data("focus_object") self.assertIsInstance(focus_object_proxy, NonePtrProxy)
class TestLoadProxy(unittest.TestCase): # test_misc.TestLoadProxy def setUp(self): file = test_blend_file # file = r"D:\work\data\test_files\BlenderSS 2_82.blend" bpy.ops.wm.open_mainfile(filepath=file) self.proxy = BpyDataProxy() self.proxy.load(test_properties) def check(self, item, expected_elements): self.assertSetEqual(set(item._data.keys()), set(expected_elements)) def expected_uuids(self, collection: bpy.types.Collection, names: Iterable[str]) -> Set[str]: return {collection[name].mixer_uuid for name in names} # @unittest.skip("") def test_blenddata(self): # test_misc.TestLoadProxy.test_blenddata blend_data = self.proxy._data expected_data = {"scenes", "collections", "objects", "materials", "lights"} self.assertTrue(all([e in blend_data.keys() for e in expected_data])) expected_uuids = self.expected_uuids(bpy.data.scenes, ["Scene_0", "Scene_1"]) self.check(self.proxy._data["scenes"], expected_uuids) expected_uuids = self.expected_uuids(bpy.data.cameras, ["Camera_0", "Camera_1"]) self.check(self.proxy._data["cameras"], expected_uuids) expected_uuids = self.expected_uuids( bpy.data.objects, ["Camera_obj_0", "Camera_obj_1", "Cone", "Cube", "Light"] ) self.check(self.proxy._data["objects"], expected_uuids) expected_uuids = self.expected_uuids( bpy.data.collections, ["Collection_0_0", "Collection_0_1", "Collection_0_0_0", "Collection_1_0"] ) self.check(self.proxy._data["collections"], expected_uuids) def test_blenddata_filtered(self): blend_data = self.proxy._data scene = blend_data["scenes"].search_one("Scene_0")._data self.assertTrue("eevee" in scene) filter_stack = copy.copy(test_filter) filter_stack.append({T.Scene: TypeFilterOut(T.SceneEEVEE)}) proxy = BpyDataProxy() proxy.load(SynchronizedProperties(filter_stack)) blend_data_ = proxy._data scene_ = blend_data_["scenes"].search_one("Scene_0")._data self.assertFalse("eevee" in scene_) # @unittest.skip("") def test_scene(self): # test_misc.TestLoadProxy.test_scene scene = self.proxy._data["scenes"].search_one("Scene_0")._data # will vary slightly during tuning of the default filter self.assertGreaterEqual(len(scene), 45) self.assertLessEqual(len(scene), 55) # objects = scene["objects"]._data # self.assertEqual(4, len(objects)) # for o in objects.values(): # self.assertEqual(type(o), DatablockRefProxy, o) # builtin attributes (floats) frame_properties = [name for name in scene.keys() if name.startswith("frame_")] self.assertEqual(7, len(frame_properties)) # bpy_struct eevee = scene["eevee"]._data self.assertEqual(58, len(eevee)) # Currently mot loaded # # PropertiesGroup # cycles_proxy = scene["view_layers"]._data["View Layer"]._data["cycles"] # self.assertIsInstance(cycles_proxy, StructProxy) # self.assertEqual(32, len(cycles_proxy._data)) # # The master collection # master_collection = scene["collection"] # self.assertIsInstance(master_collection, DatablockProxy) def test_collections(self): # test_misc.TestLoadProxy.test_collections collections = self.proxy._data["collections"] coll_0_0 = collections.search_one("Collection_0_0")._data coll_0_0_children = coll_0_0["children"] expected_uuids = self.expected_uuids(bpy.data.collections, ["Collection_0_0_0"]) self.check(coll_0_0_children, expected_uuids) for c in coll_0_0_children._data.values(): self.assertIsInstance(c, DatablockRefProxy) coll_0_0_objects = coll_0_0["objects"] expected_uuids = self.expected_uuids(bpy.data.objects, ["Camera_obj_0", "Camera_obj_1", "Cube", "Light"]) self.check(coll_0_0_objects, expected_uuids) for o in coll_0_0_objects._data.values(): self.assertIsInstance(o, DatablockRefProxy) def test_camera_focus_object_idref(self): # test_misc.TestLoadProxy.test_camera_focus_object_idref cam = D.cameras["Camera_0"] cam.dof.focus_object = D.objects["Cube"] self.proxy = BpyDataProxy() self.proxy.load(test_properties) # load into proxy cam_proxy = self.proxy.data("cameras").search_one("Camera_0") focus_object_proxy = cam_proxy.data("dof").data("focus_object") self.assertIsInstance(focus_object_proxy, DatablockRefProxy) self.assertEqual(focus_object_proxy._datablock_uuid, D.objects["Cube"].mixer_uuid) def test_camera_focus_object_none(self): # test_misc.TestLoadProxy.test_camera_focus_object_none self.proxy = BpyDataProxy() self.proxy.load(test_properties) # load into proxy cam_proxy = self.proxy.data("cameras").search_one("Camera_0") focus_object_proxy = cam_proxy.data("dof").data("focus_object") self.assertIsInstance(focus_object_proxy, NonePtrProxy)
class StructDatablockRef(DifferentialApply): # datablock reference in a struct # Scene.world def test_add(self): # set reference from None to a valid datablock # test_diff_apply.StructDatablockRef.test_add # create first so that is is correctly registered (bpt_data.diff would register it, not scene_proxy.diff) world = bpy.data.worlds.new("W") self.scene.world = None self.proxy = BpyDataProxy() self.proxy.load(test_properties) # Loaded proxy contains scene.world = None self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene.world = world self.generate_all_uuids() delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # Diff contains set scene.proxy to world self.scene.world = None # apply the diff scene = bpy.data.scenes[self.scene.name] self.scene_proxy.apply(scene, bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.world, world) def test_update(self): # set reference from None to a valid datablock # test_diff_apply.StructDatablockRef.test_update world1 = bpy.data.worlds.new("W1") world2 = bpy.data.worlds.new("W2") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene.world = world2 self.generate_all_uuids() delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # diff -> world2 # reset self.scene.world = world1 # apply the diff scene = bpy.data.scenes[self.scene.name] self.scene_proxy.apply(scene, bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.world, world2) def test_remove(self): # apply sets reference from a valid datablock to None # test_diff_apply.StructDatablockRef.test_remove world = bpy.data.worlds.new("W") self.scene.world = world self.proxy = BpyDataProxy() self.proxy.load(test_properties) # Loaded proxy contains scene.world = world self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene.world = None self.generate_all_uuids() delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # Delta contains set scene.proxy to none self.scene.world = world # apply the diff scene = bpy.data.scenes[self.scene.name] self.scene_proxy.apply(scene, bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.world, None)
class StructDatablockRef(DifferentialApply): # datablock reference in a struct # Scene.world @unittest.skip("Need BpyIDRefNoneProxy") def test_add(self): # set reference from None to a valid datablock # test_diff_apply.StructDatablockRef.test_add # TODO needs a BpyIDNoneRefProxy self.scene.world = None self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") world = bpy.data.worlds.new("W") self.scene.world = world self.generate_all_uuids() delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # diff -> set world # reset self.scene.world = None # apply the diff self.scene_proxy.apply(bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.eevee.use_bloom, True) def test_update(self): # set reference from None to a valid datablock # test_diff_apply.StructDatablockRef.test_update world1 = bpy.data.worlds.new("W1") world2 = bpy.data.worlds.new("W2") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy: DatablockProxy = self.proxy.data( "scenes").search_one("Scene") self.scene.world = world2 self.generate_all_uuids() delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # diff -> world2 # reset self.scene.world = world1 # apply the diff self.scene_proxy.apply(bpy.data.scenes, self.scene.name, delta, self.proxy.context()) self.assertEqual(self.scene.world, world2) @unittest.skip("Need BpyIDRefNoneProxy") def test_remove(self): # set reference from a valid datablock to None # test_diff_apply.StructDatablockRef.test_remove world1 = bpy.data.worlds.new("W1") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene.world = None self.generate_all_uuids() _ = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context())
class Collection(DifferentialApply): # test_differential.Collection def test_datablock_collection(self): # Scene.collection.objects # A collection of references to standalone datablocks # tests DatablockCollectionProxy.apply() # test_diff_apply.Collection.test_datablock_collection for i in range(2): empty = bpy.data.objects.new(f"Unchanged{i}", None) self.scene.collection.objects.link(empty) for i in range(2): empty = bpy.data.objects.new(f"Deleted{i}", None) self.scene.collection.objects.link(empty) self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] for i in range(2): empty = bpy.data.objects.new(f"Added{i}", None) self.scene.collection.objects.link(empty) for i in range(2): empty = bpy.data.objects[f"Deleted{i}"] self.scene.collection.objects.unlink(empty) self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # delta contains(deleted1, deleted 2, added1, added2) # reset for i in range(2): empty = bpy.data.objects[f"Deleted{i}"] self.scene.collection.objects.link(empty) for i in range(2): empty = bpy.data.objects[f"Added{i}"] self.scene.collection.objects.unlink(empty) # required because the Added{i} were created after proxy load and are not known by the proxy # at this time. IRL the depsgraph handler uses BpyBendDiff to find datablock additions, # then BpyDataProxy.update() self.proxy.load(test_properties) self.scene_proxy.apply(bpy.data.scenes, self.scene.name, scene_delta, self.proxy.context()) self.assertIn("Unchanged0", self.scene.collection.objects) self.assertIn("Unchanged1", self.scene.collection.objects) self.assertIn("Added0", self.scene.collection.objects) self.assertIn("Added1", self.scene.collection.objects) self.assertNotIn("Deleted0", self.scene.collection.objects) self.assertNotIn("Deleted1", self.scene.collection.objects) def test_key_str(self): # Scene.render.views # A bpy_prop_collection with string keys # tests StructCollectionProxy.apply() # test_diff_apply.Collection.test_key_str self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] view_right = self.scene.render.views["right"] self.scene.render.views.remove(view_right) view = self.scene.render.views.new("New") view = self.scene.render.views["left"] view_left_suffix_bak = view.file_suffix view.file_suffix = "new_suffix" self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # reset to initial state views = bpy.data.scenes["Scene"].render.views view_right = views.new("right") views["left"].file_suffix = view_left_suffix_bak view_new = views["New"] views.remove(view_new) self.scene_proxy.apply(bpy.data.scenes, self.scene.name, scene_delta, self.proxy.context()) self.assertIn("New", views) self.assertIn("left", views) self.assertEqual(views["left"].file_suffix, "new_suffix") self.assertNotIn("right", views) @unittest.skip("Not implemented: addition in array") def test_key_int(self): # Scene.view_settings.curve_mapping.curves # A bpy_prop_collection with string keys # test_diff_apply.Collection.test_key_int self.scene.view_settings.use_curve_mapping = True points0 = self.scene.view_settings.curve_mapping.curves[0].points points0.new(0.5, 0.5) points1 = self.scene.view_settings.curve_mapping.curves[1].points self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene_proxy = self.proxy.data("scenes").search_one("Scene") self.scene = bpy.data.scenes["Scene"] points0.remove(points0[1]) points1.new(2.0, 2.0) self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) # the delta contains : # curves[0]: Deletion of element 1 # curves[1]: Addition of element 2 # reset state points0.new(0.5, 0.5) points1.remove(points1[2]) self.scene_proxy.apply(bpy.data.scenes, self.scene.name, scene_delta, self.proxy.context()) self.assertEqual(len(points0), 2) self.assertEqual(list(points0[0].location), [0.0, 0.0]) self.assertEqual(list(points0[1].location), [1.0, 1.0]) self.assertEqual(len(points1), 3) self.assertEqual(list(points1[0].location), [0.0, 0.0]) self.assertEqual(list(points1[1].location), [1.0, 1.0]) self.assertEqual(list(points1[2].location), [2.0, 2.0])
class TestDiff(unittest.TestCase): def setUp(self): for w in D.worlds: D.worlds.remove(w) self.proxy = BpyDataProxy() def test_create(self): # test_diff.TestDiff.test_create self.proxy.load(test_properties) new_worlds = ["W0", "W1"] new_worlds.sort() for w in new_worlds: D.worlds.new(w) diff = BpyBlendDiff() diff.diff(self.proxy, test_properties) for collection_name, delta in diff.collection_deltas: self.assertEqual(0, len(delta.items_removed), f"removed count mismatch for {collection_name}") self.assertEqual(0, len(delta.items_renamed), f"renamed count mismatch for {collection_name}") if collection_name == "worlds": self.assertEqual( len(new_worlds), len(delta.items_added), f"added count mismatch for {collection_name}") found = [datablock.name for datablock, _ in delta.items_added] found.sort() self.assertEqual( new_worlds, found, f"added count mismatch for {collection_name}") else: self.assertEqual( 0, len(delta.items_added), f"added count mismatch for {collection_name}") def test_remove(self): # test_diff.TestDiff.test_create new_worlds = ["W0", "W1", "W2"] new_worlds.sort() for w in new_worlds: D.worlds.new(w) self.proxy.load(test_properties) removed = ["W0", "W1"] removed.sort() for w in removed: D.worlds.remove(D.worlds[w]) diff = BpyBlendDiff() diff.diff(self.proxy, test_properties) for name, delta in diff.collection_deltas: self.assertEqual(0, len(delta.items_added), f"added count mismatch for {name}") self.assertEqual(0, len(delta.items_renamed), f"renamed count mismatch for {name}") if name == "worlds": self.assertEqual(len(removed), len(delta.items_removed), f"removed count mismatch for {name}") items_removed = [ proxy.data("name") for proxy in delta.items_removed ] items_removed.sort() self.assertEqual(removed, items_removed, f"removed count mismatch for {name}") else: self.assertEqual(0, len(delta.items_added), f"added count mismatch for {name}") def test_rename(self): # test_diff.TestDiff.test_create new_worlds = ["W0", "W1", "W2"] new_worlds.sort() for w in new_worlds: D.worlds.new(w) self.proxy.load(test_properties) renamed = [("W0", "W00"), ("W2", "W22")] renamed.sort(key=sort_renamed_item) for old_name, new_name in renamed: D.worlds[old_name].name = new_name diff = BpyBlendDiff() diff.diff(self.proxy, test_properties) for name, delta in diff.collection_deltas: self.assertEqual(0, len(delta.items_added), f"added count mismatch for {name}") self.assertEqual(0, len(delta.items_removed), f"removed count mismatch for {name}") if name == "worlds": self.assertEqual(len(renamed), len(delta.items_renamed), f"renamed count mismatch for {name}") items_renamed = list(delta.items_renamed) items_renamed.sort(key=sort_renamed_item) items_renamed = [(proxy.data("name"), new_name) for proxy, new_name in items_renamed] self.assertEqual(renamed, items_renamed, f"removed count mismatch for {name}") else: self.assertEqual(0, len(delta.items_added), f"added count mismatch for {name}") def test_create_delete_rename(self): # test_diff.TestDiff.test_create new_worlds = ["W0", "W1", "W2", "W4"] new_worlds.sort() for w in new_worlds: D.worlds.new(w) self.proxy.load(test_properties) renamed = [("W0", "W00"), ("W2", "W22"), ("W4", "W44")] renamed.sort(key=sort_renamed_item) for old_name, new_name in renamed: D.worlds[old_name].name = new_name added = ["W0", "W5"] added.sort() for w in added: D.worlds.new(w) removed = ["W1", "W00"] removed.sort() for w in removed: D.worlds.remove(D.worlds[w]) diff = BpyBlendDiff() diff.diff(self.proxy, test_properties) for name, delta in diff.collection_deltas: if name == "worlds": items_added = [ datablock.name for datablock, _ in delta.items_added ] items_added.sort() self.assertEqual(items_added, ["W0", "W5"], f"added count mismatch for {name}") items_renamed = delta.items_renamed items_renamed.sort(key=sort_renamed_item) items_renamed = [(proxy.data("name"), new_name) for proxy, new_name in items_renamed] self.assertEqual(items_renamed, [("W2", "W22"), ("W4", "W44")], f"renamed count mismatch for {name}") items_removed = [ proxy.data("name") for proxy in delta.items_removed ] items_removed.sort() self.assertEqual(items_removed, ["W0", "W1"], f"removed count mismatch for {name}") else: self.assertEqual(0, len(delta.items_renamed), f"renamed count mismatch for {name}") self.assertEqual(0, len(delta.items_removed), f"removed count mismatch for {name}") self.assertEqual(0, len(delta.items_added), f"added count mismatch for {name}")
class StructDatablockRef(DifferentialCompute): # datablock reference in a struct # Scene.world def test_add(self): # set reference from NOne to a valid datablock # test_diff_compute.StructDatablockRef.test_add self.scene.world = None self.proxy = BpyDataProxy() self.proxy.load(test_properties) world = bpy.data.worlds.new("W") self.scene.world = world self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world", resolve_delta=False) self.assertIsInstance(world_delta, DeltaReplace) world_update = world_delta.value self.assertIsInstance(world_update, DatablockRefProxy) self.assertEqual(world_update._datablock_uuid, world.mixer_uuid) def test_update(self): # set reference from None to a valid datablock # test_diff_compute.StructDatablockRef.test_update world1 = bpy.data.worlds.new("W1") world2 = bpy.data.worlds.new("W2") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene.world = world2 self.generate_all_uuids() scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world", resolve_delta=False) self.assertIsInstance(world_delta, DeltaReplace) world_update = world_delta.value self.assertIsInstance(world_update, DatablockRefProxy) self.assertEqual(world_update._datablock_uuid, world2.mixer_uuid) def test_remove(self): # set reference from a valid datablock to None # test_diff_compute.StructDatablockRef.test_remove world1 = bpy.data.worlds.new("W1") self.scene.world = world1 self.proxy = BpyDataProxy() self.proxy.load(test_properties) self.scene.world = None self.generate_all_uuids() # delta contains valid ref to None scene_delta = self.scene_proxy.diff(self.scene, self.scene.name, self.scenes_property, self.proxy.context()) self.assertIsInstance(scene_delta, DeltaUpdate) world_delta = scene_delta.value.data("world", resolve_delta=False) self.assertIsInstance(world_delta, DeltaReplace) world_update = world_delta.value self.assertIsInstance(world_update, DatablockRefProxy) self.assertFalse(world_update)