use screencapturekit::cm::{CMSampleTimingInfo, CMTime};
extern "C" {
fn cm_test_copy_nsvalue_dirty_rects(
values: *const f64,
count: usize,
out_rects: *mut *mut std::ffi::c_void,
out_count: *mut usize,
) -> bool;
fn cm_sample_buffer_free_dirty_rects(rects: *mut std::ffi::c_void);
}
#[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:?}");
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));
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:?}");
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;
let pb = CVPixelBuffer::create(64, 48, 0x4247_5241).expect("create BGRA pixel buffer"); 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_image_sample_preserves_all_timing_fields() {
use screencapturekit::cm::{CMSampleBuffer, CMSampleBufferExt};
use screencapturekit::cv::CVPixelBuffer;
let pixel_buffer =
CVPixelBuffer::create(16, 16, 0x4247_5241).expect("create BGRA pixel buffer");
let presentation_time = CMTime {
value: 120,
timescale: 600,
flags: 3,
epoch: 7,
};
let duration = CMTime {
value: 10,
timescale: 600,
flags: 3,
epoch: 11,
};
let sample =
CMSampleBuffer::create_for_image_buffer(&pixel_buffer, presentation_time, duration)
.expect("create image sample");
let timing = sample.sample_timing_info(0).expect("sample timing info");
assert_eq!(timing.presentation_time_stamp, presentation_time);
assert_eq!(timing.duration, duration);
}
#[test]
fn test_sample_buffer_pixel_buffer_is_retained_and_specific() {
use screencapturekit::cm::{CMSampleBuffer, CMSampleBufferExt};
use screencapturekit::cv::CVPixelBuffer;
let pixel_buffer = {
let source = CVPixelBuffer::create(32, 24, 0x4247_5241).expect("create BGRA pixel buffer");
let sample = CMSampleBuffer::create_for_image_buffer(
&source,
CMTime::new(1, 60),
CMTime::new(1, 60),
)
.expect("create image sample");
sample.pixel_buffer().expect("specific pixel buffer")
};
assert_eq!(pixel_buffer.width(), 32);
assert_eq!(pixel_buffer.height(), 24);
}
#[test]
fn test_dirty_rect_parser_decodes_nsvalue_rectangles() {
let expected = [1.5, 2.5, 30.0, 40.0, 10.0, 20.0, 3.0, 4.0];
let mut rects = std::ptr::null_mut();
let mut count = 0;
let decoded = unsafe {
cm_test_copy_nsvalue_dirty_rects(
expected.as_ptr(),
expected.len() / 4,
&raw mut rects,
&raw mut count,
)
};
assert!(decoded);
assert_eq!(count, 2);
assert!(!rects.is_null());
let actual = unsafe { std::slice::from_raw_parts(rects.cast::<f64>(), expected.len()) };
for (actual, expected) in actual.iter().zip(expected) {
assert!((*actual - expected).abs() < f64::EPSILON);
}
unsafe { cm_sample_buffer_free_dirty_rects(rects) };
}
#[test]
fn test_sample_buffer_cg_image_returns_err_on_no_image_buffer() {
use screencapturekit::cm::{CMSampleBuffer, CMSampleBufferExt};
use screencapturekit::cv::CVPixelBuffer;
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");
let cg = sb
.cg_image()
.expect("cg_image succeeds on tiny BGRA buffer");
assert_eq!(cg.width(), 8);
assert_eq!(cg.height(), 8);
}