use super::*;
use crate::tokenizer::{state::TokenContext, tokens::TokenType};
#[test]
fn char_navigator_creation() {
let source = "Hello World";
let navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.position(), 0);
assert_eq!(navigator.line(), 1);
assert_eq!(navigator.column(), 1);
assert!(!navigator.is_at_end());
}
#[test]
fn char_navigator_advance() {
let source = "abc";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_char().unwrap(), 'a');
navigator.advance_char().unwrap();
assert_eq!(navigator.position(), 1);
assert_eq!(navigator.column(), 2);
assert_eq!(navigator.peek_char().unwrap(), 'b');
navigator.advance_char().unwrap();
assert_eq!(navigator.position(), 2);
assert_eq!(navigator.column(), 3);
assert_eq!(navigator.peek_char().unwrap(), 'c');
navigator.advance_char().unwrap();
assert_eq!(navigator.position(), 3);
assert_eq!(navigator.column(), 4);
assert!(navigator.is_at_end());
}
#[test]
fn char_navigator_newline_tracking() {
let source = "line1\nline2\r\nline3\rline4";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
assert_eq!(navigator.line(), 1);
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 2);
assert_eq!(navigator.column(), 1);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 3);
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 3);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 4);
assert_eq!(navigator.column(), 1);
}
#[test]
fn char_navigator_peek_next() {
let source = "abc";
let navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_next().unwrap(), 'b');
assert_eq!(navigator.position(), 0); }
#[test]
fn char_navigator_peek_at_end() {
let source = "";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert!(navigator.is_at_end());
let result = navigator.peek_char();
assert!(result.is_err());
}
#[test]
fn char_navigator_advance_at_end() {
let source = "a";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
navigator.advance_char().unwrap(); assert!(navigator.is_at_end());
let result = navigator.advance_char();
assert!(result.is_err());
}
#[test]
fn char_navigator_skip_whitespace() {
let source = " \t abc";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
navigator.skip_whitespace();
assert_eq!(navigator.peek_char().unwrap(), 'a');
assert_eq!(navigator.column(), 7); }
#[test]
fn char_navigator_skip_whitespace_with_newlines() {
let source = " \n \t\r\n abc";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
navigator.skip_whitespace();
assert_eq!(navigator.peek_char().unwrap(), '\n'); assert_eq!(navigator.line(), 1); }
#[test]
fn char_navigator_skip_whitespace_empty() {
let source = "";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
navigator.skip_whitespace(); assert!(navigator.is_at_end());
}
#[test]
fn char_navigator_skip_whitespace_only() {
let source = " \t\n ";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
navigator.skip_whitespace();
assert_eq!(navigator.peek_char().unwrap(), '\n'); }
#[test]
fn char_navigator_unicode_characters() {
let source = "こんにちは世界";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_char().unwrap(), 'こ');
navigator.advance_char().unwrap();
assert_eq!(navigator.peek_char().unwrap(), 'ん');
navigator.advance_char().unwrap();
assert_eq!(navigator.peek_char().unwrap(), 'に');
assert!(navigator.position() > 2); }
#[test]
fn char_navigator_emoji() {
let source = "🎬📽️🎭";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_char().unwrap(), '🎬');
navigator.advance_char().unwrap();
assert_eq!(navigator.column(), 2);
assert_eq!(navigator.peek_char().unwrap(), '📽');
}
#[test]
fn token_scanner_creation() {
let source = "test content";
let scanner = TokenScanner::new(source, 0, 1, 1);
assert_eq!(scanner.navigator().position(), 0);
assert_eq!(scanner.navigator().line(), 1);
assert_eq!(scanner.navigator().column(), 1);
}
#[test]
fn token_scanner_section_header() {
let source = "[Script Info]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header().unwrap();
assert_eq!(result, TokenType::SectionHeader);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ']');
}
#[test]
fn token_scanner_section_header_with_spaces() {
let source = "[ V4+ Styles ]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header().unwrap();
assert_eq!(result, TokenType::SectionHeader);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ']');
}
#[test]
fn token_scanner_section_header_empty() {
let source = "[]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header().unwrap();
assert_eq!(result, TokenType::SectionHeader);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ']');
}
#[test]
fn token_scanner_section_header_no_close() {
let source = "[Script Info";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header().unwrap();
assert_eq!(result, TokenType::SectionHeader);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_style_override_simple() {
let source = "{\\b1}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '}');
}
#[test]
fn token_scanner_style_override_complex() {
let source = "{\\c&H0000FF&\\fs20}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '}');
}
#[test]
fn token_scanner_style_override_empty() {
let source = "{}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '}');
}
#[test]
fn token_scanner_style_override_no_close() {
let source = "{\\b1";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_comment_semicolon() {
let source = "; This is a comment";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_comment().unwrap();
assert_eq!(result, TokenType::Comment);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_comment_exclamation() {
let source = "!: This is also a comment";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_comment().unwrap();
assert_eq!(result, TokenType::Comment);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_comment_with_newline() {
let source = "; Comment\nNext line";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_comment().unwrap();
assert_eq!(result, TokenType::Comment);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '\n');
}
#[test]
fn token_scanner_text_simple() {
let source = "Hello World";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_with_delimiters() {
let source = "Hello:World";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ':');
}
#[test]
fn token_scanner_text_field_value() {
let source = "Test Script Title";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_empty() {
let source = "";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_whitespace_only() {
let source = " \t ";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_unicode() {
let source = "こんにちは世界";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_with_special_chars() {
let source = "Test with émojis 🎬 and symbols ©®™";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_text_stops_at_brace() {
let source = "Text{override}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '{');
}
#[test]
fn token_scanner_text_stops_at_comma() {
let source = "Value1,Value2";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ',');
}
#[test]
fn token_scanner_text_stops_at_newline() {
let source = "Line1\nLine2";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '\n');
}
#[test]
fn token_scanner_navigator_access() {
let source = "test";
let scanner = TokenScanner::new(source, 0, 1, 1);
let navigator = scanner.navigator();
assert_eq!(navigator.position(), 0);
}
#[test]
fn token_scanner_navigator_mut_access() {
let source = "test";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let navigator = scanner.navigator_mut();
navigator.advance_char().unwrap();
assert_eq!(navigator.position(), 1);
}
#[test]
fn token_scanner_multiple_scans() {
let source = "[Section]\nField: Value";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result1 = scanner.scan_section_header().unwrap();
assert_eq!(result1, TokenType::SectionHeader);
scanner.navigator_mut().advance_char().unwrap();
scanner.navigator_mut().advance_char().unwrap();
let result2 = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result2, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ':');
}
#[test]
fn char_navigator_peek_with_lookahead() {
let source = "abc";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_char().unwrap(), 'a');
assert_eq!(navigator.peek_char().unwrap(), 'a');
navigator.advance_char().unwrap();
assert_eq!(navigator.position(), 1);
assert_eq!(navigator.peek_char().unwrap(), 'b');
}
#[test]
fn char_navigator_column_reset_on_newline() {
let source = "abc\ndef";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
for _ in 0..3 {
navigator.advance_char().unwrap();
}
assert_eq!(navigator.column(), 4);
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 2);
assert_eq!(navigator.column(), 1);
}
#[test]
fn token_scanner_error_handling() {
let source = "";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document);
assert!(result.is_ok());
}
#[test]
fn char_navigator_bounds_checking() {
let source = "a";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
assert!(!navigator.is_at_end());
navigator.advance_char().unwrap();
assert!(navigator.is_at_end());
let result = navigator.peek_char();
assert!(result.is_err());
let result = navigator.advance_char();
assert!(result.is_err());
}
#[test]
fn mixed_line_endings_handling() {
let source = "line1\r\nline2\nline3\rline4";
let mut navigator = CharNavigator::new(source, 0, 1, 1);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 2);
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 2);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 3);
for _ in 0..5 {
navigator.advance_char().unwrap();
}
navigator.advance_char().unwrap(); assert_eq!(navigator.line(), 4);
}
#[test]
fn is_hex_value_plain_hex() {
assert!(TokenScanner::is_hex_value("FF00FF"));
assert!(TokenScanner::is_hex_value("0000FF"));
assert!(TokenScanner::is_hex_value("AABBCC"));
assert!(TokenScanner::is_hex_value("123456"));
}
#[test]
fn is_hex_value_with_prefix_suffix() {
assert!(TokenScanner::is_hex_value("&HFF0000&"));
assert!(TokenScanner::is_hex_value("&H00FF00&"));
assert!(TokenScanner::is_hex_value("&H0000FF&"));
}
#[test]
fn is_hex_value_with_prefix_no_suffix() {
assert!(TokenScanner::is_hex_value("&HFF0000"));
assert!(TokenScanner::is_hex_value("&H00FF00"));
assert!(TokenScanner::is_hex_value("&H0000FF"));
}
#[test]
fn is_hex_value_invalid_cases() {
assert!(!TokenScanner::is_hex_value(""));
assert!(!TokenScanner::is_hex_value("G00000"));
assert!(!TokenScanner::is_hex_value("FF00F")); assert!(!TokenScanner::is_hex_value("&H&"));
assert!(!TokenScanner::is_hex_value("&HG0000&"));
assert!(!TokenScanner::is_hex_value("&HFF00F&")); assert!(!TokenScanner::is_hex_value("0x123456")); }
#[test]
fn is_hex_value_edge_cases() {
assert!(!TokenScanner::is_hex_value("&H&"));
assert!(!TokenScanner::is_hex_value("&H"));
assert!(!TokenScanner::is_hex_value("abc")); assert!(TokenScanner::is_hex_value("ab")); }
#[test]
fn scan_field_value_basic() {
let source = "Test Script Title";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_field_value_with_colons() {
let source = "0:00:30.50";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Number);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_field_value_stops_at_comma() {
let source = "Value1,Value2";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ',');
}
#[test]
fn scan_field_value_stops_at_newline() {
let source = "Value\nNext line";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '\n');
}
#[test]
fn scan_field_value_stops_at_brace() {
let source = "Text{override}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '{');
}
#[test]
fn scan_field_value_stops_at_bracket() {
let source = "Text[section]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '[');
}
#[test]
fn scan_field_value_numeric_content() {
let source = "123.45";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Number);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_field_value_negative_number() {
let source = "-123.45";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Number);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_field_value_empty() {
let source = "";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_field_value_whitespace_only() {
let source = " ";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value().unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_section_header_context() {
let source = "Script Info";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::SectionHeader).unwrap();
assert_eq!(result, TokenType::SectionName);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_hex_detection() {
let source = "&HFF0000&";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::HexValue);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_number_detection() {
let source = "123.456";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Number);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_negative_number() {
let source = "-456.789";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Number);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_field_value_context_with_colon() {
let source = "0:01:23.45,next";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ',');
}
#[test]
fn scan_text_document_context_stops_at_colon() {
let source = "Field:Value";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), ':');
}
#[test]
fn scan_text_stops_at_semicolon_in_document() {
let source = "Text;comment";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn scan_text_semicolon_not_delimiter_in_field_value() {
let source = "Text;more text";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(result, TokenType::Text);
assert!(scanner.navigator().is_at_end());
}
#[test]
fn token_scanner_style_override_nested_braces() {
let source = "{\\t({\\an8})\\b1}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '}');
}
#[test]
fn token_scanner_style_override_multiple_levels() {
let source = "{{inner}outer}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override().unwrap();
assert_eq!(result, TokenType::OverrideBlock);
assert_eq!(scanner.navigator_mut().peek_char().unwrap(), '}');
}
#[test]
fn char_navigator_peek_next_at_end() {
let source = "a";
let navigator = CharNavigator::new(source, 0, 1, 1);
let result = navigator.peek_next();
assert!(result.is_err());
}
#[test]
fn char_navigator_peek_next_with_unicode() {
let source = "こんにちは";
let navigator = CharNavigator::new(source, 0, 1, 1);
assert_eq!(navigator.peek_next().unwrap(), 'ん');
}