#![allow(clippy::unwrap_used, missing_docs)]
use oxigdal_gpu::pipeline_cache::{
PipelineCache, PipelineCacheKey, fnv1a_64, new_shared_pipeline_cache,
};
use std::sync::Arc;
#[test]
fn test_fnv1a_hash_different_strings_differ() {
let h_hello = fnv1a_64(b"hello");
let h_world = fnv1a_64(b"world");
assert_ne!(
h_hello, h_world,
"distinct strings must produce distinct FNV-1a hashes"
);
}
#[test]
fn test_fnv1a_hash_same_string_stable() {
let data = b"reproducible test string";
let first = fnv1a_64(data);
let second = fnv1a_64(data);
assert_eq!(first, second, "FNV-1a must be deterministic");
}
#[test]
fn test_fnv1a_hash_empty_slice() {
let expected: u64 = 0xcbf2_9ce4_8422_2325;
assert_eq!(fnv1a_64(b""), expected);
}
#[test]
fn test_fnv1a_hash_single_byte_sensitivity() {
assert_ne!(fnv1a_64(b"a"), fnv1a_64(b"b"));
}
#[test]
fn test_fnv1a_hash_prefix_sensitivity() {
assert_ne!(fnv1a_64(b"abc"), fnv1a_64(b"abcd"));
}
#[test]
fn test_fnv1a_hash_unicode_stability() {
let src = "привет мир"; let h1 = fnv1a_64(src.as_bytes());
let h2 = fnv1a_64(src.as_bytes());
assert_eq!(h1, h2);
}
#[test]
fn test_pipeline_cache_key_equality() {
let k1 = PipelineCacheKey::new("shader source", "main", "r-w");
let k2 = PipelineCacheKey::new("shader source", "main", "r-w");
assert_eq!(k1, k2, "identical arguments must produce equal keys");
}
#[test]
fn test_pipeline_cache_key_different_source() {
let k1 = PipelineCacheKey::new("shader_a", "main", "r-w");
let k2 = PipelineCacheKey::new("shader_b", "main", "r-w");
assert_ne!(k1, k2, "different source text must yield different keys");
}
#[test]
fn test_pipeline_cache_key_different_entry_point() {
let k1 = PipelineCacheKey::new("src", "entry_alpha", "r-w");
let k2 = PipelineCacheKey::new("src", "entry_beta", "r-w");
assert_ne!(k1, k2);
}
#[test]
fn test_pipeline_cache_key_different_layout_tag() {
let k1 = PipelineCacheKey::new("src", "main", "r-w");
let k2 = PipelineCacheKey::new("src", "main", "r-r-w");
assert_ne!(k1, k2);
}
#[test]
fn test_pipeline_cache_key_new_hashes_source() {
let src = "// wgsl source for hashing test";
let key = PipelineCacheKey::new(src, "cs_main", "r-w");
assert_eq!(
key.shader_hash,
fnv1a_64(src.as_bytes()),
"key.shader_hash must equal fnv1a_64 of the source bytes"
);
}
#[test]
fn test_pipeline_cache_key_clone_is_equal() {
let key = PipelineCacheKey::new("src", "main", "r-w");
assert_eq!(key.clone(), key);
}
#[test]
fn test_pipeline_cache_key_from_hash_constructor() {
let hash: u64 = 0xdead_beef_cafe_babe;
let key = PipelineCacheKey::from_hash(hash, "cs", "u-r-w");
assert_eq!(key.shader_hash, hash);
assert_eq!(key.entry_point, "cs");
assert_eq!(key.layout_tag, "u-r-w");
}
#[test]
fn test_pipeline_cache_key_display_contains_components() {
let key = PipelineCacheKey::from_hash(0x0123_4567_89ab_cdef, "cs_main", "r-w");
let s = format!("{}", key);
assert!(
s.contains("0123456789abcdef"),
"display must contain hex hash"
);
assert!(s.contains("cs_main"), "display must contain entry point");
assert!(s.contains("r-w"), "display must contain layout tag");
}
#[test]
fn test_pipeline_cache_len_empty() {
let cache = PipelineCache::new();
assert_eq!(cache.len(), 0, "new cache must report length 0");
assert!(cache.is_empty(), "new cache must be empty");
}
#[test]
fn test_pipeline_cache_clear_empties() {
let mut cache = PipelineCache::new();
cache.clear();
assert!(cache.is_empty(), "clear on empty cache must leave it empty");
assert_eq!(cache.len(), 0);
}
#[test]
fn test_pipeline_cache_evict_absent_key_returns_false() {
let mut cache = PipelineCache::new();
let key = PipelineCacheKey::new("src", "main", "r-w");
let removed = cache.evict(&key);
assert!(!removed, "evict on absent key must return false");
assert_eq!(cache.len(), 0);
}
#[test]
fn test_pipeline_cache_evict_specific_key() {
let mut cache = PipelineCache::new();
let key_a = PipelineCacheKey::new("src_a", "main", "r-w");
let key_b = PipelineCacheKey::new("src_b", "main", "r-w");
let _: Result<Arc<wgpu::ComputePipeline>, &str> =
cache.get_or_insert_with(key_a.clone(), || Err("no gpu"));
let _: Result<Arc<wgpu::ComputePipeline>, &str> =
cache.get_or_insert_with(key_b.clone(), || Err("no gpu"));
assert_eq!(cache.len(), 0);
assert!(!cache.evict(&key_a));
assert_eq!(cache.len(), 0);
}
#[test]
fn test_pipeline_cache_error_factory_not_cached() {
let mut cache = PipelineCache::new();
let key = PipelineCacheKey::new("shader", "ep", "r-r-w");
let result: Result<Arc<wgpu::ComputePipeline>, String> =
cache.get_or_insert_with(key.clone(), || Err("compilation failed".to_owned()));
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "compilation failed");
assert_eq!(cache.len(), 0, "failed factory must not pollute the cache");
}
#[test]
fn test_pipeline_cache_factory_called_on_miss() {
let mut cache = PipelineCache::new();
let key = PipelineCacheKey::new("src", "main", "r-w");
let mut call_count = 0u32;
let _: Result<Arc<wgpu::ComputePipeline>, &str> = cache.get_or_insert_with(key.clone(), || {
call_count += 1;
Err("no gpu")
});
assert_eq!(
call_count, 1,
"factory must be invoked exactly once on miss"
);
}
#[test]
fn test_pipeline_cache_retain_noop_on_empty() {
let mut cache = PipelineCache::new();
cache.retain(|_| false);
assert!(cache.is_empty());
}
#[test]
fn test_pipeline_cache_keys_iter_on_empty() {
let cache = PipelineCache::new();
let count = cache.keys().count();
assert_eq!(count, 0);
}
#[test]
fn test_shared_pipeline_cache_is_empty_on_creation() {
let shared = new_shared_pipeline_cache();
let guard = shared.lock().unwrap();
assert!(guard.is_empty());
assert_eq!(guard.len(), 0);
}
#[test]
fn test_shared_pipeline_cache_arc_ptr_equality() {
let cache = new_shared_pipeline_cache();
let cache2 = Arc::clone(&cache);
assert!(
Arc::ptr_eq(&cache, &cache2),
"clone must share the same allocation"
);
}
#[test]
fn test_shared_pipeline_cache_lock_and_evict() {
let shared = new_shared_pipeline_cache();
{
let mut guard = shared.lock().unwrap();
let key = PipelineCacheKey::new("src", "main", "r-w");
assert!(!guard.evict(&key));
}
let guard = shared.lock().unwrap();
assert!(guard.is_empty());
}
#[test]
fn test_shared_pipeline_cache_clear_via_lock() {
let shared = new_shared_pipeline_cache();
{
let mut guard = shared.lock().unwrap();
guard.clear();
}
let guard = shared.lock().unwrap();
assert!(guard.is_empty());
}
#[allow(dead_code)]
fn _assert_gpu_context_pipeline_cache_accessor_type(
ctx: &oxigdal_gpu::GpuContext,
) -> &oxigdal_gpu::SharedPipelineCache {
ctx.pipeline_cache()
}