#![allow(clippy::unwrap_used, clippy::panic)]
use dfajit::{JitDfa, TransitionTable};
use matchkit::Match;
#[test]
#[cfg(debug_assertions)]
fn test_new_table_overflow_panics_in_debug() {
let huge = usize::MAX;
let result = std::panic::catch_unwind(|| {
let _ = TransitionTable::new(huge, 2).unwrap();
});
assert!(result.is_err(), "Should panic on overflow in debug mode");
}
#[test]
fn test_large_but_valid_table() {
let table = TransitionTable::new(10000, 256).unwrap();
assert_eq!(table.transitions().len(), 2_560_000);
}
#[test]
fn test_pattern_id_bounds_in_accept_states() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(0, b'x', 1);
table.add_accept(1, 0);
let jit = JitDfa::compile(&table).unwrap();
let mut matches = vec![Match::from_parts(0, 0, 0); 10];
let count = jit.scan(b"x", &mut matches);
assert_eq!(count, 1);
}
#[test]
fn test_missing_pattern_length_caught() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(0, b'x', 1);
table.accept_states_mut().push((1, 0));
let result = JitDfa::compile(&table);
assert!(result.is_err());
let err = result.unwrap_err().to_string();
assert!(err.contains("has no length defined"));
}
#[test]
fn test_from_bytes_overflow_protection() {
let mut bytes = vec![0u8; 8];
bytes[0..4].copy_from_slice(&u32::MAX.to_le_bytes());
bytes[4..8].copy_from_slice(&2u32.to_le_bytes());
let result = TransitionTable::from_bytes(&bytes);
assert!(
result.is_err(),
"Should reject overflow-inducing dimensions"
);
}
#[test]
fn test_transition_state_index_overflow() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(0, b'x', 1);
table.add_accept(1, 0);
table.set_pattern_length(0, 1);
table.transitions_mut()[0] = u32::MAX;
let result = JitDfa::compile(&table);
assert!(result.is_err());
}
#[test]
fn test_serialization_max_values() {
let table = TransitionTable::new(1000, 256).unwrap();
let bytes = table.to_bytes();
let restored = TransitionTable::from_bytes(&bytes).unwrap();
assert_eq!(restored.state_count(), table.state_count());
assert_eq!(restored.class_count(), table.class_count());
}
#[test]
fn test_compute_ranges_edge_cases() {
let table = TransitionTable::new(0, 256).unwrap();
let ranges = table.compute_ranges();
assert!(ranges.is_empty());
let table_err = TransitionTable::new(1, 0);
assert!(table_err.is_err());
}
#[test]
fn test_minimize_edge_cases() {
let table = TransitionTable::new(0, 256).unwrap();
let minimized = table.minimize();
assert!(minimized.is_none());
let table = TransitionTable::new(1, 256).unwrap();
let minimized = table.minimize();
assert!(minimized.is_none());
}
#[test]
fn test_corrupted_transition_detection() {
let mut table = TransitionTable::new(3, 256).unwrap();
table.set_transition(0, b'a', 1);
table.set_transition(1, b'b', 2);
table.add_accept(2, 0);
table.set_pattern_length(0, 2);
table.transitions_mut()[0] = 100;
let result = JitDfa::compile(&table);
assert!(result.is_err());
}
#[test]
fn test_invalid_accept_state_rejected() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.accept_states_mut().push((999, 0));
table.pattern_lengths_mut().push(1);
let result = JitDfa::compile(&table);
assert!(result.is_err());
}
#[test]
fn test_estimated_code_size_no_overflow() {
let table = TransitionTable::new(10000, 256).unwrap();
let size = table.estimated_code_size();
assert!(size > 0);
assert!(size < usize::MAX / 2);
}
#[test]
fn test_pattern_lengths_resize() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.add_accept(1, 5);
assert!(table.pattern_lengths().len() > 5);
table.set_pattern_length(5, 10);
assert_eq!(table.pattern_lengths()[5], 10);
}
#[test]
fn test_from_bytes_truncated_pattern_lengths() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(0, b'x', 1);
table.add_accept(1, 0);
table.set_pattern_length(0, 1);
let bytes = table.to_bytes();
for truncate_at in 1..=4 {
if bytes.len() > truncate_at {
let truncated = &bytes[..bytes.len() - truncate_at];
let result = TransitionTable::from_bytes(truncated);
assert!(
result.is_err(),
"Should fail when truncated by {}",
truncate_at
);
}
}
}
#[test]
fn test_dfa_state_count_limit() {
let result = TransitionTable::new(65537, 256);
assert!(result.is_err(), "65537 states must be rejected");
}
#[test]
fn test_jit_eligibility_boundary() {
let table_4096 = TransitionTable::new(4096, 256).unwrap();
assert!(table_4096.is_jit_eligible());
let table_4097 = TransitionTable::new(4097, 256).unwrap();
assert!(!table_4097.is_jit_eligible());
}
#[test]
fn test_transition_density_out_of_bounds() {
let table = TransitionTable::new(2, 256).unwrap();
let density = table.transition_density(100);
assert_eq!(density, 0);
}
#[test]
#[cfg(not(debug_assertions))]
fn test_set_transition_out_of_bounds_release() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(100, b'x', 1);
assert_eq!(table.transitions_mut()[0], 0);
}
#[test]
#[cfg(debug_assertions)]
fn test_set_transition_out_of_bounds_debug() {
let mut table = TransitionTable::new(2, 256).unwrap();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
table.set_transition(100, b'x', 1);
}));
assert!(result.is_err());
}
#[test]
fn test_from_bytes_forged_accept_count() {
let mut table = TransitionTable::new(2, 256).unwrap();
table.set_transition(0, b'x', 1);
let mut bytes = table.to_bytes();
let trans_len = 2 * 256 * 4; let accept_count_offset = 8 + trans_len;
if bytes.len() > accept_count_offset + 4 {
bytes[accept_count_offset..accept_count_offset + 4]
.copy_from_slice(&u32::MAX.to_le_bytes());
let result = TransitionTable::from_bytes(&bytes);
assert!(result.is_err());
}
}