From a809be2625412ed80b99a4422446a744006c7eb9 Mon Sep 17 00:00:00 2001 From: GridexX Date: Fri, 24 Feb 2023 15:23:42 +0100 Subject: [PATCH] test(external_api): parse json and check delimiter Signed-off-by: GridexX --- agent/Cargo.toml | 9 +- agent/lib/src/external_api/service.rs | 210 ++++++++++++++++++++++---- agent/lib/src/internal_api/model.rs | 1 - agent/lib/src/internal_api/service.rs | 36 ++--- agent/src/main.rs | 2 +- 5 files changed, 204 insertions(+), 54 deletions(-) diff --git a/agent/Cargo.toml b/agent/Cargo.toml index f7ba732..76bb19a 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -6,7 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -agent_lib = { path = "./lib" } log = "0.4.0" env_logger = "0.10.0" anyhow = "1.0.69" +serialport = "4.2.0" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" +unshare = "0.7.0" + +[lib] +name = "agent_lib" +path = "lib/src/lib.rs" diff --git a/agent/lib/src/external_api/service.rs b/agent/lib/src/external_api/service.rs index 6a9fcc4..83478fb 100644 --- a/agent/lib/src/external_api/service.rs +++ b/agent/lib/src/external_api/service.rs @@ -1,5 +1,3 @@ - - use anyhow::{anyhow, Result}; use log::{error, info}; @@ -9,17 +7,14 @@ pub struct ExternalApi { serial_read_path: String, serial_write_path: String, serial_baud_rate: u32, - data_received: Vec, } impl ExternalApi { pub fn new(serial_read_path: String, serial_write_path: String, serial_baud_rate: u32) -> Self { - let data_received: Vec = Vec::new(); Self { serial_read_path, serial_write_path, serial_baud_rate, - data_received, } } @@ -33,27 +28,19 @@ impl ExternalApi { // Create a buffer to hold the data let mut buf = [0; 128]; - loop { + + // Create the final vector to hold the data + let mut data_received: Vec = Vec::new(); + + let mut find_delimiter = false; + + while !find_delimiter { match serial.read(&mut buf) { Ok(t) => { if t > 0 { info!("Buffer received {:?}", &buf[..t]); - - // find char 1c (record separator) in the buffer - if let Some(i) = buf.iter().position(|&r| r == 0x1c) { - // Split the buffer at the position of the record separator - let (data_to_add, _) = buf.split_at(i); - - // Add the data to the data vector - self.data_received.extend_from_slice(data_to_add); - - info!("Delimiter found at index: {}", i); - - break; - } else { - // Add the data to the data vector - self.data_received.extend_from_slice(&buf[..t]); - } + find_delimiter = + self.append_data_before_delimiter(&buf, &mut data_received)?; } } Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => (), @@ -61,9 +48,9 @@ impl ExternalApi { } } - info!("Final received data: {:?}", self.data_received); - - let code_entry = self.parse_json_payload()?; + info!("Final received data: {:?}", data_received); + + let code_entry = self.parse_json_payload(&data_received)?; // Flush the serial port serial @@ -73,17 +60,36 @@ impl ExternalApi { Ok(code_entry) } - pub fn parse_json_payload(&mut self) -> Result { - + pub fn append_data_before_delimiter( + &mut self, + buf: &[u8], + data_received: &mut Vec, + ) -> Result { + // find char 1c (record separator) in the buffer + if let Some(i) = buf.iter().position(|&r| r == 0x1c) { + // Split the buffer at the position of the record separator + let (data_to_add, _) = buf.split_at(i); + + // Add the data to the data vector + data_received.extend_from_slice(data_to_add); + + info!("Delimiter found at index: {}", i); + + Ok(true) + } else { + // Add the data to the data vector + data_received.extend_from_slice(&buf[..buf.len()]); + Ok(false) + } + } + + pub fn parse_json_payload(&mut self, data: &[u8]) -> Result { // Convert the data vector to a codeEntry struct - let code_entry: CodeEntry = serde_json::from_slice(&self.data_received) + let code_entry: CodeEntry = serde_json::from_slice(data) .map_err(|e| anyhow!("Failed to parse JSON payload: {}", e))?; info!("Code entry: {:?}", code_entry); - // Clear the data vector - self.data_received = Vec::new(); - Ok(code_entry) } @@ -108,3 +114,145 @@ impl ExternalApi { Ok(()) } } + +#[cfg(test)] +mod tests { + + use anyhow::Result; + + use super::ExternalApi; + + #[test] + fn test_parse_json_payload() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + // Data vector with the following JSON payload: + // { + // "files": [ + // { + // "filename": "test.py", + // "content": "print('Hello World')" + // } + // ], + // "script": [ + // "python3 test.py" + // ] + // } + + let data = [ + 123, 10, 32, 32, 34, 102, 105, 108, 101, 115, 34, 58, 32, 91, 10, 32, 32, 32, 32, 123, + 10, 32, 32, 32, 32, 32, 32, 34, 102, 105, 108, 101, 110, 97, 109, 101, 34, 58, 32, 34, + 116, 101, 115, 116, 46, 112, 121, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 99, 111, 110, + 116, 101, 110, 116, 34, 58, 32, 34, 112, 114, 105, 110, 116, 40, 39, 72, 101, 108, 108, + 111, 32, 87, 111, 114, 108, 100, 39, 41, 34, 10, 32, 32, 32, 32, 125, 10, 32, 32, 93, + 44, 10, 32, 32, 34, 115, 99, 114, 105, 112, 116, 34, 58, 32, 91, 10, 32, 32, 32, 32, + 34, 112, 121, 116, 104, 111, 110, 51, 32, 116, 101, 115, 116, 46, 112, 121, 34, 10, 32, + 32, 93, 10, 32, 32, 10, 125, + ]; + + let code_entry = internal_api.parse_json_payload(&data)?; + assert_eq!(code_entry.files[0].filename, "test.py"); + assert_eq!(code_entry.files[0].content, "print('Hello World')"); + assert_eq!(code_entry.script[0], "python3 test.py"); + Ok(()) + } + + #[test] + fn test_parse_json_payload_failed() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + // Data vector with missing comma + let data = [ + 123, 10, 32, 32, 34, 102, 105, 108, 101, 34, 58, 32, 91, 10, 32, 32, 32, 32, 123, 10, + 32, 32, 32, 32, 32, 32, 34, 102, 105, 108, 101, 110, 97, 109, 101, 34, 58, 32, 34, 116, + 101, 115, 116, 46, 112, 121, 34, 44, 10, 32, 32, 32, 32, 32, 32, 34, 99, 111, 110, 116, + 101, 110, 116, 34, 58, 32, 34, 112, 114, 105, 110, 116, 40, 39, 72, 101, 108, 108, 111, + 32, 87, 111, 114, 108, 100, 39, 41, 34, 10, 32, 32, 32, 32, 125, 10, 32, 32, 93, 44, + 10, 32, 32, 34, 115, 99, 114, 105, 112, 116, 34, 58, 32, 91, 10, 32, 32, 32, 32, 34, + 112, 121, 116, 104, 111, 110, 51, 32, 116, 101, 115, 116, 46, 112, 121, 34, 10, 32, 32, + 93, 10, 32, 32, 10, 125, 10, + ]; + + let code_entry = internal_api.parse_json_payload(&data); + + assert!(code_entry.is_err()); + + Ok(()) + } + + #[test] + fn test_data_cut_before_delimiter() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + let data = [97, 98, 99, 28, 1, 2, 3, 4, 5, 6, 7]; + let mut data_received: Vec = Vec::new(); + + let find_demiliter = + internal_api.append_data_before_delimiter(&data, &mut data_received)?; + + assert!(find_demiliter); + assert_eq!(data_received, [97, 98, 99]); + + Ok(()) + } + + #[test] + fn test_data_transferred_without_delimiter() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + let data = [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]; + let mut data_received: Vec = Vec::new(); + + let find_demiliter = + internal_api.append_data_before_delimiter(&data, &mut data_received)?; + + assert!(!find_demiliter); + assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]); + + Ok(()) + } + + #[test] + fn test_data_transferred_multiple_time() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + let data = [97, 98, 99]; + let data2 = [1, 2, 3, 4, 5, 6, 7]; + let mut data_received: Vec = Vec::new(); + + let find_demiliter = + internal_api.append_data_before_delimiter(&data, &mut data_received)?; + let find_demiliter2 = + internal_api.append_data_before_delimiter(&data2, &mut data_received)?; + + assert!(!find_demiliter); + assert!(!find_demiliter2); + assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7]); + + Ok(()) + } + + #[test] + fn test_data_transferred_with_delimiter() -> Result<()> { + let mut internal_api = ExternalApi::new("".to_string(), "".to_string(), 0); + + let data = [97, 98, 99]; + let data2 = [1, 2, 3, 4, 5, 6, 7]; + let data3 = [8, 9, 10, 28, 11, 12, 13]; + let mut data_received: Vec = Vec::new(); + + let find_demiliter = + internal_api.append_data_before_delimiter(&data, &mut data_received)?; + let find_demiliter2 = + internal_api.append_data_before_delimiter(&data2, &mut data_received)?; + let find_demiliter3 = + internal_api.append_data_before_delimiter(&data3, &mut data_received)?; + + assert!(!find_demiliter); + assert!(!find_demiliter2); + assert!(find_demiliter3); + assert_eq!(data_received, [97, 98, 99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + Ok(()) + } +} diff --git a/agent/lib/src/internal_api/model.rs b/agent/lib/src/internal_api/model.rs index b57c454..ae91a1f 100644 --- a/agent/lib/src/internal_api/model.rs +++ b/agent/lib/src/internal_api/model.rs @@ -8,7 +8,6 @@ pub struct FileModel { pub path: PathBuf, pub file_name: String, pub content: String, - } impl FileModel { diff --git a/agent/lib/src/internal_api/service.rs b/agent/lib/src/internal_api/service.rs index c6d1a77..3fc04da 100644 --- a/agent/lib/src/internal_api/service.rs +++ b/agent/lib/src/internal_api/service.rs @@ -1,9 +1,13 @@ -use std::{io::{BufReader, Read}, fs::File, path::{PathBuf, Path}}; +use super::model::{CodeReturn, InternalError}; +use crate::{external_api::model::CodeEntry, internal_api::model::FileModel}; use anyhow::{anyhow, Result}; use log::{error, info}; -use crate::{external_api::model::{CodeEntry}, internal_api::model::FileModel}; use std::io::Write; -use super::model::{CodeReturn, InternalError}; +use std::{ + fs::File, + io::{BufReader, Read}, + path::{Path, PathBuf}, +}; use unshare::Command; const WORKSPACE_PATH: &str = "/tmp"; @@ -18,15 +22,12 @@ impl InternalApi { } pub fn create_workspace(&mut self) -> Result<()> { - info!("Creating workspace for code execution"); - // Create a vector of FileModel and a root path let mut file_models: Vec = Vec::new(); let root_path = PathBuf::from(WORKSPACE_PATH); - self.code_entry.files.iter().for_each(|file| { let mut file_path = PathBuf::from(&file.filename); file_path.pop(); @@ -39,19 +40,19 @@ impl InternalApi { // Extract the file name from the path and create a FileModel if let Some(file_name_str) = file_name { - let fns = file_name_str.to_os_string(); let file_name_string_option = fns.to_str(); if let Some(file_name_string) = file_name_string_option { - - let file_model = FileModel::new(file_path, file_name_string.to_string(), file.content.clone()); + let file_model = FileModel::new( + file_path, + file_name_string.to_string(), + file.content.clone(), + ); file_models.push(file_model); - } else { error!("Failed to convert file name to string"); } - } else { error!("Failed to extract file name from path"); } @@ -61,12 +62,12 @@ impl InternalApi { // For each file model, create the directory and the file file_models.iter().for_each(|file_model| { - let file_path = file_model.path.clone(); let file_name = file_model.file_name.clone(); // Create the directory - let op_dir = std::fs::create_dir_all(&file_path).map_err(|e| anyhow!("Failed to create directory: {}", e)); + let op_dir = std::fs::create_dir_all(&file_path) + .map_err(|e| anyhow!("Failed to create directory: {}", e)); if op_dir.is_err() { error!("Failed to create directory: {:?}", op_dir.err()); } else { @@ -75,12 +76,12 @@ impl InternalApi { // Create the file let file_path = file_path.join(file_name); - let op_file = File::create(file_path).map_err(|e| anyhow!("Failed to create file: {}", e)); + let op_file = + File::create(file_path).map_err(|e| anyhow!("Failed to create file: {}", e)); if let Err(e) = op_file { error!("Failed to create file: {:?}", e); } else { - let mut file = op_file.unwrap(); info!("File created: {:?}", file); @@ -92,17 +93,12 @@ impl InternalApi { } else { info!("File written: {:?}", file); } - - } - - }); Ok(()) } - pub fn write_log(&self) -> String { "Hello".to_string() } diff --git a/agent/src/main.rs b/agent/src/main.rs index 8a89d0e..61085cf 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -8,7 +8,7 @@ fn main() -> Result<()> { info!("Starting agent"); let mut external_api = - ExternalApi::new("/dev/pts/5".to_string(), "/dev/pts/6".to_string(), 9600); + ExternalApi::new("/dev/pts/4".to_string(), "/dev/pts/6".to_string(), 9600); let code_entry = external_api.read_from_serial()?; let mut internal_api = InternalApi::new(code_entry);