Detailed changes
@@ -39,9 +39,9 @@ impl App {
Self::new(current_platform(), asset_source, http_client)
}
- #[cfg(any(test, feature = "test"))]
- pub fn test() -> Self {
- let platform = Arc::new(super::TestPlatform::new());
+ #[cfg(any(test, feature = "test-support"))]
+ pub fn test(seed: u64) -> Self {
+ let platform = Arc::new(crate::TestPlatform::new(seed));
let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response();
Self::new(platform, asset_source, http_client)
@@ -145,8 +145,10 @@ impl Executor {
match future.as_mut().poll(&mut cx) {
Poll::Ready(result) => return result,
Poll::Pending => {
- // todo!("call tick on test dispatcher")
- parker.park();
+ if !self.dispatcher.poll() {
+ // todo!("forbid_parking")
+ parker.park();
+ }
}
}
}
@@ -1,7 +1,7 @@
mod keystroke;
#[cfg(target_os = "macos")]
mod mac;
-#[cfg(any(test, feature = "test"))]
+#[cfg(any(test, feature = "test-support"))]
mod test;
use crate::{
@@ -30,7 +30,7 @@ use std::{
pub use keystroke::*;
#[cfg(target_os = "macos")]
pub use mac::*;
-#[cfg(any(test, feature = "test"))]
+#[cfg(any(test, feature = "test-support"))]
pub use test::*;
pub use time::UtcOffset;
@@ -161,6 +161,9 @@ pub trait PlatformDispatcher: Send + Sync {
fn dispatch(&self, runnable: Runnable);
fn dispatch_on_main_thread(&self, runnable: Runnable);
fn dispatch_after(&self, duration: Duration, runnable: Runnable);
+ fn poll(&self) -> bool;
+ #[cfg(any(test, feature = "test-support"))]
+ fn advance_clock(&self, duration: Duration);
}
pub trait PlatformTextSystem: Send + Sync {
@@ -67,6 +67,14 @@ impl PlatformDispatcher for MacDispatcher {
);
}
}
+
+ fn poll(&self) -> bool {
+ false
+ }
+
+ fn advance_clock(&self, _: Duration) {
+ unimplemented!()
+ }
}
extern "C" fn trampoline(runnable: *mut c_void) {
@@ -1,188 +1,5 @@
-use super::Platform;
-use crate::{DisplayId, Executor};
+mod dispatcher;
+mod platform;
-pub struct TestPlatform;
-
-impl TestPlatform {
- pub fn new() -> Self {
- TestPlatform
- }
-}
-
-// todo!("implement out what our tests needed in GPUI 1")
-impl Platform for TestPlatform {
- fn executor(&self) -> Executor {
- unimplemented!()
- }
-
- fn text_system(&self) -> std::sync::Arc<dyn crate::PlatformTextSystem> {
- unimplemented!()
- }
-
- fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
- unimplemented!()
- }
-
- fn quit(&self) {
- unimplemented!()
- }
-
- fn restart(&self) {
- unimplemented!()
- }
-
- fn activate(&self, _ignoring_other_apps: bool) {
- unimplemented!()
- }
-
- fn hide(&self) {
- unimplemented!()
- }
-
- fn hide_other_apps(&self) {
- unimplemented!()
- }
-
- fn unhide_other_apps(&self) {
- unimplemented!()
- }
-
- fn displays(&self) -> Vec<std::rc::Rc<dyn crate::PlatformDisplay>> {
- unimplemented!()
- }
-
- fn display(&self, _id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
- unimplemented!()
- }
-
- fn main_window(&self) -> Option<crate::AnyWindowHandle> {
- unimplemented!()
- }
-
- fn open_window(
- &self,
- _handle: crate::AnyWindowHandle,
- _options: crate::WindowOptions,
- ) -> Box<dyn crate::PlatformWindow> {
- unimplemented!()
- }
-
- fn set_display_link_output_callback(
- &self,
- _display_id: DisplayId,
- _callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp)>,
- ) {
- unimplemented!()
- }
-
- fn start_display_link(&self, _display_id: DisplayId) {
- unimplemented!()
- }
-
- fn stop_display_link(&self, _display_id: DisplayId) {
- unimplemented!()
- }
-
- fn open_url(&self, _url: &str) {
- unimplemented!()
- }
-
- fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {
- unimplemented!()
- }
-
- fn prompt_for_paths(
- &self,
- _options: crate::PathPromptOptions,
- ) -> futures::channel::oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
- unimplemented!()
- }
-
- fn prompt_for_new_path(
- &self,
- _directory: &std::path::Path,
- ) -> futures::channel::oneshot::Receiver<Option<std::path::PathBuf>> {
- unimplemented!()
- }
-
- fn reveal_path(&self, _path: &std::path::Path) {
- unimplemented!()
- }
-
- fn on_become_active(&self, _callback: Box<dyn FnMut()>) {
- unimplemented!()
- }
-
- fn on_resign_active(&self, _callback: Box<dyn FnMut()>) {
- unimplemented!()
- }
-
- fn on_quit(&self, _callback: Box<dyn FnMut()>) {
- unimplemented!()
- }
-
- fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
- unimplemented!()
- }
-
- fn on_event(&self, _callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
- unimplemented!()
- }
-
- fn os_name(&self) -> &'static str {
- unimplemented!()
- }
-
- fn os_version(&self) -> anyhow::Result<crate::SemanticVersion> {
- unimplemented!()
- }
-
- fn app_version(&self) -> anyhow::Result<crate::SemanticVersion> {
- unimplemented!()
- }
-
- fn app_path(&self) -> anyhow::Result<std::path::PathBuf> {
- unimplemented!()
- }
-
- fn local_timezone(&self) -> time::UtcOffset {
- unimplemented!()
- }
-
- fn path_for_auxiliary_executable(&self, _name: &str) -> anyhow::Result<std::path::PathBuf> {
- unimplemented!()
- }
-
- fn set_cursor_style(&self, _style: crate::CursorStyle) {
- unimplemented!()
- }
-
- fn should_auto_hide_scrollbars(&self) -> bool {
- unimplemented!()
- }
-
- fn write_to_clipboard(&self, _item: crate::ClipboardItem) {
- unimplemented!()
- }
-
- fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
- unimplemented!()
- }
-
- fn write_credentials(
- &self,
- _url: &str,
- _username: &str,
- _password: &[u8],
- ) -> anyhow::Result<()> {
- unimplemented!()
- }
-
- fn read_credentials(&self, _url: &str) -> anyhow::Result<Option<(String, Vec<u8>)>> {
- unimplemented!()
- }
-
- fn delete_credentials(&self, _url: &str) -> anyhow::Result<()> {
- unimplemented!()
- }
-}
+pub use dispatcher::*;
+pub use platform::*;
@@ -0,0 +1,150 @@
+use crate::PlatformDispatcher;
+use async_task::Runnable;
+use collections::{BTreeMap, VecDeque};
+use parking_lot::Mutex;
+use rand::prelude::*;
+use std::time::{Duration, Instant};
+
+pub struct TestDispatcher(Mutex<TestDispatcherState>);
+
+struct TestDispatcherState {
+ random: StdRng,
+ foreground: VecDeque<Runnable>,
+ background: Vec<Runnable>,
+ delayed: BTreeMap<Instant, Runnable>,
+ time: Instant,
+ is_main_thread: bool,
+}
+
+impl TestDispatcher {
+ pub fn new(random: StdRng) -> Self {
+ let state = TestDispatcherState {
+ random,
+ foreground: VecDeque::new(),
+ background: Vec::new(),
+ delayed: BTreeMap::new(),
+ time: Instant::now(),
+ is_main_thread: true,
+ };
+
+ TestDispatcher(Mutex::new(state))
+ }
+}
+
+impl PlatformDispatcher for TestDispatcher {
+ fn is_main_thread(&self) -> bool {
+ self.0.lock().is_main_thread
+ }
+
+ fn dispatch(&self, runnable: Runnable) {
+ self.0.lock().background.push(runnable);
+ }
+
+ fn dispatch_on_main_thread(&self, runnable: Runnable) {
+ self.0.lock().foreground.push_back(runnable);
+ }
+
+ fn dispatch_after(&self, duration: std::time::Duration, runnable: Runnable) {
+ let mut state = self.0.lock();
+ let next_time = state.time + duration;
+ state.delayed.insert(next_time, runnable);
+ }
+
+ fn poll(&self) -> bool {
+ let mut state = self.0.lock();
+
+ while let Some((deadline, _)) = state.delayed.first_key_value() {
+ if *deadline > state.time {
+ break;
+ }
+ let (_, runnable) = state.delayed.pop_first().unwrap();
+ state.background.push(runnable);
+ }
+
+ if state.foreground.is_empty() && state.background.is_empty() {
+ return false;
+ }
+
+ let foreground_len = state.foreground.len();
+ let background_len = state.background.len();
+ let main_thread = background_len == 0
+ || state
+ .random
+ .gen_ratio(foreground_len as u32, background_len as u32);
+ let was_main_thread = state.is_main_thread;
+ state.is_main_thread = main_thread;
+
+ let runnable = if main_thread {
+ state.foreground.pop_front().unwrap()
+ } else {
+ let ix = state.random.gen_range(0..background_len);
+ state.background.remove(ix)
+ };
+
+ drop(state);
+ runnable.run();
+
+ self.0.lock().is_main_thread = was_main_thread;
+
+ true
+ }
+
+ fn advance_clock(&self, by: Duration) {
+ self.0.lock().time += by;
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::Executor;
+ use std::sync::Arc;
+
+ #[test]
+ fn test_dispatch() {
+ let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
+ let executor = Executor::new(Arc::new(dispatcher));
+
+ let result = executor.block(async { executor.run_on_main(|| 1).await });
+ assert_eq!(result, 1);
+
+ let result = executor.block({
+ let executor = executor.clone();
+ async move {
+ executor
+ .spawn_on_main({
+ let executor = executor.clone();
+ assert!(executor.is_main_thread());
+ || async move {
+ assert!(executor.is_main_thread());
+ let result = executor
+ .spawn({
+ let executor = executor.clone();
+ async move {
+ assert!(!executor.is_main_thread());
+
+ let result = executor
+ .spawn_on_main({
+ let executor = executor.clone();
+ || async move {
+ assert!(executor.is_main_thread());
+ 2
+ }
+ })
+ .await;
+
+ assert!(!executor.is_main_thread());
+ result
+ }
+ })
+ .await;
+ assert!(executor.is_main_thread());
+ result
+ }
+ })
+ .await
+ }
+ });
+ assert_eq!(result, 2);
+ }
+}
@@ -0,0 +1,194 @@
+use crate::{DisplayId, Executor, Platform, PlatformTextSystem, TestDispatcher};
+use rand::prelude::*;
+use std::sync::Arc;
+
+pub struct TestPlatform {
+ executor: Executor,
+}
+
+impl TestPlatform {
+ pub fn new(seed: u64) -> Self {
+ let rng = StdRng::seed_from_u64(seed);
+ TestPlatform {
+ executor: Executor::new(Arc::new(TestDispatcher::new(rng))),
+ }
+ }
+}
+
+// todo!("implement out what our tests needed in GPUI 1")
+impl Platform for TestPlatform {
+ fn executor(&self) -> Executor {
+ self.executor.clone()
+ }
+
+ fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
+ Arc::new(crate::platform::mac::MacTextSystem::new())
+ }
+
+ fn run(&self, _on_finish_launching: Box<dyn FnOnce()>) {
+ unimplemented!()
+ }
+
+ fn quit(&self) {
+ unimplemented!()
+ }
+
+ fn restart(&self) {
+ unimplemented!()
+ }
+
+ fn activate(&self, _ignoring_other_apps: bool) {
+ unimplemented!()
+ }
+
+ fn hide(&self) {
+ unimplemented!()
+ }
+
+ fn hide_other_apps(&self) {
+ unimplemented!()
+ }
+
+ fn unhide_other_apps(&self) {
+ unimplemented!()
+ }
+
+ fn displays(&self) -> Vec<std::rc::Rc<dyn crate::PlatformDisplay>> {
+ unimplemented!()
+ }
+
+ fn display(&self, _id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
+ unimplemented!()
+ }
+
+ fn main_window(&self) -> Option<crate::AnyWindowHandle> {
+ unimplemented!()
+ }
+
+ fn open_window(
+ &self,
+ _handle: crate::AnyWindowHandle,
+ _options: crate::WindowOptions,
+ ) -> Box<dyn crate::PlatformWindow> {
+ unimplemented!()
+ }
+
+ fn set_display_link_output_callback(
+ &self,
+ _display_id: DisplayId,
+ _callback: Box<dyn FnMut(&crate::VideoTimestamp, &crate::VideoTimestamp)>,
+ ) {
+ unimplemented!()
+ }
+
+ fn start_display_link(&self, _display_id: DisplayId) {
+ unimplemented!()
+ }
+
+ fn stop_display_link(&self, _display_id: DisplayId) {
+ unimplemented!()
+ }
+
+ fn open_url(&self, _url: &str) {
+ unimplemented!()
+ }
+
+ fn on_open_urls(&self, _callback: Box<dyn FnMut(Vec<String>)>) {
+ unimplemented!()
+ }
+
+ fn prompt_for_paths(
+ &self,
+ _options: crate::PathPromptOptions,
+ ) -> futures::channel::oneshot::Receiver<Option<Vec<std::path::PathBuf>>> {
+ unimplemented!()
+ }
+
+ fn prompt_for_new_path(
+ &self,
+ _directory: &std::path::Path,
+ ) -> futures::channel::oneshot::Receiver<Option<std::path::PathBuf>> {
+ unimplemented!()
+ }
+
+ fn reveal_path(&self, _path: &std::path::Path) {
+ unimplemented!()
+ }
+
+ fn on_become_active(&self, _callback: Box<dyn FnMut()>) {
+ unimplemented!()
+ }
+
+ fn on_resign_active(&self, _callback: Box<dyn FnMut()>) {
+ unimplemented!()
+ }
+
+ fn on_quit(&self, _callback: Box<dyn FnMut()>) {
+ unimplemented!()
+ }
+
+ fn on_reopen(&self, _callback: Box<dyn FnMut()>) {
+ unimplemented!()
+ }
+
+ fn on_event(&self, _callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
+ unimplemented!()
+ }
+
+ fn os_name(&self) -> &'static str {
+ unimplemented!()
+ }
+
+ fn os_version(&self) -> anyhow::Result<crate::SemanticVersion> {
+ unimplemented!()
+ }
+
+ fn app_version(&self) -> anyhow::Result<crate::SemanticVersion> {
+ unimplemented!()
+ }
+
+ fn app_path(&self) -> anyhow::Result<std::path::PathBuf> {
+ unimplemented!()
+ }
+
+ fn local_timezone(&self) -> time::UtcOffset {
+ unimplemented!()
+ }
+
+ fn path_for_auxiliary_executable(&self, _name: &str) -> anyhow::Result<std::path::PathBuf> {
+ unimplemented!()
+ }
+
+ fn set_cursor_style(&self, _style: crate::CursorStyle) {
+ unimplemented!()
+ }
+
+ fn should_auto_hide_scrollbars(&self) -> bool {
+ unimplemented!()
+ }
+
+ fn write_to_clipboard(&self, _item: crate::ClipboardItem) {
+ unimplemented!()
+ }
+
+ fn read_from_clipboard(&self) -> Option<crate::ClipboardItem> {
+ unimplemented!()
+ }
+
+ fn write_credentials(
+ &self,
+ _url: &str,
+ _username: &str,
+ _password: &[u8],
+ ) -> anyhow::Result<()> {
+ unimplemented!()
+ }
+
+ fn read_credentials(&self, _url: &str) -> anyhow::Result<Option<(String, Vec<u8>)>> {
+ unimplemented!()
+ }
+
+ fn delete_credentials(&self, _url: &str) -> anyhow::Result<()> {
+ unimplemented!()
+ }
+}
@@ -143,7 +143,7 @@ mod tests {
#[test]
fn test_wrap_line() {
- App::test().run(|cx| {
+ App::test(0).run(|cx| {
let text_system = cx.text_system().clone();
let mut wrapper = LineWrapper::new(
text_system.font_id(&font("Courier")).unwrap(),
@@ -12,10 +12,10 @@ pub use util::*;
// timer.race(future).await
// }
-#[cfg(any(test, feature = "test"))]
+#[cfg(any(test, feature = "test-support"))]
pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
-#[cfg(any(test, feature = "test"))]
+#[cfg(any(test, feature = "test-support"))]
impl<'a> std::fmt::Debug for CwdBacktrace<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use backtrace::{BacktraceFmt, BytesOrWideString};
@@ -1819,7 +1819,7 @@ impl AnyWindowHandle {
}
}
-#[cfg(any(test, feature = "test"))]
+#[cfg(any(test, feature = "test-support"))]
impl From<SmallVec<[u32; 16]>> for StackingOrder {
fn from(small_vec: SmallVec<[u32; 16]>) -> Self {
StackingOrder(small_vec)