use std::cmp::Ordering;
use proptest::prelude::*;
use token_lang::{Span, Spanned, Symbol, Token, TokenKind};
fn any_span() -> impl Strategy<Value = Span> {
(any::<u32>(), any::<u32>()).prop_map(|(a, b)| {
let (lo, hi) = if a <= b { (a, b) } else { (b, a) };
Span::new(lo, hi)
})
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Classified {
trivia: bool,
eof: bool,
sym: Option<u32>,
}
impl TokenKind for Classified {
fn is_trivia(&self) -> bool {
self.trivia
}
fn is_eof(&self) -> bool {
self.eof
}
fn symbol(&self) -> Option<Symbol> {
self.sym.and_then(Symbol::from_u32)
}
}
fn any_classified() -> impl Strategy<Value = Classified> {
(
any::<bool>(),
any::<bool>(),
proptest::option::of(1..=u32::MAX),
)
.prop_map(|(trivia, eof, sym)| Classified { trivia, eof, sym })
}
proptest! {
#[test]
fn new_preserves_kind_and_span(kind in any::<i64>(), span in any_span()) {
let tok = Token::new(kind, span);
prop_assert_eq!(*tok.kind(), kind);
prop_assert_eq!(tok.span(), span);
}
#[test]
fn spanned_round_trip(kind in any::<i64>(), span in any_span()) {
let tok = Token::new(kind, span);
let spanned: Spanned<i64> = tok.into();
prop_assert_eq!(spanned, Spanned::new(span, kind));
prop_assert_eq!(Token::from(spanned), tok);
}
#[test]
fn map_transforms_kind_keeps_span(kind in any::<i32>(), span in any_span()) {
let mapped = Token::new(kind, span).map(|k| i64::from(k) * 2);
prop_assert_eq!(*mapped.kind(), i64::from(kind) * 2);
prop_assert_eq!(mapped.span(), span);
}
#[test]
fn orders_by_span_then_kind(
ka in any::<i64>(), sa in any_span(),
kb in any::<i64>(), sb in any_span(),
) {
let a = Token::new(ka, sa);
let b = Token::new(kb, sb);
prop_assert_eq!(a.cmp(&b), (sa, ka).cmp(&(sb, kb)));
}
#[test]
fn sorting_yields_non_decreasing_spans(
items in proptest::collection::vec((any::<i64>(), any_span()), 0..64),
) {
let mut tokens: Vec<Token<i64>> =
items.into_iter().map(|(k, s)| Token::new(k, s)).collect();
tokens.sort();
for pair in tokens.windows(2) {
prop_assert!(matches!(
pair[0].span().start().cmp(&pair[1].span().start()),
Ordering::Less | Ordering::Equal
));
}
}
#[test]
fn delegators_match_kind(kind in any_classified(), span in any_span()) {
let tok = Token::new(kind, span);
prop_assert_eq!(tok.is_trivia(), kind.is_trivia());
prop_assert_eq!(tok.is_eof(), kind.is_eof());
prop_assert_eq!(tok.symbol(), kind.symbol());
}
#[test]
fn display_is_kind_at_span(kind in any::<i64>(), span in any_span()) {
let tok = Token::new(kind, span);
prop_assert_eq!(tok.to_string(), format!("{} @ {}", kind, span));
}
}