use regex::Regex;
use sashite_sin::{Identifier, Side};
struct Token {
text: String,
letter: char,
side: Side,
}
fn every_token() -> Vec<Token> {
let mut tokens = Vec::with_capacity(52);
for upper in b'A'..=b'Z' {
for (side, byte) in [(Side::First, upper), (Side::Second, upper + 32)] {
tokens.push(Token {
text: (byte as char).to_string(),
letter: upper as char,
side,
});
}
}
tokens
}
#[test]
fn the_closed_domain_has_52_tokens() {
let tokens = every_token();
assert_eq!(tokens.len(), 52);
let mut texts: Vec<&str> = tokens.iter().map(|t| t.text.as_str()).collect();
texts.sort_unstable();
texts.dedup();
assert_eq!(texts.len(), 52);
}
#[test]
fn every_token_round_trips_and_decodes_correctly() {
for token in every_token() {
let id = Identifier::parse(&token.text)
.unwrap_or_else(|e| panic!("parse {:?} failed: {e:?}", token.text));
assert_eq!(id.letter().as_char(), token.letter, "{:?}", token.text);
assert_eq!(id.side(), token.side, "{:?}", token.text);
assert_eq!(id.encode().as_str(), token.text);
assert_eq!(id.to_string(), token.text);
assert_eq!(id.to_char(), token.text.chars().next().unwrap());
assert!(Identifier::is_valid(&token.text));
let rebuilt = Identifier::new(id.letter(), id.side());
assert_eq!(rebuilt, id);
assert_eq!(rebuilt.encode().as_str(), token.text);
}
}
fn spec_regex() -> Regex {
Regex::new(r"\A[A-Za-z]\z").expect("valid regex")
}
fn assert_agreement(s: &str, re: &Regex) {
assert_eq!(
Identifier::is_valid(s),
re.is_match(s),
"parser and spec regex disagree on {s:?}",
);
}
#[test]
fn parser_matches_spec_regex_over_all_ascii_inputs() {
let re = spec_regex();
let mut buf = [0u8; 2];
assert_agreement("", &re);
for a in 0u8..=127 {
buf[0] = a;
assert_agreement(std::str::from_utf8(&buf[..1]).unwrap(), &re);
for b in 0u8..=127 {
buf[1] = b;
assert_agreement(std::str::from_utf8(&buf[..2]).unwrap(), &re);
}
}
}
#[test]
fn parser_matches_spec_regex_on_non_ascii_and_long_inputs() {
let re = spec_regex();
for s in [
"é", "W♔", "♔", "WWWW", "Wx", " ", "\n", "W\n", ] {
assert_agreement(s, &re);
}
}