#![allow(dead_code)] use std::{ fmt::{Debug, Display}, sync::atomic::AtomicUsize, }; use otvec::{transform, Operation}; pub fn test_transform( a: &Operation, b: &Operation, input: &[T], expect: &[T], ) { // Print operations println!("A/B:"); a.print_on(input); b.print_on(input); let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); dbg!(&a2, &b2); check_op(&a2, a, b); check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); b2.apply(&mut data); let mut data2 = input.to_vec(); b.clone().apply(&mut data2); a2.apply(&mut data2); assert_eq!(data, data2, "OT condition unfulfilled"); assert_eq!(data, expect, "a, b2"); assert_eq!(data2, expect, "b, a2"); } #[derive(Default)] pub struct CondCatchCtr { total: AtomicUsize, ok: AtomicUsize, } pub fn test_transform_cond_catch< T: PartialEq + Clone + Debug + Display + std::panic::RefUnwindSafe, >( a: &Operation, b: &Operation, input: &[T], ctr: Option<&CondCatchCtr>, ) { let do_count = !a.is_empty() && !b.is_empty(); if do_count { if let Some(ctr) = ctr { ctr.total.fetch_add(1, std::sync::atomic::Ordering::Relaxed); } } let res = std::panic::catch_unwind(|| { let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); (a2, b2) }); if let Ok((a2, b2)) = res { // check_op(&a2, a, b); // check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); b2.apply(&mut data); let mut data2 = input.to_vec(); b.clone().apply(&mut data2); a2.apply(&mut data2); if data != data2 { // Print operations println!("A/B: Operation::{a:?}, Operation::{b:?}"); a.print_on(input); b.print_on(input); if let Some(ctr) = ctr { let total = ctr.total.load(std::sync::atomic::Ordering::Relaxed); let success = ctr.ok.load(std::sync::atomic::Ordering::Relaxed); let perc = (success as f64 / total as f64) * 100.0; println!("Cases passed: {success}/{total} ({perc:0.2}%)"); } panic!("OT condition unfulfilled"); } if do_count { if let Some(ctr) = ctr { ctr.ok.fetch_add(1, std::sync::atomic::Ordering::Relaxed); } } } } pub fn print_cond_catch_ctr(ctr: &CondCatchCtr) { let total = ctr.total.load(std::sync::atomic::Ordering::Relaxed); let success = ctr.ok.load(std::sync::atomic::Ordering::Relaxed); let perc = (success as f64 / total as f64) * 100.0; if success < total { panic!("❌ Cases passed: {success}/{total} ({perc:0.2}%)"); } else { println!("✅ Cases passed: {success}/{total} ({perc:0.2}%)"); } } pub fn test_transform_sym( a: &Operation, b: &Operation, input: &[T], expect: &[T], ) { test_transform(a, b, input, expect); test_transform(b, a, input, expect); } pub fn test_transform_cond( a: &Operation, b: &Operation, input: &[T], ) { let (mut a2, mut b2) = (a.clone(), b.clone()); transform(&mut a2, &mut b2); // dbg!(&a2, &b2); check_op(&a2, a, b); check_op(&b2, a, b); let mut data = input.to_vec(); a.clone().apply(&mut data); b2.apply(&mut data); let mut data2 = input.to_vec(); b.clone().apply(&mut data2); a2.apply(&mut data2); assert_eq!(data, data2, "ops:\nA: {a:?}\nB: {b:?}"); } fn check_op(op: &Operation, a: &Operation, b: &Operation) { match op { Operation::Nop => {} Operation::Ins { val, .. } => { assert!(val.len() > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}") } Operation::Del { n, .. } => assert!(*n > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}"), Operation::Mov { pos, n, to } => { assert!(pos != to && *n > 0, "empty op: {op:?}\nfrom: {a:?}; {b:?}") } Operation::Seq { ops } => ops.iter().for_each(|op| check_op(op, a, b)), } }