#![cfg(feature = "arbitrary")]
use nybbles::Nibbles;
use proptest::{collection::vec, prelude::*};
fn valid_nibbles(nibbles: &Nibbles) -> bool {
nibbles.to_vec().iter().all(|&nibble| nibble <= 0xf)
}
proptest! {
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn pack_unpack_roundtrip(input in vec(any::<u8>(), 0..=32)) {
let nibbles = Nibbles::unpack(&input);
prop_assert!(valid_nibbles(&nibbles));
let packed = nibbles.pack();
prop_assert_eq!(&packed[..], input);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn from_nibbles_roundtrip(nibbles_data in vec(0u8..16, 0..=64)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
prop_assert_eq!(nibbles.to_vec(), &nibbles_data[..]);
prop_assert_eq!(nibbles.len(), nibbles_data.len());
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn push_pop_roundtrip(
initial_nibbles in vec(0u8..16, 0..64),
extra_nibble in 0u8..16
) {
let mut nibbles = Nibbles::from_nibbles(&initial_nibbles);
let original_len = nibbles.len();
nibbles.push(extra_nibble);
prop_assert_eq!(nibbles.len(), original_len + 1);
prop_assert_eq!(nibbles.last(), Some(extra_nibble));
let popped = nibbles.pop();
prop_assert_eq!(popped, Some(extra_nibble));
prop_assert_eq!(nibbles.len(), original_len);
prop_assert_eq!(nibbles.to_vec(), &initial_nibbles[..]);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn get_byte_consistency(nibbles_data in vec(0u8..16, 2..=64)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
for i in 0..nibbles_data.len()-1 {
let expected = (nibbles_data[i] << 4) | nibbles_data[i + 1];
prop_assert_eq!(nibbles.get_byte(i), Some(expected));
}
if nibbles_data.len() >= 2 {
prop_assert!(nibbles.get_byte(nibbles_data.len()-2).is_some());
}
prop_assert_eq!(nibbles.get_byte(nibbles_data.len()-1), None);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn first_last_properties(nibbles_data in vec(0u8..16, 1..=64)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
prop_assert_eq!(nibbles.first(), Some(nibbles_data[0]));
prop_assert_eq!(nibbles.last(), Some(*nibbles_data.last().unwrap()));
let empty = Nibbles::new();
prop_assert_eq!(empty.first(), None);
prop_assert_eq!(empty.last(), None);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn set_at_properties(
nibbles_data in vec(0u8..16, 1..=64),
new_value in 0u8..16
) {
let len = nibbles_data.len();
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
nibbles.set_at(0, new_value);
prop_assert_eq!(nibbles.get_unchecked(0), new_value);
prop_assert_eq!(nibbles.len(), len);
if len > 1 {
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
nibbles.set_at(len - 1, new_value);
prop_assert_eq!(nibbles.get_unchecked(len - 1), new_value);
prop_assert_eq!(nibbles.len(), len);
#[allow(clippy::needless_range_loop)]
for i in 0..len-1 {
prop_assert_eq!(nibbles.get_unchecked(i), nibbles_data[i]);
}
}
if len > 2 {
let mid = len / 2;
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
nibbles.set_at(mid, new_value);
prop_assert_eq!(nibbles.get_unchecked(mid), new_value);
prop_assert_eq!(nibbles.len(), len);
for (i, &original) in nibbles_data.iter().enumerate() {
if i != mid {
prop_assert_eq!(nibbles.get_unchecked(i), original);
}
}
}
}
}
proptest! {
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn slice_consistency(nibbles_data in vec(0u8..16, 1..=64)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
let len = nibbles_data.len();
let full_slice = nibbles.slice(..);
prop_assert_eq!(full_slice.to_vec(), &nibbles_data[..]);
if len > 1 {
let mid = len / 2;
let first_half = nibbles.slice(..mid);
prop_assert_eq!(first_half.to_vec(), &nibbles_data[..mid]);
let second_half = nibbles.slice(mid..);
prop_assert_eq!(second_half.to_vec(), &nibbles_data[mid..]);
if mid + 1 < len {
let middle_slice = nibbles.slice(mid..mid+1);
prop_assert_eq!(middle_slice.to_vec(), &nibbles_data[mid..mid+1]);
}
}
let empty_slice = nibbles.slice(0..0);
prop_assert_eq!(empty_slice.len(), 0);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn extend_consistency(
nibbles1 in vec(0u8..16, 0..=32),
nibbles2 in vec(0u8..16, 0..=32)
) {
let mut result = Nibbles::from_nibbles(&nibbles1);
let other = Nibbles::from_nibbles(&nibbles2);
result.extend(&other);
let expected: Vec<u8> = nibbles1.into_iter().chain(nibbles2.into_iter()).collect();
prop_assert_eq!(result.to_vec(), &expected[..]);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn join_consistency(
nibbles1 in vec(0u8..16, 0..=32),
nibbles2 in vec(0u8..16, 0..=32)
) {
let n1 = Nibbles::from_nibbles(&nibbles1);
let n2 = Nibbles::from_nibbles(&nibbles2);
let joined = n1.join(&n2);
let expected: Vec<u8> = nibbles1.into_iter().chain(nibbles2.into_iter()).collect();
prop_assert_eq!(joined.to_vec(), &expected[..]);
prop_assert_eq!(joined.len(), n1.len() + n2.len());
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn truncate_consistency(nibbles_data in vec(0u8..16, 1..=64)) {
let original_len = nibbles_data.len();
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
nibbles.truncate(0);
prop_assert_eq!(nibbles.len(), 0);
if original_len > 1 {
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
let new_len = original_len / 2;
nibbles.truncate(new_len);
prop_assert_eq!(nibbles.len(), new_len);
prop_assert_eq!(nibbles.to_vec(), &nibbles_data[..new_len]);
}
let mut nibbles = Nibbles::from_nibbles(&nibbles_data);
nibbles.truncate(original_len);
prop_assert_eq!(nibbles.len(), original_len);
prop_assert_eq!(nibbles.to_vec(), &nibbles_data[..]);
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn increment_properties(nibbles_data in vec(0u8..16, 1..=32)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
if let Some(incremented) = nibbles.increment() {
prop_assert_eq!(incremented.len(), nibbles.len());
prop_assert!(incremented > nibbles);
let all_f = nibbles_data.iter().all(|&x| x == 0xf);
if !all_f {
prop_assert!(true); }
} else {
let all_f = nibbles_data.iter().all(|&x| x == 0xf);
prop_assert!(all_f, "increment() returned None but input wasn't all 0xF");
}
}
}
proptest! {
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn common_prefix_length_properties(
nibbles1 in vec(0u8..16, 0..=64),
nibbles2 in vec(0u8..16, 0..=64)
) {
let n1 = Nibbles::from_nibbles(&nibbles1);
let n2 = Nibbles::from_nibbles(&nibbles2);
let common_len = n1.common_prefix_length(&n2);
prop_assert_eq!(common_len, n2.common_prefix_length(&n1));
prop_assert!(common_len <= n1.len());
prop_assert!(common_len <= n2.len());
if common_len > 0 && !nibbles1.is_empty() && !nibbles2.is_empty() {
for i in 0..common_len {
prop_assert_eq!(nibbles1[i], nibbles2[i]);
}
}
if common_len < n1.len() && common_len < n2.len() {
prop_assert_ne!(nibbles1[common_len], nibbles2[common_len]);
}
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn starts_with_consistency(nibbles_data in vec(0u8..16, 1..=64)) {
let nibbles = Nibbles::from_nibbles(&nibbles_data);
let len = nibbles_data.len();
prop_assert!(nibbles.starts_with(&Nibbles::default()));
prop_assert!(nibbles.starts_with(&nibbles));
if len > 1 {
let prefix_len = len / 2;
let prefix = nibbles.slice(..prefix_len);
prop_assert!(nibbles.starts_with(&prefix));
prop_assert!(nibbles.starts_with(&prefix));
if prefix_len > 0 {
let mut different_prefix = prefix;
different_prefix.set_at(0, (different_prefix.get_unchecked(0) + 1) % 16);
if different_prefix != prefix {
prop_assert!(!nibbles.starts_with(&different_prefix));
}
}
}
}
#[test]
#[cfg_attr(miri, ignore = "no proptest")]
fn ordering_properties(
nibbles1 in vec(0u8..16, 0..=32),
nibbles2 in vec(0u8..16, 0..=32)
) {
let n1 = Nibbles::from_nibbles(&nibbles1);
let n2 = Nibbles::from_nibbles(&nibbles2);
let slice_cmp = nibbles1.cmp(&nibbles2);
let nibbles_cmp = n1.cmp(&n2);
prop_assert_eq!(slice_cmp, nibbles_cmp);
prop_assert_eq!(n1.cmp(&n1), core::cmp::Ordering::Equal);
prop_assert_eq!(n1.cmp(&n2), n2.cmp(&n1).reverse());
}
}