ass-core 0.1.1

High-performance ASS subtitle format parser and analyzer
Documentation
//! Comprehensive coverage tests for parser/main.rs
//!
//! This module contains tests specifically designed to achieve complete coverage
//! of the main parser functionality, focusing on error paths and edge cases.

use ass_core::parser::{IssueCategory, IssueSeverity, Script};

#[cfg(test)]
mod parser_main_coverage_tests {
    use super::*;

    #[test]
    fn test_input_size_limit_exceeded() {
        // Test the input size limit check (lines 63-67, 70)
        // Create a string that exceeds the 50MB limit
        let large_content = "A".repeat(51 * 1024 * 1024); // 51MB
        let script = Script::parse(&large_content);

        // Should succeed but have issues about size limit
        assert!(script.is_ok());
        let script = script.unwrap();
        let issues = script.issues();

        // Should have a security issue about input size limit
        assert!(issues.iter().any(|issue| {
            issue.severity == IssueSeverity::Error
                && issue.category == IssueCategory::Security
                && issue.message.contains("Input size limit exceeded")
        }));
    }

    #[test]
    fn test_bom_validation_warning() {
        // Test BOM validation warning (lines 75-79)
        // Create content with invalid BOM or BOM-related issues
        let mut content_with_invalid_bom = vec![0xFF, 0xFE]; // Invalid BOM
        content_with_invalid_bom.extend_from_slice(b"[Script Info]\nTitle: Test\n");

        // Convert to string, handling potential encoding issues
        let content_str = String::from_utf8_lossy(&content_with_invalid_bom);
        let script = Script::parse(&content_str);

        assert!(script.is_ok());
        let script = script.unwrap();
        let issues = script.issues();

        // Check if BOM validation issues are present
        // Note: This might not trigger the exact path due to UTF-8 conversion,
        // but it tests the general BOM handling logic
        println!("Issues found: {issues:?}");
    }

