use super::gpu_backend::*;
use serial_test::serial;
#[test]
#[serial(gpu)]
fn test_gpu_available_check() {
let _ = GpuAccelerator::is_available();
}
#[test]
#[serial(gpu)]
fn test_gpu_accelerator_creation() {
let gpu = GpuAccelerator::new();
if gpu.is_some() {
println!("GPU available for testing");
} else {
println!("No GPU available, skipping GPU tests");
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_empty_input() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu
.batch_cosine_similarity(&[], &[1.0, 0.0, 0.0], 3)
.unwrap();
assert!(results.is_empty());
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_identical_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![1.0, 0.0, 0.0];
let results = gpu.batch_cosine_similarity(&vectors, &query, 3).unwrap();
assert_eq!(results.len(), 1);
assert!(
(results[0] - 1.0).abs() < 0.01,
"Expected ~1.0, got {}",
results[0]
);
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_orthogonal_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![0.0, 1.0, 0.0];
let results = gpu.batch_cosine_similarity(&vectors, &query, 3).unwrap();
assert_eq!(results.len(), 1);
assert!(results[0].abs() < 0.01, "Expected ~0.0, got {}", results[0]);
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_multiple_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![
1.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.0, 0.0, 0.0, ];
let results = gpu.batch_cosine_similarity(&vectors, &query, 3).unwrap();
assert_eq!(results.len(), 3);
assert!((results[0] - 1.0).abs() < 0.01, "Expected ~1.0");
assert!(results[1].abs() < 0.01, "Expected ~0.0");
assert!((results[2] + 1.0).abs() < 0.01, "Expected ~-1.0");
}
}
#[test]
#[serial(gpu)]
fn test_batch_euclidean_empty_input() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_euclidean_distance(&[], &[1.0, 0.0, 0.0], 3);
assert!(results.is_empty());
}
}
#[test]
#[serial(gpu)]
fn test_batch_euclidean_identical_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 2.0, 3.0];
let vectors = vec![1.0, 2.0, 3.0];
let results = gpu.batch_euclidean_distance(&vectors, &query, 3);
assert_eq!(results.len(), 1);
assert!(results[0].abs() < 0.01, "Expected ~0.0, got {}", results[0]);
}
}
#[test]
#[serial(gpu)]
fn test_batch_euclidean_known_distance() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![0.0, 0.0, 0.0];
let vectors = vec![3.0, 4.0, 0.0];
let results = gpu.batch_euclidean_distance(&vectors, &query, 3);
assert_eq!(results.len(), 1);
assert!(
(results[0] - 5.0).abs() < 0.01,
"Expected ~5.0, got {}",
results[0]
);
}
}
#[test]
#[serial(gpu)]
fn test_batch_dot_product_empty_input() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_dot_product(&[], &[1.0, 0.0, 0.0], 3);
assert!(results.is_empty());
}
}
#[test]
#[serial(gpu)]
fn test_batch_dot_product_orthogonal() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![0.0, 1.0, 0.0];
let results = gpu.batch_dot_product(&vectors, &query, 3);
assert_eq!(results.len(), 1);
assert!(results[0].abs() < 0.01, "Expected ~0.0, got {}", results[0]);
}
}
#[test]
#[serial(gpu)]
fn test_batch_dot_product_parallel() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![2.0, 3.0, 4.0];
let vectors = vec![2.0, 3.0, 4.0];
let results = gpu.batch_dot_product(&vectors, &query, 3);
assert_eq!(results.len(), 1);
assert!(
(results[0] - 29.0).abs() < 0.01,
"Expected ~29.0, got {}",
results[0]
);
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_zero_dimension() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_cosine_similarity(&[1.0, 2.0, 3.0], &[1.0], 0);
let values = results.expect("zero-dimension cosine must return Ok(empty)");
assert!(values.is_empty(), "Zero dimension should return empty");
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_dimension_mismatch() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0];
let vectors = vec![1.0, 0.0, 0.0]; let results = gpu.batch_cosine_similarity(&vectors, &query, 3);
let values = results.expect("dimension mismatch should not panic");
assert_eq!(values.len(), 1);
}
}
#[test]
#[serial(gpu)]
fn test_batch_euclidean_empty_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_euclidean_distance(&[], &[1.0, 2.0, 3.0], 3);
assert!(results.is_empty(), "Empty vectors should return empty");
}
}
#[test]
#[serial(gpu)]
fn test_batch_dot_product_single_element() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![3.0];
let vectors = vec![4.0];
let results = gpu.batch_dot_product(&vectors, &query, 1);
assert_eq!(results.len(), 1);
assert!(
(results[0] - 12.0).abs() < 0.01,
"Expected 3*4=12, got {}",
results[0]
);
}
}
#[test]
#[serial(gpu)]
fn test_batch_cosine_large_batch() {
if let Some(gpu) = GpuAccelerator::new() {
let dim = 8;
let num_vectors = 1024;
let query: Vec<f32> = vec![1.0; dim];
let vectors: Vec<f32> = vec![1.0; dim * num_vectors];
let results = gpu.batch_cosine_similarity(&vectors, &query, dim);
let values = results.expect("large cosine batch should succeed");
assert_eq!(values.len(), num_vectors);
for (i, &r) in values.iter().enumerate() {
assert!((r - 1.0).abs() < 0.05, "Vector {i}: expected ~1.0, got {r}");
}
}
}
#[test]
#[serial(gpu)]
fn test_gpu_no_panic_on_edge_inputs() {
if let Some(gpu) = GpuAccelerator::new() {
let cases: Vec<(&[f32], &[f32], usize)> = vec![
(&[], &[], 0),
(&[1.0], &[1.0], 1),
(&[f32::NAN], &[1.0], 1),
(&[f32::INFINITY], &[1.0], 1),
(&[f32::NEG_INFINITY], &[1.0], 1),
(&[0.0; 1024], &[0.0; 8], 8), ];
for (vectors, query, dim) in cases {
let _ = gpu.batch_cosine_similarity(vectors, query, dim);
let _ = gpu.batch_euclidean_distance(vectors, query, dim);
let _ = gpu.batch_dot_product(vectors, query, dim);
}
}
}
#[test]
#[serial(gpu)]
fn test_gpu_cosine_zero_norm_vectors() {
if let Some(gpu) = GpuAccelerator::new() {
let query = vec![1.0, 0.0, 0.0];
let vectors = vec![0.0, 0.0, 0.0];
let results = gpu.batch_cosine_similarity(&vectors, &query, 3);
let values = results.expect("zero-norm cosine should return Ok");
assert_eq!(values.len(), 1);
assert!(
values[0].is_finite(),
"Zero-norm cosine should be finite, got {}",
values[0]
);
}
}
#[test]
#[serial(gpu)]
fn test_gpu_euclidean_zero_dimension() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_euclidean_distance(&[1.0], &[1.0], 0);
assert!(results.is_empty(), "Zero dimension should return empty");
}
}
#[test]
#[serial(gpu)]
fn test_gpu_dot_product_zero_dimension() {
if let Some(gpu) = GpuAccelerator::new() {
let results = gpu.batch_dot_product(&[1.0], &[1.0], 0);
assert!(results.is_empty(), "Zero dimension should return empty");
}
}