Skip to content

Commit 7f366cb

Browse files
committed
Replacing mutex with channels
1 parent 5bef56b commit 7f366cb

2 files changed

Lines changed: 59 additions & 46 deletions

File tree

crates/hotfix/tests/common/mock_counterparty.rs

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,74 @@
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>) -> Option<&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+
if let Some(msg) = msg {
43+
self.messages.push(msg);
44+
self.messages.last()
45+
} else {
46+
None
47+
}
48+
}
4549

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-
}
50+
pub async fn assert_next<F>(&mut self, timeout: Option<Duration>, assertion: F)
51+
where
52+
F: FnOnce(Option<&WriterMessage>) -> bool,
53+
{
54+
let msg = self.get_next(timeout).await;
55+
assert!(assertion(msg));
56+
}
5257

53-
tokio::time::sleep(Duration::from_millis(1)).await;
54-
}
58+
pub async fn assert_disconnected(&mut self) {
59+
self.assert_next(None, |msg| matches!(msg, Some(&WriterMessage::Disconnect)))
60+
.await;
61+
assert!(self.receiver.is_closed());
5562
}
5663

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

64-
(WriterRef::new(sender), received_messages)
71+
(WriterRef::new(sender), rx)
6572
}
6673

6774
fn create_reader() -> (ReaderRef, oneshot::Sender<()>) {
@@ -70,19 +77,18 @@ impl MockCounterparty {
7077
}
7178

7279
async fn run_writer(
73-
mut mailbox: Receiver<WriterMessage>,
74-
received_messages: Arc<Mutex<Vec<RawFixMessage>>>,
80+
mut mailbox: mpsc::Receiver<WriterMessage>,
81+
received_messages: mpsc::UnboundedSender<WriterMessage>,
7582
) {
7683
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-
}
84+
println!("Received message from session: {msg:?}");
85+
let disconnect = matches!(msg, WriterMessage::Disconnect);
86+
if let Err(e) = received_messages.send(msg) {
87+
panic!("Failed to send message. Error: {e:?}");
88+
}
89+
if disconnect {
90+
println!("Disconnecting");
91+
break;
8692
}
8793
}
8894
}

crates/hotfix/tests/session_tests.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ 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
16+
.assert_next(None, |msg| msg.is_some())
17+
.await;
18+
19+
session
20+
.disconnect("Test Session Finished".to_string())
21+
.await;
22+
mock_counterparty.assert_disconnected().await;
1623
}
1724

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

0 commit comments

Comments
 (0)