Skip to content

Commit

Permalink
rust: Add initial template for tests
Browse files Browse the repository at this point in the history
Signed-off-by: Siddharth Chandrasekaran <[email protected]>
  • Loading branch information
sidcha committed Jan 28, 2024
1 parent 512f348 commit 22b0e08
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 0 deletions.
68 changes: 68 additions & 0 deletions rust/tests/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::{sync::MutexGuard, thread, time};

use libosdp::{
channel::{MemoryChannel, Read, Write},
ControlPanel, OsdpCommand, OsdpCommandBuzzer, OsdpEvent, OsdpEventCardRead, PeripheralDevice,
};

use crate::common::{device::CpDevice, device::PdDevice, threadbus::ThreadBus};

mod common;

type Result<T> = core::result::Result<T, libosdp::OsdpError>;

fn send_command(mut cp: MutexGuard<'_, ControlPanel>, command: OsdpCommand) -> Result<()> {
cp.send_command(0, command)
}

fn notify_event(mut pd: MutexGuard<'_, PeripheralDevice>, event: OsdpEvent) -> Result<()> {
pd.notify_event(event)
}

#[test]
fn test_thread_bus_channel() -> Result<()> {
let mut a = ThreadBus::new("conn-0");
let mut b = a.clone();
let mut c = a.clone();

let buf_write = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let mut buf_read = [0; 100];
assert_eq!(a.write(&buf_write)?, buf_write.len());
assert_eq!(b.read(&mut buf_read)?, buf_write.len());
assert_eq!(c.read(&mut buf_read)?, buf_write.len());
Ok(())
}

#[test]
fn test_commands() -> Result<()> {
common::setup();
let (cp_bus, pd_bus) = MemoryChannel::new();
let pd = PdDevice::new(pd_bus)?;
let cp = CpDevice::new(cp_bus)?;

thread::sleep(time::Duration::from_secs(2));
loop {
if pd.get_device().is_sc_active() {
break;
}
println!("Waiting for devices to establish a secure channel");
thread::sleep(time::Duration::from_secs(1));
}

let command = OsdpCommand::Buzzer(OsdpCommandBuzzer::default());
send_command(cp.get_device(), command.clone())?;
println!("Send, waiting for cmd in PD");
let cmd_rx = pd.receiver.recv().unwrap();
println!("Got command");
assert_eq!(cmd_rx, command, "Buzzer command check failed");

let event = OsdpEvent::CardRead(OsdpEventCardRead::new_ascii(vec![0x55, 0xAA]));
notify_event(pd.get_device(), event.clone())?;
assert_eq!(
cp.receiver.recv().unwrap(),
(0 as i32, event),
"Cardread event check failed"
);

Ok(())
}
125 changes: 125 additions & 0 deletions rust/tests/common/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::{
sync::{mpsc::Receiver, Arc, Mutex, MutexGuard},
thread, time,
};

use libosdp::{
channel::{Channel, OsdpChannel},
ControlPanel, OsdpCommand, OsdpEvent, OsdpFlag, PdCapEntity, PdCapability, PdId, PdInfo,
PeripheralDevice,
};
type Result<T> = core::result::Result<T, libosdp::OsdpError>;

pub struct CpDevice {
dev: Arc<Mutex<ControlPanel>>,
pub receiver: Receiver<(i32, OsdpEvent)>,
}

impl CpDevice {
pub fn new<T: Channel + 'static>(bus: T) -> Result<Self> {
let pd_info = vec![PdInfo::for_cp(
"PD 101",
101,
115200,
OsdpFlag::empty(),
OsdpChannel::new::<T>(Box::new(bus)),
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
],
)];
let mut cp = ControlPanel::new(pd_info)?;
let (event_tx, event_rx) = std::sync::mpsc::channel::<(i32, OsdpEvent)>();

cp.set_event_callback(|pd, event| {
event_tx.send((pd, event)).unwrap();
0
});

let dev = Arc::new(Mutex::new(cp));
let dev_clone = dev.clone();
let _ = thread::Builder::new()
.name("CP Thread".to_string())
.spawn(move || {
let dev = dev_clone;
let sender = event_tx;
dev.lock().unwrap().set_event_callback(|pd, event| {
sender.send((pd, event)).expect("CP event send");
0
});
loop {
dev.lock().unwrap().refresh();
thread::sleep(time::Duration::from_millis(10));
}
});
Ok(Self {
dev,
receiver: event_rx,
})
}

