Uncomment project2 tests (#3200)
This commit is contained in:
@@ -376,7 +376,7 @@ impl AppContext {
|
||||
self.observers.remove(&entity_id);
|
||||
self.event_listeners.remove(&entity_id);
|
||||
for mut release_callback in self.release_listeners.remove(&entity_id) {
|
||||
release_callback(&mut entity, self);
|
||||
release_callback(entity.as_mut(), self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,12 @@ impl EntityMap {
|
||||
dropped_entity_ids
|
||||
.into_iter()
|
||||
.map(|entity_id| {
|
||||
ref_counts.counts.remove(entity_id);
|
||||
let count = ref_counts.counts.remove(entity_id).unwrap();
|
||||
debug_assert_eq!(
|
||||
count.load(SeqCst),
|
||||
0,
|
||||
"dropped an entity that was referenced"
|
||||
);
|
||||
(entity_id, self.entities.remove(entity_id).unwrap())
|
||||
})
|
||||
.collect()
|
||||
@@ -211,7 +216,7 @@ impl Drop for AnyModel {
|
||||
let count = entity_map
|
||||
.counts
|
||||
.get(self.entity_id)
|
||||
.expect("Detected over-release of a model.");
|
||||
.expect("detected over-release of a handle.");
|
||||
let prev_count = count.fetch_sub(1, SeqCst);
|
||||
assert_ne!(prev_count, 0, "Detected over-release of a model.");
|
||||
if prev_count == 1 {
|
||||
@@ -395,12 +400,16 @@ impl AnyWeakModel {
|
||||
}
|
||||
|
||||
pub fn upgrade(&self) -> Option<AnyModel> {
|
||||
let entity_map = self.entity_ref_counts.upgrade()?;
|
||||
entity_map
|
||||
.read()
|
||||
.counts
|
||||
.get(self.entity_id)?
|
||||
.fetch_add(1, SeqCst);
|
||||
let ref_counts = &self.entity_ref_counts.upgrade()?;
|
||||
let ref_counts = ref_counts.read();
|
||||
let ref_count = ref_counts.counts.get(self.entity_id)?;
|
||||
|
||||
// entity_id is in dropped_entity_ids
|
||||
if ref_count.load(SeqCst) == 0 {
|
||||
return None;
|
||||
}
|
||||
ref_count.fetch_add(1, SeqCst);
|
||||
|
||||
Some(AnyModel {
|
||||
entity_id: self.entity_id,
|
||||
entity_type: self.entity_type,
|
||||
@@ -499,3 +508,60 @@ impl<T> PartialEq<Model<T>> for WeakModel<T> {
|
||||
self.entity_id() == other.any_model.entity_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::EntityMap;
|
||||
|
||||
struct TestEntity {
|
||||
pub i: i32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_map_slot_assignment_before_cleanup() {
|
||||
// Tests that slots are not re-used before take_dropped.
|
||||
let mut entity_map = EntityMap::new();
|
||||
|
||||
let slot = entity_map.reserve::<TestEntity>();
|
||||
entity_map.insert(slot, TestEntity { i: 1 });
|
||||
|
||||
let slot = entity_map.reserve::<TestEntity>();
|
||||
entity_map.insert(slot, TestEntity { i: 2 });
|
||||
|
||||
let dropped = entity_map.take_dropped();
|
||||
assert_eq!(dropped.len(), 2);
|
||||
|
||||
assert_eq!(
|
||||
dropped
|
||||
.into_iter()
|
||||
.map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
|
||||
.collect::<Vec<i32>>(),
|
||||
vec![1, 2],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_map_weak_upgrade_before_cleanup() {
|
||||
// Tests that weak handles are not upgraded before take_dropped
|
||||
let mut entity_map = EntityMap::new();
|
||||
|
||||
let slot = entity_map.reserve::<TestEntity>();
|
||||
let handle = entity_map.insert(slot, TestEntity { i: 1 });
|
||||
let weak = handle.downgrade();
|
||||
drop(handle);
|
||||
|
||||
let strong = weak.upgrade();
|
||||
assert_eq!(strong, None);
|
||||
|
||||
let dropped = entity_map.take_dropped();
|
||||
assert_eq!(dropped.len(), 1);
|
||||
|
||||
assert_eq!(
|
||||
dropped
|
||||
.into_iter()
|
||||
.map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
|
||||
.collect::<Vec<i32>>(),
|
||||
vec![1],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
AnyWindowHandle, AppContext, AsyncAppContext, Context, Executor, MainThread, Model,
|
||||
ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext,
|
||||
AnyWindowHandle, AppContext, AsyncAppContext, Context, EventEmitter, Executor, MainThread,
|
||||
Model, ModelContext, Result, Task, TestDispatcher, TestPlatform, WindowContext,
|
||||
};
|
||||
use futures::SinkExt;
|
||||
use parking_lot::Mutex;
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
@@ -63,8 +64,8 @@ impl TestAppContext {
|
||||
}
|
||||
|
||||
pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
|
||||
let mut lock = self.app.lock();
|
||||
f(&mut *lock)
|
||||
let mut cx = self.app.lock();
|
||||
cx.update(f)
|
||||
}
|
||||
|
||||
pub fn read_window<R>(
|
||||
@@ -149,4 +150,22 @@ impl TestAppContext {
|
||||
executor: self.executor.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe<T: 'static + EventEmitter + Send>(
|
||||
&mut self,
|
||||
entity: &Model<T>,
|
||||
) -> futures::channel::mpsc::UnboundedReceiver<T::Event>
|
||||
where
|
||||
T::Event: 'static + Send + Clone,
|
||||
{
|
||||
let (mut tx, rx) = futures::channel::mpsc::unbounded();
|
||||
entity
|
||||
.update(self, |_, cx: &mut ModelContext<T>| {
|
||||
cx.subscribe(entity, move |_, _, event, cx| {
|
||||
cx.executor().block(tx.send(event.clone())).unwrap();
|
||||
})
|
||||
})
|
||||
.detach();
|
||||
rx
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ use std::{
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering::SeqCst},
|
||||
Arc,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
@@ -136,7 +139,11 @@ impl Executor {
|
||||
pub fn block<R>(&self, future: impl Future<Output = R>) -> R {
|
||||
pin_mut!(future);
|
||||
let (parker, unparker) = parking::pair();
|
||||
let awoken = Arc::new(AtomicBool::new(false));
|
||||
let awoken2 = awoken.clone();
|
||||
|
||||
let waker = waker_fn(move || {
|
||||
awoken2.store(true, SeqCst);
|
||||
unparker.unpark();
|
||||
});
|
||||
let mut cx = std::task::Context::from_waker(&waker);
|
||||
@@ -146,9 +153,20 @@ impl Executor {
|
||||
Poll::Ready(result) => return result,
|
||||
Poll::Pending => {
|
||||
if !self.dispatcher.poll() {
|
||||
if awoken.swap(false, SeqCst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
if let Some(_) = self.dispatcher.as_test() {
|
||||
panic!("blocked with nothing left to run")
|
||||
if let Some(test) = self.dispatcher.as_test() {
|
||||
if !test.parking_allowed() {
|
||||
let mut backtrace_message = String::new();
|
||||
if let Some(backtrace) = test.waiting_backtrace() {
|
||||
backtrace_message =
|
||||
format!("\nbacktrace of waiting future:\n{:?}", backtrace);
|
||||
}
|
||||
panic!("parked with nothing left to run\n{:?}", backtrace_message)
|
||||
}
|
||||
}
|
||||
parker.park();
|
||||
}
|
||||
@@ -206,12 +224,12 @@ impl Executor {
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn start_waiting(&self) {
|
||||
todo!("start_waiting")
|
||||
self.dispatcher.as_test().unwrap().start_waiting();
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn finish_waiting(&self) {
|
||||
todo!("finish_waiting")
|
||||
self.dispatcher.as_test().unwrap().finish_waiting();
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@@ -229,6 +247,11 @@ impl Executor {
|
||||
self.dispatcher.as_test().unwrap().run_until_parked()
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn allow_parking(&self) {
|
||||
self.dispatcher.as_test().unwrap().allow_parking();
|
||||
}
|
||||
|
||||
pub fn num_cpus(&self) -> usize {
|
||||
num_cpus::get()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::PlatformDispatcher;
|
||||
use async_task::Runnable;
|
||||
use backtrace::Backtrace;
|
||||
use collections::{HashMap, VecDeque};
|
||||
use parking_lot::Mutex;
|
||||
use rand::prelude::*;
|
||||
@@ -28,6 +29,8 @@ struct TestDispatcherState {
|
||||
time: Duration,
|
||||
is_main_thread: bool,
|
||||
next_id: TestDispatcherId,
|
||||
allow_parking: bool,
|
||||
waiting_backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
impl TestDispatcher {
|
||||
@@ -40,6 +43,8 @@ impl TestDispatcher {
|
||||
time: Duration::ZERO,
|
||||
is_main_thread: true,
|
||||
next_id: TestDispatcherId(1),
|
||||
allow_parking: false,
|
||||
waiting_backtrace: None,
|
||||
};
|
||||
|
||||
TestDispatcher {
|
||||
@@ -66,7 +71,7 @@ impl TestDispatcher {
|
||||
self.state.lock().time = new_now;
|
||||
}
|
||||
|
||||
pub fn simulate_random_delay(&self) -> impl Future<Output = ()> {
|
||||
pub fn simulate_random_delay(&self) -> impl 'static + Send + Future<Output = ()> {
|
||||
pub struct YieldNow {
|
||||
count: usize,
|
||||
}
|
||||
@@ -93,6 +98,29 @@ impl TestDispatcher {
|
||||
pub fn run_until_parked(&self) {
|
||||
while self.poll() {}
|
||||
}
|
||||
|
||||
pub fn parking_allowed(&self) -> bool {
|
||||
self.state.lock().allow_parking
|
||||
}
|
||||
|
||||
pub fn allow_parking(&self) {
|
||||
self.state.lock().allow_parking = true
|
||||
}
|
||||
|
||||
pub fn start_waiting(&self) {
|
||||
self.state.lock().waiting_backtrace = Some(Backtrace::new_unresolved());
|
||||
}
|
||||
|
||||
pub fn finish_waiting(&self) {
|
||||
self.state.lock().waiting_backtrace.take();
|
||||
}
|
||||
|
||||
pub fn waiting_backtrace(&self) -> Option<Backtrace> {
|
||||
self.state.lock().waiting_backtrace.take().map(|mut b| {
|
||||
b.resolve();
|
||||
b
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TestDispatcher {
|
||||
|
||||
Reference in New Issue
Block a user