Files
Ely-GPUI-Components/src/generative/tests/sound.rs
T
ZacharyZhang-NY a4f101f7b3 T25d Generative: sound and motion
AudioGenerationPlayer shows a generated sound's prompt, model and
length, a bar while it is made, then chat::AudioMessage to play it;
Regenerate and Download wait for it. TTSVoicePicker lists voices with a
sample to play, the chosen one washed with a check, tags dropping below
when narrow. VideoGenerationTimeline lays shots along their time under
a ruler, with posters, placeholders, a playhead and Extend.

AudioMessage's waveform is one Tab stop where Left and Right seek, and
it gives way at 280px. debug::ruler serves the timeline's ruler. A mask
shown to read no longer follows the pointer.
2026-09-26 13:16:42 -04:00

188 lines
6.1 KiB
Rust

use std::{cell::RefCell, rc::Rc, time::Duration};
use gpui::{
Context, IntoElement, Modifiers, ParentElement, Render, Styled, TestAppContext, Window, div,
point, px,
};
use super::{press, settle, setup, tab};
use crate::generative::{
AudioGenerationPlayer, Outcome, Shot, TTSVoicePicker, VideoGenerationTimeline, Voice,
};
type Heard = Rc<RefCell<Vec<String>>>;
/// A made sound a quarter played and one still being made, and what they heard.
struct Players(Heard);
impl Render for Players {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let (sought, made, making) = (self.0.clone(), self.0.clone(), self.0.clone());
let length = Duration::from_secs(40);
div()
.w(px(420.0))
.child(
AudioGenerationPlayer::new("made", "rain", "Orchid", vec![0.5; 8], length)
.played(Duration::from_secs(10), false)
.on_toggle(|_, _, _| {})
.on_seek(move |share, _, _| {
sought.borrow_mut().push(format!("seek {share:.2}"))
})
.on_regenerate(move |_, _| made.borrow_mut().push("regenerate made".into())),
)
.child(
AudioGenerationPlayer::new("making", "cello", "Orchid", vec![0.5; 8], length)
.making(0.4)
.on_regenerate(move |_, _| {
making.borrow_mut().push("regenerate making".into())
}),
)
}
}
#[gpui::test]
fn the_waveform_seeks_with_keys_and_a_sound_being_made_waits(cx: &mut TestAppContext) {
setup(cx);
let heard = Heard::default();
let store = heard.clone();
let (_, cx) = cx.add_window_view(|_, _| Players(store));
cx.update(|window, _| window.activate_window());
settle(cx);
tab(1, cx);
press("enter", cx);
tab(2, cx);
press("right", cx);
tab(1, cx);
press("enter", cx);
let heard = heard.borrow().clone();
assert_eq!(
heard,
["regenerate made", "seek 0.30", "regenerate made"],
"Tab passes the waiting button"
);
}
/// Two voices, the first chosen, and what they heard.
struct Voices(Heard);
impl Render for Voices {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let (chosen, played) = (self.0.clone(), self.0.clone());
let voice = |key: &str| Voice {
key: key.to_string().into(),
name: key.to_string().into(),
about: "a voice".into(),
tags: vec!["English".into()],
};
div().w(px(420.0)).child(
TTSVoicePicker::new("voices", [voice("iris"), voice("theo")])
.selected("iris")
.on_select(move |key, _, _| chosen.borrow_mut().push(format!("choose {key}")))
.on_play(move |key, _, _| played.borrow_mut().push(format!("play {key:?}"))),
)
}
}
#[gpui::test]
fn a_sample_plays_without_choosing_its_voice(cx: &mut TestAppContext) {
setup(cx);
let heard = Heard::default();
let store = heard.clone();
let (_, cx) = cx.add_window_view(|_, _| Voices(store));
cx.update(|window, _| window.activate_window());
settle(cx);
tab(3, cx);
press("enter", cx);
cx.simulate_click(point(px(23.0), px(83.0)), Modifiers::none());
settle(cx);
assert_eq!(
*heard.borrow(),
["choose theo", "play Some(\"theo\")"],
"a sample's press stays off its row"
);
}
/// Two shots, the first chosen, and what they heard.
struct Timeline(Heard);
impl Render for Timeline {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let (chosen, extended) = (self.0.clone(), self.0.clone());
let shot = |key: &str, outcome| Shot {
key: key.to_string().into(),
prompt: "a pan".into(),
length: Duration::from_secs(4),
outcome,
};
div().w(px(420.0)).child(
VideoGenerationTimeline::new(
"timeline",
[
shot("a", Outcome::Done("missing.jpg".into())),
shot("b", Outcome::Pending(None)),
],
Duration::ZERO,
)
.selected("a")
.on_select(move |key, _, _| chosen.borrow_mut().push(format!("choose {key}")))
.on_extend(move |_, _| extended.borrow_mut().push("extend".into())),
)
}
}
#[gpui::test]
fn a_shot_is_chosen_once_and_extend_asks_for_the_next(cx: &mut TestAppContext) {
setup(cx);
let heard = Heard::default();
let store = heard.clone();
let (_, cx) = cx.add_window_view(|_, _| Timeline(store));
cx.update(|window, _| window.activate_window());
settle(cx);
for _ in 0..3 {
tab(1, cx);
press("enter", cx);
}
assert_eq!(*heard.borrow(), ["choose b", "extend"]);
}
/// A voice list and a timeline shown to read, with nothing to take a choice.
struct Shown;
impl Render for Shown {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
let voice = Voice {
key: "iris".into(),
name: "Iris".into(),
about: "a voice".into(),
tags: Vec::new(),
};
let shot = Shot {
key: "a".into(),
prompt: "a pan".into(),
length: Duration::from_secs(4),
outcome: Outcome::Pending(None),
};
div()
.w(px(420.0))
.child(TTSVoicePicker::new("voices", [voice]).selected("iris"))
.child(VideoGenerationTimeline::new("timeline", [shot], Duration::ZERO).selected("a"))
}
}
#[gpui::test]
fn lists_shown_to_read_take_no_focus(cx: &mut TestAppContext) {
setup(cx);
let (_, cx) = cx.add_window_view(|_, _| Shown);
cx.update(|window, _| window.activate_window());
settle(cx);
tab(1, cx);
assert!(cx.update(|window, cx| window.focused(cx).is_none()));
}
#[test]
#[should_panic(expected = "a sound's peaks lie within 0 to 1")]
fn a_peak_past_full_fails_loud() {
let _ =
AudioGenerationPlayer::new("sound", "rain", "Orchid", vec![1.5], Duration::from_secs(1));
}