Repository: Genesis-Embodied-AI/Genesis
File: tests/test_sensor_camera.py (Python)

Code before:
            entity_idx=sphere.idx,
            link_idx_local=0,
            offset_T=trans_to_T(np.array([0.0, 0.0, 3.0])),
        )
    )

    scene.build(n_envs=n_envs)

    scene.step()
    for camera in (camera_1, camera_2):
        data = camera.read()
        if n_envs > 0:
            for i in range(n_envs):
                assert rgb_array_to_png_bytes(data.rgb[i]) == png_snapshot
        else:
            assert rgb_array_to_png_bytes(data.rgb) == png_snapshot


@pytest.mark.required
def test_destroy_unbuilt_scene_with_camera():
    """Test that destroy on an unbuilt scene with cameras doesn't crash."""
    scene = gs.Scene(show_viewer=False)
    scene.add_entity(morph=gs.morphs.Plane())
    scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))

    # Destroy without building - should not crash
    scene.destroy()


@pytest.mark.required
def test_destroy_idempotent_with_camera():
    """Test that calling destroy twice on a scene with cameras doesn't crash."""
    scene = gs.Scene(show_viewer=False)
    scene.add_entity(morph=gs.morphs.Plane())
    camera = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))

    scene.build()
    camera.read()

    scene.destroy()
    scene.destroy()  # Should not crash


@pytest.mark.required
def test_cameras_share_metadata():
    scene = gs.Scene(show_viewer=False)
    scene.add_entity(morph=gs.morphs.Plane())

    cam1 = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))
    cam2 = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(32, 32)))


Review comment (bug):
I think it should "crash" actually. In a sense that only a scene that was build should be destroyed. Here it should raise an exception. Of course, no rendering backend should be started before build.

Code after:
            **camera_common_options,
            entity_idx=sphere.idx,
            link_idx_local=0,
            offset_T=trans_to_T(np.array([0.0, 0.0, 3.0])),
        )
    )

    scene.build(n_envs=n_envs)

    scene.step()
    for camera in (camera_1, camera_2):
        data = camera.read()
        if n_envs > 0:
            for i in range(n_envs):
                assert rgb_array_to_png_bytes(data.rgb[i]) == png_snapshot
        else:
            assert rgb_array_to_png_bytes(data.rgb) == png_snapshot


@pytest.mark.required
def test_destroy_unbuilt_scene_with_camera():
    """Test that destroy on an unbuilt scene with cameras doesn't crash."""
    scene = gs.Scene(show_viewer=False)
    scene.add_entity(morph=gs.morphs.Plane())
    scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))

    # Scene.__del__ calls destroy(), and a crash in destroy() would result in some
    # logspam.
    scene.destroy()


@pytest.mark.required
def test_destroy_idempotent_with_camera():
    """Test that calling destroy twice on a scene with cameras doesn't crash."""
    scene = gs.Scene(show_viewer=False)
    camera = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))

    scene.build()
    camera.read()

    scene.destroy()
    # Scene.__del__ calls destroy(), which means it's expected that destroy() will
    # be called twice. A crash in destroy() would result in some logspam.
    scene.destroy()


@pytest.mark.required
def test_rasterizer_destroy():
    scene = gs.Scene(show_viewer=False)
    cam1 = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(64, 64)))
    cam2 = scene.add_sensor(gs.sensors.RasterizerCameraOptions(res=(32, 32)))