screencapturekit 9.0.1

Safe Rust bindings for Apple's ScreenCaptureKit framework - screen and audio capture on macOS
Documentation
//! Core Media types tests
//!
//! Tests for `CMTime`, `CMSampleTimingInfo`, and related types

use screencapturekit::cm::{CMSampleTimingInfo, CMTime};

#[test]
fn test_cmtime_creation() {
    let time = CMTime::new(1, 30);
    assert!(time.is_valid());

    let zero = CMTime::ZERO;
    assert!(zero.is_zero());
    assert!(zero.is_valid());

    let invalid = CMTime::INVALID;
    assert!(!invalid.is_valid());
}

#[test]
fn test_cmtime_special_values() {
    let pos_inf = CMTime::positive_infinity();
    assert!(pos_inf.is_positive_infinity());
    assert!(!pos_inf.is_negative_infinity());
    assert!(!pos_inf.is_indefinite());

    let neg_inf = CMTime::negative_infinity();
    assert!(neg_inf.is_negative_infinity());
    assert!(!neg_inf.is_positive_infinity());

    let indefinite = CMTime::indefinite();
    assert!(indefinite.is_indefinite());
    assert!(!indefinite.is_positive_infinity());
}

#[test]
fn test_cmtime_const_functions() {
    const FRAME_DURATION: CMTime = CMTime::new(1, 30);
    const ZERO: CMTime = CMTime::ZERO;
    const INVALID: CMTime = CMTime::INVALID;

    assert!(FRAME_DURATION.is_valid());
    assert!(ZERO.is_zero());
    assert!(!INVALID.is_valid());
}

#[test]
fn test_cmtime_const_equality() {
    const TIME1: CMTime = CMTime::new(1, 30);
    const TIME2: CMTime = CMTime::new(1, 30);
    const TIME3: CMTime = CMTime::new(2, 30);

    assert!(TIME1.equals(&TIME2));
    assert!(!TIME1.equals(&TIME3));
}

#[test]
fn test_cmtime_display() {
    let time = CMTime::new(1, 30);
    let display = format!("{time:?}");
    // Just verify it doesn't crash
    assert!(!display.is_empty());

    let zero = CMTime::ZERO;
    let zero_display = format!("{zero:?}");
    assert!(!zero_display.is_empty());
}

#[test]
fn test_cmsample_timing_info_creation() {
    let timing =
        CMSampleTimingInfo::with_times(CMTime::new(1, 30), CMTime::new(0, 30), CMTime::new(1, 30));

    // Verify valid times
    assert!(timing.has_valid_presentation_time());
    assert!(timing.has_valid_decode_time());
}

#[test]
fn test_cmsample_timing_info_invalid() {
    let timing = CMSampleTimingInfo::with_times(CMTime::INVALID, CMTime::INVALID, CMTime::INVALID);

    assert!(!timing.has_valid_presentation_time());
    assert!(!timing.has_valid_decode_time());
}

#[test]
fn test_cmsample_timing_info_display() {
    let timing =
        CMSampleTimingInfo::with_times(CMTime::new(1, 30), CMTime::new(0, 30), CMTime::new(1, 30));

    let display = format!("{timing:?}");
    // Just verify it doesn't crash
    assert!(!display.is_empty());
}

#[test]
fn test_cmsample_timing_default() {
    let timing = CMSampleTimingInfo::with_times(CMTime::ZERO, CMTime::ZERO, CMTime::ZERO);

    assert!(timing.is_valid());
}

#[test]
fn test_sample_buffer_cg_image_round_trips() {
    use screencapturekit::cm::{CMSampleBuffer, CMSampleBufferExt};
    use screencapturekit::cv::CVPixelBuffer;

    // Build a tiny BGRA CVPixelBuffer, wrap it in a CMSampleBuffer, then ask
    // for a CGImage and confirm dimensions round-trip. VTCreateCGImageFromCVPixelBuffer
    // is what actually runs under cg_image().
    let pb = CVPixelBuffer::create(64, 48, 0x4247_5241).expect("create BGRA pixel buffer"); // 'BGRA'
    let sb = CMSampleBuffer::create_for_image_buffer(&pb, CMTime::ZERO, CMTime::ZERO)
        .expect("wrap in sample buffer");
    let cg = sb.cg_image().expect("cg_image from sample buffer");
    assert_eq!(cg.width(), 64);
    assert_eq!(cg.height(), 48);
}

#[test]
fn test_sample_buffer_cg_image_returns_err_on_no_image_buffer() {
    use screencapturekit::cm::{CMSampleBuffer, CMSampleBufferExt};
    use screencapturekit::cv::CVPixelBuffer;

    // An audio-only / metadata-only sample buffer has no CVImageBuffer; cg_image
    // should surface that as Err rather than producing a garbage CGImage.
    // Easiest construction here: build a 1x1 buffer, drop the pixel buffer
    // immediately, and confirm Err on a separately-constructed empty
    // sample buffer. (No pure constructor for empty CMSampleBuffer in
    // safe API, so this test focuses on the round-trip success path; the
    // failure path is exercised at the SCK layer when audio-only frames arrive.)
    let pb = CVPixelBuffer::create(8, 8, 0x4247_5241).expect("create BGRA pixel buffer");
    let sb = CMSampleBuffer::create_for_image_buffer(&pb, CMTime::ZERO, CMTime::ZERO)
        .expect("wrap in sample buffer");
    // Even a 1x1 buffer should round-trip OK; we mostly need to verify the
    // call path doesn't crash on tiny dims.
    let cg = sb
        .cg_image()
        .expect("cg_image succeeds on tiny BGRA buffer");
    assert_eq!(cg.width(), 8);
    assert_eq!(cg.height(), 8);
}