#![cfg(test)]
extern crate std;
use crate::serial::SerialPort;
use crate::state::AppState;
use crate::storage::Storage;
use crate::types::{FixedString, RingBuffer};
use proptest::collection::vec;
use proptest::prelude::*;
use std::cmp::min;
use std::vec::Vec;
struct MockSerial {
pub output: Vec<u8>,
}
impl MockSerial {
fn new() -> Self {
Self { output: Vec::new() }
}
}
impl SerialPort for MockSerial {
fn write_byte(&mut self, byte: u8) {
self.output.push(byte);
}
fn read_byte(&mut self) -> Option<u8> {
None
}
}
struct MockStorage {
pub data: Vec<u8>,
}
impl MockStorage {
fn new() -> Self {
Self { data: Vec::new() }
}
}
impl Storage for MockStorage {
fn read(&mut self, offset: usize, buffer: &mut [u8]) -> Result<usize, &'static str> {
if offset >= self.data.len() {
return Ok(0);
}
let end = min(offset + buffer.len(), self.data.len());
let len = end - offset;
buffer[..len].copy_from_slice(&self.data[offset..end]);
Ok(len)
}
fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), &'static str> {
let end = offset + buffer.len();
if end > self.data.len() {
self.data.resize(end, 0);
}
self.data[offset..end].copy_from_slice(buffer);
Ok(())
}
fn flush(&mut self) -> Result<(), &'static str> {
Ok(())
}
}
#[test]
fn test_fixed_string() {
let mut string = FixedString::<5>::new();
assert!(string.is_empty());
assert!(string.push(b'H'));
assert!(string.push(b'e'));
assert_eq!(string.len(), 2);
assert_eq!(string.as_str().unwrap(), "He");
assert_eq!(string.pop(), Some(b'e'));
assert_eq!(string.len(), 1);
assert_eq!(string.as_str().unwrap(), "H");
}
#[test]
fn test_ring_buffer() {
let mut ring_buffer = RingBuffer::<u32, 3>::new();
assert!(ring_buffer.is_empty());
ring_buffer.push(1);
ring_buffer.push(2);
ring_buffer.push(3);
assert!(ring_buffer.is_full());
assert_eq!(ring_buffer.len(), 3);
ring_buffer.push(4);
assert_eq!(ring_buffer.len(), 3);
assert_eq!(*ring_buffer.get(0).unwrap(), 2);
assert_eq!(*ring_buffer.get(1).unwrap(), 3);
assert_eq!(*ring_buffer.get(2).unwrap(), 4);
assert_eq!(ring_buffer.get(3), None);
}
#[test]
fn test_storage_roundtrip() {
let mut storage = MockStorage::new();
let mut port = MockSerial::new();
let mut state1 = AppState::uninit();
state1.init(&mut storage, &mut port).unwrap();
assert!(state1.initialized);
for &byte in b"/user 1\r" {
state1.process_byte(byte, &mut port, &mut storage).unwrap();
}
for &b in b"/name Alice\r" {
state1.process_byte(b, &mut port, &mut storage).unwrap();
}
for &byte in b"Hello World\r" {
state1.process_byte(byte, &mut port, &mut storage).unwrap();
}
let mut state2 = AppState::uninit();
let mut port2 = MockSerial::new();
state2.init(&mut storage, &mut port2).unwrap();
assert!(state2.initialized);
let user = &state2.users[0];
assert_eq!(user.id, 1);
assert_eq!(user.status, 0);
assert_eq!(user.name.as_str().unwrap(), "Alice");
assert_eq!(state2.history.len(), 1);
let msg = state2.history.get(0).unwrap();
assert_eq!(msg.user_id, 1);
assert_eq!(msg.content.as_str().unwrap(), "Hello World");
}
proptest! {
#[test]
fn fuzzy_process_byte(bytes in vec(any::<u8>(), 0..512)) {
let mut storage = MockStorage::new();
let mut port = MockSerial::new();
let mut state = AppState::uninit();
let _ = state.init(&mut storage, &mut port);
for byte in bytes {
let _ = state.process_byte(byte, &mut port, &mut storage);
}
}
}