Detailed changes
@@ -568,9 +568,9 @@ dependencies = [
[[package]]
name = "ctor"
-version = "0.1.19"
+version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
+checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
dependencies = [
"quote",
"syn",
@@ -1005,6 +1005,7 @@ dependencies = [
"pathfinder_color",
"pathfinder_geometry",
"png",
+ "postage",
"rand 0.8.3",
"replace_with",
"resvg",
@@ -2449,6 +2450,7 @@ dependencies = [
"anyhow",
"arrayvec",
"crossbeam-channel 0.5.0",
+ "ctor",
"dirs",
"easy-parallel",
"fsevent",
@@ -15,6 +15,7 @@ ordered-float = "2.1.1"
parking_lot = "0.11.1"
pathfinder_color = "0.5"
pathfinder_geometry = "0.5"
+postage = {version = "0.4.1", features = ["futures-traits"]}
rand = "0.8.3"
replace_with = "0.1.7"
resvg = "0.14"
@@ -13,11 +13,12 @@ use keymap::MatchResult;
use parking_lot::Mutex;
use pathfinder_geometry::{rect::RectF, vector::vec2f};
use platform::Event;
+use postage::{sink::Sink as _, stream::Stream as _};
use smol::prelude::*;
use std::{
any::{type_name, Any, TypeId},
cell::RefCell,
- collections::{HashMap, HashSet, VecDeque},
+ collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
fmt::{self, Debug},
hash::{Hash, Hasher},
marker::PhantomData,
@@ -384,6 +385,7 @@ pub struct MutableAppContext {
next_task_id: usize,
subscriptions: HashMap<usize, Vec<Subscription>>,
observations: HashMap<usize, Vec<Observation>>,
+ async_observations: HashMap<usize, postage::broadcast::Sender<()>>,
window_invalidations: HashMap<usize, WindowInvalidation>,
presenters_and_platform_windows:
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
@@ -424,6 +426,7 @@ impl MutableAppContext {
next_task_id: 0,
subscriptions: HashMap::new(),
observations: HashMap::new(),
+ async_observations: HashMap::new(),
window_invalidations: HashMap::new(),
presenters_and_platform_windows: HashMap::new(),
debug_elements_callbacks: HashMap::new(),
@@ -877,11 +880,13 @@ impl MutableAppContext {
self.ctx.models.remove(&model_id);
self.subscriptions.remove(&model_id);
self.observations.remove(&model_id);
+ self.async_observations.remove(&model_id);
}
for (window_id, view_id) in dropped_views {
self.subscriptions.remove(&view_id);
self.observations.remove(&view_id);
+ self.async_observations.remove(&view_id);
if let Some(window) = self.ctx.windows.get_mut(&window_id) {
self.window_invalidations
.entry(window_id)
@@ -1047,6 +1052,12 @@ impl MutableAppContext {
}
}
}
+
+ if let Entry::Occupied(mut entry) = self.async_observations.entry(observed_id) {
+ if entry.get_mut().blocking_send(()).is_err() {
+ entry.remove_entry();
+ }
+ }
}
fn notify_view_observers(&mut self, window_id: usize, view_id: usize) {
@@ -2012,6 +2023,36 @@ impl<T: Entity> ModelHandle<T> {
{
app.update_model(self, update)
}
+
+ pub fn condition(
+ &self,
+ ctx: &TestAppContext,
+ mut predicate: impl 'static + FnMut(&T, &AppContext) -> bool,
+ ) -> impl 'static + Future<Output = ()> {
+ let mut ctx = ctx.0.borrow_mut();
+ let tx = ctx
+ .async_observations
+ .entry(self.id())
+ .or_insert_with(|| postage::broadcast::channel(128).0);
+ let mut rx = tx.subscribe();
+ let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap();
+ let handle = self.clone();
+
+ async move {
+ loop {
+ {
+ let ctx = ctx.borrow();
+ let ctx = ctx.as_ref();
+ if predicate(handle.read(ctx), ctx) {
+ break;
+ }
+ }
+ if rx.recv().await.is_none() {
+ break;
+ }
+ }
+ }
+ }
}
impl<T> Clone for ModelHandle<T> {
@@ -2145,6 +2186,36 @@ impl<T: View> ViewHandle<T> {
app.focused_view_id(self.window_id)
.map_or(false, |focused_id| focused_id == self.view_id)
}
+
+ pub fn condition(
+ &self,
+ ctx: &TestAppContext,
+ mut predicate: impl 'static + FnMut(&T, &AppContext) -> bool,
+ ) -> impl 'static + Future<Output = ()> {
+ let mut ctx = ctx.0.borrow_mut();
+ let tx = ctx
+ .async_observations
+ .entry(self.id())
+ .or_insert_with(|| postage::broadcast::channel(128).0);
+ let mut rx = tx.subscribe();
+ let ctx = ctx.weak_self.as_ref().unwrap().upgrade().unwrap();
+ let handle = self.clone();
+
+ async move {
+ loop {
+ {
+ let ctx = ctx.borrow();
+ let ctx = ctx.as_ref();
+ if predicate(handle.read(ctx), ctx) {
+ break;
+ }
+ }
+ if rx.recv().await.is_none() {
+ break;
+ }
+ }
+ }
+ }
}
impl<T> Clone for ViewHandle<T> {
@@ -16,10 +16,11 @@ path = "src/main.rs"
anyhow = "1.0.38"
arrayvec = "0.5.2"
crossbeam-channel = "0.5.0"
+ctor = "0.1.20"
dirs = "3.0"
easy-parallel = "3.1.0"
-futures-core = "0.3"
fsevent = {path = "../fsevent"}
+futures-core = "0.3"
gpui = {path = "../gpui"}
ignore = {git = "https://github.com/zed-industries/ripgrep", rev = "1d152118f35b3e3590216709b86277062d79b8a0"}
lazy_static = "1.4.0"
@@ -31,8 +32,8 @@ postage = {version = "0.4.1", features = ["futures-traits"]}
rand = "0.8.3"
rust-embed = "5.9.0"
seahash = "4.1"
+serde = {version = "1", features = ["derive"]}
simplelog = "0.9"
-serde = { version = "1", features = ["derive"] }
smallvec = "1.6.1"
smol = "1.2.5"
@@ -1,4 +1,8 @@
+use crate::time::ReplicaId;
+use ctor::ctor;
+use log::LevelFilter;
use rand::Rng;
+use simplelog::SimpleLogger;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
@@ -6,7 +10,10 @@ use std::{
};
use tempdir::TempDir;
-use crate::time::ReplicaId;
+#[ctor]
+fn init_logger() {
+ SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
+}
#[derive(Clone)]
struct Envelope<T: Clone> {
@@ -402,44 +402,56 @@ mod tests {
// Open the first entry
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
- app.finish_pending_tasks().await;
- app.read(|ctx| {
- assert_eq!(
- workspace_view
- .read(ctx)
- .active_pane()
- .read(ctx)
- .items()
- .len(),
- 1
- )
- });
+ workspace_view
+ .condition(&app, |workspace_view, ctx| {
+ workspace_view.active_pane().read(ctx).items().len() == 1
+ })
+ .await;
// Open the second entry
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[1], ctx));
- app.finish_pending_tasks().await;
+
+ workspace_view
+ .condition(&app, |workspace_view, ctx| {
+ workspace_view.active_pane().read(ctx).items().len() == 2
+ })
+ .await;
app.read(|ctx| {
- let active_pane = workspace_view.read(ctx).active_pane().read(ctx);
- assert_eq!(active_pane.items().len(), 2);
assert_eq!(
- active_pane.active_item().unwrap().entry_id(ctx),
+ workspace_view
+ .read(ctx)
+ .active_pane()
+ .read(ctx)
+ .active_item()
+ .unwrap()
+ .entry_id(ctx),
Some(entries[1])
);
});
// Open the first entry again
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
- app.finish_pending_tasks().await;
+
+ {
+ let entries = entries.clone();
+ workspace_view
+ .condition(&app, move |workspace_view, ctx| {
+ workspace_view
+ .active_pane()
+ .read(ctx)
+ .active_item()
+ .unwrap()
+ .entry_id(ctx)
+ == Some(entries[0])
+ })
+ .await;
+ }
app.read(|ctx| {
let active_pane = workspace_view.read(ctx).active_pane().read(ctx);
assert_eq!(active_pane.items().len(), 2);
- assert_eq!(
- active_pane.active_item().unwrap().entry_id(ctx),
- Some(entries[0])
- );
});
// Open the third entry twice concurrently
@@ -447,19 +459,12 @@ mod tests {
w.open_entry(entries[2], ctx);
w.open_entry(entries[2], ctx);
});
- app.finish_pending_tasks().await;
- app.read(|ctx| {
- assert_eq!(
- workspace_view
- .read(ctx)
- .active_pane()
- .read(ctx)
- .items()
- .len(),
- 3
- );
- });
+ workspace_view
+ .condition(&app, |workspace_view, ctx| {
+ workspace_view.active_pane().read(ctx).items().len() == 3
+ })
+ .await;
});
}