unbundle 5.2.0

Unbundle media files - extract still frames, audio tracks, and subtitles from video files
Documentation
//! Stream probing integration tests.
//!
//! These tests require the fixture files generated by
//! `tests/fixtures/generate_fixtures.sh` (or `.bat` on Windows).

use std::path::Path;

use unbundle::{MediaFile, MediaProbe, UnbundleError};

fn sample_video_path() -> &'static str {
    "tests/fixtures/sample_video.mp4"
}

fn sample_audio_only_path() -> &'static str {
    "tests/fixtures/sample_audio_only.mp4"
}

#[test]
fn probe_returns_metadata() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        eprintln!("Skipping: fixture '{path}' not found.");
        return;
    }

    let metadata = MediaProbe::probe(path).expect("Failed to probe");
    assert!(metadata.video.is_some(), "Expected video metadata");
    assert!(metadata.audio.is_some(), "Expected audio metadata");
    assert!(!metadata.format.is_empty(), "Expected format name");
}

#[test]
fn probe_matches_unbundler_metadata() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        eprintln!("Skipping: fixture '{path}' not found.");
        return;
    }

    let probed = MediaProbe::probe(path).expect("Failed to probe");
    let unbundler = unbundle::MediaFile::open(path).expect("Failed to open");
    let direct = unbundler.metadata();

    assert_eq!(probed.format, direct.format);
    assert_eq!(probed.duration, direct.duration);

    let probed_video = probed.video.as_ref().unwrap();
    let direct_video = direct.video.as_ref().unwrap();
    assert_eq!(probed_video.width, direct_video.width);
    assert_eq!(probed_video.height, direct_video.height);
    assert_eq!(probed_video.frame_count, direct_video.frame_count);
}

#[test]
fn probe_audio_only_file() {
    let path = sample_audio_only_path();
    if !Path::new(path).exists() {
        eprintln!("Skipping: fixture '{path}' not found.");
        return;
    }

    let metadata = MediaProbe::probe(path).expect("Failed to probe");
    assert!(metadata.video.is_none(), "Audio-only file has no video");
    assert!(metadata.audio.is_some(), "Expected audio metadata");
}

#[test]
fn probe_nonexistent_file() {
    let result = MediaProbe::probe("nonexistent_file.xyz");
    assert!(result.is_err(), "Should error on nonexistent file");
    match result.unwrap_err() {
        UnbundleError::FileOpen { .. } => {}
        other => panic!("Expected FileOpen, got: {other:?}"),
    }
}

#[test]
fn probe_many_mixed_results() {
    let valid_path = sample_video_path();
    if !Path::new(valid_path).exists() {
        eprintln!("Skipping: fixture '{valid_path}' not found.");
        return;
    }

    let results = MediaProbe::probe_many(&[valid_path, "nonexistent.xyz"]);
    assert_eq!(results.len(), 2);
    assert!(results[0].is_ok(), "Valid file should probe successfully");
    assert!(results[1].is_err(), "Invalid file should error");
}

#[test]
fn probe_many_all_valid() {
    let path1 = sample_video_path();
    let path2 = sample_audio_only_path();
    if !Path::new(path1).exists() || !Path::new(path2).exists() {
        eprintln!("Skipping: fixtures not found.");
        return;
    }

    let results = MediaProbe::probe_many(&[path1, path2]);
    assert!(results.iter().all(|r| r.is_ok()), "All should succeed");
}

#[test]
fn media_file_probe_only_matches_media_probe() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        eprintln!("Skipping: fixture '{path}' not found.");
        return;
    }

    let via_media_file = MediaFile::probe_only(path).expect("probe_only failed");
    let via_probe = MediaProbe::probe(path).expect("MediaProbe::probe failed");

    assert_eq!(via_media_file.format, via_probe.format);
    assert_eq!(via_media_file.duration, via_probe.duration);
}