use ass_core::{
parser::{IssueCategory, IssueSeverity},
utils::errors::{encoding::validate_bom_handling, resource::check_input_size_limit},
Script, Section,
};
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[test]
fn test_input_size_limit_exceeded() {
const TEST_LIMIT: usize = 1024;
let large_source = "x".repeat(TEST_LIMIT + 1);
let result = check_input_size_limit(large_source.len(), TEST_LIMIT);
assert!(result.is_err());
let normal_script = "[Script Info]\nTitle: Test\n[Events]\nDialogue: 0:00:00.00,0:00:05.00,Default,,0,0,0,,Test";
let script = Script::parse(normal_script).expect("Script parsing should work");
assert!(!script
.issues()
.iter()
.any(|issue| matches!(issue.category, IssueCategory::Security)));
}
#[test]
#[allow(clippy::similar_names)]
fn test_invalid_bom_handling() {
let utf16_le_bytes = [0xFF, 0xFE, b'[', b'S', 0x00, b'c', 0x00];
let result = validate_bom_handling(&utf16_le_bytes);
assert!(result.is_err());
let utf16_be_bytes = [0xFE, 0xFF, 0x00, b'[', 0x00, b'S'];
let result = validate_bom_handling(&utf16_be_bytes);
assert!(result.is_err());
let malformed_bom = [0xEF, 0xBB, b'X']; let result = validate_bom_handling(&malformed_bom);
assert!(result.is_err());
let source_with_utf16_bom = String::from_utf8_lossy(&utf16_le_bytes);
let script = Script::parse(&source_with_utf16_bom).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.category, IssueCategory::Format)));
}
#[test]
fn test_malformed_section_error_recovery() {
let malformed_script = r"
[Script Info]
Title: Test
[Malformed Section
This section has no closing bracket
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(malformed_script).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| { matches!(issue.category, IssueCategory::Structure) }));
assert!(!script.sections().is_empty());
}
#[test]
fn test_expected_section_header_error() {
let script_no_bracket = r"
Script Info]
Title: Test
";
let script = Script::parse(script_no_bracket).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.severity, IssueSeverity::Error)));
}
#[test]
fn test_unclosed_section_header_error() {
let script_unclosed = r"
[Script Info
Title: Test
";
let script = Script::parse(script_unclosed).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.severity, IssueSeverity::Error)));
}
#[test]
fn test_fonts_section_parsing() {
let script_with_fonts = r"
[Script Info]
Title: Test
[Fonts]
fontname: Arial
0
M 0 0 L 100 100
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(script_with_fonts).expect("Script parsing should work");
assert!(script
.sections()
.iter()
.any(|section| matches!(section, Section::Fonts(_))));
}
#[test]
fn test_graphics_section_parsing() {
let script_with_graphics = r"
[Script Info]
Title: Test
[Graphics]
filename: logo.png
0
89504E470D0A1A0A
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(script_with_graphics).expect("Script parsing should work");
assert!(script
.sections()
.iter()
.any(|section| matches!(section, Section::Graphics(_))));
}
#[test]
fn test_unknown_section_with_suggestions() {
let script_with_typo = r"
[Script Info]
Title: Test
[Event]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(script_with_typo).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.severity, IssueSeverity::Warning)));
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.severity, IssueSeverity::Info)));
}
#[test]
fn test_skip_to_next_section_suggestions() {
let script_style_suggestion = r"
[Unknown Section]
Style: Default,Arial,20,&Hffffff,&H0,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(script_style_suggestion).expect("Script parsing should work");
assert!(script.issues().iter().any(|issue| {
matches!(issue.severity, IssueSeverity::Info) && issue.message.contains("V4+ Styles")
}));
let script_dialogue_suggestion = r"
[Wrong Events]
Dialogue: 0:00:00.00,0:00:05.00,Default,Test text
";
let script = Script::parse(script_dialogue_suggestion).expect("Script parsing should work");
assert!(script.issues().iter().any(|issue| {
matches!(issue.severity, IssueSeverity::Info) && issue.message.contains("Events")
}));
let script_title_suggestion = r"
[Bad Section]
Title: My Subtitle File
";
let script = Script::parse(script_title_suggestion).expect("Script parsing should work");
assert!(script.issues().iter().any(|issue| {
matches!(issue.severity, IssueSeverity::Info) && issue.message.contains("Script Info")
}));
}
#[test]
fn test_abrupt_file_ending() {
let truncated_script = "[Script Info]\nTitle: Test\n[Events";
let script = Script::parse(truncated_script).expect("Script parsing should work");
assert!(script
.issues()
.iter()
.any(|issue| matches!(issue.severity, IssueSeverity::Error)));
}
#[test]
fn test_empty_section_name() {
let empty_section = "[]";
let script = Script::parse(empty_section).expect("Script parsing should work");
assert!(!script.issues().is_empty());
}
#[test]
fn test_whitespace_only_section_name() {
let whitespace_section = "[ \t ]";
let script = Script::parse(whitespace_section).expect("Script parsing should work");
assert!(!script.issues().is_empty());
}
#[test]
fn test_multiple_unknown_sections() {
let multi_unknown = r"
[Unknown1]
Some content
[Unknown2]
More content
[Unknown3]
Even more content
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(multi_unknown).expect("Script parsing should work");
assert!(
script
.issues()
.iter()
.filter(|issue| {
matches!(issue.severity, IssueSeverity::Warning)
&& issue.message.contains("Unknown section")
})
.count()
>= 3
);
}
#[test]
fn test_empty_section_content() {
let empty_content = r"
[Script Info]
[Events]
Format: Start, End, Style, Text
Dialogue: 0:00:00.00,0:00:05.00,Default,Test
";
let script = Script::parse(empty_content).expect("Script parsing should work");
assert!(!script.sections().is_empty());
}
}