Implement TestAppContext::new

Co-Authored-By: Max <max@zed.dev>
Co-Authored-By: Conrad <conrad@zed.dev>
Co-Authored-By: Kyle <kyle@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-10-25 18:27:00 +02:00
parent 98e2490807
commit 52f9f90ccb
4 changed files with 97 additions and 91 deletions

View file

@ -39,72 +39,11 @@ pub struct App(Arc<Mutex<AppContext>>);
impl App { impl App {
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self { pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
let http_client = http::client(); Self(AppContext::new(
Self::new(current_platform(), asset_source, http_client) current_platform(),
} asset_source,
http::client(),
#[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)
}
fn new(
platform: Arc<dyn Platform>,
asset_source: Arc<dyn AssetSource>,
http_client: Arc<dyn HttpClient>,
) -> Self {
let executor = platform.executor();
assert!(
executor.is_main_thread(),
"must construct App on main thread"
);
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let mut entities = EntityMap::new();
let unit_entity = entities.insert(entities.reserve(), ());
let app_metadata = AppMetadata {
os_name: platform.os_name(),
os_version: platform.os_version().ok(),
app_version: platform.app_version().ok(),
};
Self(Arc::new_cyclic(|this| {
Mutex::new(AppContext {
this: this.clone(),
text_system,
platform: MainThreadOnly::new(platform, executor.clone()),
app_metadata,
flushing_effects: false,
pending_updates: 0,
next_frame_callbacks: Default::default(),
executor,
svg_renderer: SvgRenderer::new(asset_source.clone()),
asset_source,
image_cache: ImageCache::new(http_client),
text_style_stack: Vec::new(),
globals_by_type: HashMap::default(),
unit_entity,
entities,
windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())),
global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(),
observers: SubscriberSet::new(),
event_listeners: SubscriberSet::new(),
release_listeners: SubscriberSet::new(),
global_observers: SubscriberSet::new(),
quit_observers: SubscriberSet::new(),
layout_id_buffer: Default::default(),
propagate_event: true,
active_drag: None,
})
}))
} }
pub fn run<F>(self, on_finish_launching: F) pub fn run<F>(self, on_finish_launching: F)
@ -210,6 +149,62 @@ pub struct AppContext {
} }
impl AppContext { impl AppContext {
pub(crate) fn new(
platform: Arc<dyn Platform>,
asset_source: Arc<dyn AssetSource>,
http_client: Arc<dyn HttpClient>,
) -> Arc<Mutex<Self>> {
let executor = platform.executor();
assert!(
executor.is_main_thread(),
"must construct App on main thread"
);
let text_system = Arc::new(TextSystem::new(platform.text_system()));
let mut entities = EntityMap::new();
let unit_entity = entities.insert(entities.reserve(), ());
let app_metadata = AppMetadata {
os_name: platform.os_name(),
os_version: platform.os_version().ok(),
app_version: platform.app_version().ok(),
};
Arc::new_cyclic(|this| {
Mutex::new(AppContext {
this: this.clone(),
text_system,
platform: MainThreadOnly::new(platform, executor.clone()),
app_metadata,
flushing_effects: false,
pending_updates: 0,
next_frame_callbacks: Default::default(),
executor,
svg_renderer: SvgRenderer::new(asset_source.clone()),
asset_source,
image_cache: ImageCache::new(http_client),
text_style_stack: Vec::new(),
globals_by_type: HashMap::default(),
unit_entity,
entities,
windows: SlotMap::with_key(),
keymap: Arc::new(RwLock::new(Keymap::default())),
global_action_listeners: HashMap::default(),
action_builders: HashMap::default(),
pending_effects: VecDeque::new(),
pending_notifications: HashSet::default(),
pending_global_notifications: HashSet::default(),
observers: SubscriberSet::new(),
event_listeners: SubscriberSet::new(),
release_listeners: SubscriberSet::new(),
global_observers: SubscriberSet::new(),
quit_observers: SubscriberSet::new(),
layout_id_buffer: Default::default(),
propagate_event: true,
active_drag: None,
})
})
}
pub fn quit(&mut self) { pub fn quit(&mut self) {
let mut futures = Vec::new(); let mut futures = Vec::new();

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, Handle, MainThread, AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, Handle, MainThread,
ModelContext, Result, Task, WindowContext, ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{any::Any, future::Future, sync::Arc}; use std::{any::Any, future::Future, sync::Arc};
@ -37,6 +37,17 @@ impl Context for TestAppContext {
} }
impl TestAppContext { impl TestAppContext {
pub fn new(dispatcher: TestDispatcher) -> Self {
let executor = Executor::new(Arc::new(dispatcher));
let platform = Arc::new(TestPlatform::new(executor.clone()));
let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response();
Self {
app: AppContext::new(platform, asset_source, http_client),
executor,
}
}
pub fn refresh(&mut self) -> Result<()> { pub fn refresh(&mut self) -> Result<()> {
let mut lock = self.app.lock(); let mut lock = self.app.lock();
lock.refresh(); lock.refresh();
@ -47,27 +58,27 @@ impl TestAppContext {
&self.executor &self.executor
} }
pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> Result<R> { pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
let mut lock = self.app.lock(); let mut lock = self.app.lock();
Ok(f(&mut *lock)) f(&mut *lock)
} }
pub fn read_window<R>( pub fn read_window<R>(
&self, &self,
handle: AnyWindowHandle, handle: AnyWindowHandle,
update: impl FnOnce(&WindowContext) -> R, read: impl FnOnce(&WindowContext) -> R,
) -> Result<R> { ) -> R {
let mut app_context = self.app.lock(); let mut app_context = self.app.lock();
app_context.read_window(handle.id, update) app_context.read_window(handle.id, read).unwrap()
} }
pub fn update_window<R>( pub fn update_window<R>(
&self, &self,
handle: AnyWindowHandle, handle: AnyWindowHandle,
update: impl FnOnce(&mut WindowContext) -> R, update: impl FnOnce(&mut WindowContext) -> R,
) -> Result<R> { ) -> R {
let mut app = self.app.lock(); let mut app = self.app.lock();
app.update_window(handle.id, update) app.update_window(handle.id, update).unwrap()
} }
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R> pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
@ -94,22 +105,22 @@ impl TestAppContext {
pub fn run_on_main<R>( pub fn run_on_main<R>(
&self, &self,
f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static, f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
) -> Result<Task<R>> ) -> Task<R>
where where
R: Send + 'static, R: Send + 'static,
{ {
let mut app_context = self.app.lock(); let mut app_context = self.app.lock();
Ok(app_context.run_on_main(f)) app_context.run_on_main(f)
} }
pub fn has_global<G: 'static>(&self) -> Result<bool> { pub fn has_global<G: 'static>(&self) -> bool {
let lock = self.app.lock(); let lock = self.app.lock();
Ok(lock.has_global::<G>()) lock.has_global::<G>()
} }
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> { pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
let lock = self.app.lock(); let lock = self.app.lock();
Ok(read(lock.global(), &lock)) read(lock.global(), &lock)
} }
pub fn try_read_global<G: 'static, R>( pub fn try_read_global<G: 'static, R>(
@ -123,9 +134,9 @@ impl TestAppContext {
pub fn update_global<G: 'static, R>( pub fn update_global<G: 'static, R>(
&mut self, &mut self,
update: impl FnOnce(&mut G, &mut AppContext) -> R, update: impl FnOnce(&mut G, &mut AppContext) -> R,
) -> Result<R> { ) -> R {
let mut lock = self.app.lock(); let mut lock = self.app.lock();
Ok(lock.update_global(update)) lock.update_global(update)
} }
fn to_async(&self) -> AsyncAppContext { fn to_async(&self) -> AsyncAppContext {

View file

@ -1,5 +1,4 @@
use crate::{DisplayId, Executor, Platform, PlatformTextSystem, TestDispatcher}; use crate::{DisplayId, Executor, Platform, PlatformTextSystem};
use rand::prelude::*;
use std::sync::Arc; use std::sync::Arc;
pub struct TestPlatform { pub struct TestPlatform {
@ -7,11 +6,8 @@ pub struct TestPlatform {
} }
impl TestPlatform { impl TestPlatform {
pub fn new(seed: u64) -> Self { pub fn new(executor: Executor) -> Self {
let rng = StdRng::seed_from_u64(seed); TestPlatform { executor }
TestPlatform {
executor: Executor::new(Arc::new(TestDispatcher::new(rng))),
}
} }
} }

View file

@ -139,11 +139,15 @@ impl Boundary {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{font, App}; use crate::{font, TestAppContext, TestDispatcher};
use rand::prelude::*;
#[test] #[test]
fn test_wrap_line() { fn test_wrap_line() {
App::test(0).run(|cx| { let dispatcher = TestDispatcher::new(StdRng::seed_from_u64(0));
let cx = TestAppContext::new(dispatcher);
cx.update(|cx| {
let text_system = cx.text_system().clone(); let text_system = cx.text_system().clone();
let mut wrapper = LineWrapper::new( let mut wrapper = LineWrapper::new(
text_system.font_id(&font("Courier")).unwrap(), text_system.font_id(&font("Courier")).unwrap(),