use std::cell::RefCell; use std::rc::Rc; use gpui::{ AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render, SharedString, Styled as _, TestAppContext, Window, div, }; use gpui_native_core::combobox::{Combobox, ComboboxOption, ComboboxState}; use gpui_native_core::init; fn option(label: &'static str) -> ComboboxOption { (SharedString::from(label), SharedString::from(label)) } struct Host { combobox: Entity>, renders: Rc>, } impl Render for Host { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { *self.renders.borrow_mut() += 1; div().size_full().p_4().child(self.combobox.clone()) } } #[test] fn real_keystrokes_are_not_pathologically_slow() { let cx = TestAppContext::single(); cx.update(init); let (state, renders, window) = cx.update(|cx| { let renders = Rc::new(RefCell::new(0)); let state_cell: Rc>>>> = Rc::new(RefCell::new(None)); let renders_for_host = renders.clone(); let cell = state_cell.clone(); let window = cx .open_window(gpui::WindowOptions::default(), |window, cx| { let state = cx.new(|cx| { ComboboxState::new( Combobox::new( "bench-combo", "Fruit", vec![ option("main"), option("maintain"), option("release"), option("banana"), ], ), window, cx, ) }); let state_for_host = state.clone(); cell.borrow_mut().replace(state.clone()); cx.new(|_| Host { combobox: state_for_host, renders: renders_for_host, }) }) .unwrap(); let state = state_cell.borrow().clone().expect("state captured"); (state, renders, window) }); let mut visual = gpui::VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { state.update(cx, |state, cx| state.focus(window, cx)); window.draw(cx).clear(cx); }); // The focused caret must ride executor timers, never frame requests. let frames = visual.update(|window, cx| window.simulate_next_frame(cx)); assert_eq!( frames, 0, "an idle focused combobox must not request animation frames" ); for letter in "abcdefghijklmnopqrstuvwxyz".chars() { visual.simulate_keystrokes(&letter.to_string()); visual.update(|window, cx| { window.draw(cx).clear(cx); }); } let total_renders = *renders.borrow(); println!( "26 real keystrokes: {total_renders} host renders ({:.2} per keystroke)", total_renders as f32 / 26. ); assert!( total_renders >= 26, "every keystroke must redraw the page; got {total_renders} renders" ); assert!( total_renders <= 26 * 3, "typing caused {total_renders} host renders; healthy filtering stays under 3 per keystroke" ); }