use std::error::Error;
pub trait OlaClient: Send + Sync {
fn send_dmx(&mut self, universe: u32, buffer: &ola::DmxBuffer) -> Result<(), Box<dyn Error>>;
fn reconnect(&mut self) -> Result<(), Box<dyn Error>>;
}
#[cfg(not(test))]
pub struct RealOlaClient {
client: ola::StreamingClient<std::net::TcpStream>,
config: ola::client::StreamingClientConfig,
}
#[cfg(not(test))]
impl RealOlaClient {
pub fn new(
client: ola::StreamingClient<std::net::TcpStream>,
config: ola::client::StreamingClientConfig,
) -> Self {
Self { client, config }
}
}
#[cfg(not(test))]
impl OlaClient for RealOlaClient {
fn send_dmx(&mut self, universe: u32, buffer: &ola::DmxBuffer) -> Result<(), Box<dyn Error>> {
self.client.send_dmx(universe, buffer)?;
Ok(())
}
fn reconnect(&mut self) -> Result<(), Box<dyn Error>> {
self.client = ola::connect_with_config(self.config.clone())?;
Ok(())
}
}
#[cfg(test)]
pub struct MockOlaClient {
pub sent_messages: std::sync::Arc<parking_lot::Mutex<Vec<DmxMessage>>>,
pub should_fail: bool,
}
#[derive(Debug, Clone)]
#[cfg(test)]
pub struct DmxMessage {
pub universe: u32,
pub buffer: ola::DmxBuffer,
}
#[cfg(test)]
impl Default for MockOlaClient {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
impl MockOlaClient {
pub fn new() -> Self {
Self {
sent_messages: std::sync::Arc::new(parking_lot::Mutex::new(Vec::new())),
should_fail: false,
}
}
pub fn message_count(&self) -> usize {
self.sent_messages.lock().len()
}
pub fn get_last_message(&self) -> Option<DmxMessage> {
self.sent_messages.lock().last().cloned()
}
pub fn clear_messages(&self) {
self.sent_messages.lock().clear();
}
pub fn get_messages_for_universe(&self, universe: u32) -> Vec<DmxMessage> {
self.sent_messages
.lock()
.iter()
.filter(|msg| msg.universe == universe)
.cloned()
.collect()
}
pub fn get_buffer_for_universe(&self, universe: u32) -> Option<ola::DmxBuffer> {
self.get_messages_for_universe(universe)
.last()
.map(|msg| msg.buffer.clone())
}
}
#[cfg(test)]
impl OlaClient for MockOlaClient {
fn send_dmx(&mut self, universe: u32, buffer: &ola::DmxBuffer) -> Result<(), Box<dyn Error>> {
if self.should_fail {
return Err("Mock OLA client failure".into());
}
let message = DmxMessage {
universe,
buffer: buffer.clone(),
};
self.sent_messages.lock().push(message);
Ok(())
}
fn reconnect(&mut self) -> Result<(), Box<dyn Error>> {
if self.should_fail {
return Err("Mock OLA client reconnect failure".into());
}
Ok(())
}
}
#[cfg(not(test))]
pub struct NullOlaClient;
#[cfg(not(test))]
impl OlaClient for NullOlaClient {
fn send_dmx(&mut self, _universe: u32, _buffer: &ola::DmxBuffer) -> Result<(), Box<dyn Error>> {
Ok(())
}
fn reconnect(&mut self) -> Result<(), Box<dyn Error>> {
Ok(())
}
}
pub struct OlaClientFactory;
impl OlaClientFactory {
#[cfg(not(test))]
pub fn create_real_client(
config: ola::client::StreamingClientConfig,
) -> Result<Box<dyn OlaClient>, Box<dyn Error>> {
let client = ola::connect_with_config(config.clone())?;
Ok(Box::new(RealOlaClient::new(client, config)))
}
#[cfg(test)]
pub fn create_mock_client() -> Box<dyn OlaClient> {
Box::new(MockOlaClient::new())
}
#[cfg(test)]
pub fn create_mock_client_unconditional() -> Box<dyn OlaClient> {
Box::new(MockOlaClient::new())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mock_ola_client_dmx_verification() {
let mut mock_client = MockOlaClient::new();
let buffer = ola::DmxBuffer::new();
let result = mock_client.send_dmx(1, &buffer);
assert!(result.is_ok());
assert_eq!(mock_client.message_count(), 1);
let last_message = mock_client.get_last_message().unwrap();
assert_eq!(last_message.universe, 1);
let universe_1_messages = mock_client.get_messages_for_universe(1);
assert_eq!(universe_1_messages.len(), 1);
let universe_2_messages = mock_client.get_messages_for_universe(2);
assert_eq!(universe_2_messages.len(), 0);
let retrieved_buffer = mock_client.get_buffer_for_universe(1).unwrap();
assert_eq!(retrieved_buffer.len(), 512); }
#[test]
fn test_mock_ola_client_multiple_messages() {
let mut mock_client = MockOlaClient::new();
let buffer1 = ola::DmxBuffer::new();
mock_client.send_dmx(1, &buffer1).unwrap();
let buffer2 = ola::DmxBuffer::new();
mock_client.send_dmx(1, &buffer2).unwrap();
let buffer3 = ola::DmxBuffer::new();
mock_client.send_dmx(2, &buffer3).unwrap();
assert_eq!(mock_client.message_count(), 3);
let universe_1_messages = mock_client.get_messages_for_universe(1);
assert_eq!(universe_1_messages.len(), 2);
let universe_2_messages = mock_client.get_messages_for_universe(2);
assert_eq!(universe_2_messages.len(), 1);
let last_message = mock_client.get_last_message().unwrap();
assert_eq!(last_message.universe, 2);
}
#[test]
fn test_mock_ola_client_clear_messages() {
let mut mock_client = MockOlaClient::new();
let buffer = ola::DmxBuffer::new();
mock_client.send_dmx(1, &buffer).unwrap();
assert_eq!(mock_client.message_count(), 1);
mock_client.clear_messages();
assert_eq!(mock_client.message_count(), 0);
}
#[test]
fn test_mock_ola_client_failure_mode() {
let mut mock_client = MockOlaClient::new();
mock_client.should_fail = true;
let buffer = ola::DmxBuffer::new();
assert!(mock_client.send_dmx(1, &buffer).is_err());
assert!(mock_client.reconnect().is_err());
assert_eq!(mock_client.message_count(), 0);
}
#[test]
fn test_mock_ola_client_reconnect_success() {
let mut mock_client = MockOlaClient::new();
assert!(mock_client.reconnect().is_ok());
}
#[test]
fn test_mock_ola_client_default() {
let mock_client = MockOlaClient::default();
assert_eq!(mock_client.message_count(), 0);
assert!(!mock_client.should_fail);
}
#[test]
fn test_mock_ola_client_no_last_message_when_empty() {
let mock_client = MockOlaClient::new();
assert!(mock_client.get_last_message().is_none());
assert!(mock_client.get_buffer_for_universe(1).is_none());
}
#[test]
fn test_ola_client_factory_mock() {
let _client = OlaClientFactory::create_mock_client();
let _client2 = OlaClientFactory::create_mock_client_unconditional();
}
}