Skip to content

Commit 6e7ea11

Browse files
committed
Replacing mutex with channels
1 parent 5bef56b commit 6e7ea11

2 files changed

Lines changed: 53 additions & 46 deletions

File tree

crates/hotfix/tests/common/mock_counterparty.rs

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,71 @@
1-
use hotfix::message::{FixMessage, RawFixMessage};
1+
use hotfix::message::FixMessage;
22
use hotfix::session::SessionRef;
33
use hotfix::transport::FixConnection;
44
use hotfix::transport::reader::ReaderRef;
55
use hotfix::transport::writer::{WriterMessage, WriterRef};
6-
use std::sync::Arc;
7-
use std::time::{Duration, Instant};
8-
use tokio::sync::mpsc::Receiver;
9-
use tokio::sync::{Mutex, mpsc, oneshot};
6+
use std::time::Duration;
7+
use tokio::sync::{mpsc, oneshot};
8+
9+
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(1);
1010

1111
pub struct MockCounterparty {
12-
received_messages: Arc<Mutex<Vec<RawFixMessage>>>,
12+
// Receiver-End of the channel
13+
receiver: mpsc::UnboundedReceiver<WriterMessage>,
14+
// History of Received messages from the client
15+
messages: Vec<WriterMessage>,
1316
_connection: FixConnection,
1417
_dc_sender: oneshot::Sender<()>,
1518
}
1619

17-
type MessageStore = Arc<Mutex<Vec<RawFixMessage>>>;
18-
1920
impl MockCounterparty {
2021
pub async fn start(session_ref: SessionRef<impl FixMessage>) -> Self {
21-
let (writer_ref, received_messages) = Self::spawn_writer();
22+
let (writer_ref, receiver) = Self::spawn_writer();
2223
let (reader_ref, dc_sender) = Self::create_reader();
2324
let connection = FixConnection::new(writer_ref, reader_ref);
2425

2526
session_ref.register_writer(connection.get_writer()).await;
2627

2728
Self {
28-
received_messages,
29+
receiver,
30+
messages: vec![],
2931
_connection: connection,
3032
_dc_sender: dc_sender,
3133
}
3234
}
3335

34-
pub async fn assert_message_count(&self, expected_count: usize, timeout_secs: f32) {
35-
let timeout_duration = Duration::from_secs_f32(timeout_secs);
36-
let start_time = Instant::now();
37-
38-
loop {
39-
{
40-
let messages = self.received_messages.lock().await;
41-
if messages.len() >= expected_count {
42-
return;
43-
}
44-
}
36+
/// Listen to the next message on the channel
37+
pub async fn get_next(&mut self, timeout: Option<Duration>) -> &WriterMessage {
38+
let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT);
39+
let msg = tokio::time::timeout(timeout, self.receiver.recv())
40+
.await
41+
.unwrap_or_else(|_| panic!("Message not received in less than {timeout:?}"))
42+
.unwrap_or_else(|| panic!("Received message is None"));
43+
self.messages.push(msg);
44+
self.messages.last().unwrap()
45+
}
4546

46-
if start_time.elapsed() >= timeout_duration {
47-
let current_count = self.received_messages.lock().await.len();
48-
panic!(
49-
"Expected {expected_count} messages, but only received {current_count} within {timeout_secs} seconds"
50-
);
51-
}
47+
pub async fn assert_next<F>(&mut self, timeout: Option<Duration>, assertion: F)
48+
where
49+
F: FnOnce(&WriterMessage) -> bool,
50+
{
51+
let msg = self.get_next(timeout).await;
52+
assert!(assertion(msg));
53+
}
5254

53-
tokio::time::sleep(Duration::from_millis(1)).await;
54-
}
55+
pub async fn assert_disconnected(&mut self, timeout: Option<Duration>) {
56+
self.assert_next(timeout, |msg| matches!(msg, &WriterMessage::Disconnect))
57+
.await;
58+
assert!(self.receiver.is_closed());
5559
}
5660

57-
fn spawn_writer() -> (WriterRef, MessageStore) {
61+
fn spawn_writer() -> (WriterRef, mpsc::UnboundedReceiver<WriterMessage>) {
5862
// mock implementation to receive messages from the session
5963
// and hold on to them for test assertions
60-
let received_messages: MessageStore = Arc::new(Mutex::new(vec![]));
64+
let (tx, rx) = mpsc::unbounded_channel();
6165
let (sender, mailbox) = mpsc::channel(10);
62-
tokio::spawn(Self::run_writer(mailbox, received_messages.clone()));
66+
tokio::spawn(Self::run_writer(mailbox, tx));
6367

64-
(WriterRef::new(sender), received_messages)
68+
(WriterRef::new(sender), rx)
6569
}
6670

6771
fn create_reader() -> (ReaderRef, oneshot::Sender<()>) {
@@ -70,19 +74,18 @@ impl MockCounterparty {
7074
}
7175

7276
async fn run_writer(
73-
mut mailbox: Receiver<WriterMessage>,
74-
received_messages: Arc<Mutex<Vec<RawFixMessage>>>,
77+
mut mailbox: mpsc::Receiver<WriterMessage>,
78+
received_messages: mpsc::UnboundedSender<WriterMessage>,
7579
) {
7680
while let Some(msg) = mailbox.recv().await {
77-
match msg {
78-
WriterMessage::SendMessage(fix_message) => {
79-
println!("Received message from session: {fix_message:?}");
80-
received_messages.lock().await.push(fix_message);
81-
}
82-
WriterMessage::Disconnect => {
83-
println!("Disconnecting");
84-
break;
85-
}
81+
println!("Received message from session: {msg:?}");
82+
let disconnect = matches!(msg, WriterMessage::Disconnect);
83+
if let Err(e) = received_messages.send(msg) {
84+
panic!("Failed to send message. Error: {e:?}");
85+
}
86+
if disconnect {
87+
println!("Disconnecting");
88+
break;
8689
}
8790
}
8891
}

crates/hotfix/tests/session_tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ mod common;
1111
#[tokio::test]
1212
async fn test_happy_login_flow() {
1313
let session = create_session();
14-
let mock_counterparty = MockCounterparty::start(session).await;
15-
mock_counterparty.assert_message_count(1, 0.5).await;
14+
let mut mock_counterparty = MockCounterparty::start(session.clone()).await;
15+
mock_counterparty.assert_next(None, |_msg| true).await;
16+
session
17+
.disconnect("Test Session Finished".to_string())
18+
.await;
19+
mock_counterparty.assert_disconnected(None).await;
1620
}
1721

1822
fn create_session() -> SessionRef<TestMessage> {

0 commit comments

Comments
 (0)