#[cfg(test)]
mod commands_capture_tests {
use crabcamera::commands::capture::{
capture_photo_sequence, capture_single_photo, capture_with_quality_retry,
capture_with_reconnect, get_capture_stats, get_or_create_camera, reconnect_camera,
release_camera, save_frame_compressed, save_frame_to_disk, start_camera_preview,
stop_camera_preview, CaptureStats, FramePool,
};
use crabcamera::tests::{set_mock_camera_mode, MockCaptureMode};
use crabcamera::types::{CameraFormat, CameraFrame};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::time::timeout;
fn create_test_frame() -> CameraFrame {
let test_data = vec![255u8; 640 * 480 * 3]; CameraFrame::new(test_data, 640, 480, "test_device".to_string())
}
#[tokio::test]
async fn test_capture_single_photo_success() {
set_mock_camera_mode("0", MockCaptureMode::Success);
let result = capture_single_photo(None, None).await;
assert!(result.is_ok(), "Single photo capture should succeed");
let frame = result.unwrap();
assert!(frame.width > 0, "Frame should have positive width");
assert!(frame.height > 0, "Frame should have positive height");
assert!(!frame.data.is_empty(), "Frame data should not be empty");
assert_eq!(frame.device_id, "0", "Should use default device ID");
}
#[tokio::test]
async fn test_capture_single_photo_with_device_id() {
set_mock_camera_mode("test_camera_1", MockCaptureMode::Success);
let result = capture_single_photo(Some("test_camera_1".to_string()), None).await;
assert!(
result.is_ok(),
"Single photo capture with device ID should succeed"
);
let frame = result.unwrap();
assert_eq!(
frame.device_id, "test_camera_1",
"Should use specified device ID"
);
}
#[tokio::test]
async fn test_capture_single_photo_with_format() {
set_mock_camera_mode("test_camera_format", MockCaptureMode::Success);
let format = CameraFormat::new(1920, 1080, 60.0);
let result =
capture_single_photo(Some("test_camera_format".to_string()), Some(format)).await;
assert!(
result.is_ok(),
"Single photo capture with format should succeed"
);
}
#[tokio::test]
async fn test_capture_single_photo_failure() {
set_mock_camera_mode("fail_camera", MockCaptureMode::Failure);
let result = capture_single_photo(Some("fail_camera".to_string()), None).await;
assert!(
result.is_err(),
"Single photo capture should fail with Failure mode"
);
let error = result.unwrap_err();
assert!(
error.contains("Failed to capture frame"),
"Error should mention capture failure"
);
}
#[tokio::test]
async fn test_capture_photo_sequence_success() {
set_mock_camera_mode("seq_camera", MockCaptureMode::Success);
let result = capture_photo_sequence("seq_camera".to_string(), 3, 50, None).await;
assert!(result.is_ok(), "Photo sequence capture should succeed");
let frames = result.unwrap();
assert_eq!(frames.len(), 3, "Should capture exactly 3 frames");
for (i, frame) in frames.iter().enumerate() {
assert_eq!(
frame.device_id, "seq_camera",
"Frame {} should have correct device ID",
i
);
assert!(
frame.width > 0 && frame.height > 0,
"Frame {} should have valid dimensions",
i
);
}
}
#[tokio::test]
async fn test_capture_photo_sequence_invalid_count() {
let result = capture_photo_sequence("test".to_string(), 0, 50, None).await;
assert!(result.is_err(), "Should fail with count 0");
assert!(result.unwrap_err().contains("Invalid photo count"));
let result = capture_photo_sequence("test".to_string(), 25, 50, None).await;
assert!(result.is_err(), "Should fail with count > 20");
assert!(result.unwrap_err().contains("Invalid photo count"));
}
#[tokio::test]
async fn test_capture_photo_sequence_with_failure() {
set_mock_camera_mode("seq_fail", MockCaptureMode::Failure);
let result = capture_photo_sequence("seq_fail".to_string(), 2, 50, None).await;
assert!(
result.is_err(),
"Photo sequence should fail if capture fails"
);
let error = result.unwrap_err();
assert!(
error.contains("Failed to capture frame"),
"Error should mention capture failure"
);
}
#[tokio::test]
async fn test_capture_photo_sequence_timing() {
set_mock_camera_mode("seq_timing", MockCaptureMode::Success);
let start = std::time::Instant::now();
let result = capture_photo_sequence("seq_timing".to_string(), 3, 100, None).await;
let duration = start.elapsed();
assert!(result.is_ok(), "Sequence capture should succeed");
assert!(
duration.as_millis() >= 200,
"Should respect interval timing"
);
}
#[tokio::test]
async fn test_start_camera_preview() {
set_mock_camera_mode("preview_start", MockCaptureMode::Success);
let result = start_camera_preview("preview_start".to_string(), None).await;
assert!(result.is_ok(), "Starting preview should succeed");
let message = result.unwrap();
assert!(
message.contains("Preview started"),
"Should return success message"
);
assert!(
message.contains("preview_start"),
"Should mention device ID"
);
}
#[tokio::test]
async fn test_start_camera_preview_with_format() {
set_mock_camera_mode("preview_format", MockCaptureMode::Success);
let format = CameraFormat::new(1280, 720, 30.0);
let result = start_camera_preview("preview_format".to_string(), Some(format)).await;
assert!(
result.is_ok(),
"Starting preview with format should succeed"
);
}
#[tokio::test]
async fn test_stop_camera_preview_success() {
set_mock_camera_mode("preview_stop", MockCaptureMode::Success);
let _ = start_camera_preview("preview_stop".to_string(), None).await;
let result = stop_camera_preview("preview_stop".to_string()).await;
assert!(result.is_ok(), "Stopping preview should succeed");
let message = result.unwrap();
assert!(
message.contains("Preview stopped"),
"Should return success message"
);
assert!(message.contains("preview_stop"), "Should mention device ID");
}
#[tokio::test]
async fn test_stop_camera_preview_not_active() {
let result = stop_camera_preview("nonexistent_preview".to_string()).await;
assert!(result.is_err(), "Should fail to stop non-existent preview");
let error = result.unwrap_err();
assert!(
error.contains("No active camera found"),
"Should mention camera not found"
);
}
#[tokio::test]
async fn test_release_camera_success() {
set_mock_camera_mode("release_test", MockCaptureMode::Success);
let _ = start_camera_preview("release_test".to_string(), None).await;
let result = release_camera("release_test".to_string()).await;
assert!(result.is_ok(), "Releasing camera should succeed");
let message = result.unwrap();
assert!(
message.contains("released"),
"Should return success message"
);
assert!(message.contains("release_test"), "Should mention device ID");
}
#[tokio::test]
async fn test_release_camera_not_active() {
let result = release_camera("nonexistent_release".to_string()).await;
assert!(
result.is_ok(),
"Releasing non-existent camera should not error"
);
let message = result.unwrap();
assert!(
message.contains("No active camera found"),
"Should mention camera not found"
);
}
#[tokio::test]
async fn test_get_capture_stats_active_camera() {
set_mock_camera_mode("stats_test", MockCaptureMode::Success);
let _ = start_camera_preview("stats_test".to_string(), None).await;
let result = get_capture_stats("stats_test".to_string()).await;
assert!(result.is_ok(), "Getting stats should succeed");
let stats = result.unwrap();
assert_eq!(stats.device_id, "stats_test");
assert!(stats.is_active, "Camera should be active");
assert!(
stats.device_info.is_some(),
"Should have device info for active camera"
);
}
#[tokio::test]
async fn test_get_capture_stats_inactive_camera() {
let result = get_capture_stats("stats_inactive".to_string()).await;
assert!(
result.is_ok(),
"Getting stats for inactive camera should succeed"
);
let stats = result.unwrap();
assert_eq!(stats.device_id, "stats_inactive");
assert!(!stats.is_active, "Camera should not be active");
assert!(
stats.device_info.is_none(),
"Should have no device info for inactive camera"
);
}
#[tokio::test]
async fn test_save_frame_to_disk() {
let frame = create_test_frame();
let temp_file = std::env::temp_dir().join("test_frame_save.bin");
let file_path = temp_file.to_string_lossy().to_string();
let result = save_frame_to_disk(frame, file_path.clone()).await;
assert!(result.is_ok(), "Saving frame to disk should succeed");
let message = result.unwrap();
assert!(
message.contains("Frame saved to"),
"Should return success message"
);
assert!(temp_file.exists(), "File should have been created");
let _ = tokio::fs::remove_file(temp_file).await;
}
#[tokio::test]
async fn test_save_frame_to_disk_invalid_path() {
let frame = create_test_frame();
#[cfg(windows)]
let invalid_path = "Z:\\nonexistent\\path\\with<>invalid|chars\\test.bin";
#[cfg(not(windows))]
let invalid_path = "/nonexistent/root/path/that/does/not/exist/deeply/nested/test.bin";
let result = save_frame_to_disk(frame, invalid_path.to_string()).await;
assert!(result.is_err(), "Should fail with invalid path");
let error = result.unwrap_err();
assert!(
error.contains("Failed to save frame"),
"Should mention save failure"
);
}
#[tokio::test]
async fn test_save_frame_compressed() {
let frame = create_test_frame();
let temp_file = std::env::temp_dir().join("test_frame_compressed.jpg");
let file_path = temp_file.to_string_lossy().to_string();
let result = save_frame_compressed(frame, file_path.clone(), Some(90)).await;
assert!(result.is_ok(), "Saving compressed frame should succeed");
let message = result.unwrap();
assert!(
message.contains("Compressed frame saved"),
"Should return success message"
);
assert!(
temp_file.exists(),
"Compressed file should have been created"
);
let _ = tokio::fs::remove_file(temp_file).await;
}
#[tokio::test]
async fn test_save_frame_compressed_default_quality() {
let frame = create_test_frame();
let temp_file = std::env::temp_dir().join("test_frame_default_quality.jpg");
let file_path = temp_file.to_string_lossy().to_string();
let result = save_frame_compressed(frame, file_path, None).await;
assert!(
result.is_ok(),
"Saving compressed frame with default quality should succeed"
);
let _ = tokio::fs::remove_file(temp_file).await;
}
#[tokio::test]
async fn test_get_or_create_camera() {
set_mock_camera_mode("get_create_test", MockCaptureMode::Success);
let format = CameraFormat::new(640, 480, 30.0);
let result1 = get_or_create_camera("get_create_test".to_string(), format.clone()).await;
assert!(result1.is_ok(), "First get_or_create should succeed");
let result2 = get_or_create_camera("get_create_test".to_string(), format).await;
assert!(result2.is_ok(), "Second get_or_create should succeed");
let camera1 = result1.unwrap();
let camera2 = result2.unwrap();
assert!(
Arc::ptr_eq(&camera1, &camera2),
"Should return same camera instance"
);
}
#[tokio::test]
async fn test_frame_pool_operations() {
let pool = FramePool::new(3, 1024);
let buffer1 = pool.get_buffer().await;
let buffer2 = pool.get_buffer().await;
let buffer3 = pool.get_buffer().await;
let buffer4 = pool.get_buffer().await;
assert_eq!(buffer1.capacity(), 1024);
assert_eq!(buffer2.capacity(), 1024);
assert_eq!(buffer3.capacity(), 1024);
assert_eq!(buffer4.capacity(), 1024);
pool.return_buffer(buffer1).await;
pool.return_buffer(buffer2).await;
pool.return_buffer(buffer3).await;
pool.return_buffer(buffer4).await;
let buffer5 = pool.get_buffer().await;
assert_eq!(buffer5.capacity(), 1024);
}
#[tokio::test]
async fn test_capture_stats_serialization() {
let stats = CaptureStats {
device_id: "test_device".to_string(),
is_active: true,
device_info: Some("Test Camera Info".to_string()),
};
let serialized = serde_json::to_string(&stats);
assert!(serialized.is_ok(), "Should serialize successfully");
let json = serialized.unwrap();
let deserialized: Result<CaptureStats, _> = serde_json::from_str(&json);
assert!(deserialized.is_ok(), "Should deserialize successfully");
let restored_stats = deserialized.unwrap();
assert_eq!(restored_stats.device_id, stats.device_id);
assert_eq!(restored_stats.is_active, stats.is_active);
assert_eq!(restored_stats.device_info, stats.device_info);
}
#[tokio::test]
async fn test_concurrent_camera_operations() {
set_mock_camera_mode("concurrent_test", MockCaptureMode::Success);
let mut handles = vec![];
for i in 0..5 {
let device_id = format!("concurrent_test_{}", i);
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let handle = tokio::spawn(async move {
let _ = capture_single_photo(Some(device_id.clone()), None).await;
let _ = start_camera_preview(device_id.clone(), None).await;
let _ = get_capture_stats(device_id.clone()).await;
let _ = release_camera(device_id).await;
i });
handles.push(handle);
}
for (i, handle) in handles.into_iter().enumerate() {
let result = handle.await.unwrap();
assert_eq!(
result, i,
"Concurrent operation {} should complete successfully",
i
);
}
}
#[tokio::test]
async fn test_error_recovery() {
set_mock_camera_mode("error_recovery", MockCaptureMode::Failure);
let result1 = capture_single_photo(Some("error_recovery".to_string()), None).await;
assert!(result1.is_err(), "Should fail in failure mode");
set_mock_camera_mode("error_recovery", MockCaptureMode::Success);
let result2 = capture_single_photo(Some("error_recovery".to_string()), None).await;
assert!(result2.is_ok(), "Should succeed in success mode");
}
#[tokio::test]
async fn test_camera_lifecycle() {
let device_id = "lifecycle_test".to_string();
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let result = start_camera_preview(device_id.clone(), None).await;
assert!(result.is_ok(), "Should start preview");
let result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(result.is_ok(), "Should capture photo");
let result = get_capture_stats(device_id.clone()).await;
assert!(result.is_ok(), "Should get stats");
let stats = result.unwrap();
assert!(stats.is_active, "Camera should be active");
let result = stop_camera_preview(device_id.clone()).await;
assert!(result.is_ok(), "Should stop preview");
let result = release_camera(device_id.clone()).await;
assert!(result.is_ok(), "Should release camera");
let result = get_capture_stats(device_id).await;
assert!(result.is_ok(), "Should get stats");
let stats = result.unwrap();
assert!(!stats.is_active, "Camera should no longer be active");
}
#[tokio::test]
async fn test_quality_retry_success() {
set_mock_camera_mode("quality_success", MockCaptureMode::Success);
let result = capture_with_quality_retry(
Some("quality_success".to_string()),
Some(5), Some(0.5), None,
)
.await;
assert!(result.is_ok(), "Quality retry should succeed");
let frame = result.unwrap();
assert_eq!(frame.device_id, "quality_success");
assert!(
frame.width > 0 && frame.height > 0,
"Frame should have valid dimensions"
);
}
#[tokio::test]
async fn test_quality_retry_high_threshold() {
set_mock_camera_mode("quality_high_threshold", MockCaptureMode::Success);
let result = capture_with_quality_retry(
Some("quality_high_threshold".to_string()),
Some(3), Some(0.99), None,
)
.await;
assert!(
result.is_ok(),
"Should return best frame even if threshold not met"
);
let frame = result.unwrap();
assert_eq!(frame.device_id, "quality_high_threshold");
}
#[tokio::test]
async fn test_quality_retry_failure_mode() {
set_mock_camera_mode("quality_failure", MockCaptureMode::Failure);
let result = capture_with_quality_retry(
Some("quality_failure".to_string()),
Some(3),
Some(0.5),
None,
)
.await;
assert!(result.is_err(), "Should fail when camera always fails");
let error = result.unwrap_err();
println!("Actual error: {}", error);
assert!(
error.contains("Failed to capture")
|| error.contains("quality")
|| error.contains("attempt")
|| error.contains("Capture error"),
"Error should be descriptive: {}",
error
);
}
#[tokio::test]
async fn test_quality_retry_parameter_validation() {
set_mock_camera_mode("quality_params", MockCaptureMode::Success);
let result = capture_with_quality_retry(
Some("quality_params".to_string()),
Some(100), Some(1.5), None,
)
.await;
assert!(
result.is_ok(),
"Should handle parameter clamping gracefully"
);
}
#[tokio::test]
async fn test_capture_with_reconnect_success() {
set_mock_camera_mode("reconnect_success", MockCaptureMode::Success);
let format = CameraFormat::new(640, 480, 30.0);
let result = capture_with_reconnect(
"reconnect_success".to_string(),
format,
3, )
.await;
assert!(result.is_ok(), "Capture with reconnect should succeed");
let frame = result.unwrap();
assert_eq!(frame.device_id, "reconnect_success");
}
#[tokio::test]
async fn test_capture_with_reconnect_failure_then_success() {
set_mock_camera_mode("reconnect_recovery", MockCaptureMode::Failure);
let format = CameraFormat::new(640, 480, 30.0);
let capture_handle = tokio::spawn(async move {
capture_with_reconnect("reconnect_recovery".to_string(), format, 3).await
});
tokio::time::sleep(Duration::from_millis(10)).await;
set_mock_camera_mode("reconnect_recovery", MockCaptureMode::Success);
let result = capture_handle.await.unwrap();
assert!(result.is_ok(), "Should succeed after recovery");
}
#[tokio::test]
async fn test_reconnect_camera_function() {
set_mock_camera_mode("reconnect_test", MockCaptureMode::Success);
let format = CameraFormat::new(1280, 720, 30.0);
let result = reconnect_camera(
"reconnect_test".to_string(),
format,
2, )
.await;
assert!(result.is_ok(), "Reconnect should succeed");
let camera = result.unwrap();
assert!(camera.lock().is_ok(), "Camera mutex should be accessible");
}
#[tokio::test]
async fn test_reconnect_camera_max_retries() {
set_mock_camera_mode("reconnect_test", MockCaptureMode::Failure);
let format = CameraFormat::new(640, 480, 30.0);
let result = reconnect_camera(
"reconnect_test".to_string(),
format,
2, )
.await;
assert!(
result.is_ok(),
"Reconnect should succeed (camera creation succeeds in mock)"
);
let capture_result = capture_single_photo(Some("reconnect_test".to_string()), None).await;
assert!(
capture_result.is_err(),
"Captures should fail with failure mode"
);
}
#[tokio::test]
async fn test_capture_timeout_scenarios() {
set_mock_camera_mode("timeout_test", MockCaptureMode::SlowCapture);
let start = Instant::now();
let result = timeout(
Duration::from_secs(5), capture_single_photo(Some("timeout_test".to_string()), None),
)
.await;
let duration = start.elapsed();
assert!(result.is_ok(), "Capture should complete within timeout");
let capture_result = result.unwrap();
assert!(
capture_result.is_ok(),
"Capture should succeed with slow mode"
);
assert!(
duration >= Duration::from_millis(100),
"Should take time in slow mode"
);
}
#[tokio::test]
async fn test_massive_concurrent_captures() {
let device_base = "massive_concurrent";
let num_cameras = 20;
let captures_per_camera = 5;
for i in 0..num_cameras {
let device_id = format!("{}_cam_{}", device_base, i);
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
}
let mut handles = Vec::new();
for cam_id in 0..num_cameras {
for cap_id in 0..captures_per_camera {
let device_id = format!("{}_cam_{}", device_base, cam_id);
let handle = tokio::spawn(async move {
let result = capture_single_photo(Some(device_id.clone()), None).await;
(cam_id, cap_id, device_id, result)
});
handles.push(handle);
}
}
let mut success_count = 0;
for handle in handles {
let (cam_id, cap_id, device_id, result) = handle.await.unwrap();
assert!(
result.is_ok(),
"Capture {}-{} should succeed for device {}",
cam_id,
cap_id,
device_id
);
if result.is_ok() {
success_count += 1;
}
}
let expected_total = num_cameras * captures_per_camera;
assert_eq!(success_count, expected_total, "All captures should succeed");
}
#[tokio::test]
async fn test_frame_pool_stress() {
let pool = Arc::new(FramePool::new(5, 2048));
let mut handles = Vec::new();
for i in 0..50 {
let pool_clone = pool.clone(); let handle = tokio::spawn(async move {
let buffer = pool_clone.get_buffer().await;
assert!(
buffer.capacity() >= 2048,
"Buffer should have correct capacity"
);
tokio::time::sleep(Duration::from_millis(1)).await;
pool_clone.return_buffer(buffer).await;
i });
handles.push(handle);
}
for handle in handles {
let operation_id = handle.await.unwrap();
assert!(
operation_id < 50,
"Operation {} should complete",
operation_id
);
}
}
#[tokio::test]
async fn test_camera_hot_unplug_simulation() {
let device_id = "hotplug_test".to_string();
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let preview_result = start_camera_preview(device_id.clone(), None).await;
assert!(preview_result.is_ok(), "Preview should start");
let capture_result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(capture_result.is_ok(), "Initial capture should work");
set_mock_camera_mode(&device_id, MockCaptureMode::Failure);
let capture_result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(capture_result.is_err(), "Capture should fail after unplug");
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let capture_result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(capture_result.is_ok(), "Capture should work after replug");
let _ = release_camera(device_id).await;
}
#[tokio::test]
async fn test_format_negotiation_edge_cases() {
let device_id = "format_negotiation".to_string();
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let edge_case_formats = vec![
CameraFormat::new(1, 1, 1.0), CameraFormat::new(7680, 4320, 120.0), CameraFormat::new(640, 480, 0.1), CameraFormat::new(1920, 1080, 240.0), ];
for (i, format) in edge_case_formats.into_iter().enumerate() {
let test_device_id = format!("{}_fmt_{}", device_id, i);
set_mock_camera_mode(&test_device_id, MockCaptureMode::Success);
let result =
capture_single_photo(Some(test_device_id.clone()), Some(format.clone())).await;
match result {
Ok(frame) => {
assert_eq!(frame.device_id, test_device_id);
}
Err(_) => {
}
}
}
}
#[tokio::test]
async fn test_resource_cleanup_verification() {
let base_device_id = "cleanup_verification";
for iteration in 0..20 {
let device_id = format!("{}_iter_{}", base_device_id, iteration);
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let preview_result = start_camera_preview(device_id.clone(), None).await;
assert!(
preview_result.is_ok(),
"Preview should start for iteration {}",
iteration
);
let stats_result = get_capture_stats(device_id.clone()).await;
assert!(stats_result.is_ok(), "Stats should be available");
let stats = stats_result.unwrap();
assert!(
stats.is_active,
"Camera should be active for iteration {}",
iteration
);
let release_result = release_camera(device_id.clone()).await;
assert!(
release_result.is_ok(),
"Release should succeed for iteration {}",
iteration
);
let stats_result = get_capture_stats(device_id).await;
assert!(
stats_result.is_ok(),
"Stats should still be available after release"
);
let stats = stats_result.unwrap();
assert!(
!stats.is_active,
"Camera should be inactive after release for iteration {}",
iteration
);
}
}
#[tokio::test]
async fn test_save_frame_format_detection() {
let frame = create_test_frame();
let test_cases = vec![
("test.png", "PNG"),
("test.jpg", "JPEG"),
("test.jpeg", "JPEG"),
("test.JPG", "JPEG"),
("test.JPEG", "JPEG"),
("test.unknown", "PNG"), ("test", "PNG"), ];
for (filename, expected_format) in test_cases {
let temp_file = std::env::temp_dir().join(filename);
let file_path = temp_file.to_string_lossy().to_string();
let result = save_frame_to_disk(frame.clone(), file_path.clone()).await;
assert!(
result.is_ok(),
"Save should succeed for format: {}",
expected_format
);
assert!(
temp_file.exists(),
"File should exist for format: {}",
expected_format
);
let _ = tokio::fs::remove_file(temp_file).await;
}
}
#[tokio::test]
async fn test_capture_sequence_interruption() {
set_mock_camera_mode("sequence_interrupt", MockCaptureMode::Success);
let sequence_handle = tokio::spawn(async {
capture_photo_sequence(
"sequence_interrupt".to_string(),
10, 100, None,
)
.await
});
tokio::time::sleep(Duration::from_millis(50)).await;
set_mock_camera_mode("sequence_interrupt", MockCaptureMode::Failure);
let result = sequence_handle.await.unwrap();
assert!(
result.is_err(),
"Sequence should be interrupted by camera failure"
);
}
#[tokio::test]
async fn test_camera_registry_isolation() {
let camera_ids = vec![
"isolation_cam_1".to_string(),
"isolation_cam_2".to_string(),
"isolation_cam_3".to_string(),
];
set_mock_camera_mode(&camera_ids[0], MockCaptureMode::Success);
set_mock_camera_mode(&camera_ids[1], MockCaptureMode::SlowCapture);
set_mock_camera_mode(&camera_ids[2], MockCaptureMode::Failure);
let camera_ids_clone1 = camera_ids.clone();
let handle1 = tokio::spawn(async move {
capture_single_photo(Some(camera_ids_clone1[0].clone()), None).await
});
let camera_ids_clone2 = camera_ids.clone();
let handle2 = tokio::spawn(async move {
capture_single_photo(Some(camera_ids_clone2[1].clone()), None).await
});
let camera_ids_clone3 = camera_ids.clone();
let handle3 = tokio::spawn(async move {
capture_single_photo(Some(camera_ids_clone3[2].clone()), None).await
});
let (result1, result2, result3) = tokio::join!(handle1, handle2, handle3);
assert!(result1.unwrap().is_ok(), "Camera 1 should succeed");
assert!(result2.unwrap().is_ok(), "Camera 2 should succeed (slow)");
assert!(result3.unwrap().is_err(), "Camera 3 should fail");
}
#[tokio::test]
async fn test_error_message_consistency() {
set_mock_camera_mode("error_msg_test", MockCaptureMode::Failure);
let result = capture_single_photo(Some("error_msg_test".to_string()), None).await;
assert!(result.is_err(), "Should fail for failing camera");
let error = result.unwrap_err();
assert!(!error.is_empty(), "Error message should not be empty");
assert!(
error.contains("Failed to capture frame"),
"Error should be descriptive"
);
let result = capture_photo_sequence("any".to_string(), 0, 100, None).await;
assert!(result.is_err(), "Should fail for invalid count");
let error = result.unwrap_err();
assert!(
error.contains("Invalid photo count"),
"Error should mention invalid count"
);
let result = capture_photo_sequence("any".to_string(), 25, 100, None).await;
assert!(result.is_err(), "Should fail for too many photos");
let error = result.unwrap_err();
assert!(
error.contains("Invalid photo count"),
"Error should mention invalid count"
);
}
#[tokio::test]
async fn test_mixed_operation_patterns() {
let device_id = "mixed_ops".to_string();
set_mock_camera_mode(&device_id, MockCaptureMode::Success);
let result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(result.is_ok(), "Single capture should work");
let result = start_camera_preview(device_id.clone(), None).await;
assert!(result.is_ok(), "Preview should start");
let result = capture_photo_sequence(device_id.clone(), 3, 10, None).await;
assert!(result.is_ok(), "Sequence should work with preview running");
let result = get_capture_stats(device_id.clone()).await;
assert!(result.is_ok(), "Stats should be available");
let result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(result.is_ok(), "Another single capture should work");
let result = stop_camera_preview(device_id.clone()).await;
assert!(result.is_ok(), "Should stop preview");
let result = capture_single_photo(Some(device_id.clone()), None).await;
assert!(result.is_ok(), "Final capture should work");
let result = release_camera(device_id).await;
assert!(result.is_ok(), "Should release camera");
}
}