pub fn get_device(&self) -> MutexGuard<'_, ControlPanel> {
self.dev.lock().unwrap()
}
}

pub struct PdDevice {
dev: Arc<Mutex<PeripheralDevice>>,
pub receiver: Receiver<OsdpCommand>,
}

impl PdDevice {
pub fn new<T: Channel + 'static>(bus: T) -> Result<Self> {
let pd_info = PdInfo::for_pd(
"PD 101",
101,
115200,
OsdpFlag::empty(),
PdId::from_number(101),
vec![
PdCapability::CommunicationSecurity(PdCapEntity::new(1, 1)),
PdCapability::AudibleOutput(PdCapEntity::new(1, 1)),
PdCapability::LedControl(PdCapEntity::new(1, 1)),
],
OsdpChannel::new::<T>(Box::new(bus)),
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
0x0e, 0x0f,
],
);
let mut pd = PeripheralDevice::new(pd_info)?;
let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::<OsdpCommand>();
pd.set_command_callback(|command| {
cmd_tx.send(command).unwrap();
0
});

let dev = Arc::new(Mutex::new(pd));
let dev_clone = dev.clone();
let _ = thread::Builder::new()
.name("PD Thread".to_string())
.spawn(move || {
let dev = dev_clone;
let sender = cmd_tx;
dev.lock().unwrap().set_command_callback(|command| {
println!("--> Got here! {:?}", command);
sender.send(command).expect("PD command send");
println!("<-- Got out!");
0
});
loop {
dev.lock().unwrap().refresh();
thread::sleep(time::Duration::from_millis(10));
}
});

Ok(Self {
dev,
receiver: cmd_rx,
})
}

pub fn get_device(&self) -> MutexGuard<'_, PeripheralDevice> {
self.dev.lock().unwrap()
}
}
10 changes: 10 additions & 0 deletions rust/tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod device;
pub mod threadbus;

pub fn setup() {
env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.format_target(false)
.format_timestamp(None)
.init();
}
94 changes: 94 additions & 0 deletions rust/tests/common/threadbus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use multiqueue::{BroadcastReceiver, BroadcastSender};
use std::{
collections::hash_map::DefaultHasher,
fmt::Debug,
hash::{Hash, Hasher},
io::Error,
io::ErrorKind,
sync::Mutex,
};

fn str_to_channel_id(key: &str) -> i32 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
let mut id: u64 = hasher.finish();
id = (id >> 32) ^ id & 0xffffffff;
id as i32
}

pub struct ThreadBus {
name: String,
id: i32,
send: Mutex<BroadcastSender<Vec<u8>>>,
recv: Mutex<BroadcastReceiver<Vec<u8>>>,
}

impl ThreadBus {
pub fn new(name: &str) -> Self {
let (send, recv) = multiqueue::broadcast_queue(4);
Self {
name: name.into(),
id: str_to_channel_id(name),
send: Mutex::new(send),
recv: Mutex::new(recv),
}
}
}

impl Clone for ThreadBus {
fn clone(&self) -> Self {
let send = Mutex::new(self.send.lock().unwrap().clone());
let recv = Mutex::new(self.recv.lock().unwrap().add_stream());
Self {
name: self.name.clone(),
id: self.id,
send,
recv,
}
}
}

impl Debug for ThreadBus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ThreadBus")
.field("name", &self.name)
.field("id", &self.id)
.finish()
}
}

impl std::io::Read for ThreadBus {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let v = self.recv.lock().unwrap().try_recv().map_err(|e| match e {
std::sync::mpsc::TryRecvError::Empty => Error::new(ErrorKind::WouldBlock, "No data"),
std::sync::mpsc::TryRecvError::Disconnected => {
Error::new(ErrorKind::ConnectionReset, "disconnected")
}
})?;
buf[..v.len()].copy_from_slice(&v[..]);
Ok(v.len())
}
}

impl std::io::Write for ThreadBus {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let v = buf.into();
self.send.lock().unwrap().try_send(v).map_err(|e| match e {
std::sync::mpsc::TrySendError::Full(_) => Error::new(ErrorKind::WouldBlock, "No space"),
std::sync::mpsc::TrySendError::Disconnected(_) => {
Error::new(ErrorKind::ConnectionReset, "disconnected")
}
})?;
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

impl libosdp::channel::Channel for ThreadBus {
fn get_id(&self) -> i32 {
self.id
}
}

0 comments on commit 22b0e08

Please sign in to comment.