1use std::path::Path;
7use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
8use std::sync::Arc;
9use std::time::{Duration, Instant};
10
11use parking_lot::Mutex;
12
13use super::engine::{
14 self, Acceptor, Application, CallbackOutcome, GroupEntry, MessageRef, OwnedMessage, Section,
15 SessionCallbacks, SessionKey, Settings,
16};
17use crate::error::ConnectorError;
18
19#[derive(Debug, Clone, Default)]
21pub struct TestGroupEntry {
22 pub fields: Vec<(i32, String)>,
24 pub groups: Vec<(i32, Vec<TestGroupEntry>)>,
26}
27
28#[derive(Debug, Clone, Default)]
30pub struct TestMessage {
31 pub msg_type: String,
33 pub header_fields: Vec<(i32, String)>,
35 pub fields: Vec<(i32, String)>,
37 pub groups: Vec<(i32, Vec<TestGroupEntry>)>,
39}
40
41#[derive(Default)]
42struct VenueCallbacks {
43 logged_on: AtomicBool,
44 logons: AtomicUsize,
45 rejects: Mutex<Vec<Vec<u8>>>,
46 logon_requests: Mutex<Vec<Vec<u8>>>,
47 application: Mutex<Vec<Vec<u8>>>,
48}
49
50impl SessionCallbacks for VenueCallbacks {
51 fn on_logon(&self) {
52 self.logons.fetch_add(1, Ordering::SeqCst);
53 self.logged_on.store(true, Ordering::SeqCst);
54 }
55
56 fn on_logout(&self) {
57 self.logged_on.store(false, Ordering::SeqCst);
58 }
59
60 fn inbound_admin(&self, message: MessageRef<'_>) -> CallbackOutcome {
61 let msg_type = message.header().and_then(|header| header.field(35));
62 let received = match msg_type {
63 Ok(Some(b"3")) => &self.rejects,
64 Ok(Some(b"A")) => &self.logon_requests,
65 _ => return CallbackOutcome::Accept,
66 };
67 let mut bytes = Vec::new();
68 if message.serialize_into(&mut bytes).is_ok() {
69 received.lock().push(bytes);
70 }
71 CallbackOutcome::Accept
72 }
73
74 fn inbound_app(&self, message: MessageRef<'_>) -> CallbackOutcome {
75 let mut bytes = Vec::new();
76 if message.serialize_into(&mut bytes).is_ok() {
77 self.application.lock().push(bytes);
78 }
79 CallbackOutcome::Accept
80 }
81
82 fn on_panic(&self, _callback: &'static str) {}
83}
84
85pub struct FixTestVenue {
88 port: u16,
89 key: SessionKey,
90 acceptor: Acceptor,
91 callbacks: Arc<VenueCallbacks>,
92}
93
94fn engine_error(error: engine::EngineError) -> ConnectorError {
95 ConnectorError::from(error)
96}
97
98impl FixTestVenue {
99 pub fn start(
105 store: &Path,
106 dictionary: &Path,
107 begin_string: &str,
108 venue_comp_id: &str,
109 client_comp_id: &str,
110 ) -> Result<Self, ConnectorError> {
111 Self::start_with_settings(
112 store,
113 dictionary,
114 begin_string,
115 venue_comp_id,
116 client_comp_id,
117 &[],
118 )
119 }
120
121 pub fn start_with_settings(
126 store: &Path,
127 dictionary: &Path,
128 begin_string: &str,
129 venue_comp_id: &str,
130 client_comp_id: &str,
131 settings: &[(&'static str, String)],
132 ) -> Result<Self, ConnectorError> {
133 let port = std::net::TcpListener::bind("127.0.0.1:0")
134 .and_then(|listener| listener.local_addr())
135 .map(|address| address.port())?;
136 Self::listen(
137 port,
138 store,
139 dictionary,
140 begin_string,
141 venue_comp_id,
142 client_comp_id,
143 settings,
144 )
145 }
146
147 pub fn listen(
152 port: u16,
153 store: &Path,
154 dictionary: &Path,
155 begin_string: &str,
156 venue_comp_id: &str,
157 client_comp_id: &str,
158 settings: &[(&'static str, String)],
159 ) -> Result<Self, ConnectorError> {
160 let mut defaults = vec![
161 ("ConnectionType", "acceptor".to_string()),
162 ("SocketAcceptPort", port.to_string()),
163 ("FileStorePath", store.display().to_string()),
164 ("UseDataDictionary", "Y".to_string()),
165 ("DataDictionary", dictionary.display().to_string()),
166 ("StartTime", "00:00:00".to_string()),
167 ("EndTime", "00:00:00".to_string()),
168 ("PersistMessages", "Y".to_string()),
169 ];
170 defaults.extend(settings.iter().cloned());
171 let key = SessionKey {
172 begin_string: begin_string.to_string(),
173 sender_comp_id: venue_comp_id.to_string(),
174 target_comp_id: client_comp_id.to_string(),
175 qualifier: None,
176 };
177 let settings = Settings::single_session(&defaults, &key, &[]).map_err(engine_error)?;
178 let callbacks = Arc::new(VenueCallbacks::default());
179 let application = Application::new(callbacks.clone()).map_err(engine_error)?;
180 let acceptor = Acceptor::new(settings, application).map_err(engine_error)?;
181 acceptor.start().map_err(engine_error)?;
182 Ok(Self {
183 port,
184 key,
185 acceptor,
186 callbacks,
187 })
188 }
189
190 #[must_use]
192 pub fn port(&self) -> u16 {
193 self.port
194 }
195
196 #[must_use]
198 pub fn logged_on(&self) -> bool {
199 self.callbacks.logged_on.load(Ordering::SeqCst)
200 }
201
202 #[must_use]
204 pub fn logons(&self) -> usize {
205 self.callbacks.logons.load(Ordering::SeqCst)
206 }
207
208 #[must_use]
210 pub fn wait_logged_on(&self, timeout: Duration) -> bool {
211 let deadline = Instant::now() + timeout;
212 while !self.logged_on() {
213 if Instant::now() >= deadline {
214 return false;
215 }
216 std::thread::sleep(Duration::from_millis(10));
217 }
218 true
219 }
220
221 pub fn send(&self, message: &TestMessage) -> Result<(), ConnectorError> {
226 let mut outbound = OwnedMessage::new().map_err(engine_error)?;
227 outbound
228 .set_field(Section::Header, 35, message.msg_type.as_bytes())
229 .map_err(engine_error)?;
230 for (tag, value) in &message.header_fields {
231 outbound
232 .set_field(Section::Header, *tag, value.as_bytes())
233 .map_err(engine_error)?;
234 }
235 for (tag, value) in &message.fields {
236 outbound
237 .set_field(Section::Body, *tag, value.as_bytes())
238 .map_err(engine_error)?;
239 }
240 for (count_tag, entries) in &message.groups {
241 for entry in entries {
242 outbound
243 .add_group(*count_tag, &borrow(entry))
244 .map_err(engine_error)?;
245 }
246 }
247 outbound.send_to(&self.key).map_err(engine_error)
248 }
249
250 pub fn skip_sequence_numbers(&self, count: u64) -> Result<(), ConnectorError> {
256 let state = engine::session_state(&self.key).map_err(engine_error)?;
257 engine::set_next_sender(&self.key, state.next_sender + count).map_err(engine_error)
258 }
259
260 pub fn sequence_reset(&self, new_seq: u64) -> Result<(), ConnectorError> {
266 let mut reset = OwnedMessage::new().map_err(engine_error)?;
267 reset
268 .set_field(Section::Header, 35, b"4")
269 .map_err(engine_error)?;
270 reset
271 .set_field(Section::Body, 36, new_seq.to_string().as_bytes())
272 .map_err(engine_error)?;
273 reset.send_to(&self.key).map_err(engine_error)?;
274 engine::set_next_sender(&self.key, new_seq).map_err(engine_error)
275 }
276
277 pub fn reset_session(&self) -> Result<(), ConnectorError> {
282 engine::reset_session(&self.key).map_err(engine_error)
283 }
284
285 pub fn next_sender(&self) -> Result<u64, ConnectorError> {
290 Ok(engine::session_state(&self.key)
291 .map_err(engine_error)?
292 .next_sender)
293 }
294
295 #[must_use]
297 pub fn rejects(&self) -> Vec<Vec<u8>> {
298 self.callbacks.rejects.lock().clone()
299 }
300
301 #[must_use]
303 pub fn logon_requests(&self) -> Vec<Vec<u8>> {
304 self.callbacks.logon_requests.lock().clone()
305 }
306
307 #[must_use]
309 pub fn received_application(&self) -> Vec<Vec<u8>> {
310 self.callbacks.application.lock().clone()
311 }
312
313 pub fn stop(&self) -> Result<(), ConnectorError> {
318 self.acceptor.stop(true).map_err(engine_error)
319 }
320}
321
322fn borrow(entry: &TestGroupEntry) -> GroupEntry<'_> {
323 GroupEntry {
324 fields: entry
325 .fields
326 .iter()
327 .map(|(tag, value)| (*tag, value.as_str()))
328 .collect(),
329 groups: entry
330 .groups
331 .iter()
332 .map(|(tag, entries)| (*tag, entries.iter().map(borrow).collect()))
333 .collect(),
334 }
335}