    #[test]
    fn test_bom_handling_with_utf8_bom() {
        // Test with UTF-8 BOM
        let content_with_utf8_bom = "\u{FEFF}[Script Info]\nTitle: Test\n";
        let script = Script::parse(content_with_utf8_bom);

        assert!(script.is_ok());
        let script = script.unwrap();

        // Should handle UTF-8 BOM gracefully
        // Script should parse successfully with BOM
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_with_empty_input() {
        // Test empty input edge case
        let script = Script::parse("");
        assert!(script.is_ok());

        let script = script.unwrap();
        assert!(script.sections().is_empty());
    }

    #[test]
    fn test_parser_with_whitespace_only() {
        // Test whitespace-only input
        let script = Script::parse("   \n\t\r\n   ");
        assert!(script.is_ok());

        let script = script.unwrap();
        // Should have minimal content
        assert!(script.sections().is_empty() || script.sections().len() <= 1);
    }

    #[test]
    fn test_parser_with_malformed_sections() {
        // Test various malformed section headers to trigger error paths
        let malformed_inputs = vec![
            "[Invalid Section\n", // Missing closing bracket
            "[]Section]\n",       // Empty section name
            "[Script Info\nNo closing bracket",
            "[Events]\nFormat: Invalid\nDialogue: malformed",
            "[V4+ Styles]\nFormat: Invalid\nStyle: malformed",
        ];

        for input in malformed_inputs {
            let script = Script::parse(input);
            assert!(
                script.is_ok(),
                "Parser should not fail on malformed input: {input}"
            );

            let script = script.unwrap();
            // Should have parsing issues
            println!(
                "Malformed input '{}' produced {} issues",
                input.chars().take(20).collect::<String>(),
                script.issues().len()
            );
        }
    }

    #[test]
    fn test_parser_with_extremely_long_lines() {
        // Test parser behavior with extremely long lines
        let long_line = "A".repeat(100_000);
        let content = format!("[Script Info]\nTitle: {long_line}\n");

        let script = Script::parse(&content);
        assert!(script.is_ok());

        let script = script.unwrap();
        // Should handle long lines without crashing
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_with_unicode_edge_cases() {
        // Test various Unicode edge cases
        let unicode_content = "[Script Info]\n\
                              Title: Test with 🎵 emojis and ñáéíóú accents\n\
                              ScriptType: v4.00+\n\
                              \n\
                              [V4+ Styles]\n\
                              Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n\
                              Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,30,30,30,1\n\
                              \n\
                              [Events]\n\
                              Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n\
                              Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Unicode text: 日本語 العربية Русский\n";

        let script = Script::parse(unicode_content);
        assert!(script.is_ok());

        let script = script.unwrap();
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_error_recovery() {
        // Test parser's ability to recover from errors and continue parsing
        let content_with_errors = "[Script Info]\n\
                                  Title: Test\n\
                                  Invalid: Line\n\
                                  \n\
                                  [Invalid Section Name]\n\
                                  Some content\n\
                                  \n\
                                  [V4+ Styles]\n\
                                  Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n\
                                  Style: Default,Arial,16,&Hffffff,&Hffffff,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,30,30,30,1\n\
                                  Invalid: Style line\n\
                                  \n\
                                  [Events]\n\
                                  Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n\
                                  Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Valid event\n\
                                  Invalid: Event line\n";

        let script = Script::parse(content_with_errors);
        assert!(script.is_ok());

        let script = script.unwrap();

        // Should still parse valid parts
        assert!(!script.sections().is_empty());

        // Should have parsing issues for invalid parts
        assert!(!script.issues().is_empty());

        // Should have parsed at least some sections
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_with_mixed_line_endings() {
        // Test handling of mixed line endings (CR, LF, CRLF)
        let content = "[Script Info]\rTitle: Test\r\nScriptType: v4.00+\n\n[Events]\r\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\nDialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Mixed line endings\r";

        let script = Script::parse(content);
        assert!(script.is_ok());

        let script = script.unwrap();
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_memory_efficiency() {
        // Test that parser doesn't consume excessive memory for reasonable inputs
        let mut reasonable_content = String::from("[Script Info]\n");
        reasonable_content.push_str(&"Title: Test\n".repeat(1000));
        reasonable_content.push_str("\n[Events]\n");
        reasonable_content.push_str(
            "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
        );
        reasonable_content.push_str(
            &"Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Event text\n".repeat(1000),
        );

        let script = Script::parse(&reasonable_content);
        assert!(script.is_ok());

        let script = script.unwrap();
        // Should handle reasonable amounts of repetitive content
        assert!(!script.sections().is_empty());
    }

    #[test]
    fn test_parser_version_detection() {
        // Test different ASS version strings and formats
        let version_tests = vec![
            "[Script Info]\nScriptType: v4.00+\n",
            "[Script Info]\nScriptType: v4.00\n",
            "[Script Info]\nScriptType: ASS\n",
            "[Script Info]\n!: This is v4.00+ style\n",
            "[Script Info]\nTitle: Test\n", // No explicit version
        ];

        for content in version_tests {
            let script = Script::parse(content);
            assert!(script.is_ok(), "Failed to parse version test: {content}");

            let script = script.unwrap();
            // Should detect some version information
            println!(
                "Version detected for '{}': {:?}",
                content.lines().nth(1).unwrap_or(""),
                script.version()
            );
        }
    }

    #[test]
    fn test_parser_with_binary_data() {
        // Test parser behavior when encountering binary-like data
        let mut binary_content = b"[Script Info]\nTitle: Test\n".to_vec();
        binary_content.extend_from_slice(&[0x00, 0x01, 0x02, 0xFF, 0xFE, 0xFD]);
        binary_content.extend_from_slice(b"\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n");

        let content_str = String::from_utf8_lossy(&binary_content);
        let script = Script::parse(&content_str);

        assert!(script.is_ok());
        let script = script.unwrap();

        // Should handle binary data gracefully (likely with replacement characters)
        assert!(!script.sections().is_empty());
    }
}