use std::sync::Arc as StdArc;
use std::sync::atomic::{AtomicUsize, Ordering};
use hyperion_framework::containerisation::client_broker::ClientBroker;
use hyperion_framework::containerisation::container_state::ContainerState;
use hyperion_framework::containerisation::hyperion_container::HyperionContainer;
use hyperion_framework::containerisation::traits::{HyperionContainerDirectiveMessage, Run};
use hyperion_framework::messages::client_broker_message::ClientBrokerMessage;
use hyperion_framework::messages::container_directive::ContainerDirective;
use hyperion_framework::network::network_topology::{ClientConnections, NetworkTopology};
use serde::{Deserialize, Serialize};
use tokio::sync::{Notify, mpsc};
use tokio::time::{Duration, sleep};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
enum TestMessage {
Framework(ContainerDirective),
UserIncrement,
}
impl HyperionContainerDirectiveMessage for TestMessage {
fn get_container_directive_message(&self) -> Option<&ContainerDirective> {
if let TestMessage::Framework(d) = self {
Some(d)
} else {
None
}
}
}
#[derive(Debug, Clone)]
struct DummyComponent;
#[async_trait::async_trait]
impl Run for DummyComponent {
type Message = TestMessage;
async fn run(
self,
mut comp_in_rx: mpsc::Receiver<Self::Message>,
_comp_out_tx: mpsc::Sender<ClientBrokerMessage<Self::Message>>,
) {
while let Some(msg) = comp_in_rx.recv().await {
match msg {
TestMessage::Framework(ContainerDirective::Shutdown) => break,
TestMessage::UserIncrement => {
TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
}
_ => {}
}
}
}
}
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
fn empty_client_broker<T>(
container_state: StdArc<AtomicUsize>,
container_state_notify: StdArc<Notify>,
) -> ClientBroker<T>
where
T: std::fmt::Debug + Send + 'static + for<'de> Deserialize<'de> + Sync + Clone + Serialize,
{
let topology = StdArc::new(NetworkTopology {
container_name: "test".into(),
server_address: "127.0.0.1:0".into(),
client_connections: ClientConnections {
client_connection_vec: vec![],
},
});
ClientBroker::init(topology, container_state, container_state_notify)
}
#[tokio::test]
async fn forwards_non_framework_messages_to_component() {
let container_state: StdArc<AtomicUsize> =
StdArc::new(AtomicUsize::new(ContainerState::Running as usize));
let container_state_notify: StdArc<Notify> = StdArc::new(Notify::new());
let (main_tx, main_rx) = mpsc::channel::<TestMessage>(32);
let (_server_tx, server_rx) = mpsc::channel::<TestMessage>(32);
let client_broker =
empty_client_broker::<TestMessage>(container_state.clone(), container_state_notify.clone());
let mut container = HyperionContainer::create(
DummyComponent,
container_state.clone(),
container_state_notify.clone(),
client_broker,
main_rx,
server_rx,
);
tokio::spawn(async move {
container.run().await;
});
TEST_COUNTER.store(0, Ordering::SeqCst);
main_tx.send(TestMessage::UserIncrement).await.unwrap();
sleep(Duration::from_millis(200)).await;
assert_eq!(TEST_COUNTER.load(Ordering::SeqCst), 1);
main_tx
.send(TestMessage::Framework(ContainerDirective::Shutdown))
.await
.unwrap();
let deadline = tokio::time::Instant::now() + Duration::from_secs(5);
loop {
if container_state.load(Ordering::SeqCst) == ContainerState::Closed as usize {
break;
}
if tokio::time::Instant::now() > deadline {
panic!("timeout waiting for container to close");
}
sleep(Duration::from_millis(50)).await;
}
}
#[tokio::test]
async fn shutdown_and_system_shutdown_transitions_state() {
let container_state: StdArc<AtomicUsize> =
StdArc::new(AtomicUsize::new(ContainerState::Running as usize));
let container_state_notify: StdArc<Notify> = StdArc::new(Notify::new());
let (main_tx, main_rx) = mpsc::channel::<TestMessage>(32);
let (_server_tx, server_rx) = mpsc::channel::<TestMessage>(32);
let client_broker =
empty_client_broker::<TestMessage>(container_state.clone(), container_state_notify.clone());
let mut container = HyperionContainer::create(
DummyComponent,
container_state.clone(),
container_state_notify.clone(),
client_broker,
main_rx,
server_rx,
);
let handle = tokio::spawn(async move {
container.run().await;
});
main_tx
.send(TestMessage::Framework(ContainerDirective::SystemShutdown))
.await
.unwrap();
let deadline = tokio::time::Instant::now() + Duration::from_secs(6);
loop {
if container_state.load(Ordering::SeqCst) == ContainerState::Closed as usize {
break;
}
if tokio::time::Instant::now() > deadline {
panic!("timeout waiting for container to close after system shutdown");
}
sleep(Duration::from_millis(50)).await;
}
handle.await.unwrap();
}