WIP: Add a condition method to model and view handles for use in tests
It returns a future that resolves when the provided predicate returns true. The predicate is called any time the handle's targeted entity calls notify. Still need to add a timeout and completely remove finsih_pending_tasks.
This commit is contained in:
parent
69a43afcbd
commit
a4c1fe5a0b
6 changed files with 126 additions and 39 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -568,9 +568,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.1.19"
|
version = "0.1.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8f45d9ad417bcef4817d614a501ab55cdd96a6fdb24f49aab89a54acfd66b19"
|
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -1005,6 +1005,7 @@ dependencies = [
|
||||||
"pathfinder_color",
|
"pathfinder_color",
|
||||||
"pathfinder_geometry",
|
"pathfinder_geometry",
|
||||||
"png",
|
"png",
|
||||||
|
"postage",
|
||||||
"rand 0.8.3",
|
"rand 0.8.3",
|
||||||
"replace_with",
|
"replace_with",
|
||||||
"resvg",
|
"resvg",
|
||||||
|
@ -2449,6 +2450,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"crossbeam-channel 0.5.0",
|
"crossbeam-channel 0.5.0",
|
||||||
|
"ctor",
|
||||||
"dirs",
|
"dirs",
|
||||||
"easy-parallel",
|
"easy-parallel",
|
||||||
"fsevent",
|
"fsevent",
|
||||||
|
|
|
@ -15,6 +15,7 @@ ordered-float = "2.1.1"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
pathfinder_color = "0.5"
|
pathfinder_color = "0.5"
|
||||||
pathfinder_geometry = "0.5"
|
pathfinder_geometry = "0.5"
|
||||||
|
postage = {version = "0.4.1", features = ["futures-traits"]}
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
replace_with = "0.1.7"
|
replace_with = "0.1.7"
|
||||||
resvg = "0.14"
|
resvg = "0.14"
|
||||||
|
|
|
@ -13,11 +13,12 @@ use keymap::MatchResult;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use pathfinder_geometry::{rect::RectF, vector::vec2f};
|
use pathfinder_geometry::{rect::RectF, vector::vec2f};
|
||||||
use platform::Event;
|
use platform::Event;
|
||||||
|
use postage::{sink::Sink as _, stream::Stream as _};
|
||||||
use smol::prelude::*;
|
use smol::prelude::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::{type_name, Any, TypeId},
|
any::{type_name, Any, TypeId},
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
collections::{HashMap, HashSet, VecDeque},
|
collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
|
||||||
fmt::{self, Debug},
|
fmt::{self, Debug},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
|
@ -384,6 +385,7 @@ pub struct MutableAppContext {
|
||||||
next_task_id: usize,
|
next_task_id: usize,
|
||||||
subscriptions: HashMap<usize, Vec<Subscription>>,
|
subscriptions: HashMap<usize, Vec<Subscription>>,
|
||||||
observations: HashMap<usize, Vec<Observation>>,
|
observations: HashMap<usize, Vec<Observation>>,
|
||||||
|
async_observations: HashMap<usize, postage::broadcast::Sender<()>>,
|
||||||
window_invalidations: HashMap<usize, WindowInvalidation>,
|
window_invalidations: HashMap<usize, WindowInvalidation>,
|
||||||
presenters_and_platform_windows:
|
presenters_and_platform_windows:
|
||||||
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
|
HashMap<usize, (Rc<RefCell<Presenter>>, Box<dyn platform::Window>)>,
|
||||||
|
@ -424,6 +426,7 @@ impl MutableAppContext {
|
||||||
next_task_id: 0,
|
next_task_id: 0,
|
||||||
subscriptions: HashMap::new(),
|
subscriptions: HashMap::new(),
|
||||||
observations: HashMap::new(),
|
observations: HashMap::new(),
|
||||||
|
async_observations: HashMap::new(),
|
||||||
window_invalidations: HashMap::new(),
|
window_invalidations: HashMap::new(),
|
||||||
presenters_and_platform_windows: HashMap::new(),
|
presenters_and_platform_windows: HashMap::new(),
|
||||||
debug_elements_callbacks: HashMap::new(),
|
debug_elements_callbacks: HashMap::new(),
|
||||||
|
@ -877,11 +880,13 @@ impl MutableAppContext {
|
||||||
self.ctx.models.remove(&model_id);
|
self.ctx.models.remove(&model_id);
|
||||||
self.subscriptions.remove(&model_id);
|
self.subscriptions.remove(&model_id);
|
||||||
self.observations.remove(&model_id);
|
self.observations.remove(&model_id);
|
||||||
|
self.async_observations.remove(&model_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (window_id, view_id) in dropped_views {
|
for (window_id, view_id) in dropped_views {
|
||||||
self.subscriptions.remove(&view_id);
|
self.subscriptions.remove(&view_id);
|
||||||
self.observations.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) {
|
if let Some(window) = self.ctx.windows.get_mut(&window_id) {
|
||||||
self.window_invalidations
|
self.window_invalidations
|
||||||
.entry(window_id)
|
.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) {
|
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)
|
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> {
|
impl<T> Clone for ModelHandle<T> {
|
||||||
|
@ -2145,6 +2186,36 @@ impl<T: View> ViewHandle<T> {
|
||||||
app.focused_view_id(self.window_id)
|
app.focused_view_id(self.window_id)
|
||||||
.map_or(false, |focused_id| focused_id == self.view_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> {
|
impl<T> Clone for ViewHandle<T> {
|
||||||
|
|
|
@ -16,10 +16,11 @@ path = "src/main.rs"
|
||||||
anyhow = "1.0.38"
|
anyhow = "1.0.38"
|
||||||
arrayvec = "0.5.2"
|
arrayvec = "0.5.2"
|
||||||
crossbeam-channel = "0.5.0"
|
crossbeam-channel = "0.5.0"
|
||||||
|
ctor = "0.1.20"
|
||||||
dirs = "3.0"
|
dirs = "3.0"
|
||||||
easy-parallel = "3.1.0"
|
easy-parallel = "3.1.0"
|
||||||
futures-core = "0.3"
|
|
||||||
fsevent = {path = "../fsevent"}
|
fsevent = {path = "../fsevent"}
|
||||||
|
futures-core = "0.3"
|
||||||
gpui = {path = "../gpui"}
|
gpui = {path = "../gpui"}
|
||||||
ignore = {git = "https://github.com/zed-industries/ripgrep", rev = "1d152118f35b3e3590216709b86277062d79b8a0"}
|
ignore = {git = "https://github.com/zed-industries/ripgrep", rev = "1d152118f35b3e3590216709b86277062d79b8a0"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
@ -31,8 +32,8 @@ postage = {version = "0.4.1", features = ["futures-traits"]}
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
rust-embed = "5.9.0"
|
rust-embed = "5.9.0"
|
||||||
seahash = "4.1"
|
seahash = "4.1"
|
||||||
|
serde = {version = "1", features = ["derive"]}
|
||||||
simplelog = "0.9"
|
simplelog = "0.9"
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
smol = "1.2.5"
|
smol = "1.2.5"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
|
use crate::time::ReplicaId;
|
||||||
|
use ctor::ctor;
|
||||||
|
use log::LevelFilter;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
use simplelog::SimpleLogger;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -6,7 +10,10 @@ use std::{
|
||||||
};
|
};
|
||||||
use tempdir::TempDir;
|
use tempdir::TempDir;
|
||||||
|
|
||||||
use crate::time::ReplicaId;
|
#[ctor]
|
||||||
|
fn init_logger() {
|
||||||
|
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Envelope<T: Clone> {
|
struct Envelope<T: Clone> {
|
||||||
|
|
|
@ -402,7 +402,21 @@ mod tests {
|
||||||
|
|
||||||
// Open the first entry
|
// Open the first entry
|
||||||
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
|
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
|
||||||
app.finish_pending_tasks().await;
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
workspace_view
|
||||||
|
.condition(&app, |workspace_view, ctx| {
|
||||||
|
workspace_view.active_pane().read(ctx).items().len() == 2
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
app.read(|ctx| {
|
app.read(|ctx| {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -410,36 +424,34 @@ mod tests {
|
||||||
.read(ctx)
|
.read(ctx)
|
||||||
.active_pane()
|
.active_pane()
|
||||||
.read(ctx)
|
.read(ctx)
|
||||||
.items()
|
.active_item()
|
||||||
.len(),
|
.unwrap()
|
||||||
1
|
.entry_id(ctx),
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open the second entry
|
|
||||||
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[1], ctx));
|
|
||||||
app.finish_pending_tasks().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[1])
|
Some(entries[1])
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Open the first entry again
|
// Open the first entry again
|
||||||
workspace_view.update(&mut app, |w, ctx| w.open_entry(entries[0], ctx));
|
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| {
|
app.read(|ctx| {
|
||||||
let active_pane = workspace_view.read(ctx).active_pane().read(ctx);
|
let active_pane = workspace_view.read(ctx).active_pane().read(ctx);
|
||||||
assert_eq!(active_pane.items().len(), 2);
|
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
|
// Open the third entry twice concurrently
|
||||||
|
@ -447,19 +459,12 @@ mod tests {
|
||||||
w.open_entry(entries[2], ctx);
|
w.open_entry(entries[2], ctx);
|
||||||
w.open_entry(entries[2], ctx);
|
w.open_entry(entries[2], ctx);
|
||||||
});
|
});
|
||||||
app.finish_pending_tasks().await;
|
|
||||||
|
|
||||||
app.read(|ctx| {
|
workspace_view
|
||||||
assert_eq!(
|
.condition(&app, |workspace_view, ctx| {
|
||||||
workspace_view
|
workspace_view.active_pane().read(ctx).items().len() == 3
|
||||||
.read(ctx)
|
})
|
||||||
.active_pane()
|
.await;
|
||||||
.read(ctx)
|
|
||||||
.items()
|
|
||||||
.len(),
|
|
||||||
3
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue