Skip to content

Commit

Permalink
tests: Add file transfer test
Browse files Browse the repository at this point in the history
Signed-off-by: Siddharth Chandrasekaran <sidcha.dev@gmail.com>
  • Loading branch information
sidcha committed Mar 15, 2024
1 parent 194f73e commit 17169c9
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 22 deletions.
2 changes: 2 additions & 0 deletions libosdp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ thiserror = { version = "1.0.50", optional = true }
[dev-dependencies]
env_logger = "0.11.3"
multiqueue = "0.3.2"
rand = "0.8.5"
ringbuf = "0.3.3"
sha256 = "1.5.0"

[features]
default = ["std"]
Expand Down
11 changes: 5 additions & 6 deletions libosdp/tests/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
//
// SPDX-License-Identifier: Apache-2.0

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

use std::{sync::MutexGuard, thread, time};

use libosdp::{
Expand All @@ -14,10 +17,6 @@ use crate::common::{
device::CpDevice, device::PdDevice, memory_channel::MemoryChannel, 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)
}
Expand Down Expand Up @@ -64,8 +63,8 @@ fn test_commands() -> Result<()> {
notify_event(pd.get_device(), event.clone())?;
assert_eq!(
cp.receiver.recv().unwrap(),
(0 as i32, event),
"Cardread event check failed"
(0_i32, event),
"Card read event check failed"
);

Ok(())
Expand Down
15 changes: 1 addition & 14 deletions libosdp/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,14 @@
//
// SPDX-License-Identifier: Apache-2.0

use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};

pub mod device;
pub mod memory_channel;
pub mod threadbus;

pub fn setup() {
env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.filter_level(log::LevelFilter::Info)
.format_target(false)
.format_timestamp(None)
.init();
}

pub 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
}
19 changes: 17 additions & 2 deletions libosdp/tests/common/threadbus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
// SPDX-License-Identifier: Apache-2.0

use multiqueue::{BroadcastReceiver, BroadcastSender};
use std::{fmt::Debug, io::Error, io::ErrorKind, sync::Mutex};
use std::{
collections::hash_map::DefaultHasher,
fmt::Debug,
hash::{Hash, Hasher},
io::Error,
io::ErrorKind,
sync::Mutex,
};

