ass-core 0.1.2

High-performance ASS subtitle format parser and analyzer
Documentation
//! Tests for ASS time parsing and formatting.

use super::*;

#[test]
fn parse_ass_times() {
    assert_eq!(parse_ass_time("0:00:00.00").unwrap(), 0);
    assert_eq!(parse_ass_time("0:00:01.00").unwrap(), 100);
    assert_eq!(parse_ass_time("0:01:00.00").unwrap(), 6000);
    assert_eq!(parse_ass_time("1:00:00.00").unwrap(), 360_000);
    assert_eq!(parse_ass_time("0:01:30.50").unwrap(), 9050);
}

#[test]
fn parse_ass_times_invalid() {
    assert!(parse_ass_time("invalid").is_err());
    assert!(parse_ass_time("0:60:00.00").is_err()); // Invalid minutes
    assert!(parse_ass_time("0:00:60.00").is_err()); // Invalid seconds
    assert!(parse_ass_time("0:00:00.xx").is_err()); // Non-numeric fraction
}

#[test]
fn parse_ass_time_fractional_is_libass_faithful() {
    // libass reads the fractional field as an integer count of centiseconds
    // (`sscanf %d`, then `ms = field * 10`), ignoring the digit count and never
    // normalising. We replicate that exactly so event timing matches the player.
    assert_eq!(parse_ass_time("0:00:00.5").unwrap(), 5); // ".5" -> 5cs (NOT 50)
    assert_eq!(parse_ass_time("0:00:00.05").unwrap(), 5); // ".05" -> 5cs
    assert_eq!(parse_ass_time("0:00:00.50").unwrap(), 50); // ".50" -> 50cs
    assert_eq!(parse_ass_time("0:00:00.098").unwrap(), 98); // ".098" -> 98cs (NOT 9)
    assert_eq!(parse_ass_time("0:00:27.021").unwrap(), 2721); // 2700 + 21

    // A 3-digit field >= 100 is taken literally and rolls past the second,
    // exactly as libass's `field * 10` does (".100" -> 100cs = one second).
    assert_eq!(parse_ass_time("0:00:00.100").unwrap(), 100);
    assert_eq!(parse_ass_time("0:00:30.150").unwrap(), 3150);
}

#[test]
fn format_ass_times() {
    assert_eq!(format_ass_time(0), "0:00:00.00");
    assert_eq!(format_ass_time(100), "0:00:01.00");
    assert_eq!(format_ass_time(6000), "0:01:00.00");
    assert_eq!(format_ass_time(360_000), "1:00:00.00");
    assert_eq!(format_ass_time(9050), "0:01:30.50");
}

#[test]
fn parse_ass_time_edge_cases() {
    // Test maximum valid values
    assert!(parse_ass_time("23:59:59.99").is_ok());

    // Test zero padding variations
    assert_eq!(parse_ass_time("0:0:0.0").unwrap(), 0);
    assert_eq!(parse_ass_time("0:00:00.0").unwrap(), 0);
    assert_eq!(parse_ass_time("0:00:00.00").unwrap(), 0);

    // Test missing components
    assert!(parse_ass_time("0:00").is_err());
    assert!(parse_ass_time("0").is_err());
    assert!(parse_ass_time("").is_err());

    // Test extra components
    assert!(parse_ass_time("0:0:0:0.0").is_err());
    // Note: parse_ass_time("0:0:0.0.0") actually succeeds by taking first decimal part
    assert!(parse_ass_time("0:0:0.0.0").is_ok());

    // Test negative values
    assert!(parse_ass_time("-1:00:00.00").is_err());
    assert!(parse_ass_time("0:-1:00.00").is_err());
    assert!(parse_ass_time("0:00:-1.00").is_err());
    assert!(parse_ass_time("0:00:00.-1").is_err());

    // Test non-numeric values
    assert!(parse_ass_time("a:00:00.00").is_err());
    assert!(parse_ass_time("0:b:00.00").is_err());
    assert!(parse_ass_time("0:00:c.00").is_err());
    assert!(parse_ass_time("0:00:00.d").is_err());

    // Test boundary values that should fail
    assert!(parse_ass_time("0:60:00.00").is_err()); // 60 minutes
    assert!(parse_ass_time("0:00:60.00").is_err()); // 60 seconds
    assert_eq!(parse_ass_time("0:00:00.100").unwrap(), 100); // libass: ".100" -> 100cs
}

#[test]
fn format_ass_time_edge_cases() {
    // Test very large values
    assert_eq!(format_ass_time(u32::MAX), "11930:27:52.95");

    // Test boundary values
    assert_eq!(format_ass_time(99), "0:00:00.99");
    assert_eq!(format_ass_time(5999), "0:00:59.99");
    assert_eq!(format_ass_time(359_999), "0:59:59.99");

    // Test values requiring padding
    assert_eq!(format_ass_time(1), "0:00:00.01");
    assert_eq!(format_ass_time(10), "0:00:00.10");
    assert_eq!(format_ass_time(601), "0:00:06.01");
    assert_eq!(format_ass_time(3661), "0:00:36.61");
}