Skip to content

Commit deafeaa

Browse files
authored
Test: check focus after double clicks (#11700)
### Related Part of #8948 ### What - Adds automated test checking what happens on double clicks - Removes `check_focus.py` from release checklist
1 parent e915426 commit deafeaa

File tree

14 files changed

+210
-85
lines changed

14 files changed

+210
-85
lines changed

crates/viewer/re_viewer/src/viewer_test_utils/mod.rs

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,48 @@ use crate::{
1313
#[derive(Default)]
1414
pub struct HarnessOptions {
1515
pub window_size: Option<egui::Vec2>,
16+
pub max_steps: Option<u64>,
17+
pub step_dt: Option<f32>,
1618
}
1719

1820
/// Convenience function for creating a kittest harness of the viewer App.
1921
pub fn viewer_harness(options: &HarnessOptions) -> Harness<'static, App> {
2022
let window_size = options.window_size.unwrap_or(egui::vec2(1024.0, 768.0));
2123

22-
re_ui::testing::new_harness(re_ui::testing::TestOptions::Rendering3D, window_size).build_eframe(
23-
|cc| {
24-
cc.egui_ctx.set_os(egui::os::OperatingSystem::Nix);
25-
customize_eframe_and_setup_renderer(cc).expect("Failed to customize eframe");
26-
let mut app = App::new(
27-
MainThreadToken::i_promise_i_am_only_using_this_for_a_test(),
28-
build_info!(),
29-
AppEnvironment::Test,
30-
StartupOptions {
31-
// Don't show the welcome / example screen in tests.
32-
// See also: https://github.com/rerun-io/rerun/issues/10989
33-
hide_welcome_screen: true,
34-
// Don't calculate memory limit in tests.
35-
memory_limit: re_memory::MemoryLimit::UNLIMITED,
36-
..Default::default()
37-
},
38-
cc,
39-
Some(re_redap_client::ConnectionRegistry::new_without_stored_credentials()),
40-
AsyncRuntimeHandle::from_current_tokio_runtime_or_wasmbindgen()
41-
.expect("Failed to create AsyncRuntimeHandle"),
42-
);
43-
// Force the FFmpeg path to be wrong so we have a reproducible behavior.
44-
app.app_options_mut().video_decoder_ffmpeg_path = "/fake/ffmpeg/path".to_owned();
45-
app.app_options_mut().video_decoder_override_ffmpeg_path = true;
46-
app
47-
},
48-
)
24+
let mut harness_builder =
25+
re_ui::testing::new_harness(re_ui::testing::TestOptions::Rendering3D, window_size);
26+
if let Some(max_steps) = options.max_steps {
27+
harness_builder = harness_builder.with_max_steps(max_steps);
28+
}
29+
if let Some(step_dt) = options.step_dt {
30+
harness_builder = harness_builder.with_step_dt(step_dt);
31+
}
32+
33+
harness_builder.build_eframe(|cc| {
34+
cc.egui_ctx.set_os(egui::os::OperatingSystem::Nix);
35+
customize_eframe_and_setup_renderer(cc).expect("Failed to customize eframe");
36+
let mut app = App::new(
37+
MainThreadToken::i_promise_i_am_only_using_this_for_a_test(),
38+
build_info!(),
39+
AppEnvironment::Test,
40+
StartupOptions {
41+
// Don't show the welcome / example screen in tests.
42+
// See also: https://github.com/rerun-io/rerun/issues/10989
43+
hide_welcome_screen: true,
44+
// Don't calculate memory limit in tests.
45+
memory_limit: re_memory::MemoryLimit::UNLIMITED,
46+
..Default::default()
47+
},
48+
cc,
49+
Some(re_redap_client::ConnectionRegistry::new_without_stored_credentials()),
50+
AsyncRuntimeHandle::from_current_tokio_runtime_or_wasmbindgen()
51+
.expect("Failed to create AsyncRuntimeHandle"),
52+
);
53+
// Force the FFmpeg path to be wrong so we have a reproducible behavior.
54+
app.app_options_mut().video_decoder_ffmpeg_path = "/fake/ffmpeg/path".to_owned();
55+
app.app_options_mut().video_decoder_override_ffmpeg_path = true;
56+
app
57+
})
4958
}
5059

5160
/// Steps through the harness until the `predicate` closure returns `true`.

crates/viewer/re_viewer/tests/app_kittest.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ async fn settings_screen() {
2222

2323
let mut harness = viewer_test_utils::viewer_harness(&HarnessOptions {
2424
window_size: Some(egui::vec2(1024.0, 1080.0)), // Settings screen can be a bit tall
25+
..Default::default()
2526
});
2627
harness.get_by_label("Menu").click();
2728
harness.run_ok();

tests/python/release_checklist/check_focus.py

Lines changed: 0 additions & 58 deletions
This file was deleted.

tests/rust/re_integration_test/src/kittest_harness_ext.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub trait HarnessExt<'h> {
7171
// Get the position of a node in the UI by its label.
7272
fn get_panel_position(&mut self, label: &str) -> egui::Rect;
7373

74+
// Click at a position in the UI.
75+
fn click_at(&mut self, pos: egui::Pos2);
76+
7477
// Drag-and-drop functions based on position
7578
fn drag_at(&mut self, pos: egui::Pos2);
7679
fn hover_at(&mut self, pos: egui::Pos2);
@@ -302,6 +305,18 @@ impl<'h> HarnessExt<'h> for egui_kittest::Harness<'h, re_viewer::App> {
302305
self.get_by_role_and_label(Role::Pane, label).rect()
303306
}
304307

308+
fn click_at(&mut self, pos: egui::Pos2) {
309+
for pressed in [true, false] {
310+
self.event(egui::Event::PointerButton {
311+
pos,
312+
button: PointerButton::Primary,
313+
pressed,
314+
modifiers: Modifiers::NONE,
315+
});
316+
self.run();
317+
}
318+
}
319+
305320
fn drag_at(&mut self, pos: egui::Pos2) {
306321
self.event(egui::Event::PointerButton {
307322
pos,
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//! This test logs a few boxes and performs the following focus checks:
2+
//!
3+
//! - Double-click on a box in the first view
4+
//! - check ONLY the corresponding view expands and scrolls
5+
//! - check the streams view expands and scrolls
6+
//! - Double-click on the leaf "boxes3d" entity in the streams view, check both views expand (manual scrolling might be needed).
7+
8+
use re_integration_test::HarnessExt as _;
9+
use re_sdk::TimePoint;
10+
use re_sdk::log::RowId;
11+
use re_viewer::external::re_viewer_context::ViewClass as _;
12+
use re_viewer::external::{re_types, re_view_spatial};
13+
use re_viewer::viewer_test_utils::{self, HarnessOptions};
14+
use re_viewport_blueprint::ViewBlueprint;
15+
16+
fn make_test_harness<'a>() -> egui_kittest::Harness<'a, re_viewer::App> {
17+
let mut harness = viewer_test_utils::viewer_harness(&HarnessOptions {
18+
window_size: Some(egui::vec2(1024.0, 768.0)),
19+
max_steps: Some(200), // Allow animations to finish.
20+
step_dt: Some(1.0 / 60.0), // Allow double clicks to go through.
21+
});
22+
harness.init_recording();
23+
harness.set_selection_panel_opened(false);
24+
25+
// Log some data.
26+
harness.log_entity("group/boxes3d", |builder| {
27+
builder.with_archetype(
28+
RowId::new(),
29+
TimePoint::default(),
30+
&re_types::archetypes::Boxes3D::from_centers_and_half_sizes(
31+
[(1.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0)],
32+
[(0.2, 0.4, 0.2), (0.2, 0.2, 0.4), (0.4, 0.2, 0.2)],
33+
)
34+
.with_colors([0xFF0000FF, 0x00FF00FF, 0x0000FFFF])
35+
.with_fill_mode(re_types::components::FillMode::Solid),
36+
)
37+
});
38+
harness.log_entity("txt/hello", |builder| {
39+
builder.with_archetype(
40+
RowId::new(),
41+
TimePoint::STATIC,
42+
&re_types::archetypes::TextDocument::new("Hello World!"),
43+
)
44+
});
45+
46+
harness
47+
}
48+
49+
fn setup_single_view_blueprint(harness: &mut egui_kittest::Harness<'_, re_viewer::App>) {
50+
harness.clear_current_blueprint();
51+
52+
let root_cid = harness.add_blueprint_container(egui_tiles::ContainerKind::Horizontal, None);
53+
let tab_cid = harness.add_blueprint_container(egui_tiles::ContainerKind::Tabs, Some(root_cid));
54+
let vertical_cid =
55+
harness.add_blueprint_container(egui_tiles::ContainerKind::Vertical, Some(root_cid));
56+
57+
let mut view_1 =
58+
ViewBlueprint::new_with_root_wildcard(re_view_spatial::SpatialView3D::identifier());
59+
view_1.display_name = Some("3D view 1".into());
60+
let mut view_2 =
61+
ViewBlueprint::new_with_root_wildcard(re_view_spatial::SpatialView3D::identifier());
62+
view_2.display_name = Some("3D view 2".into());
63+
64+
harness.setup_viewport_blueprint(move |_viewer_context, blueprint| {
65+
let text_views = (0..20).map(|i| {
66+
let mut view = ViewBlueprint::new_with_root_wildcard(
67+
re_view_text_document::TextDocumentView::identifier(),
68+
);
69+
view.display_name = Some(format!("Text view {i}"));
70+
view
71+
});
72+
blueprint.add_views(text_views, Some(tab_cid), None);
73+
blueprint.add_views([view_1, view_2].into_iter(), Some(vertical_cid), None);
74+
});
75+
}
76+
77+
#[tokio::test(flavor = "multi_thread")]
78+
pub async fn test_check_focus() {
79+
let mut harness = make_test_harness();
80+
setup_single_view_blueprint(&mut harness);
81+
82+
// Make the left panel wider.
83+
let centerline = harness.get_panel_position("Text view 0").left_center();
84+
let target_pos = centerline + egui::vec2(100.0, 0.0);
85+
harness.drag_at(centerline);
86+
harness.snapshot_app("check_focus_1");
87+
harness.hover_at(target_pos);
88+
harness.snapshot_app("check_focus_2");
89+
harness.drop_at(target_pos);
90+
harness.snapshot_app("check_focus_3");
91+
92+
// One of the boxes is at the center of the view.
93+
let pixel_of_a_box = harness.get_panel_position("3D view 1").center();
94+
95+
// Hover over the box.
96+
harness.hover_at(pixel_of_a_box);
97+
98+
// Let the app render. This will run the picking logic which needs the GPU
99+
// and lets the app find the hovered box.
100+
harness.render().expect("Cannot render app");
101+
harness.run();
102+
harness.snapshot_app("check_focus_4");
103+
104+
// Double click on the box, see how it expands the view.
105+
harness.click_at(pixel_of_a_box);
106+
harness.click_at(pixel_of_a_box);
107+
harness.snapshot_app("check_focus_5");
108+
109+
// Scroll down to see the second view stays collapsed.
110+
harness.blueprint_tree().hover_label("3D view 1");
111+
harness.event(egui::Event::MouseWheel {
112+
unit: egui::MouseWheelUnit::Page,
113+
delta: egui::vec2(0.0, -1.0),
114+
modifiers: egui::Modifiers::NONE,
115+
});
116+
harness.snapshot_app("check_focus_6");
117+
118+
// Double click the entity on the streams tree and see all views expand.
119+
harness.streams_tree().hover_label("boxes3d");
120+
harness.streams_tree().click_label("boxes3d");
121+
harness.streams_tree().click_label("boxes3d");
122+
harness.snapshot_app("check_focus_7");
123+
124+
// Scroll down to see the second view is entirely expanded.
125+
harness.blueprint_tree().hover_label("3D view 1");
126+
harness.event(egui::Event::MouseWheel {
127+
unit: egui::MouseWheelUnit::Page,
128+
delta: egui::vec2(0.0, -1.0),
129+
modifiers: egui::Modifiers::NONE,
130+
});
131+
harness.snapshot_app("check_focus_8");
132+
}

tests/rust/re_integration_test/tests/multi_container_test.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use re_viewport_blueprint::ViewBlueprint;
1111
fn make_multi_view_test_harness<'a>() -> egui_kittest::Harness<'a, re_viewer::App> {
1212
let mut harness = viewer_test_utils::viewer_harness(&HarnessOptions {
1313
window_size: Some(egui::Vec2::new(1024.0, 1024.0)),
14+
max_steps: Some(100), // Allow animations to finish
15+
..Default::default()
1416
});
1517
harness.init_recording();
1618

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)