use re_chunk_store::{LatestAtQuery, external::re_chunk::external::crossbeam::atomic::AtomicCell};
use re_log_types::{EntityPath, TimePoint, TimelineName};
use re_sdk_types::{
ViewClassIdentifier, archetypes, blueprint::archetypes as blueprint_archetypes,
};
use re_test_context::TestContext;
use re_test_viewport::TestContextExt as _;
use re_viewer_context::{RecommendedView, ViewClass as _};
use re_viewport_blueprint::{ViewBlueprint, ViewProperty};
struct TestScenario {
name: &'static str,
target_frame: &'static str,
view_class: ViewClassIdentifier,
}
fn setup_scene(test_context: &mut TestContext) {
test_context.log_entity("transforms", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Transform3D::new()
.with_child_frame("points3d")
.with_parent_frame("world"),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Transform3D::new()
.with_child_frame("misplaced_image")
.with_parent_frame("world"),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Transform3D::new()
.with_child_frame("misplaced_depth_image")
.with_parent_frame("world"),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Transform3D::new()
.with_child_frame("points2d")
.with_parent_frame("pinhole"),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Transform3D::new()
.with_child_frame("misplaced_boxes3d")
.with_parent_frame("pinhole"),
)
});
test_context.log_entity("pinhole_entity", |builder| {
builder.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Pinhole::from_focal_length_and_resolution([1.0, 1.0], [100.0, 100.0])
.with_child_frame("pinhole")
.with_parent_frame("world"),
)
});
test_context.log_entity("points3d_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Points3D::new([[1.0, 1.0, 1.0]]),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("points3d"),
)
});
test_context.log_entity("points2d_entity", |builder| {
builder
.with_archetype_auto_row(TimePoint::STATIC, &archetypes::Points2D::new([[1.0, 1.0]]))
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("points2d"),
)
});
test_context.log_entity("disconnected_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Ellipsoids3D::from_half_sizes([[1.0, 1.0, 1.0]]),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("disconnected"),
)
});
test_context.log_entity("misplaced_boxes3d_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Boxes3D::from_half_sizes([[1.0, 1.0, 1.0]]),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("misplaced_boxes3d"),
)
});
test_context.log_entity("misplaced_image_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Image::from_elements(
&[255u8, 0, 0],
[1, 1],
re_sdk_types::datatypes::ColorModel::RGB,
),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("misplaced_image"),
)
});
test_context.log_entity("misplaced_depth_image_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::DepthImage::try_from(re_sdk_types::datatypes::TensorData::new(
vec![1u64, 1u64],
re_sdk_types::datatypes::TensorBuffer::U16(vec![1u16].into()),
))
.expect("Failed to create depth image from tensor data"),
)
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("misplaced_depth_image"),
)
});
}
fn snapshot_visualizer_errors(
test_context: &TestContext,
view_id: re_viewer_context::ViewId,
) -> String {
let query_result_tree = AtomicCell::new(Default::default());
let mut harness = test_context
.setup_kittest_for_rendering_ui([100.0, 100.0])
.build_ui(|ui| {
test_context.run_ui(ui, |ctx, ui| {
test_context.ui_for_single_view(ui, ctx, view_id);
let query_results = ctx
.query_results
.get(&view_id)
.expect("query results should exist for the test view");
query_result_tree.store(query_results.tree.clone());
});
});
harness.run();
let visualizer_errors = test_context
.view_states
.lock()
.per_visualizer_type_reports(&test_context.recording_store_id, view_id)
.cloned()
.unwrap_or_default();
let query_result_tree = query_result_tree.take();
visualizer_errors
.iter()
.map(|(visualizer_type, error)| {
let error = match error {
re_viewer_context::VisualizerTypeReport::OverallError(err) => {
err.diagnostic.summary.clone()
}
re_viewer_context::VisualizerTypeReport::PerInstructionReport(errors) => errors
.iter()
.flat_map(|(instr_id, reports)| {
let data_result = query_result_tree
.lookup_result_by_visualizer_instruction(*instr_id)
.expect("visualizer instruction should resolve to a query result");
reports.iter().map(|report| {
format!(
"{:?}: {}",
data_result.entity_path, report.diagnostic.summary
)
})
})
.collect::<Vec<_>>()
.join("\n"),
};
format!("{visualizer_type:?}: {error}")
})
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn test_topology_errors() {
let mut test_context = TestContext::new();
test_context.register_view_class::<re_view_spatial::SpatialView3D>();
test_context.register_view_class::<re_view_spatial::SpatialView2D>();
setup_scene(&mut test_context);
let scenarios = [
TestScenario {
name: "3d_view_at_root",
target_frame: "world",
view_class: re_view_spatial::SpatialView3D::identifier(),
},
TestScenario {
name: "2d_view_at_root",
target_frame: "world",
view_class: re_view_spatial::SpatialView2D::identifier(),
},
TestScenario {
name: "2d_view_at_pinhole",
target_frame: "pinhole",
view_class: re_view_spatial::SpatialView2D::identifier(),
},
TestScenario {
name: "3d_view_at_pinhole",
target_frame: "pinhole",
view_class: re_view_spatial::SpatialView3D::identifier(),
},
];
for scenario in scenarios {
let view_id = test_context.setup_viewport_blueprint(|ctx, blueprint| {
let view_blueprint = ViewBlueprint::new(scenario.view_class, RecommendedView::root());
let view_id = view_blueprint.id;
ViewProperty::from_archetype_for_view::<blueprint_archetypes::SpatialInformation>(
ctx, view_id,
)
.save_blueprint_component(
ctx,
&blueprint_archetypes::SpatialInformation::descriptor_target_frame(),
&re_tf::TransformFrameId::new(scenario.target_frame),
);
blueprint.add_views(std::iter::once(view_blueprint), None, None);
view_id
});
insta::assert_snapshot!(
scenario.name,
snapshot_visualizer_errors(&test_context, view_id)
);
}
}
#[test]
fn test_topology_errors_for_nested_pinholes() {
let mut test_context = TestContext::new();
test_context.register_view_class::<re_view_spatial::SpatialView2D>();
test_context.log_entity("outer_pinhole_entity", |builder| {
builder.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Pinhole::from_focal_length_and_resolution([1.0, 1.0], [100.0, 100.0])
.with_child_frame("outer_pinhole")
.with_parent_frame("world"),
)
});
test_context.log_entity("inner_pinhole_entity", |builder| {
builder.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Pinhole::from_focal_length_and_resolution([1.0, 1.0], [100.0, 100.0])
.with_child_frame("inner_pinhole")
.with_parent_frame("outer_pinhole"),
)
});
test_context.log_entity("points2d_entity", |builder| {
builder
.with_archetype_auto_row(TimePoint::STATIC, &archetypes::Points2D::new([[1.0, 1.0]]))
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::CoordinateFrame::new("outer_pinhole"),
)
});
let view_id = test_context.setup_viewport_blueprint(|ctx, blueprint| {
let view_blueprint = ViewBlueprint::new(
re_view_spatial::SpatialView2D::identifier(),
RecommendedView::root(),
);
let view_id = view_blueprint.id;
ViewProperty::from_archetype_for_view::<blueprint_archetypes::SpatialInformation>(
ctx, view_id,
)
.save_blueprint_component(
ctx,
&blueprint_archetypes::SpatialInformation::descriptor_target_frame(),
&re_tf::TransformFrameId::new("inner_pinhole"),
);
blueprint.add_views(std::iter::once(view_blueprint), None, None);
view_id
});
insta::assert_snapshot!(
"2d_view_at_inner_pinhole_with_nested_pinhole",
snapshot_visualizer_errors(&test_context, view_id)
);
}
#[test]
fn test_empty_coordinate_frame_name_falls_back_to_implicit_frame() {
let mut test_context = TestContext::new();
test_context.register_view_class::<re_view_spatial::SpatialView3D>();
test_context.log_entity("points3d_entity", |builder| {
builder
.with_archetype_auto_row(
TimePoint::STATIC,
&archetypes::Points3D::new([[1.0, 1.0, 1.0]]),
)
.with_archetype_auto_row(TimePoint::STATIC, &archetypes::CoordinateFrame::new(""))
});
let stored_frame = test_context
.store_hub
.lock()
.entity_db(&test_context.recording_store_id)
.unwrap()
.latest_at_component::<re_tf::TransformFrameId>(
&EntityPath::from("points3d_entity"),
&LatestAtQuery::latest(TimelineName::log_tick()),
archetypes::CoordinateFrame::descriptor_frame().component,
)
.map(|(_, frame)| frame);
assert_eq!(stored_frame, Some(re_tf::TransformFrameId::new("")));
let view_id = test_context.setup_viewport_blueprint(|ctx, blueprint| {
let view_blueprint = ViewBlueprint::new(
re_view_spatial::SpatialView3D::identifier(),
RecommendedView::root(),
);
let view_id = view_blueprint.id;
ViewProperty::from_archetype_for_view::<blueprint_archetypes::SpatialInformation>(
ctx, view_id,
)
.save_blueprint_component(
ctx,
&blueprint_archetypes::SpatialInformation::descriptor_target_frame(),
&re_tf::TransformFrameId::new("tf#/"),
);
blueprint.add_views(std::iter::once(view_blueprint), None, None);
view_id
});
insta::assert_snapshot!(
"empty_coordinate_frame_name_falls_back_to_implicit_frame",
snapshot_visualizer_errors(&test_context, view_id)
);
}