From a30a0a41d78b0db9b4b8b84dfe0c50bb2914626b Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 8 Feb 2026 11:38:48 +0800 Subject: [PATCH] refactor: refactor pybmap test --- .../BMapSharp/BMapSharp/BMapSharp.csproj | 5 +- .../BMapSharp/BMapSharpTest/Cli.cs | 4 +- .../BMapSharp/BMapSharpTest/Program.cs | 2 +- .../BMapSharp/BMapSharpTest/TestSuits.cs | 30 +-- Assets/BMapBindings/pybmap/pyproject.toml | 5 +- Assets/BMapBindings/pybmap/tests/cli.py | 40 ++++ Assets/BMapBindings/pybmap/tests/main.py | 35 ++++ Assets/BMapBindings/pybmap/tests/testbench.py | 178 ------------------ Assets/BMapBindings/pybmap/tests/testsuits.py | 177 +++++++++++++++++ 9 files changed, 276 insertions(+), 200 deletions(-) create mode 100644 Assets/BMapBindings/pybmap/tests/cli.py create mode 100644 Assets/BMapBindings/pybmap/tests/main.py delete mode 100644 Assets/BMapBindings/pybmap/tests/testbench.py create mode 100644 Assets/BMapBindings/pybmap/tests/testsuits.py diff --git a/Assets/BMapBindings/BMapSharp/BMapSharp/BMapSharp.csproj b/Assets/BMapBindings/BMapSharp/BMapSharp/BMapSharp.csproj index 914d65b..c4b1513 100644 --- a/Assets/BMapBindings/BMapSharp/BMapSharp/BMapSharp.csproj +++ b/Assets/BMapBindings/BMapSharp/BMapSharp/BMapSharp.csproj @@ -5,9 +5,12 @@ enable BMapSharp - 1.0.0 + BMapSharp + 0.4.0 yyc12345 + The C# binding to BMap. BearKidsTeam + SPDX:MIT diff --git a/Assets/BMapBindings/BMapSharp/BMapSharpTest/Cli.cs b/Assets/BMapBindings/BMapSharp/BMapSharpTest/Cli.cs index 4629aef..4b1b55d 100644 --- a/Assets/BMapBindings/BMapSharp/BMapSharpTest/Cli.cs +++ b/Assets/BMapBindings/BMapSharp/BMapSharpTest/Cli.cs @@ -26,7 +26,7 @@ namespace BMapSharpTest { if (ballance_dir is null) { throw new CliException("You must specify BMAP_BALLANCE_DIR environment variable before running this test."); } - this.BallanceDirectory = ballance_dir; + this.BallanceDir = ballance_dir; var encodings = System.Environment.GetEnvironmentVariable("BMAP_ENCODINGS"); if (encodings is null) { @@ -42,7 +42,7 @@ namespace BMapSharpTest { /// /// The path to the Ballance directory for finding textures /// - public string BallanceDirectory { get; private set; } + public string BallanceDir { get; private set; } /// /// The name of encodings used by BMap for loading map. /// diff --git a/Assets/BMapBindings/BMapSharp/BMapSharpTest/Program.cs b/Assets/BMapBindings/BMapSharp/BMapSharpTest/Program.cs index dfbde1b..a2a63fe 100644 --- a/Assets/BMapBindings/BMapSharp/BMapSharpTest/Program.cs +++ b/Assets/BMapBindings/BMapSharp/BMapSharpTest/Program.cs @@ -33,7 +33,7 @@ namespace BMapSharpTest { string file_name = cli.FileName; var temp_dir_info = Directory.CreateTempSubdirectory(); string temp_dir = temp_dir_info.FullName; - string texture_dir = Path.Combine(cli.BallanceDirectory, "Textures"); + string texture_dir = Path.Combine(cli.BallanceDir, "Textures"); string[] encodings = cli.Encodings; using (var reader = new BMapSharp.BMapWrapper.BMFileReader(file_name, temp_dir, texture_dir, encodings)) { diff --git a/Assets/BMapBindings/BMapSharp/BMapSharpTest/TestSuits.cs b/Assets/BMapBindings/BMapSharp/BMapSharpTest/TestSuits.cs index 2dcf3a1..5691731 100644 --- a/Assets/BMapBindings/BMapSharp/BMapSharpTest/TestSuits.cs +++ b/Assets/BMapBindings/BMapSharp/BMapSharpTest/TestSuits.cs @@ -108,15 +108,15 @@ namespace BMapSharpTest.TestSuits { Console.WriteLine($"\tVisibility: {lit.GetVisibility()}"); Console.WriteLine($"\tMatrix: {lit.GetWorldMatrix().ToManaged()}"); - Console.WriteLine($"Type: {lit.GetLightType()}"); - Console.WriteLine($"Color: {lit.GetColor().ToManagedRGBA()}"); - Console.WriteLine($"Constant Attenuation: {lit.GetConstantAttenuation()}"); - Console.WriteLine($"Linear Attenuation: {lit.GetLinearAttenuation()}"); - Console.WriteLine($"Quadratic Attenuation: {lit.GetQuadraticAttenuation()}"); - Console.WriteLine($"Range: {lit.GetRange()}"); - Console.WriteLine($"Hot Spot: {lit.GetHotSpot()}"); - Console.WriteLine($"Falloff: {lit.GetFalloff()}"); - Console.WriteLine($"Falloff Shape: {lit.GetFalloffShape()}"); + Console.WriteLine($"\tType: {lit.GetLightType()}"); + Console.WriteLine($"\tColor: {lit.GetColor().ToManagedRGBA()}"); + Console.WriteLine($"\tConstant Attenuation: {lit.GetConstantAttenuation()}"); + Console.WriteLine($"\tLinear Attenuation: {lit.GetLinearAttenuation()}"); + Console.WriteLine($"\tQuadratic Attenuation: {lit.GetQuadraticAttenuation()}"); + Console.WriteLine($"\tRange: {lit.GetRange()}"); + Console.WriteLine($"\tHot Spot: {lit.GetHotSpot()}"); + Console.WriteLine($"\tFalloff: {lit.GetFalloff()}"); + Console.WriteLine($"\tFalloff Shape: {lit.GetFalloffShape()}"); } } @@ -127,14 +127,14 @@ namespace BMapSharpTest.TestSuits { Console.WriteLine($"\tVisibility: {cam.GetVisibility()}"); Console.WriteLine($"\tMatrix: {cam.GetWorldMatrix().ToManaged()}"); - Console.WriteLine($"Type: {cam.GetProjectionType()}"); - Console.WriteLine($"Orthographic Zoom: {cam.GetOrthographicZoom()}"); - Console.WriteLine($"Front Plane: {cam.GetFrontPlane()}"); - Console.WriteLine($"Back Plane: {cam.GetBackPlane()}"); - Console.WriteLine($"Fov: {cam.GetFov()}"); + Console.WriteLine($"\tType: {cam.GetProjectionType()}"); + Console.WriteLine($"\tOrthographic Zoom: {cam.GetOrthographicZoom()}"); + Console.WriteLine($"\tFront Plane: {cam.GetFrontPlane()}"); + Console.WriteLine($"\tBack Plane: {cam.GetBackPlane()}"); + Console.WriteLine($"\tFov: {cam.GetFov()}"); cam.GetAspectRatio(out var width, out var height); - Console.WriteLine($"Aspect Ratio: {width}:{height}"); + Console.WriteLine($"\tAspect Ratio: {width}:{height}"); } } } diff --git a/Assets/BMapBindings/pybmap/pyproject.toml b/Assets/BMapBindings/pybmap/pyproject.toml index 0c3d6e7..a4bafc2 100644 --- a/Assets/BMapBindings/pybmap/pyproject.toml +++ b/Assets/BMapBindings/pybmap/pyproject.toml @@ -3,9 +3,8 @@ name = "pybmap" version = "0.4.0" description = "The Python binding to BMap." readme = "README.md" -authors = [ - { name = "yyc12345" } -] +license = "SPDX:MIT" +authors = [{ name = "yyc12345" }] classifiers = ["Private :: Do Not Upload"] requires-python = ">=3.11" dependencies = [] diff --git a/Assets/BMapBindings/pybmap/tests/cli.py b/Assets/BMapBindings/pybmap/tests/cli.py new file mode 100644 index 0000000..500b23e --- /dev/null +++ b/Assets/BMapBindings/pybmap/tests/cli.py @@ -0,0 +1,40 @@ +import os +from pathlib import Path + +class CliException(Exception): + """Error occurs when parsing test arguments.""" + pass + +class Cli: + + file_name: Path + """ + The path to the map for loading. + """ + ballance_dir: Path + """ + The path to the Ballance directory for finding textures + """ + encodings: tuple[str, ...] + """ + The name of encodings used by BMap for loading map. + """ + + def __init__(self) -> None: + file_name = os.environ.get('BMAP_FILE_NAME', None) + if file_name is None: + raise CliException('You must specify BMAP_FILE_NAME environment variable before running this test.') + else: + self.file_name = Path(file_name).resolve() + + ballance_dir = os.environ.get('BMAP_BALLANCE_DIR', None) + if ballance_dir is None: + raise CliException('You must specify BMAP_BALLANCE_DIR environment variable before running this test.') + else: + self.ballance_dir = Path(ballance_dir).resolve() + + encodings = os.environ.get('BMAP_ENCODINGS', None) + if encodings is None: + raise CliException('You must specify BMAP_ENCODINGS environment variable before running this test.') + else: + self.encodings = tuple(encodings.split(',')) diff --git a/Assets/BMapBindings/pybmap/tests/main.py b/Assets/BMapBindings/pybmap/tests/main.py new file mode 100644 index 0000000..fff896a --- /dev/null +++ b/Assets/BMapBindings/pybmap/tests/main.py @@ -0,0 +1,35 @@ +import os +from pathlib import Path +from tempfile import TemporaryDirectory +import pybmap.bmap_wrapper as bmap +import cli +import testsuits + +def main() -> None: + # Parse arguments + try: + cliopts = cli.Cli() + except cli.CliException as e: + print(f'Can not launch test. Reason: {e}') + return + + # Check BMap status. + # if True: + # print('Fail to initialize native BMap.') + # return + + # Waiting debugger + input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...') + + # Start testbench + with TemporaryDirectory() as tempdir: + file_name = str(cliopts.file_name) + temp_folder = str(tempdir) + texture_folder = str(cliopts.ballance_dir / 'Textures') + encodings = cliopts.encodings + with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader: + testsuits.TestCommon.test(reader) + testsuits.TestEq.test(reader) + +if __name__ == '__main__': + main() diff --git a/Assets/BMapBindings/pybmap/tests/testbench.py b/Assets/BMapBindings/pybmap/tests/testbench.py deleted file mode 100644 index a16e2b1..0000000 --- a/Assets/BMapBindings/pybmap/tests/testbench.py +++ /dev/null @@ -1,178 +0,0 @@ -import os -import argparse -import PyBMap.bmap_wrapper as bmap - -def main(file_name: str, temp_folder: str, texture_folder: str, encodings: tuple[str, ...]) -> None: - input(f'Python PID is {os.getpid()}. Waiting for debugger, press any key to continue...') - - # file_name: str = 'LightCameraTest.nmo' - # temp_folder: str = 'Temp' - # texture_folder: str = 'F:\\Ballance\\Ballance\\Textures' - # encodings: tuple[str, ...] = ('cp1252', ) - with bmap.BMFileReader(file_name, temp_folder, texture_folder, encodings) as reader: - test_common(reader) - test_equatable(reader) - -def test_common(reader: bmap.BMFileReader): - # print('===== Groups =====') - # for gp in reader.get_groups(): - # print(gp.get_name()) - # for gp_item in gp.get_objects(): - # print(f'\t{gp_item.get_name()}') - - # print('===== 3dObjects =====') - # for obj in reader.get_3dobjects(): - # print(obj.get_name()) - - # current_mesh = obj.get_current_mesh() - # mesh_name = '' if current_mesh is None else current_mesh.get_name() - # print(f'\tMesh: {mesh_name}') - # print(f'\tVisibility: {obj.get_visibility()}') - # print(f'\tMatrix: {obj.get_world_matrix().to_const()}') - - # print('===== Meshes =====') - # for mesh in reader.get_meshs(): - # print(mesh.get_name()) - - # print(f'\tLit Mode: {mesh.get_lit_mode()}') - # print(f'\tVertex Count: {mesh.get_vertex_count()}') - # print(f'\tFace Count: {mesh.get_face_count()}') - # print(f'\tMaterial Slot Count: {mesh.get_material_slot_count()}') - - # print('===== Materials =====') - # for mtl in reader.get_materials(): - # print(mtl.get_name()) - - # print(f'\tDiffuse: {mtl.get_diffuse().to_const_rgba()}') - # print(f'\tAmbient: {mtl.get_ambient().to_const_rgba()}') - # print(f'\tSpecular: {mtl.get_specular().to_const_rgba()}') - # print(f'\tEmissive: {mtl.get_emissive().to_const_rgba()}') - - # print(f'\tSpecular Power: {mtl.get_specular_power()}') - - # print(f'\tTexture Border Color: {mtl.get_texture_border_color().to_const_rgba()}') - - # print(f'\tTexture Blend Mode: {mtl.get_texture_blend_mode()}') - # print(f'\tTexture Min Mode: {mtl.get_texture_min_mode()}') - # print(f'\tTexture Mag Mode: {mtl.get_texture_mag_mode()}') - # print(f'\tSource Blend: {mtl.get_source_blend()}') - # print(f'\tDest Blend: {mtl.get_dest_blend()}') - # print(f'\tFill Mode: {mtl.get_fill_mode()}') - # print(f'\tShade Mode: {mtl.get_shade_mode()}') - - # print(f'\tAlpha Test Enabled: {mtl.get_alpha_test_enabled()}') - # print(f'\tAlpha Blend Enabled: {mtl.get_alpha_blend_enabled()}') - # print(f'\tPerspective Correction Enabled: {mtl.get_perspective_correction_enabled()}') - # print(f'\tZ Write Enabled: {mtl.get_z_write_enabled()}') - # print(f'\tTwo Sided Enabled: {mtl.get_two_sided_enabled()}') - - # print(f'\tAlpha Ref: {mtl.get_alpha_ref()}') - - # print(f'\tAlpha Func: {mtl.get_alpha_func()}') - # print(f'\tZ Func: {mtl.get_z_func()}') - - # print('===== Textures =====') - # for tex in reader.get_textures(): - # print(tex.get_name()) - - # print(f'\tFile Name: {tex.get_file_name()}') - # print(f'\tSave Options: {tex.get_save_options()}') - # print(f'\tVideo Format: {tex.get_video_format()}') - - print('===== Target Lights =====') - for lit in reader.get_target_lights(): - print(lit.get_name()) - - print(f'\tVisibility: {lit.get_visibility()}') - print(f'\tMatrix: {lit.get_world_matrix().to_const()}') - - print(f'\tType: {lit.get_type()}') - print(f'\tColor: {lit.get_color().to_const_rgba()}') - print(f'\tConstant Attenuation: {lit.get_constant_attenuation()}') - print(f'\tLinear Attenuation: {lit.get_linear_attenuation()}') - print(f'\tQuadratic Attenuation: {lit.get_quadratic_attenuation()}') - print(f'\tRange: {lit.get_range()}') - print(f'\tHot Spot: {lit.get_hot_spot()}') - print(f'\tFalloff: {lit.get_falloff()}') - print(f'\tFalloff Shape: {lit.get_falloff_shape()}') - - print('===== END =====') - -def test_equatable(reader: bmap.BMFileReader): - # Check requirements - assert (reader.get_3dobject_count() >= 2), ''' - Invalid file for test IEquatable. - We can not perform IEquatable test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform. - ''' - - # Prepare variables - all_3dobjects: tuple[bmap.BM3dObject, ...] = tuple(reader.get_3dobjects()) - first_3dobj: bmap.BM3dObject = all_3dobjects[0] - second_3dobj: bmap.BM3dObject = all_3dobjects[1] - all_3dobjects = tuple(reader.get_3dobjects()) - first_3dobj_again: bmap.BM3dObject = all_3dobjects[0] - - # Test set - test_set: set[bmap.BM3dObject] = set() - - test_set.add(first_3dobj) - assert len(test_set) == 1 - - assert first_3dobj in test_set - assert first_3dobj_again in test_set - assert second_3dobj not in test_set - - test_set.add(first_3dobj_again) - assert len(test_set) == 1 - test_set.add(second_3dobj) - assert len(test_set) == 2 - - assert second_3dobj in test_set - - # Test dict - test_dict: dict[bmap.BM3dObject, str | None] = {} - - test_dict[first_3dobj] = first_3dobj.get_name() - assert len(test_dict) == 1 - - assert first_3dobj in test_dict - assert first_3dobj_again in test_dict - assert second_3dobj not in test_dict - - test_dict[first_3dobj_again] = first_3dobj_again.get_name() - assert len(test_dict) == 1 - test_dict[second_3dobj] = second_3dobj.get_name() - assert len(test_dict) == 2 - - assert second_3dobj in test_dict - -if __name__ == '__main__': - # parse argument - parser = argparse.ArgumentParser( - prog='PyBMap Testbench', - description='The testbench of PyBMap.' - ) - parser.add_argument( - '--file-path', - action='store', dest='file_path', required=True, - help='The path to input Virtools file.' - ) - parser.add_argument( - '--temp-dir', - action='store', dest='temp_dir', required=True, - help='The temp folder used by BMap.' - ) - parser.add_argument( - '--texture-dir', - action='store', dest='texture_dir', required=True, - help='The texture folder containing Ballance texture resources.' - ) - parser.add_argument( - '--encodings', - action='extend', nargs='+', dest='encodings', required=True, - help='The encodings used to parse the names stroed in input Virtools file.' - ) - args = parser.parse_args() - - # run main function - main(args.file_path, args.temp_dir, args.texture_dir, tuple(args.encodings)) diff --git a/Assets/BMapBindings/pybmap/tests/testsuits.py b/Assets/BMapBindings/pybmap/tests/testsuits.py new file mode 100644 index 0000000..7617833 --- /dev/null +++ b/Assets/BMapBindings/pybmap/tests/testsuits.py @@ -0,0 +1,177 @@ +import pybmap.bmap_wrapper as bmap +from pybmap.bmap_wrapper import BMFileReader + +class TestCommon: + @staticmethod + def test(reader: BMFileReader) -> None: + print('===== Groups =====') + TestCommon.__test_group(reader) + print('===== 3dObjects =====') + TestCommon.__test_3dobject(reader) + print('===== Meshes =====') + TestCommon.__test_mesh(reader) + print('===== Materials =====') + TestCommon.__test_material(reader) + print('===== Textures =====') + TestCommon.__test_texture(reader) + print('===== Target Lights =====') + TestCommon.__test_target_light(reader) + print('===== Target Cameras =====') + TestCommon.__test_target_camera(reader) + print('===== END =====') + + @staticmethod + def __test_group(reader: BMFileReader) -> None: + for gp in reader.get_groups(): + print(gp.get_name()) + for gp_item in gp.get_objects(): + print(f'\t{gp_item.get_name()}') + + @staticmethod + def __test_3dobject(reader: BMFileReader) -> None: + for obj in reader.get_3dobjects(): + print(obj.get_name()) + + current_mesh = obj.get_current_mesh() + mesh_name = '' if current_mesh is None else current_mesh.get_name() + print(f'\tMesh: {mesh_name}') + print(f'\tVisibility: {obj.get_visibility()}') + print(f'\tMatrix: {obj.get_world_matrix().to_const()}') + + @staticmethod + def __test_mesh(reader: BMFileReader) -> None: + for mesh in reader.get_meshs(): + print(mesh.get_name()) + + print(f'\tLit Mode: {mesh.get_lit_mode()}') + print(f'\tVertex Count: {mesh.get_vertex_count()}') + print(f'\tFace Count: {mesh.get_face_count()}') + print(f'\tMaterial Slot Count: {mesh.get_material_slot_count()}') + + @staticmethod + def __test_material(reader: BMFileReader) -> None: + for mtl in reader.get_materials(): + print(mtl.get_name()) + + print(f'\tDiffuse: {mtl.get_diffuse().to_const_rgba()}') + print(f'\tAmbient: {mtl.get_ambient().to_const_rgba()}') + print(f'\tSpecular: {mtl.get_specular().to_const_rgba()}') + print(f'\tEmissive: {mtl.get_emissive().to_const_rgba()}') + + print(f'\tSpecular Power: {mtl.get_specular_power()}') + + print(f'\tTexture Border Color: {mtl.get_texture_border_color().to_const_rgba()}') + + print(f'\tTexture Blend Mode: {mtl.get_texture_blend_mode()}') + print(f'\tTexture Min Mode: {mtl.get_texture_min_mode()}') + print(f'\tTexture Mag Mode: {mtl.get_texture_mag_mode()}') + print(f'\tSource Blend: {mtl.get_source_blend()}') + print(f'\tDest Blend: {mtl.get_dest_blend()}') + print(f'\tFill Mode: {mtl.get_fill_mode()}') + print(f'\tShade Mode: {mtl.get_shade_mode()}') + + print(f'\tAlpha Test Enabled: {mtl.get_alpha_test_enabled()}') + print(f'\tAlpha Blend Enabled: {mtl.get_alpha_blend_enabled()}') + print(f'\tPerspective Correction Enabled: {mtl.get_perspective_correction_enabled()}') + print(f'\tZ Write Enabled: {mtl.get_z_write_enabled()}') + print(f'\tTwo Sided Enabled: {mtl.get_two_sided_enabled()}') + + print(f'\tAlpha Ref: {mtl.get_alpha_ref()}') + + print(f'\tAlpha Func: {mtl.get_alpha_func()}') + print(f'\tZ Func: {mtl.get_z_func()}') + + @staticmethod + def __test_texture(reader: BMFileReader) -> None: + for tex in reader.get_textures(): + print(tex.get_name()) + + print(f'\tFile Name: {tex.get_file_name()}') + print(f'\tSave Options: {tex.get_save_options()}') + print(f'\tVideo Format: {tex.get_video_format()}') + + @staticmethod + def __test_target_light(reader: BMFileReader) -> None: + for lit in reader.get_target_lights(): + print(lit.get_name()) + + print(f'\tVisibility: {lit.get_visibility()}') + print(f'\tMatrix: {lit.get_world_matrix().to_const()}') + + print(f'\tType: {lit.get_type()}') + print(f'\tColor: {lit.get_color().to_const_rgba()}') + print(f'\tConstant Attenuation: {lit.get_constant_attenuation()}') + print(f'\tLinear Attenuation: {lit.get_linear_attenuation()}') + print(f'\tQuadratic Attenuation: {lit.get_quadratic_attenuation()}') + print(f'\tRange: {lit.get_range()}') + print(f'\tHot Spot: {lit.get_hot_spot()}') + print(f'\tFalloff: {lit.get_falloff()}') + print(f'\tFalloff Shape: {lit.get_falloff_shape()}') + + @staticmethod + def __test_target_camera(reader: BMFileReader) -> None: + for cam in reader.get_target_cameras(): + print(cam.get_name()) + + print(f'\tVisibility: {cam.get_visibility()}') + print(f'\tMatrix: {cam.get_world_matrix().to_const()}') + + print(f'\tType: {cam.get_projection_type()}') + print(f'\tOrthographic Zoom: {cam.get_orthographic_zoom()}') + print(f'\tFront Plane: {cam.get_front_plane()}') + print(f'\tBack Plane: {cam.get_back_plane()}') + print(f'\tFov: {cam.get_fov()}') + + (width, height) = cam.get_aspect_ratio() + print(f'\tAspect Ratio: {width}:{height}') + +class TestEq: + @staticmethod + def test(reader: BMFileReader) -> None: + # Check requirements + assert (reader.get_3dobject_count() >= 2), ''' + Invalid file for test __eq__. + We can not perform __eq__ test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform. + ''' + + # Prepare variables + all_3dobjects: tuple[bmap.BM3dObject, ...] = tuple(reader.get_3dobjects()) + first_3dobj: bmap.BM3dObject = all_3dobjects[0] + second_3dobj: bmap.BM3dObject = all_3dobjects[1] + all_3dobjects = tuple(reader.get_3dobjects()) + first_3dobj_again: bmap.BM3dObject = all_3dobjects[0] + + # Test set + test_set: set[bmap.BM3dObject] = set() + + test_set.add(first_3dobj) + assert len(test_set) == 1 + + assert first_3dobj in test_set + assert first_3dobj_again in test_set + assert second_3dobj not in test_set + + test_set.add(first_3dobj_again) + assert len(test_set) == 1 + test_set.add(second_3dobj) + assert len(test_set) == 2 + + assert second_3dobj in test_set + + # Test dict + test_dict: dict[bmap.BM3dObject, str | None] = {} + + test_dict[first_3dobj] = first_3dobj.get_name() + assert len(test_dict) == 1 + + assert first_3dobj in test_dict + assert first_3dobj_again in test_dict + assert second_3dobj not in test_dict + + test_dict[first_3dobj_again] = first_3dobj_again.get_name() + assert len(test_dict) == 1 + test_dict[second_3dobj] = second_3dobj.get_name() + assert len(test_dict) == 2 + + assert second_3dobj in test_dict +