use ass_core::{
analysis::styles::resolved_style::ResolvedStyle,
parser::{
ast::{Section, SectionType, Span, Style},
Script,
},
plugin::{tags::karaoke::create_karaoke_handlers, ExtensionRegistry, TagResult},
};
#[test]
fn test_v4plusplus_full_parsing() {
let script_text = r"[Script Info]
ScriptType: v4.00++
[V4++ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginT, MarginB, Encoding, RelativeTo
Style: Default,Arial,20,&HFFFFFF,&H0,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,10,10,15,25,1,0
Style: Title,Times New Roman,28,&HFF0000,&H0,&H0,&H0,1,0,0,0,110,110,2,0,1,3,1,5,20,20,30,35,1,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginT, MarginB, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,5,5,,{\kt500}Hello v4++
Dialogue: 1,0:00:05.00,0:00:10.00,Title,,0,0,10,15,,Advanced margins test
";
let script = Script::parse(script_text).unwrap();
if let Some(Section::Styles(styles)) = script.find_section(SectionType::Styles) {
let default_style = &styles[0];
assert_eq!(default_style.name, "Default");
assert_eq!(default_style.margin_l, "10");
assert_eq!(default_style.margin_r, "10");
assert_eq!(default_style.margin_v, ""); assert_eq!(default_style.margin_t, Some("15"));
assert_eq!(default_style.margin_b, Some("25"));
assert_eq!(default_style.relative_to, Some("0"));
let title_style = &styles[1];
assert_eq!(title_style.name, "Title");
assert_eq!(title_style.margin_t, Some("30"));
assert_eq!(title_style.margin_b, Some("35"));
assert_eq!(title_style.relative_to, Some("1"));
} else {
panic!("Styles section not found");
}
if let Some(Section::Events(events)) = script.find_section(SectionType::Events) {
let first_event = &events[0];
assert_eq!(first_event.margin_l, "0");
assert_eq!(first_event.margin_r, "0");
assert_eq!(first_event.margin_v, ""); assert_eq!(first_event.margin_t, Some("5"));
assert_eq!(first_event.margin_b, Some("5"));
assert!(first_event.text.contains("{\\kt500}"));
let second_event = &events[1];
assert_eq!(second_event.margin_t, Some("10"));
assert_eq!(second_event.margin_b, Some("15"));
} else {
panic!("Events section not found");
}
}
#[test]
fn test_v4plus_backward_compatibility() {
let script_text = r"[Script Info]
ScriptType: v4.00+
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,20,&HFFFFFF,&H0,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,10,10,15,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,10,,Hello v4+
";
let script = Script::parse(script_text).unwrap();
if let Some(Section::Styles(styles)) = script.find_section(SectionType::Styles) {
let style = &styles[0];
assert_eq!(style.name, "Default");
assert_eq!(style.margin_l, "10");
assert_eq!(style.margin_r, "10");
assert_eq!(style.margin_v, "15");
assert_eq!(style.margin_t, None); assert_eq!(style.margin_b, None); assert_eq!(style.relative_to, None); } else {
panic!("Styles section not found");
}
if let Some(Section::Events(events)) = script.find_section(SectionType::Events) {
let event = &events[0];
assert_eq!(event.margin_l, "0");
assert_eq!(event.margin_r, "0");
assert_eq!(event.margin_v, "10");
assert_eq!(event.margin_t, None); assert_eq!(event.margin_b, None); } else {
panic!("Events section not found");
}
}
#[test]
fn test_mixed_format_robustness() {
let script_text = r"[Script Info]
ScriptType: v4.00++
[V4++ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: OldStyle,Arial,16,&HFFFFFF,&H0,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,10,10,15,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,OldStyle,,0,0,10,,Mixed format test
";
let script = Script::parse(script_text).unwrap();
if let Some(Section::Styles(styles)) = script.find_section(SectionType::Styles) {
let style = &styles[0];
assert_eq!(style.name, "OldStyle");
assert_eq!(style.margin_v, "15"); assert_eq!(style.margin_t, None); assert_eq!(style.margin_b, None);
assert_eq!(style.relative_to, None);
} else {
panic!("Styles section not found");
}
}
#[test]
fn test_malformed_v4plusplus_lines() {
let script_text = r"[Script Info]
ScriptType: v4.00++
[V4++ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginT, Encoding
Style: IncompleteStyle,Arial,16,&HFFFFFF,&H0,&H0,&H0,0,0,0,0,100,100,0,0,1,2,0,2,10,10,20,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginT, Effect, Text
Dialogue: 0,0:00:00.00,0:00:05.00,IncompleteStyle,,0,0,5,,Incomplete margin format
";
let script = Script::parse(script_text).unwrap();
if let Some(Section::Styles(styles)) = script.find_section(SectionType::Styles) {
let style = &styles[0];
assert_eq!(style.margin_t, Some("20"));
assert_eq!(style.margin_b, None); assert_eq!(style.relative_to, None);
}
if let Some(Section::Events(events)) = script.find_section(SectionType::Events) {
let event = &events[0];
assert_eq!(event.margin_t, Some("5"));
assert_eq!(event.margin_b, None); }
}
#[test]
fn test_resolved_style_margin_logic() {
let v4plus_style = Style {
name: "V4Plus",
parent: None,
fontname: "Arial",
fontsize: "20",
primary_colour: "&H00FFFFFF",
secondary_colour: "&H000000FF",
outline_colour: "&H00000000",
back_colour: "&H80000000",
bold: "0",
italic: "0",
underline: "0",
strikeout: "0",
scale_x: "100",
scale_y: "100",
spacing: "0",
angle: "0",
border_style: "1",
outline: "2",
shadow: "0",
alignment: "2",
margin_l: "10",
margin_r: "10",
margin_v: "15",
margin_t: None,
margin_b: None,
encoding: "1",
relative_to: None,
span: Span::new(0, 0, 0, 0),
};
let resolved_v4plus = ResolvedStyle::from_style(&v4plus_style).unwrap();
assert_eq!(resolved_v4plus.margin_l(), 10);
assert_eq!(resolved_v4plus.margin_r(), 10);
assert_eq!(resolved_v4plus.margin_t(), 15); assert_eq!(resolved_v4plus.margin_b(), 15);
let v4plusplus_style = Style {
name: "V4PlusPlus",
parent: None,
fontname: "Arial",
fontsize: "20",
primary_colour: "&H00FFFFFF",
secondary_colour: "&H000000FF",
outline_colour: "&H00000000",
back_colour: "&H80000000",
bold: "0",
italic: "0",
underline: "0",
strikeout: "0",
scale_x: "100",
scale_y: "100",
spacing: "0",
angle: "0",
border_style: "1",
outline: "2",
shadow: "0",
alignment: "2",
margin_l: "10",
margin_r: "10",
margin_v: "0", margin_t: Some("20"),
margin_b: Some("25"),
encoding: "1",
relative_to: Some("1"),
span: Span::new(0, 0, 0, 0),
};
let resolved_v4plusplus = ResolvedStyle::from_style(&v4plusplus_style).unwrap();
assert_eq!(resolved_v4plusplus.margin_l(), 10);
assert_eq!(resolved_v4plusplus.margin_r(), 10);
assert_eq!(resolved_v4plusplus.margin_t(), 20); assert_eq!(resolved_v4plusplus.margin_b(), 25); }
#[test]
fn test_kt_tag_handler() {
let mut registry = ExtensionRegistry::new();
for handler in create_karaoke_handlers() {
registry.register_tag_handler(handler).unwrap();
}
assert!(registry.has_tag_handler("kt"));
let result = registry.process_tag("kt", "500");
assert!(matches!(result, Some(TagResult::Processed)));
let result = registry.process_tag("kt", "invalid");
assert!(matches!(result, Some(TagResult::Failed(_))));
let result = registry.process_tag("kt", "0");
assert!(matches!(result, Some(TagResult::Processed)));
let result = registry.process_tag("kt", "999999");
assert!(matches!(result, Some(TagResult::Processed)));
}
#[test]
fn test_v4plusplus_script_with_kt_tags() {
let script_text = r"[Script Info]
ScriptType: v4.00++
[V4++ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginT, MarginB, Encoding, RelativeTo
Style: Karaoke,Arial,24,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,1,0,0,0,100,100,0,0,1,2,0,2,10,10,20,20,1,0
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginT, MarginB, Effect, Text
Dialogue: 0,0:00:00.00,0:00:10.00,Karaoke,,0,0,0,0,,{\kt100}Ka{\kt150}ra{\kt200}o{\kt100}ke {\kt300}test
";
let script = Script::parse(script_text).unwrap();
if let Some(Section::Events(events)) = script.find_section(SectionType::Events) {
let event = &events[0];
assert!(event.text.contains("{\\kt100}"));
assert!(event.text.contains("{\\kt150}"));
assert!(event.text.contains("{\\kt200}"));
assert!(event.text.contains("{\\kt300}"));
assert_eq!(event.margin_t, Some("0"));
assert_eq!(event.margin_b, Some("0"));
} else {
panic!("Events section not found");
}
}