use super::super::{Tsm, TsmConfig, TsmResponse};
use super::TransactionProgress;
use bacnet_types::MacAddr;
use bacnet_types::enums::ConfirmedServiceChoice;
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Waker};
use tokio::time::{Duration, timeout};
fn polls_ready<F: Future>(future: F) -> bool {
let mut future = pin!(future);
let mut cx = Context::from_waker(Waker::noop());
future.as_mut().poll(&mut cx).is_ready()
}
fn mac() -> MacAddr {
MacAddr::from_slice(&[127, 0, 0, 1, 0xBA, 0xC0])
}
fn segmented_tsm() -> (Tsm, u8, super::super::TransactionRegistration) {
let mut tsm = Tsm::new(TsmConfig::default());
let invoke_id = tsm.allocate_invoke_id(&mac()).unwrap();
let registration = tsm.register_transaction_with_progress(
mac(),
invoke_id,
ConfirmedServiceChoice::READ_PROPERTY,
);
(tsm, invoke_id, registration)
}
#[test]
fn nothing_published_means_no_change() {
let (_tsm, _, mut registration) = segmented_tsm();
assert_eq!(
registration.progress.borrow_and_update(),
TransactionProgress::AwaitingResponse
);
assert!(!polls_ready(registration.progress.changed()));
}
#[test]
fn publish_before_the_wait_is_seen() {
let (mut tsm, invoke_id, mut registration) = segmented_tsm();
registration.progress.borrow_and_update();
let generation = tsm
.begin_segmented_response(&mac(), invoke_id, ®istration.owner)
.unwrap();
assert!(polls_ready(registration.progress.changed()));
assert_eq!(
registration.progress.borrow_and_update(),
TransactionProgress::SegmentedResponse { generation }
);
assert!(!polls_ready(registration.progress.changed()));
}
#[test]
fn publishing_the_same_value_still_counts_as_a_change() {
let mut tsm = Tsm::new(TsmConfig::default());
let invoke_id = tsm.allocate_invoke_id(&mac()).unwrap();
let mut registration = tsm.register_segmented_transaction_with_progress(
mac(),
invoke_id,
ConfirmedServiceChoice::READ_PROPERTY,
);
registration.progress.borrow_and_update();
let mut token = tsm
.begin_final_segment_send(&mac(), invoke_id, ®istration.owner)
.unwrap();
assert!(tsm.mark_final_segment_issued(&mac(), invoke_id, ®istration.owner, &mut token));
assert!(tsm.finish_segmented_request(&mac(), invoke_id, ®istration.owner));
assert!(polls_ready(registration.progress.changed()));
assert_eq!(
registration.progress.borrow_and_update(),
TransactionProgress::AwaitingResponse
);
}
#[tokio::test(start_paused = true)]
async fn publish_wakes_a_parked_waiter() {
let (mut tsm, invoke_id, mut registration) = segmented_tsm();
registration.progress.borrow_and_update();
let owner = registration.owner.clone();
let mut progress = registration.progress;
let waiter = tokio::spawn(async move {
progress.changed().await;
progress.borrow_and_update()
});
tokio::task::yield_now().await;
assert!(!waiter.is_finished());
let generation = tsm
.begin_segmented_response(&mac(), invoke_id, &owner)
.unwrap();
let seen = timeout(Duration::from_secs(1), waiter)
.await
.expect("publish did not wake the parked waiter")
.unwrap();
assert_eq!(seen, TransactionProgress::SegmentedResponse { generation });
}
#[test]
fn progress_after_completion_is_not_published() {
let (mut tsm, invoke_id, mut registration) = segmented_tsm();
registration.progress.borrow_and_update();
tsm.begin_segmented_response(&mac(), invoke_id, ®istration.owner)
.unwrap();
tsm.complete_transaction(
&mac(),
invoke_id,
Some(ConfirmedServiceChoice::READ_PROPERTY),
TsmResponse::SimpleAck,
);
assert_eq!(
tsm.record_segmented_response_activity(&mac(), invoke_id, ®istration.owner),
None
);
assert!(!tsm.reset_segmented_response(&mac(), invoke_id, ®istration.owner));
assert!(polls_ready(registration.progress.changed()));
registration.progress.borrow_and_update();
assert!(!polls_ready(registration.progress.changed()));
assert!(matches!(
registration.response.try_recv(),
Ok(TsmResponse::SimpleAck)
));
}