Files
MetaCrate/tests/compat/tests/current_outfit_policy_semantics.rs

416 lines
19 KiB
Rust

// Exact Rust translations of the CompositeCurrentOutfitPolicyTests fixture in
// LibreMetaverse.Tests/CurrentOutfitFolderTests.cs.
// Source SHA-256: 07177fff27fbee0d0714286c652253985add3a9401a50e8d7a829ce6c2b3b7a86
use libremetaverse::appearance::{CompositeCurrentOutfitPolicy, ICurrentOutfitPolicy};
use libremetaverse::{InventoryItem, InventoryWearable};
use libremetaverse_compat_tests::block_on;
use libremetaverse_types::compat::{CancellationToken, CancellationTokenSource};
use libremetaverse_types::{AssetType, Error, UUID};
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
struct TestPolicy {
allow_attach: AtomicBool,
allow_detach: AtomicBool,
attach_calls: AtomicUsize,
detach_calls: AtomicUsize,
report_calls: AtomicUsize,
last_added: Mutex<Option<Arc<Vec<InventoryItem>>>>,
last_removed: Mutex<Option<Arc<Vec<InventoryItem>>>>,
}
impl TestPolicy {
fn new() -> Self {
Self {
allow_attach: AtomicBool::new(true),
allow_detach: AtomicBool::new(true),
attach_calls: AtomicUsize::new(0),
detach_calls: AtomicUsize::new(0),
report_calls: AtomicUsize::new(0),
last_added: Mutex::new(None),
last_removed: Mutex::new(None),
}
}
}
impl ICurrentOutfitPolicy for TestPolicy {
fn can_attach(&self, _item: Option<Arc<InventoryItem>>) -> Result<bool, Error> {
self.attach_calls.fetch_add(1, Ordering::SeqCst);
Ok(self.allow_attach.load(Ordering::SeqCst))
}
fn can_detach(&self, _item: Option<Arc<InventoryItem>>) -> Result<bool, Error> {
self.detach_calls.fetch_add(1, Ordering::SeqCst);
Ok(self.allow_detach.load(Ordering::SeqCst))
}
fn report_item_change(
&self,
added: Option<Arc<Vec<InventoryItem>>>,
removed: Option<Arc<Vec<InventoryItem>>>,
_cancellation_token: Option<CancellationToken>,
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + '_>> {
self.report_calls.fetch_add(1, Ordering::SeqCst);
*self.last_added.lock().unwrap() = added;
*self.last_removed.lock().unwrap() = removed;
Box::pin(async { Ok(()) })
}
}
fn item() -> Arc<InventoryItem> {
let mut wearable = InventoryWearable::new(UUID::random().unwrap()).unwrap();
wearable.base.set_asset_type(AssetType::Clothing);
wearable.base.base.set_name("Test Item".into());
Arc::new(wearable.base)
}
fn as_policy(policy: &Arc<TestPolicy>) -> Arc<dyn ICurrentOutfitPolicy> {
policy.clone()
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_WithValidPolicy_ReturnsThis::test 31c1829eaa2b82b498cc1eaea131d2e34d1a0b5620be24f4da853c62c49807cc translated
#[test]
fn add_policy_with_valid_policy_returns_this() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let policy = Arc::new(TestPolicy::new());
let result = composite.add_policy(Some(as_policy(&policy))).unwrap();
assert!(std::ptr::eq(
std::ptr::from_ref(result),
std::ptr::from_ref(&composite)
));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_WithNullPolicy_ThrowsArgumentNullException::test 6a111f0ad2e91c8566b5459939e3ecc76282cd033bb8d6483e1672c36891e597 translated
#[test]
fn add_policy_with_null_policy_throws_argument_null_exception() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
assert!(matches!(
composite.add_policy(None),
Err(Error::ArgumentNull)
));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_MultiplePolicies_AllAreConsulted::test 1767ee3dee6b07e71f10b52613962e211fcf9906a45d12188f517cf751896f24 translated
#[test]
fn add_policy_multiple_policies_all_are_consulted() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_attach.store(true, Ordering::SeqCst);
second.allow_attach.store(true, Ordering::SeqCst);
let result = composite.can_attach(Some(item())).unwrap();
assert_eq!(first.attach_calls.load(Ordering::SeqCst), 1);
assert_eq!(second.attach_calls.load(Ordering::SeqCst), 1);
assert!(result);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddPolicy_SamePolicyTwice_OnlyAddsOnce::test e3d1340780680bfc46267194ddb3c47e4ae24299cf532d92dd71d9770f1955fa translated
#[test]
fn add_policy_same_policy_twice_only_adds_once() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let policy = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&policy))).unwrap();
composite.add_policy(Some(as_policy(&policy))).unwrap();
policy.allow_attach.store(true, Ordering::SeqCst);
composite.can_attach(Some(item())).unwrap();
assert_eq!(policy.attach_calls.load(Ordering::SeqCst), 1);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithAddedPolicy_RemovesPolicy::test 9dd407dceeacdcc60fccb0d9869ad261d90acd74ae4257331e0cf8d4be2ee603 translated
#[test]
fn remove_policy_with_added_policy_removes_policy() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let policy = Arc::new(TestPolicy::new());
let policy_dyn = as_policy(&policy);
composite.add_policy(Some(Arc::clone(&policy_dyn))).unwrap();
composite.remove_policy(Some(policy_dyn)).unwrap();
policy.allow_attach.store(false, Ordering::SeqCst);
assert!(composite.can_attach(Some(item())).unwrap());
assert_eq!(policy.attach_calls.load(Ordering::SeqCst), 0);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithNonAddedPolicy_DoesNotThrow::test 9fa18bd628f6f02912168309e8e2afb6346a73a235dcc16cc81bc35e2dcf766f translated
#[test]
fn remove_policy_with_non_added_policy_does_not_throw() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
composite
.remove_policy(Some(Arc::new(TestPolicy::new())))
.unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.RemovePolicy_WithNull_DoesNotThrow::test 28075d5f7c5458f1ccdf9981970e212e1663a9e5ebee65fce636a11a22390bfb translated
#[test]
fn remove_policy_with_null_does_not_throw() {
CompositeCurrentOutfitPolicy::new()
.unwrap()
.remove_policy(None)
.unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithNoPolicies_ReturnsTrue::test 74bc06bb6cedfe84a3cd7e5b4c32b251ee6eff81c272c7d899436dbd7f21886b translated
#[test]
fn can_attach_with_no_policies_returns_true() {
assert!(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.can_attach(Some(item()))
.unwrap()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithNullItem_ThrowsArgumentNullException::test a47ebf9c89b603c10109c6e643de5043ba13cd2eaf5f04d73ab1fd5f40e9b332 translated
#[test]
fn can_attach_with_null_item_throws_argument_null_exception() {
assert!(matches!(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.can_attach(None),
Err(Error::ArgumentNull)
));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithAllPoliciesAllowing_ReturnsTrue::test 32c2f5dcc5cbdf24e4205c25c833aaa3c0f203aeb8ff20c30fdac1264f12f6bd translated
#[test]
fn can_attach_with_all_policies_allowing_returns_true() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_attach.store(true, Ordering::SeqCst);
second.allow_attach.store(true, Ordering::SeqCst);
assert!(composite.can_attach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithOnePolicyDenying_ReturnsFalse::test f38d910142a563bded6d35b969c0c560ffa072fd8df073a15fbbbed745681d0d translated
#[test]
fn can_attach_with_one_policy_denying_returns_false() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_attach.store(true, Ordering::SeqCst);
second.allow_attach.store(false, Ordering::SeqCst);
assert!(!composite.can_attach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WithAllPoliciesDenying_ReturnsFalse::test 6cdda22d59a3cd690a187b597a7c8a2474e43c27902e737f15f0c71adc5bcf7c translated
#[test]
fn can_attach_with_all_policies_denying_returns_false() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_attach.store(false, Ordering::SeqCst);
second.allow_attach.store(false, Ordering::SeqCst);
assert!(!composite.can_attach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithNoPolicies_ReturnsTrue::test fe53b90f59fd9a73c4af1b7071f04cae0a7b623d6a50a3d8eb2fcae82e6872e9 translated
#[test]
fn can_detach_with_no_policies_returns_true() {
assert!(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.can_detach(Some(item()))
.unwrap()
);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithNullItem_ThrowsArgumentNullException::test 8a39f9c6e26f3a136de641f38507db144f731aed0861edbeafcfdb70306ecffb translated
#[test]
fn can_detach_with_null_item_throws_argument_null_exception() {
assert!(matches!(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.can_detach(None),
Err(Error::ArgumentNull)
));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithAllPoliciesAllowing_ReturnsTrue::test 2cbe1f0a5907c1dfa01af4329fa64177463798ec6caa357c8da8f5d3291901bf translated
#[test]
fn can_detach_with_all_policies_allowing_returns_true() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_detach.store(true, Ordering::SeqCst);
second.allow_detach.store(true, Ordering::SeqCst);
assert!(composite.can_detach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithOnePolicyDenying_ReturnsFalse::test 11896040dd8e4b5bbd7d85a1da126c648ad77eb349a73edf1e18fa5710cde1c9 translated
#[test]
fn can_detach_with_one_policy_denying_returns_false() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_detach.store(true, Ordering::SeqCst);
second.allow_detach.store(false, Ordering::SeqCst);
assert!(!composite.can_detach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanDetach_WithAllPoliciesDenying_ReturnsFalse::test 62b8478f593f2bf16b44267d80668e327a245d0b5c0386e7229a5857d669ae6c translated
#[test]
fn can_detach_with_all_policies_denying_returns_false() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
first.allow_detach.store(false, Ordering::SeqCst);
second.allow_detach.store(false, Ordering::SeqCst);
assert!(!composite.can_detach(Some(item())).unwrap());
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNoPolicies_CompletesSuccessfully::test e3f6824512503bea532826b9a9e4f7bb5abdea53f962fbbc6fdae299a6fe44a0 translated
#[test]
fn report_item_change_with_no_policies_completes_successfully() {
let added = Arc::new(vec![Arc::try_unwrap(item()).ok().unwrap()]);
let removed = Arc::new(Vec::new());
block_on(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.report_item_change(Some(added), Some(removed), None),
)
.unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithMultiplePolicies_CallsAllPolicies::test 4797cc1851d78364553dac59a6f9a894107fa04fb3fddfb52ed0e4fb5b4bcd1a translated
#[test]
fn report_item_change_with_multiple_policies_calls_all_policies() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let first = Arc::new(TestPolicy::new());
let second = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&first))).unwrap();
composite.add_policy(Some(as_policy(&second))).unwrap();
let added = Arc::new(vec![Arc::try_unwrap(item()).ok().unwrap()]);
let removed = Arc::new(Vec::new());
block_on(composite.report_item_change(
Some(Arc::clone(&added)),
Some(Arc::clone(&removed)),
None,
))
.unwrap();
assert_eq!(first.report_calls.load(Ordering::SeqCst), 1);
assert_eq!(second.report_calls.load(Ordering::SeqCst), 1);
assert!(Arc::ptr_eq(
first.last_added.lock().unwrap().as_ref().unwrap(),
&added
));
assert!(Arc::ptr_eq(
second.last_added.lock().unwrap().as_ref().unwrap(),
&added
));
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithCancelledToken_ThrowsOperationCanceledException::test e4eaf9c1ee2d55a3e6c7bca634ff1af2a0e19b7560ea616984d258df46ec89fd translated
#[test]
fn report_item_change_with_cancelled_token_throws_operation_cancelled_exception() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
composite
.add_policy(Some(Arc::new(TestPolicy::new())))
.unwrap();
let source = CancellationTokenSource::new();
source.cancel();
let added = Arc::new(vec![Arc::try_unwrap(item()).ok().unwrap()]);
let error = block_on(composite.report_item_change(
Some(added),
Some(Arc::new(Vec::new())),
Some(source.token()),
))
.unwrap_err();
assert_eq!(error, Error::Cancelled);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithEmptyLists_DoesNotThrow::test 6ba7e22fb7425e523f301612f7d7f6aed88e7d422050a70137d93b4a8004ae50 translated
#[test]
fn report_item_change_with_empty_lists_does_not_throw() {
let composite = CompositeCurrentOutfitPolicy::new().unwrap();
let policy = Arc::new(TestPolicy::new());
composite.add_policy(Some(as_policy(&policy))).unwrap();
block_on(composite.report_item_change(
Some(Arc::new(Vec::new())),
Some(Arc::new(Vec::new())),
None,
))
.unwrap();
assert_eq!(policy.report_calls.load(Ordering::SeqCst), 1);
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.ReportItemChangeAsync_WithNullLists_DoesNotThrowFromComposite::test c512c54f78c46fb4774d9b5c9c1057627592e1c25267811cddf3d687d3d1ec4d translated
#[test]
fn report_item_change_with_null_lists_does_not_throw_from_composite() {
block_on(
CompositeCurrentOutfitPolicy::new()
.unwrap()
.report_item_change(None, None, None),
)
.unwrap();
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.AddRemovePolicy_ConcurrentAccess_DoesNotThrow::test a73a01065867d90305f491e41d9ea61bc27b80c026672ed920368877bfb0fe14 translated
#[test]
fn add_remove_policy_concurrent_access_does_not_throw() {
let composite = Arc::new(CompositeCurrentOutfitPolicy::new().unwrap());
let policies: Vec<Arc<dyn ICurrentOutfitPolicy>> = (0..10)
.map(|_| Arc::new(TestPolicy::new()) as Arc<dyn ICurrentOutfitPolicy>)
.collect();
let mut threads = Vec::new();
for policy in &policies {
let composite = Arc::clone(&composite);
let policy = Arc::clone(policy);
threads.push(std::thread::spawn(move || {
composite.add_policy(Some(policy)).map(|_| ())
}));
}
for policy in policies {
let composite = Arc::clone(&composite);
threads.push(std::thread::spawn(move || {
composite.remove_policy(Some(policy))
}));
}
for thread in threads {
thread.join().unwrap().unwrap();
}
}
// parity-case: LibreMetaverse.Tests/CurrentOutfitFolderTests.cs::CompositeCurrentOutfitPolicyTests.CanAttach_WhileModifyingPolicies_DoesNotThrow::test 697999feeb776fae9a47cb9678c0ae0234d3b3831ce40c0e6f50fe622a5cc2b7 translated
#[test]
fn can_attach_while_modifying_policies_does_not_throw() {
let composite = Arc::new(CompositeCurrentOutfitPolicy::new().unwrap());
let stop = Arc::new(AtomicBool::new(false));
let read_composite = Arc::clone(&composite);
let read_stop = Arc::clone(&stop);
let test_item = item();
let reader = std::thread::spawn(move || {
while !read_stop.load(Ordering::SeqCst) {
let _ = read_composite.can_attach(Some(Arc::clone(&test_item)));
}
});
let write_composite = Arc::clone(&composite);
let writer = std::thread::spawn(move || {
for _ in 0..100 {
let policy: Arc<dyn ICurrentOutfitPolicy> = Arc::new(TestPolicy::new());
write_composite
.add_policy(Some(Arc::clone(&policy)))
.unwrap();
write_composite.remove_policy(Some(policy)).unwrap();
}
});
writer.join().unwrap();
stop.store(true, Ordering::SeqCst);
reader.join().unwrap();
}