mirror of
https://github.com/Noratrieb/haesli.git
synced 2026-01-16 12:45:04 +01:00
random testing
This commit is contained in:
parent
2455e95d45
commit
c0bfcb4089
7 changed files with 1649 additions and 1247 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
mod parser;
|
mod parser;
|
||||||
mod write;
|
mod write;
|
||||||
|
mod random;
|
||||||
|
|
||||||
use crate::parser::codegen_parser;
|
use crate::parser::codegen_parser;
|
||||||
use crate::write::codegen_write;
|
use crate::write::codegen_write;
|
||||||
|
|
@ -7,6 +8,7 @@ use heck::ToUpperCamelCase;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
use strong_xml::XmlRead;
|
use strong_xml::XmlRead;
|
||||||
|
use crate::random::codegen_random;
|
||||||
|
|
||||||
#[derive(Debug, XmlRead)]
|
#[derive(Debug, XmlRead)]
|
||||||
#[xml(tag = "amqp")]
|
#[xml(tag = "amqp")]
|
||||||
|
|
@ -91,6 +93,7 @@ fn codegen(amqp: &Amqp) {
|
||||||
codegen_class_defs(amqp);
|
codegen_class_defs(amqp);
|
||||||
codegen_parser(amqp);
|
codegen_parser(amqp);
|
||||||
codegen_write(amqp);
|
codegen_write(amqp);
|
||||||
|
codegen_random(amqp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn codegen_domain_defs(amqp: &Amqp) {
|
fn codegen_domain_defs(amqp: &Amqp) {
|
||||||
|
|
|
||||||
61
amqp_codegen/src/random.rs
Normal file
61
amqp_codegen/src/random.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use crate::{snake_case, Amqp};
|
||||||
|
use heck::ToUpperCamelCase;
|
||||||
|
|
||||||
|
pub(crate) fn codegen_random(amqp: &Amqp) {
|
||||||
|
println!(
|
||||||
|
"#[cfg(test)]
|
||||||
|
mod random {{
|
||||||
|
use rand::Rng;
|
||||||
|
use crate::classes::tests::RandomMethod;
|
||||||
|
use super::*;
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
impl_random("Class", || {
|
||||||
|
let class_lens = amqp.classes.len();
|
||||||
|
println!(" match rand::thread_rng().gen_range(0u32..{class_lens}) {{");
|
||||||
|
for (i, class) in amqp.classes.iter().enumerate() {
|
||||||
|
let class_name = class.name.to_upper_camel_case();
|
||||||
|
println!(" {i} => Class::{class_name}({class_name}::random(rng)),");
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
" _ => unreachable!(),
|
||||||
|
}}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for class in &amqp.classes {
|
||||||
|
let class_name = class.name.to_upper_camel_case();
|
||||||
|
impl_random(&class_name, || {
|
||||||
|
let method_len = class.methods.len();
|
||||||
|
println!(" match rand::thread_rng().gen_range(0u32..{method_len}) {{");
|
||||||
|
|
||||||
|
for (i, method) in class.methods.iter().enumerate() {
|
||||||
|
let method_name = method.name.to_upper_camel_case();
|
||||||
|
println!(" {i} => {class_name}::{method_name} {{");
|
||||||
|
for field in &method.fields {
|
||||||
|
let field_name = snake_case(&field.name);
|
||||||
|
println!(" {field_name}: RandomMethod::random(rng),");
|
||||||
|
}
|
||||||
|
println!(" }},");
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
" _ => unreachable!(),
|
||||||
|
}}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_random(name: &str, body: impl FnOnce()) {
|
||||||
|
println!(
|
||||||
|
"impl<R: Rng> RandomMethod<R> for {name} {{
|
||||||
|
fn random(rng: &mut R) -> Self {{"
|
||||||
|
);
|
||||||
|
|
||||||
|
body();
|
||||||
|
|
||||||
|
println!(" }}\n}}");
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,8 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
mod generated;
|
mod generated;
|
||||||
mod parse_helper;
|
mod parse_helper;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
mod write_helper;
|
mod write_helper;
|
||||||
|
|
||||||
pub type TableFieldName = String;
|
pub type TableFieldName = String;
|
||||||
|
|
@ -46,31 +48,3 @@ pub fn parse_method(payload: &[u8]) -> Result<generated::Class, TransError> {
|
||||||
Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err),
|
Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn pack_few_bits() {
|
|
||||||
let bits = [true, false, true];
|
|
||||||
|
|
||||||
let mut buffer = [0u8; 2];
|
|
||||||
super::write_helper::bit(&bits, &mut buffer.as_mut_slice()).unwrap();
|
|
||||||
|
|
||||||
let (_, parsed_bits) = super::parse_helper::bit(&buffer, 3).unwrap();
|
|
||||||
assert_eq!(bits.as_slice(), parsed_bits.as_slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn pack_many_bits() {
|
|
||||||
let bits = [
|
|
||||||
/* first 8 */
|
|
||||||
true, true, true, true, false, false, false, false, /* second 4 */
|
|
||||||
true, false, true, true,
|
|
||||||
];
|
|
||||||
let mut buffer = [0u8; 2];
|
|
||||||
super::write_helper::bit(&bits, &mut buffer.as_mut_slice()).unwrap();
|
|
||||||
|
|
||||||
let (_, parsed_bits) = super::parse_helper::bit(&buffer, 12).unwrap();
|
|
||||||
assert_eq!(bits.as_slice(), parsed_bits.as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
127
amqp_transport/src/classes/tests.rs
Normal file
127
amqp_transport/src/classes/tests.rs
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
// create random methods to test the ser/de code together. if they diverge, we have a bug
|
||||||
|
// this is not perfect, if they both have the same bug it won't be found, but tha's an ok tradeoff
|
||||||
|
|
||||||
|
use crate::classes::{Class, FieldValue};
|
||||||
|
use rand::{Rng, SeedableRng};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Allows the creation of a random instance of that type
|
||||||
|
pub(crate) trait RandomMethod<R: Rng> {
|
||||||
|
fn random(rng: &mut R) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> RandomMethod<R> for String {
|
||||||
|
fn random(_rng: &mut R) -> Self {
|
||||||
|
"randomstring".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng, T: RandomMethod<R>> RandomMethod<R> for Vec<T> {
|
||||||
|
fn random(rng: &mut R) -> Self {
|
||||||
|
let len = rng.gen_range(0_usize..10);
|
||||||
|
let mut vec = Vec::with_capacity(len);
|
||||||
|
(0..len).for_each(|_| vec.push(RandomMethod::random(rng)));
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! rand_random_method {
|
||||||
|
($($ty:ty),+) => {
|
||||||
|
$(
|
||||||
|
impl<R: Rng> RandomMethod<R> for $ty {
|
||||||
|
fn random(rng: &mut R) -> Self {
|
||||||
|
rng.gen()
|
||||||
|
}
|
||||||
|
})+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rand_random_method!(bool, u8, i8, u16, i16, u32, i32, u64, i64, f32, f64);
|
||||||
|
|
||||||
|
impl<R: Rng> RandomMethod<R> for HashMap<String, FieldValue> {
|
||||||
|
fn random(rng: &mut R) -> Self {
|
||||||
|
let len = rng.gen_range(0..3);
|
||||||
|
HashMap::from_iter((0..len).map(|_| (String::random(rng), FieldValue::random(rng))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Rng> RandomMethod<R> for FieldValue {
|
||||||
|
fn random(rng: &mut R) -> Self {
|
||||||
|
let index = rand::thread_rng().gen_range(0_u32..17);
|
||||||
|
match index {
|
||||||
|
0 => FieldValue::Boolean(RandomMethod::random(rng)),
|
||||||
|
1 => FieldValue::ShortShortInt(RandomMethod::random(rng)),
|
||||||
|
2 => FieldValue::ShortShortUInt(RandomMethod::random(rng)),
|
||||||
|
3 => FieldValue::ShortInt(RandomMethod::random(rng)),
|
||||||
|
4 => FieldValue::ShortUInt(RandomMethod::random(rng)),
|
||||||
|
5 => FieldValue::LongInt(RandomMethod::random(rng)),
|
||||||
|
6 => FieldValue::LongUInt(RandomMethod::random(rng)),
|
||||||
|
7 => FieldValue::LongLongInt(RandomMethod::random(rng)),
|
||||||
|
8 => FieldValue::LongLongUInt(RandomMethod::random(rng)),
|
||||||
|
9 => FieldValue::Float(RandomMethod::random(rng)),
|
||||||
|
10 => FieldValue::Double(RandomMethod::random(rng)),
|
||||||
|
11 => FieldValue::ShortString(RandomMethod::random(rng)),
|
||||||
|
12 => FieldValue::LongString(RandomMethod::random(rng)),
|
||||||
|
13 => FieldValue::FieldArray(RandomMethod::random(rng)),
|
||||||
|
14 => FieldValue::Timestamp(RandomMethod::random(rng)),
|
||||||
|
15 => FieldValue::FieldTable(RandomMethod::random(rng)),
|
||||||
|
16 => FieldValue::Void,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pack_few_bits() {
|
||||||
|
rand::thread_rng().gen_range(0..5);
|
||||||
|
let bits = [true, false, true];
|
||||||
|
|
||||||
|
let mut buffer = [0u8; 2];
|
||||||
|
super::write_helper::bit(&bits, &mut buffer.as_mut_slice()).unwrap();
|
||||||
|
|
||||||
|
let (_, parsed_bits) = super::parse_helper::bit(&buffer, 3).unwrap();
|
||||||
|
assert_eq!(bits.as_slice(), parsed_bits.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pack_many_bits() {
|
||||||
|
let bits = [
|
||||||
|
/* first 8 */
|
||||||
|
true, true, true, true, false, false, false, false, /* second 4 */
|
||||||
|
true, false, true, true,
|
||||||
|
];
|
||||||
|
let mut buffer = [0u8; 2];
|
||||||
|
super::write_helper::bit(&bits, &mut buffer.as_mut_slice()).unwrap();
|
||||||
|
|
||||||
|
let (_, parsed_bits) = super::parse_helper::bit(&buffer, 12).unwrap();
|
||||||
|
assert_eq!(bits.as_slice(), parsed_bits.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn random_ser_de() {
|
||||||
|
const ITERATIONS: usize = 1000;
|
||||||
|
let mut rng = rand::rngs::StdRng::from_seed([0; 32]);
|
||||||
|
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let class = Class::random(&mut rng);
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
|
||||||
|
if let Err(err) = super::write::write_method(class.clone(), &mut bytes) {
|
||||||
|
eprintln!("{class:?}");
|
||||||
|
eprintln!("{err}");
|
||||||
|
panic!("Failed to serialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
match super::parse_method(&bytes) {
|
||||||
|
Ok(parsed) => {
|
||||||
|
assert_eq!(class, parsed);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{class:?}");
|
||||||
|
eprintln!("{bytes:?}");
|
||||||
|
eprintln!("{err}");
|
||||||
|
panic!("Failed to deserialize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::classes::generated::{
|
use crate::classes::generated::{
|
||||||
Bit, Long, Longlong, Longstr, Octet, Short, Shortstr, Table, Timestamp,
|
Bit, Long, Longlong, Longstr, Octet, Short, Shortstr, Table, Timestamp,
|
||||||
};
|
};
|
||||||
use crate::classes::{FieldValue, TableFieldName};
|
use crate::classes::FieldValue;
|
||||||
use crate::error::TransError;
|
use crate::error::TransError;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use std::io;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
pub fn octet<W: Write>(value: Octet, writer: &mut W) -> Result<(), TransError> {
|
pub fn octet<W: Write>(value: Octet, writer: &mut W) -> Result<(), TransError> {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
mod classes;
|
mod classes;
|
||||||
mod connection;
|
mod connection;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue