use std::{cell::RefCell, rc::Rc, time::Duration};
use gpui::{
Animation, AnimationExt, AnyElement, Context, IntoElement, ParentElement, Render, Styled,
TestAppContext, Window, WindowHandle, div, px, size,
};
use motion::{AppExt as _, Painter};
use theme::{Appearance, Theme};
use ui::loaders;
const SECOND: Duration = Duration::from_secs(1);
const STEP: Duration = Duration::from_millis(10);
const DISPLAY_RATE: usize = 120;
const THROTTLE_FPS: f32 = 30.0;
const PERIOD: Duration = Duration::from_secs(2);
const LEASE_UNTIL: Duration = Duration::from_secs(30);
#[derive(Clone, Copy)]
enum Drive {
Still,
DisplayRate,
Throttled,
SharedClock,
Leased(f32),
}
struct Counted {
renders: Rc<RefCell<usize>>,
drive: Drive,
}
impl Render for Counted {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
*self.renders.borrow_mut() += 1;
let body: AnyElement = match self.drive {
Drive::Still => div().into_any_element(),
Drive::DisplayRate => div()
.with_animation(
"display-rate",
Animation::new(PERIOD).repeat(),
|el, delta| el.opacity(delta),
)
.into_any_element(),
Drive::Throttled => div()
.with_animation(
"throttled",
Animation::new(PERIOD).repeat().with_max_fps(THROTTLE_FPS),
|el, delta| el.opacity(delta),
)
.into_any_element(),
Drive::SharedClock => {
let theme = Theme::of(cx).clone();
let painter = Painter::of(cx);
loaders::pulse_loader("pulse", &theme, 8.0, painter, cx).into_any_element()
}
Drive::Leased(fps) => {
Painter::of(cx).lease(fps, LEASE_UNTIL, cx);
div().into_any_element()
}
};
div().size_full().child(body)
}
}
fn open(cx: &mut TestAppContext, drive: Drive) -> (Rc<RefCell<usize>>, WindowHandle<Counted>) {
cx.update(|cx| {
Theme::install_custom(Theme::for_appearance(Appearance::Dark), cx);
cx.set_pause_when_inactive(false);
});
let renders = Rc::new(RefCell::new(0));
let window = cx.open_window(size(px(200.), px(200.)), {
let renders = renders.clone();
move |_, _| Counted { renders, drive }
});
cx.run_until_parked();
(renders, window)
}
fn pending_frames(window: &WindowHandle<Counted>, cx: &mut TestAppContext) -> usize {
let pending = window
.update(cx, |_, window, cx| window.simulate_next_frame(cx))
.unwrap();
cx.run_until_parked();
pending
}
fn advance_a_second(cx: &mut TestAppContext) {
for _ in 0..(SECOND.as_millis() / STEP.as_millis()) {
cx.executor().advance_clock(STEP);
cx.run_until_parked();
}
}
#[gpui::test]
fn a_window_with_nothing_moving_draws_nothing_more(cx: &mut TestAppContext) {
let (renders, window) = open(cx, Drive::Still);
let settled = *renders.borrow();
assert_eq!(
pending_frames(&window, cx),
0,
"a still window asked for another frame"
);
advance_a_second(cx);
assert_eq!(
*renders.borrow(),
settled,
"a still window redrew itself over an idle second"
);
}
#[gpui::test]
fn gpuis_default_drive_never_stops_asking_for_frames(cx: &mut TestAppContext) {
let (renders, window) = open(cx, Drive::DisplayRate);
let settled = *renders.borrow();
for frame in 1..=10 {
assert_eq!(
pending_frames(&window, cx),
1,
"stopped asking for frames at frame {frame}, which this drive never does"
);
}
assert_eq!(
*renders.borrow() - settled,
10,
"every frame asked for was a full redraw"
);
}
#[gpui::test]
fn gpuis_throttle_leaves_no_per_frame_callback(cx: &mut TestAppContext) {
let (renders, window) = open(cx, Drive::Throttled);
let settled = *renders.borrow();
assert_eq!(
pending_frames(&window, cx),
0,
"the throttled drive asked for a frame at display rate"
);
advance_a_second(cx);
let drawn = *renders.borrow() - settled;
assert!(
drawn > 0 && drawn < DISPLAY_RATE / 2,
"throttled to {THROTTLE_FPS}fps but drew {drawn} times in a second"
);
}
#[gpui::test]
fn two_leases_are_each_notified_at_their_own_rate(cx: &mut TestAppContext) {
let (slow, _slow_window) = open(cx, Drive::Leased(10.0));
let (fast, _fast_window) = open(cx, Drive::Leased(60.0));
let (slow_settled, fast_settled) = (*slow.borrow(), *fast.borrow());
advance_a_second(cx);
let (slow_drawn, fast_drawn) = (*slow.borrow() - slow_settled, *fast.borrow() - fast_settled);
assert!(
(5..=15).contains(&slow_drawn),
"a 10fps lease drew {slow_drawn} times in a second"
);
assert!(
(40..=80).contains(&fast_drawn),
"a 60fps lease drew {fast_drawn} times in a second"
);
assert!(
fast_drawn > slow_drawn * 2,
"the two rates collapsed together: {slow_drawn} and {fast_drawn}"
);
}
#[gpui::test]
fn the_clock_parks_when_the_last_lease_lapses(cx: &mut TestAppContext) {
let (renders, window) = open(cx, Drive::SharedClock);
advance_a_second(cx);
assert!(*renders.borrow() > 1, "the loader never started");
window
.update(cx, |view, _, cx| {
view.drive = Drive::Still;
cx.notify();
})
.unwrap();
advance_a_second(cx);
let parked = *renders.borrow();
advance_a_second(cx);
assert_eq!(
*renders.borrow(),
parked,
"the clock kept drawing after the last lease lapsed"
);
}
#[gpui::test]
fn a_mounted_loader_never_drives_the_window_at_display_rate(cx: &mut TestAppContext) {
let (renders, window) = open(cx, Drive::SharedClock);
let settled = *renders.borrow();
assert_eq!(
pending_frames(&window, cx),
0,
"the loader asked for a frame at display rate"
);
advance_a_second(cx);
let drawn = *renders.borrow() - settled;
assert!(
drawn > 0,
"the loader stopped animating: it drew {drawn} times in a second"
);
assert!(
drawn < DISPLAY_RATE / 2,
"the loader drew {drawn} times in a second, near the display's {DISPLAY_RATE}"
);
}