mod common;
use rust_dix::*;
use std::sync::Arc;
#[test]
fn scope_resolves_scoped() {
let p = ServiceCollection::new()
.scoped(|_| Arc::new(common::MyService { value: 10 }))
.build()
.unwrap();
let scope = p.scope();
let svc: Arc<common::MyService> = scope.get().unwrap();
assert_eq!(svc.value, 10);
}
#[test]
fn scope_get_optional_and_get_unwrap() {
let p = ServiceCollection::new()
.scoped(|_| Arc::new(common::MyService { value: 20 }))
.build()
.unwrap();
let scope = p.scope();
let opt: Option<Arc<common::MyService>> = scope.get_optional();
assert_eq!(opt.unwrap().value, 20);
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _: Arc<common::Logger> = scope.get().unwrap();
}));
assert!(result.is_err());
}
#[test]
fn scope_get_all_returns_empty() {
let p = ServiceCollection::new().build().unwrap();
let scope = p.scope();
let all: Vec<Arc<common::MyService>> = scope.get_all();
assert!(all.is_empty());
}
#[test]
fn scope_get_named_any_delegates_to_parent() {
let p = ServiceCollection::new().build().unwrap();
p.register_named("scope_test", Arc::new(common::MyService { value: 5 }));
let scope = p.scope();
let retrieved = scope.get_named_any("scope_test");
assert!(retrieved.is_some());
}
#[test]
fn scoped_cached_within_same_scope() {
static COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
use std::sync::atomic::Ordering;
let p = ServiceCollection::new()
.scoped(|_| {
COUNTER.fetch_add(1, Ordering::SeqCst);
Arc::new(common::MyService { value: 1 })
})
.build()
.unwrap();
let scope = p.scope();
let _a = scope.get::<common::MyService>().unwrap();
let _b = scope.get::<common::MyService>().unwrap();
assert_eq!(COUNTER.load(Ordering::SeqCst), 1);
}
#[test]
fn scope_transient_scoped_dependency_binds_to_child_scope() {
static SCOPED_CALLS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
use std::sync::atomic::Ordering;
struct ScopedDep(u64);
struct TransientSvc {
scoped: Arc<ScopedDep>,
}
let provider = ServiceCollection::new()
.scoped(|_| {
let n = SCOPED_CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedDep(n))
})
.transient(|r| {
let any = r.get_any(std::any::type_name::<ScopedDep>()).unwrap();
let outer = any.downcast::<Arc<ScopedDep>>().unwrap();
let s: Arc<ScopedDep> = Arc::clone(&*outer);
Arc::new(TransientSvc { scoped: s })
})
.build()
.unwrap();
let scope1 = provider.scope();
let t1: Arc<TransientSvc> = scope1.get().unwrap();
let t1_again: Arc<TransientSvc> = scope1.get().unwrap();
assert!(
Arc::ptr_eq(&t1.scoped, &t1_again.scoped),
"同一子 scope 内 Transient 的 Scoped 依赖应复用同一实例"
);
let scope2 = provider.scope();
let t2: Arc<TransientSvc> = scope2.get().unwrap();
assert!(
!Arc::ptr_eq(&t1.scoped, &t2.scoped),
"不同子 scope 的 Transient 的 Scoped 依赖应独立(绑定子 scope 而非根)"
);
assert_eq!(t1.scoped.0, 0, "scope1 的 Scoped 应为序号 0");
assert_eq!(t2.scoped.0, 1, "scope2 的 Scoped 应为序号 1");
let root_s: Arc<ScopedDep> = provider.get().unwrap();
assert!(
!Arc::ptr_eq(&t1.scoped, &root_s),
"子 scope 的 Scoped 不应泄漏到根 root_scoped_cache"
);
assert!(
!Arc::ptr_eq(&t2.scoped, &root_s),
"子 scope 的 Scoped 不应泄漏到根 root_scoped_cache"
);
assert_eq!(root_s.0, 2, "根 scope 的 Scoped 应为序号 2");
}
#[test]
fn scope_drop_with_concurrent_resolution() {
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Barrier;
use std::thread;
static RESOLVE_COUNT: AtomicUsize = AtomicUsize::new(0);
static DISPOSE_COUNT: AtomicUsize = AtomicUsize::new(0);
struct ScopedSvc;
let provider = ServiceCollection::new()
.scoped(|r| {
let p = r.provider_arc().unwrap();
p.register_disposable(Box::new(|| {
DISPOSE_COUNT.fetch_add(1, Ordering::SeqCst);
}));
RESOLVE_COUNT.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedSvc)
})
.build()
.unwrap();
let scope = provider.scope();
let barrier = Arc::new(Barrier::new(5));
let mut handles = Vec::new();
for _ in 0..4 {
let scope_clone = scope.scope_provider().clone();
let barrier_clone = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
barrier_clone.wait();
let _: Arc<ScopedSvc> = scope_clone.get().unwrap();
}));
}
barrier.wait();
let _: Arc<ScopedSvc> = scope.get().unwrap();
for h in handles {
h.join().unwrap();
}
assert!(RESOLVE_COUNT.load(Ordering::SeqCst) >= 1, "解析应成功完成");
scope.dispose();
assert!(
DISPOSE_COUNT.load(Ordering::SeqCst) >= 1,
"dispose 应执行已注册的清理闭包"
);
}
#[test]
fn scope_factory_injected_into_singleton() {
use std::sync::atomic::{AtomicUsize, Ordering};
static SCOPE_CALLS: AtomicUsize = AtomicUsize::new(0);
struct ScopedReq(usize);
let provider = ServiceCollection::new()
.scoped(|_| {
let n = SCOPE_CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedReq(n))
})
.build()
.unwrap();
let provider_with_factory = ServiceCollection::new()
.scoped(|_| {
let n = SCOPE_CALLS.fetch_add(1, Ordering::SeqCst);
Arc::new(ScopedReq(n))
})
.singleton::<dyn ScopeFactory>(move |_| provider.clone() as Arc<dyn ScopeFactory>)
.build()
.unwrap();
let sf: Arc<dyn ScopeFactory> = provider_with_factory.get().unwrap();
let scope1 = sf.create_scope();
let req1: Arc<ScopedReq> = scope1.get().unwrap();
let scope2 = sf.create_scope();
let req2: Arc<ScopedReq> = scope2.get().unwrap();
assert!(
!Arc::ptr_eq(&req1, &req2),
"不同 scope 的 Scoped 服务应独立"
);
assert_eq!(req1.0, 0, "scope1 的 Scoped 应为序号 0");
assert_eq!(req2.0, 1, "scope2 的 Scoped 应为序号 1");
}