use super::*;
use crate::component::cell::{Cell, SortKey};
use proptest::prelude::*;
use std::cmp::Ordering;
use std::time::{Duration, SystemTime};
fn arb_sort_key() -> impl Strategy<Value = SortKey> {
prop_oneof![
any::<String>().prop_map(|s| SortKey::String(s.into())),
any::<i64>().prop_map(SortKey::I64),
any::<u64>().prop_map(SortKey::U64),
any::<f64>().prop_map(SortKey::F64),
any::<bool>().prop_map(SortKey::Bool),
any::<u32>().prop_map(|n| SortKey::Duration(Duration::from_secs(u64::from(n)))),
any::<u32>().prop_map(|n| SortKey::DateTime(
SystemTime::UNIX_EPOCH + Duration::from_secs(u64::from(n))
)),
Just(SortKey::None),
]
}
proptest! {
#[test]
fn sort_key_compare_antisymmetric(a in arb_sort_key(), b in arb_sort_key()) {
let cmp_ab = SortKey::compare(&a, &b);
let cmp_ba = SortKey::compare(&b, &a);
prop_assert_eq!(cmp_ab, cmp_ba.reverse());
}
#[test]
fn sort_key_compare_transitive(
a in arb_sort_key(),
b in arb_sort_key(),
c in arb_sort_key(),
) {
let ab = SortKey::compare(&a, &b);
let bc = SortKey::compare(&b, &c);
let ac = SortKey::compare(&a, &c);
if matches!(ab, Ordering::Less | Ordering::Equal)
&& matches!(bc, Ordering::Less | Ordering::Equal)
{
prop_assert!(matches!(ac, Ordering::Less | Ordering::Equal));
}
}
}
#[derive(Clone, Debug, PartialEq)]
struct PropRow {
id: u32,
a: i64,
b: i64,
}
impl TableRow for PropRow {
fn cells(&self) -> Vec<Cell> {
vec![
Cell::int(self.a),
Cell::int(self.b),
Cell::uint(u64::from(self.id)),
]
}
}
fn prop_columns() -> Vec<Column> {
vec![
Column::new("A", Constraint::Length(5)).sortable(),
Column::new("B", Constraint::Length(5)).sortable(),
Column::new("ID", Constraint::Length(5)).sortable(),
]
}
proptest! {
#[test]
fn sort_is_stable(
keys in proptest::collection::vec(any::<i64>(), 1..50)
) {
let rows: Vec<PropRow> = keys.iter().enumerate()
.map(|(i, &k)| PropRow { id: i as u32, a: k, b: 0 })
.collect();
let mut state = TableState::new(rows.clone(), prop_columns());
let _ = Table::<PropRow>::update(&mut state, TableMessage::SortAsc(0));
let order: Vec<PropRow> = state.display_order.iter()
.map(|&i| rows[i].clone())
.collect();
for window in order.windows(2) {
let (l, r) = (&window[0], &window[1]);
if l.a == r.a {
prop_assert!(
l.id < r.id,
"Stable sort violated at {:?} vs {:?}", l, r,
);
}
}
}
#[test]
fn multi_column_sort_respects_priority(
rows_in in proptest::collection::vec((any::<i8>(), any::<i8>()), 2..40)
) {
let rows: Vec<PropRow> = rows_in.iter().enumerate()
.map(|(i, &(a, b))| PropRow {
id: i as u32,
a: i64::from(a),
b: i64::from(b),
})
.collect();
let mut state = TableState::new(rows.clone(), prop_columns());
let _ = Table::<PropRow>::update(&mut state, TableMessage::SortAsc(0));
let _ = Table::<PropRow>::update(&mut state, TableMessage::AddSortDesc(1));
let order: Vec<PropRow> = state.display_order.iter()
.map(|&i| rows[i].clone())
.collect();
for window in order.windows(2) {
let (l, r) = (&window[0], &window[1]);
prop_assert!(
l.a <= r.a,
"Primary order violated: {:?} -> {:?}", l, r,
);
if l.a == r.a {
prop_assert!(
l.b >= r.b,
"Tiebreaker desc violated: {:?} -> {:?}", l, r,
);
}
}
}
}