use std::cell::RefCell; use std::rc::Rc; use std::time::Instant; use gpui::{ AppContext as _, Context, Entity, IntoElement, ParentElement as _, Render, SharedString, Styled as _, TestAppContext, VisualTestContext, Window, div, }; use gpui_native_core::common::v_flex; use gpui_native_core::field::Field; use gpui_native_core::init; use gpui_native_core::input::{Input, InputState}; struct FieldHost { url: Entity, renders: Rc>, } impl Render for FieldHost { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { *self.renders.borrow_mut() += 1; let url_error = self .url .read(cx) .value() .is_empty() .then_some("Required") .map(SharedString::from); div().size_full().p_4().child( v_flex().gap_3().child( Field::new("bench-url", "Server URL") .description("Where your patches are sent.") .error(url_error) .child(Input::new("bench-url-input", self.url.clone())), ), ) } } /// A field page in a real window, with the url input focused. fn field_host() -> ( TestAppContext, Entity, Rc>, VisualTestContext, ) { let cx = TestAppContext::single(); cx.update(init); let (url, renders, window) = cx.update(|cx| { let renders = Rc::new(RefCell::new(0)); let url = InputState::new(cx); let url_for_host = url.clone(); let renders_for_host = renders.clone(); let window = cx .open_window(gpui::WindowOptions::default(), |_, cx| { cx.new(|_| FieldHost { url: url_for_host, renders: renders_for_host, }) }) .unwrap(); (url, renders, window) }); let mut visual = VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { let focus = url.read(cx).focus_handle(); window.focus(&focus, cx); window.draw(cx).clear(cx); }); (cx, url, renders, visual) } fn draw(visual: &mut VisualTestContext) { visual.update(|window, cx| { window.draw(cx).clear(cx); }); } fn type_and_time(visual: &mut VisualTestContext, text: &str) -> std::time::Duration { let started = Instant::now(); for letter in text.chars() { visual.simulate_keystrokes(&letter.to_string()); draw(visual); } let elapsed = started.elapsed(); println!( "{} keystrokes: {elapsed:?} ({:.2} ms per keystroke)", text.chars().count(), elapsed.as_secs_f64() / text.chars().count() as f64 * 1000. ); elapsed } #[test] fn an_idle_field_page_schedules_no_frames() { let (_cx, _url, _renders, mut visual) = field_host(); let frames = visual.update(|window, cx| window.simulate_next_frame(cx)); assert_eq!( frames, 0, "an idle field page must not request animation frames" ); } #[test] fn field_typing_frame_cost() { let (_cx, _url, _renders, mut visual) = field_host(); let elapsed = type_and_time(&mut visual, "dsdsadsadadadada"); assert!( elapsed.as_millis() < 1000, "typing 16 characters took {elapsed:?}" ); }