use super::*;
use crate::tokenizer::{state::TokenContext, tokens::TokenType};
#[cfg(not(feature = "std"))]
use alloc::vec;
#[test]
fn char_navigator_whitespace_at_end() {
let source = "text ";
let mut nav = CharNavigator::new(source, 4, 1, 5); nav.skip_whitespace();
assert!(nav.is_at_end());
}
#[test]
fn char_navigator_mixed_newlines() {
let source = "\r\n\n\r";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 2);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 2);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 3);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 4);
}
#[test]
fn token_scanner_empty_span_handling() {
let source = ",";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), 0); }
#[test]
fn token_scanner_field_value_time_format() {
let source = "1:23:45.67";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_field_value().unwrap();
assert_eq!(token_type, TokenType::Number);
assert_eq!(scanner.navigator().position(), source.len());
}
#[test]
fn char_navigator_position_consistency() {
let source = "café🎭";
let mut nav = CharNavigator::new(source, 0, 1, 1);
let start_pos = nav.position();
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 1);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 2);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 3);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 5);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 9);
}
#[test]
fn token_scanner_all_contexts_coverage() {
let contexts = vec![
TokenContext::Document,
TokenContext::SectionHeader,
TokenContext::FieldValue,
TokenContext::StyleOverride,
];
for context in contexts {
let source = "test:value,more";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(context).unwrap();
match context {
TokenContext::SectionHeader => assert_eq!(token_type, TokenType::SectionName),
_ => assert!(matches!(
token_type,
TokenType::Text | TokenType::Number | TokenType::HexValue
)),
}
}
}
#[test]
fn char_navigator_column_reset_on_newlines() {
let source = "long line text\nshort\n";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..14 {
nav.advance_char().unwrap();
}
assert_eq!(nav.column(), 15);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
for _ in 0..5 {
nav.advance_char().unwrap();
}
assert_eq!(nav.column(), 6);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 3);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_utf8_error_handling() {
let source = "valid\x7F\x7E";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
let result = nav.advance_char();
match result {
Ok(_) | Err(_) => {
assert!(nav.position() > 0);
}
}
}
#[test]
fn char_navigator_peek_char_caching_coverage() {
let source = "abc";
let mut nav = CharNavigator::new(source, 0, 1, 1);
let first_peek = nav.peek_char();
assert_eq!(first_peek, Ok('a'));
let second_peek = nav.peek_char();
assert_eq!(second_peek, Ok('a'));
assert!(nav.advance_char().is_ok());
let third_peek = nav.peek_char();
assert_eq!(third_peek, Ok('b'));
}