pub struct ThreadBus {
name: String,
Expand All @@ -13,12 +20,20 @@ pub struct ThreadBus {
recv: Mutex<BroadcastReceiver<Vec<u8>>>,
}

fn str_to_channel_id(key: &str) -> i32 {

Check warning on line 23 in libosdp/tests/common/threadbus.rs

View workflow job for this annotation

GitHub Actions / test

function `str_to_channel_id` is never used
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
let mut id: u64 = hasher.finish();
id = (id >> 32) ^ id & 0xffffffff;
id as i32
}

impl ThreadBus {
pub fn new(name: &str) -> Self {

Check warning on line 32 in libosdp/tests/common/threadbus.rs

View workflow job for this annotation

GitHub Actions / test

associated function `new` is never used
let (send, recv) = multiqueue::broadcast_queue(4);
Self {
name: name.into(),
id: super::str_to_channel_id(name),
id: str_to_channel_id(name),
send: Mutex::new(send),
recv: Mutex::new(recv),
}
Expand Down
173 changes: 173 additions & 0 deletions libosdp/tests/file_transfer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
//
// Copyright (c) 2024 Siddharth Chandrasekaran <sidcha.dev@gmail.com>
//
// SPDX-License-Identifier: Apache-2.0

mod common;

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

use core::time::Duration;
use libosdp::{OsdpCommand, OsdpCommandFileTx, OsdpError, OsdpFileOps};
use rand::Rng;
use std::{
cmp,
collections::HashMap,
fs::File,
io::{BufWriter, Write},
path::PathBuf,
str::FromStr,
thread,
};

use crate::common::{device::CpDevice, device::PdDevice, memory_channel::MemoryChannel};

#[cfg(not(target_os = "windows"))]
use std::os::unix::prelude::FileExt;
#[cfg(target_os = "windows")]
use std::os::windows::fs::FileExt;

/// OSDP file transfer context
#[derive(Debug)]
pub struct OsdpFileManager {
files: HashMap<i32, PathBuf>,
file: Option<File>,
}

impl OsdpFileManager {
pub fn new() -> Self {
Self {
files: HashMap::new(),
file: None,
}
}

pub fn register_file(&mut self, id: i32, path: &str) {
let _ = self.files.insert(id, PathBuf::from_str(path).unwrap());
}
}

impl OsdpFileOps for OsdpFileManager {
fn open(&mut self, id: i32, read_only: bool) -> Result<usize> {
let path = self
.files
.get(&id)
.ok_or(OsdpError::FileTransfer("Invalid file ID"))?;
log::debug!("File {:?}", path);
let file = if read_only {
File::open(path.as_os_str())?
} else {
File::create(path.as_os_str())?
};
let size = file.metadata()?.len() as usize;
self.file = Some(file);
Ok(size)
}

fn offset_read(&self, buf: &mut [u8], off: u64) -> Result<usize> {
let file = self
.file
.as_ref()
.ok_or(OsdpError::FileTransfer("File not open"))?;

#[cfg(not(target_os = "windows"))]
let r = file.read_at(buf, off)?;

#[cfg(target_os = "windows")]
let r = file.seek_read(buf, off)?;

Ok(r)
}

fn offset_write(&self, buf: &[u8], off: u64) -> Result<usize> {
let file = self
.file
.as_ref()
.ok_or(OsdpError::FileTransfer("File not open"))?;

#[cfg(not(target_os = "windows"))]
let r = file.write_at(buf, off)?;

#[cfg(target_os = "windows")]
let r = file.seek_write(buf, off)?;

Ok(r)
}

fn close(&mut self) -> Result<()> {
let _ = self.file.take().unwrap();
Ok(())
}
}

fn create_random_file<P>(path: P, size: usize)
where
P: AsRef<std::path::Path>,
{
if path.as_ref().exists() {
return;
}

let mut buffer = [0; 1024];
let mut remaining_size = size;

let f = File::create(path).unwrap();
let mut writer = BufWriter::new(f);

let mut rng = rand::thread_rng();

while remaining_size > 0 {
let to_write = cmp::min(remaining_size, buffer.len());
let buffer = &mut buffer[..to_write];
rng.fill(buffer);
writer.write_all(buffer).unwrap();
remaining_size -= to_write;
}
}

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

create_random_file("/tmp/ftx_test.in", 50 * 1024);

thread::sleep(Duration::from_secs(2));

let mut fm = OsdpFileManager::new();
fm.register_file(1, "/tmp/ftx_test.in");

cp.get_device().register_file_ops(0, Box::new(fm))?;

let mut fm = OsdpFileManager::new();
fm.register_file(1, "/tmp/ftx_test.out");

pd.get_device().register_file_ops(Box::new(fm))?;

let command = OsdpCommand::FileTx(OsdpCommandFileTx::new(1, 0));
cp.get_device().send_command(0, command.clone())?;

assert_eq!(
pd.receiver.recv().unwrap(),
command,
"PD file tx command callback verification failed!"
);

loop {
let (size, offset) = pd.get_device().file_transfer_status()?;
log::info!("File TX in progress: size:{size} offset:{offset}");
if size == offset {
break;
}
thread::sleep(Duration::from_secs(1));
}

assert_eq!(
sha256::digest(std::fs::read("/tmp/ftx_test.in").unwrap()),
sha256::digest(std::fs::read("/tmp/ftx_test.out").unwrap()),
"Transferred file hash mismatch!"
);
Ok(())
}

0 comments on commit 17169c9

Please sign in to comment.