use super::*;
use std::sync::mpsc as std_mpsc;
use std::thread::ThreadId;
struct Reporting {
storage: Arc<ClassStorage>,
dropped: std_mpsc::Sender<ThreadId>,
}
impl NotificationClassPersistence for Reporting {
fn load(&self, _class: ObjectIdentifier) -> Result<Option<NotificationClassSnapshot>, Error> {
Ok(self.storage.load_saved())
}
fn save(
&self,
_class: ObjectIdentifier,
snapshot: &NotificationClassSnapshot,
) -> Result<(), Error> {
self.storage.store(snapshot)
}
}
impl Drop for Reporting {
fn drop(&mut self) {
let _ = self.dropped.send(std::thread::current().id());
}
}
#[tokio::test]
async fn a_server_dropped_without_stop_lets_its_database_go_off_the_runtime() {
let storage = holding(&[destination(1)]);
let (dropped, dropped_on) = std_mpsc::channel();
let persistence = Arc::new(Reporting {
storage: Arc::clone(&storage),
dropped,
});
let class = NotificationClass::with_persistence(
1,
"NC-1",
persistence as Arc<dyn NotificationClassPersistence>,
)
.unwrap();
let (transport, inbound) = TestTransport::inbound(4);
let mut db = ObjectDatabase::new();
let device = DeviceObject::new(DeviceConfig {
instance: 100,
..DeviceConfig::default()
});
db.add(Box::new(device.unwrap())).unwrap();
db.add(Box::new(class)).unwrap();
let server = BACnetServer::generic_builder()
.transport(transport)
.database(db)
.enable_event_enrollment(false)
.build()
.await
.unwrap();
let (started, go) = storage.hold();
let request = write_property(1, &[destination(11)]);
send(&inbound, ConfirmedServiceChoice::WRITE_PROPERTY, request).await;
save_started(started).await;
let (progress, progressed) = std_mpsc::channel::<()>();
let watchdog = std::thread::spawn(move || {
let stalled = progressed.recv_timeout(WAIT).is_err();
drop(go);
stalled
});
drop(server);
tokio::spawn(async move {
let _ = progress.send(());
})
.await
.unwrap();
let stalled = tokio::task::spawn_blocking(move || watchdog.join().unwrap())
.await
.unwrap();
assert!(!stalled, "dropping the server held the runtime's thread");
let thread = tokio::task::spawn_blocking(move || dropped_on.recv_timeout(WAIT))
.await
.unwrap()
.expect("the database was dropped");
assert_ne!(thread, std::thread::current().id());
assert_eq!(storage.load_saved(), Some(snapshot(&[destination(1)])));
}